1.1 --- a/ko-archetype-test/pom.xml Mon Jun 24 17:49:27 2013 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,48 +0,0 @@
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</groupId>
1.9 - <artifactId>html</artifactId>
1.10 - <version>0.4-SNAPSHOT</version>
1.11 - </parent>
1.12 - <groupId>org.apidesign.html</groupId>
1.13 - <artifactId>ko-archetype-test</artifactId>
1.14 - <version>0.4-SNAPSHOT</version>
1.15 - <name>Knockout 4 Java 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>0.4-SNAPSHOT</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 --- a/ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/ArchetypeVersionTest.java Mon Jun 24 17:49:27 2013 +0200
2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3 @@ -1,139 +0,0 @@
2.4 -/**
2.5 - * HTML via Java(tm) Language Bindings
2.6 - * Copyright (C) 2013 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. apidesign.org
2.16 - * designates this particular file as subject to the
2.17 - * "Classpath" exception as provided by apidesign.org
2.18 - * in the License file that accompanied this code.
2.19 - *
2.20 - * You should have received a copy of the GNU General Public License
2.21 - * along with this program. Look for COPYING file in the top folder.
2.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
2.23 - */
2.24 -package org.apidesign.html.archetype.test;
2.25 -
2.26 -import java.io.IOException;
2.27 -import java.net.URL;
2.28 -import javax.xml.XMLConstants;
2.29 -import javax.xml.parsers.DocumentBuilderFactory;
2.30 -import javax.xml.parsers.ParserConfigurationException;
2.31 -import javax.xml.xpath.XPathConstants;
2.32 -import javax.xml.xpath.XPathExpression;
2.33 -import javax.xml.xpath.XPathExpressionException;
2.34 -import javax.xml.xpath.XPathFactory;
2.35 -import javax.xml.xpath.XPathFactoryConfigurationException;
2.36 -import org.testng.annotations.Test;
2.37 -import static org.testng.Assert.*;
2.38 -import org.testng.annotations.BeforeClass;
2.39 -import org.w3c.dom.Document;
2.40 -import org.w3c.dom.NodeList;
2.41 -import org.xml.sax.SAXException;
2.42 -
2.43 -/**
2.44 - *
2.45 - * @author Jaroslav Tulach <jtulach@netbeans.org>
2.46 - */
2.47 -public class ArchetypeVersionTest {
2.48 - private String version;
2.49 -
2.50 - public ArchetypeVersionTest() {
2.51 - }
2.52 -
2.53 - @BeforeClass public void readCurrentVersion() throws Exception {
2.54 - version = findCurrentVersion();
2.55 - assertFalse(version.isEmpty(), "There should be some version string");
2.56 - }
2.57 -
2.58 -
2.59 - @Test public void testComparePomDepsVersions() throws Exception {
2.60 - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.61 - URL r = l.getResource("archetype-resources/pom.xml");
2.62 - assertNotNull(r, "Archetype pom found");
2.63 -
2.64 - final XPathFactory fact = XPathFactory.newInstance();
2.65 - XPathExpression xp2 = fact.newXPath().compile(
2.66 - "//properties/net.java.html.version/text()"
2.67 - );
2.68 -
2.69 - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.70 - String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
2.71 -
2.72 - assertEquals(arch, version, "net.java.html.json dependency needs to be on latest version");
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 -
2.89 - assertTrue(arch.matches("[0-9\\.]+"), "launcher version seems valid: " + arch);
2.90 - }
2.91 -
2.92 - @Test public void testCheckBck2Brwsr() throws Exception {
2.93 - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.94 - URL r = l.getResource("archetype-resources/pom.xml");
2.95 - assertNotNull(r, "Archetype pom found");
2.96 -
2.97 - final XPathFactory fact = XPathFactory.newInstance();
2.98 - XPathExpression xp2 = fact.newXPath().compile(
2.99 - "//properties/bck2brwsr.version/text()"
2.100 - );
2.101 -
2.102 - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.103 - String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
2.104 -
2.105 - assertTrue(arch.matches("[0-9\\.]+"), "bck2brwsr version seems valid: " + arch);
2.106 - }
2.107 -
2.108 - @Test public void testNbActions() throws Exception {
2.109 - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.110 - URL r = l.getResource("archetype-resources/nbactions.xml");
2.111 - assertNotNull(r, "Archetype nb file found");
2.112 -
2.113 - final XPathFactory fact = XPathFactory.newInstance();
2.114 - XPathExpression xp2 = fact.newXPath().compile(
2.115 - "//goal/text()"
2.116 - );
2.117 -
2.118 - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.119 - NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET);
2.120 -
2.121 - for (int i = 0; i < goals.getLength(); i++) {
2.122 - String s = goals.item(i).getTextContent();
2.123 - if (s.contains("apidesign")) {
2.124 - assertFalse(s.matches(".*apidesign.*[0-9].*"), "No numbers: " + s);
2.125 - }
2.126 - }
2.127 - }
2.128 -
2.129 - static String findCurrentVersion() throws XPathExpressionException, IOException, ParserConfigurationException, SAXException, XPathFactoryConfigurationException {
2.130 - final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.131 - URL u = l.getResource("META-INF/maven/org.apidesign.html/knockout4j-archetype/pom.xml");
2.132 - assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path"));
2.133 -
2.134 - final XPathFactory fact = XPathFactory.newInstance();
2.135 - fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
2.136 -
2.137 - XPathExpression xp = fact.newXPath().compile("project/version/text()");
2.138 -
2.139 - Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream());
2.140 - return xp.evaluate(dom);
2.141 - }
2.142 -}
3.1 --- a/ko-archetype-test/src/test/java/org/apidesign/html/archetype/test/VerifyArchetypeTest.java Mon Jun 24 17:49:27 2013 +0200
3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
3.3 @@ -1,119 +0,0 @@
3.4 -/**
3.5 - * HTML via Java(tm) Language Bindings
3.6 - * Copyright (C) 2013 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. apidesign.org
3.16 - * designates this particular file as subject to the
3.17 - * "Classpath" exception as provided by apidesign.org
3.18 - * in the License file that accompanied this code.
3.19 - *
3.20 - * You should have received a copy of the GNU General Public License
3.21 - * along with this program. Look for COPYING file in the top folder.
3.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
3.23 - */
3.24 -package org.apidesign.html.archetype.test;
3.25 -
3.26 -import java.io.File;
3.27 -import java.util.Properties;
3.28 -import java.util.zip.ZipFile;
3.29 -import org.apache.maven.it.Verifier;
3.30 -import org.testng.annotations.Test;
3.31 -import static org.testng.Assert.*;
3.32 -
3.33 -/**
3.34 - *
3.35 - * @author Jaroslav Tulach <jtulach@netbeans.org>
3.36 - */
3.37 -public class VerifyArchetypeTest {
3.38 - @Test public void fxBrwsrCompiles() throws Exception {
3.39 - final File dir = new File("target/tests/fxcompile/").getAbsoluteFile();
3.40 - generateFromArchetype(dir);
3.41 -
3.42 - File created = new File(dir, "o-a-test");
3.43 - assertTrue(created.isDirectory(), "Project created");
3.44 - assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
3.45 -
3.46 - Verifier v = new Verifier(created.getAbsolutePath());
3.47 - v.executeGoal("verify");
3.48 -
3.49 - v.verifyErrorFreeLog();
3.50 -
3.51 - for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
3.52 - if (l.contains("j2js")) {
3.53 - fail("No pre-compilaton:\n" + l);
3.54 - }
3.55 - }
3.56 -
3.57 - v.verifyTextInLog("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher");
3.58 - v.verifyTextInLog("fxcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-fxbrwsr.zip");
3.59 - }
3.60 -
3.61 - @Test public void bck2BrwsrCompiles() throws Exception {
3.62 - final File dir = new File("target/tests/b2bcompile/").getAbsoluteFile();
3.63 - generateFromArchetype(dir);
3.64 -
3.65 - File created = new File(dir, "o-a-test");
3.66 - assertTrue(created.isDirectory(), "Project created");
3.67 - assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
3.68 -
3.69 - Verifier v = new Verifier(created.getAbsolutePath());
3.70 - Properties sysProp = v.getSystemProperties();
3.71 - if (Boolean.getBoolean("java.awt.headless")) {
3.72 - sysProp.put("java.awt.headless", "true");
3.73 - }
3.74 - v.addCliOption("-Pbck2brwsr");
3.75 - v.executeGoal("verify");
3.76 -
3.77 - v.verifyErrorFreeLog();
3.78 -
3.79 - // does pre-compilation to JavaScript
3.80 - v.verifyTextInLog("j2js");
3.81 - // uses Bck2BrwsrLauncher
3.82 - v.verifyTextInLog("BaseHTTPLauncher stopServerAndBrwsr");
3.83 - // building zip:
3.84 - v.verifyTextInLog("b2bcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
3.85 -
3.86 - for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
3.87 - if (l.contains("fxbrwsr")) {
3.88 - fail("No fxbrwsr:\n" + l);
3.89 - }
3.90 - }
3.91 -
3.92 - File zip = new File(new File(created, "target"), "o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
3.93 - assertTrue(zip.isFile(), "Zip file with website was created");
3.94 -
3.95 - ZipFile zf = new ZipFile(zip);
3.96 - assertNotNull(zf.getEntry("public_html/index.html"), "index.html found");
3.97 - assertNotNull(zf.getEntry("public_html/twitterExample.css"), "css file found");
3.98 -
3.99 - }
3.100 -
3.101 - private Verifier generateFromArchetype(final File dir, String... params) throws Exception {
3.102 - Verifier v = new Verifier(dir.getAbsolutePath());
3.103 - v.setAutoclean(false);
3.104 - v.setLogFileName("generate.log");
3.105 - v.deleteDirectory("");
3.106 - dir.mkdirs();
3.107 - Properties sysProp = v.getSystemProperties();
3.108 - sysProp.put("groupId", "org.apidesign.test");
3.109 - sysProp.put("artifactId", "o-a-test");
3.110 - sysProp.put("package", "org.apidesign.test.oat");
3.111 - sysProp.put("archetypeGroupId", "org.apidesign.html");
3.112 - sysProp.put("archetypeArtifactId", "knockout4j-archetype");
3.113 - sysProp.put("archetypeVersion", ArchetypeVersionTest.findCurrentVersion());
3.114 -
3.115 - for (String p : params) {
3.116 - v.addCliOption(p);
3.117 - }
3.118 - v.executeGoal("archetype:generate");
3.119 - v.verifyErrorFreeLog();
3.120 - return v;
3.121 - }
3.122 -}
4.1 --- a/ko-archetype/pom.xml Mon Jun 24 17:49:27 2013 +0200
4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
4.3 @@ -1,58 +0,0 @@
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>html</artifactId>
4.9 - <groupId>org.apidesign</groupId>
4.10 - <version>0.4-SNAPSHOT</version>
4.11 - </parent>
4.12 - <groupId>org.apidesign.html</groupId>
4.13 - <artifactId>knockout4j-archetype</artifactId>
4.14 - <version>0.4-SNAPSHOT</version>
4.15 - <packaging>jar</packaging>
4.16 - <name>Knockout 4 Java 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 --- a/ko-archetype/src/main/java/org/apidesign/html/archetype/package-info.java Mon Jun 24 17:49:27 2013 +0200
5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
5.3 @@ -1,21 +0,0 @@
5.4 -/**
5.5 - * HTML via Java(tm) Language Bindings
5.6 - * Copyright (C) 2013 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. apidesign.org
5.16 - * designates this particular file as subject to the
5.17 - * "Classpath" exception as provided by apidesign.org
5.18 - * in the License file that accompanied this code.
5.19 - *
5.20 - * You should have received a copy of the GNU General Public License
5.21 - * along with this program. Look for COPYING file in the top folder.
5.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
5.23 - */
5.24 -package org.apidesign.html.archetype;
6.1 --- a/ko-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml Mon Jun 24 17:49:27 2013 +0200
6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
6.3 @@ -1,44 +0,0 @@
6.4 -<?xml version="1.0" encoding="UTF-8"?>
6.5 -<archetype-descriptor name="FX/Bck2Brwsr Example">
6.6 - <fileSets>
6.7 - <fileSet filtered="true" packaged="true">
6.8 - <directory>src/main/java</directory>
6.9 - <includes>
6.10 - <include>**/*.java</include>
6.11 - </includes>
6.12 - </fileSet>
6.13 - <fileSet filtered="true" packaged="true">
6.14 - <directory>src/main/resources</directory>
6.15 - <includes>
6.16 - <include>**/*.xhtml</include>
6.17 - <include>**/*.html</include>
6.18 - <include>**/*.css</include>
6.19 - </includes>
6.20 - </fileSet>
6.21 - <fileSet filtered="true" packaged="true">
6.22 - <directory>src/test/java</directory>
6.23 - <includes>
6.24 - <include>**/*Test.java</include>
6.25 - </includes>
6.26 - </fileSet>
6.27 - <fileSet filtered="true" packaged="false">
6.28 - <directory>src/main/assembly</directory>
6.29 - <includes>
6.30 - <include>**/*.xml</include>
6.31 - </includes>
6.32 - </fileSet>
6.33 - <fileSet filtered="false" packaged="false">
6.34 - <directory></directory>
6.35 - <includes>
6.36 - <include>nbactions*.xml</include>
6.37 - </includes>
6.38 - </fileSet>
6.39 - <fileSet filtered="true" packaged="false">
6.40 - <directory>assembly</directory>
6.41 - <includes>
6.42 - <include>fxbrwsr-assembly.xml</include>
6.43 - <include>bck2brwsr-assembly.xml</include>
6.44 - </includes>
6.45 - </fileSet>
6.46 - </fileSets>
6.47 -</archetype-descriptor>
6.48 \ No newline at end of file
7.1 --- a/ko-archetype/src/main/resources/archetype-resources/nbactions-bck2brwsr.xml Mon Jun 24 17:49:27 2013 +0200
7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
7.3 @@ -1,14 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/nbactions-fxbrwsr.xml Mon Jun 24 17:49:27 2013 +0200
8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
8.3 @@ -1,20 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/nbactions.xml Mon Jun 24 17:49:27 2013 +0200
9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
9.3 @@ -1,20 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/pom.xml Mon Jun 24 17:49:27 2013 +0200
10.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
10.3 @@ -1,266 +0,0 @@
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>${project.version}</net.java.html.version>
10.45 - <bck2brwsr.version>${bck2brwsr.version}</bck2brwsr.version>
10.46 - <bck2brwsr.launcher.version>${bck2brwsr.launcher.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.html</groupId>
10.256 - <artifactId>ko-bck2brwsr</artifactId>
10.257 - <version>\${net.java.html.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 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml Mon Jun 24 17:49:27 2013 +0200
11.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
11.3 @@ -1,43 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml Mon Jun 24 17:49:27 2013 +0200
12.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
12.3 @@ -1,23 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java Mon Jun 24 17:49:27 2013 +0200
13.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
13.3 @@ -1,178 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/resources/index.html Mon Jun 24 17:49:27 2013 +0200
14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
14.3 @@ -1,90 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css Mon Jun 24 17:49:27 2013 +0200
15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
15.3 @@ -1,32 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java Mon Jun 24 17:49:27 2013 +0200
16.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
16.3 @@ -1,31 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java Mon Jun 24 17:49:27 2013 +0200
17.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
17.3 @@ -1,50 +0,0 @@
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 --- a/ko-archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java Mon Jun 24 17:49:27 2013 +0200
18.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
18.3 @@ -1,73 +0,0 @@
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 --- a/ko-bck2brwsr/pom.xml Mon Jun 24 17:49:27 2013 +0200
19.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
19.3 @@ -1,96 +0,0 @@
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</groupId>
19.9 - <artifactId>html</artifactId>
19.10 - <version>0.4-SNAPSHOT</version>
19.11 - </parent>
19.12 - <groupId>org.apidesign.html</groupId>
19.13 - <artifactId>ko-bck2brwsr</artifactId>
19.14 - <version>0.4-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>${bck2brwsr.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>${bck2brwsr.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>${bck2brwsr.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>${bck2brwsr.launcher.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>0.4-SNAPSHOT</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>0.4-SNAPSHOT</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>${bck2brwsr.version}</version>
19.96 - <type>jar</type>
19.97 - </dependency>
19.98 - </dependencies>
19.99 -</project>
20.1 --- a/ko-bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/BrwsrCtxImpl.java Mon Jun 24 17:49:27 2013 +0200
20.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
20.3 @@ -1,129 +0,0 @@
20.4 -/**
20.5 - * HTML via Java(tm) Language Bindings
20.6 - * Copyright (C) 2013 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. apidesign.org
20.16 - * designates this particular file as subject to the
20.17 - * "Classpath" exception as provided by apidesign.org
20.18 - * in the License file that accompanied this code.
20.19 - *
20.20 - * You should have received a copy of the GNU General Public License
20.21 - * along with this program. Look for COPYING file in the top folder.
20.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
20.23 - */
20.24 -package org.apidesign.html.ko2brwsr;
20.25 -
20.26 -import java.io.ByteArrayOutputStream;
20.27 -import java.io.IOException;
20.28 -import java.io.InputStream;
20.29 -import java.io.InputStreamReader;
20.30 -import net.java.html.BrwsrCtx;
20.31 -import org.apidesign.html.context.spi.Contexts;
20.32 -import org.apidesign.html.json.spi.FunctionBinding;
20.33 -import org.apidesign.html.json.spi.JSONCall;
20.34 -import org.apidesign.html.json.spi.PropertyBinding;
20.35 -import org.apidesign.html.json.spi.Technology;
20.36 -import org.apidesign.html.json.spi.Transfer;
20.37 -
20.38 -/**
20.39 - *
20.40 - * @author Jaroslav Tulach <jtulach@netbeans.org>
20.41 - */
20.42 -final class BrwsrCtxImpl implements Technology<Object>, Transfer {
20.43 - private BrwsrCtxImpl() {}
20.44 -
20.45 - public static final BrwsrCtxImpl DEFAULT = new BrwsrCtxImpl();
20.46 -
20.47 - @Override
20.48 - public void extract(Object obj, String[] props, Object[] values) {
20.49 - ConvertTypes.extractJSON(obj, props, values);
20.50 - }
20.51 -
20.52 - @Override
20.53 - public void loadJSON(final JSONCall call) {
20.54 - class R implements Runnable {
20.55 - Object[] arr = { null };
20.56 - @Override
20.57 - public void run() {
20.58 - call.notifySuccess(arr[0]);
20.59 - }
20.60 - }
20.61 - R r = new R();
20.62 - if (call.isJSONP()) {
20.63 - String me = ConvertTypes.createJSONP(r.arr, r);
20.64 - ConvertTypes.loadJSONP(call.composeURL(me), me);
20.65 - } else {
20.66 - String data = null;
20.67 - if (call.isDoOutput()) {
20.68 - try {
20.69 - ByteArrayOutputStream bos = new ByteArrayOutputStream();
20.70 - call.writeData(bos);
20.71 - data = new String(bos.toByteArray(), "UTF-8");
20.72 - } catch (IOException ex) {
20.73 - call.notifyError(ex);
20.74 - }
20.75 - }
20.76 - ConvertTypes.loadJSON(call.composeURL(null), r.arr, r, call.getMethod(), data);
20.77 - }
20.78 - }
20.79 -
20.80 - @Override
20.81 - public Object wrapModel(Object model) {
20.82 - return model;
20.83 - }
20.84 -
20.85 - @Override
20.86 - public void bind(PropertyBinding b, Object model, Object data) {
20.87 - Knockout.bind(data, b, b.getPropertyName(),
20.88 - "getValue__Ljava_lang_Object_2",
20.89 - b.isReadOnly() ? null : "setValue__VLjava_lang_Object_2",
20.90 - false, false
20.91 - );
20.92 - }
20.93 -
20.94 - @Override
20.95 - public void valueHasMutated(Object data, String propertyName) {
20.96 - Knockout.valueHasMutated(data, propertyName);
20.97 - }
20.98 -
20.99 - @Override
20.100 - public void expose(FunctionBinding fb, Object model, Object d) {
20.101 - Knockout.expose(d, fb, fb.getFunctionName(), "call__VLjava_lang_Object_2Ljava_lang_Object_2");
20.102 - }
20.103 -
20.104 - @Override
20.105 - public void applyBindings(Object data) {
20.106 - Knockout.applyBindings(data);
20.107 - }
20.108 -
20.109 - @Override
20.110 - public Object wrapArray(Object[] arr) {
20.111 - return arr;
20.112 - }
20.113 -
20.114 - @Override
20.115 - public <M> M toModel(Class<M> modelClass, Object data) {
20.116 - return modelClass.cast(data);
20.117 - }
20.118 -
20.119 - @Override
20.120 - public Object toJSON(InputStream is) throws IOException {
20.121 - StringBuilder sb = new StringBuilder();
20.122 - InputStreamReader r = new InputStreamReader(is);
20.123 - for (;;) {
20.124 - int ch = r.read();
20.125 - if (ch == -1) {
20.126 - break;
20.127 - }
20.128 - sb.append((char)ch);
20.129 - }
20.130 - return ConvertTypes.parse(sb.toString());
20.131 - }
20.132 -}
21.1 --- a/ko-bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/BrwsrCtxPrvdr.java Mon Jun 24 17:49:27 2013 +0200
21.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
21.3 @@ -1,54 +0,0 @@
21.4 -/**
21.5 - * HTML via Java(tm) Language Bindings
21.6 - * Copyright (C) 2013 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. apidesign.org
21.16 - * designates this particular file as subject to the
21.17 - * "Classpath" exception as provided by apidesign.org
21.18 - * in the License file that accompanied this code.
21.19 - *
21.20 - * You should have received a copy of the GNU General Public License
21.21 - * along with this program. Look for COPYING file in the top folder.
21.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
21.23 - */
21.24 -package org.apidesign.html.ko2brwsr;
21.25 -
21.26 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
21.27 -import org.apidesign.html.context.spi.Contexts;
21.28 -import org.apidesign.html.json.spi.Technology;
21.29 -import org.apidesign.html.json.spi.Transfer;
21.30 -import org.openide.util.lookup.ServiceProvider;
21.31 -
21.32 -/** This is an implementation package - just
21.33 - * include its JAR on classpath and use official {@link Context} API
21.34 - * to access the functionality.
21.35 - * <p>
21.36 - * Provides binding between models and <a href="http://bck2brwsr.apidesign.org">
21.37 - * Bck2Brwsr</a> VM.
21.38 - * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
21.39 - *
21.40 - * @author Jaroslav Tulach <jtulach@netbeans.org>
21.41 - */
21.42 -@ServiceProvider(service = Contexts.Provider.class)
21.43 -public final class BrwsrCtxPrvdr implements Contexts.Provider {
21.44 -
21.45 - @Override
21.46 - public void fillContext(Contexts.Builder context, Class<?> requestor) {
21.47 - if (bck2BrwsrVM()) {
21.48 - context.register(Technology.class, BrwsrCtxImpl.DEFAULT, 50).
21.49 - register(Transfer.class, BrwsrCtxImpl.DEFAULT, 50);
21.50 - }
21.51 - }
21.52 -
21.53 - @JavaScriptBody(args = { }, body = "return true;")
21.54 - private static boolean bck2BrwsrVM() {
21.55 - return false;
21.56 - }
21.57 -}
22.1 --- a/ko-bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/ConvertTypes.java Mon Jun 24 17:49:27 2013 +0200
22.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
22.3 @@ -1,155 +0,0 @@
22.4 -/**
22.5 - * HTML via Java(tm) Language Bindings
22.6 - * Copyright (C) 2013 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. apidesign.org
22.16 - * designates this particular file as subject to the
22.17 - * "Classpath" exception as provided by apidesign.org
22.18 - * in the License file that accompanied this code.
22.19 - *
22.20 - * You should have received a copy of the GNU General Public License
22.21 - * along with this program. Look for COPYING file in the top folder.
22.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
22.23 - */
22.24 -package org.apidesign.html.ko2brwsr;
22.25 -
22.26 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
22.27 -
22.28 -/**
22.29 - *
22.30 - * @author Jaroslav Tulach <jtulach@netbeans.org>
22.31 - */
22.32 -final class ConvertTypes {
22.33 - ConvertTypes() {
22.34 - }
22.35 -
22.36 - public static String toString(Object object, String property) {
22.37 - Object ret = getProperty(object, property);
22.38 - return ret == null ? null : ret.toString();
22.39 - }
22.40 -
22.41 - public static double toDouble(Object object, String property) {
22.42 - Object ret = getProperty(object, property);
22.43 - return ret instanceof Number ? ((Number)ret).doubleValue() : Double.NaN;
22.44 - }
22.45 -
22.46 - public static int toInt(Object object, String property) {
22.47 - Object ret = getProperty(object, property);
22.48 - return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
22.49 - }
22.50 -
22.51 - public static <T> T toModel(Class<T> modelClass, Object object, String property) {
22.52 - Object ret = getProperty(object, property);
22.53 - if (ret == null || modelClass.isInstance(ret)) {
22.54 - return modelClass.cast(ret);
22.55 - }
22.56 - throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
22.57 - }
22.58 -
22.59 - public static String toJSON(Object value) {
22.60 - if (value == null) {
22.61 - return "null";
22.62 - }
22.63 - if (value instanceof Enum) {
22.64 - value = value.toString();
22.65 - }
22.66 - if (value instanceof String) {
22.67 - return '"' +
22.68 - ((String)value).
22.69 - replace("\"", "\\\"").
22.70 - replace("\n", "\\n").
22.71 - replace("\r", "\\r").
22.72 - replace("\t", "\\t")
22.73 - + '"';
22.74 - }
22.75 - return value.toString();
22.76 - }
22.77 -
22.78 - @JavaScriptBody(args = { "object", "property" },
22.79 - body = "if (property === null) return object;\n"
22.80 - + "var p = object[property]; return p ? p : null;"
22.81 - )
22.82 - private static Object getProperty(Object object, String property) {
22.83 - return null;
22.84 - }
22.85 -
22.86 - public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
22.87 - int h = whenDone.hashCode();
22.88 - String name;
22.89 - for (;;) {
22.90 - name = "jsonp" + Integer.toHexString(h);
22.91 - if (defineIfUnused(name, jsonResult, whenDone)) {
22.92 - return name;
22.93 - }
22.94 - h++;
22.95 - }
22.96 - }
22.97 -
22.98 - @JavaScriptBody(args = { "name", "arr", "run" }, body =
22.99 - "if (window[name]) return false;\n "
22.100 - + "window[name] = function(data) {\n "
22.101 - + " delete window[name];\n"
22.102 - + " var el = window.document.getElementById(name);\n"
22.103 - + " el.parentNode.removeChild(el);\n"
22.104 - + " arr[0] = data;\n"
22.105 - + " run.run__V();\n"
22.106 - + "};\n"
22.107 - + "return true;\n"
22.108 - )
22.109 - private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
22.110 - return true;
22.111 - }
22.112 -
22.113 - @JavaScriptBody(args = { "s" }, body = "return eval('(' + s + ')');")
22.114 - static Object parse(String s) {
22.115 - return s;
22.116 - }
22.117 -
22.118 - @JavaScriptBody(args = { "url", "arr", "callback", "method", "data" }, body = ""
22.119 - + "var request = new XMLHttpRequest();\n"
22.120 - + "if (!method) method = 'GET';\n"
22.121 - + "request.open(method, url, true);\n"
22.122 - + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
22.123 - + "request.onreadystatechange = function() {\n"
22.124 - + " if (this.readyState!==4) return;\n"
22.125 - + " try {\n"
22.126 - + " arr[0] = eval('(' + this.response + ')');\n"
22.127 - + " } catch (error) {;\n"
22.128 - + " arr[0] = this.response;\n"
22.129 - + " }\n"
22.130 - + " callback.run__V();\n"
22.131 - + "};"
22.132 - + "if (data) request.send(data);"
22.133 - + "else request.send();"
22.134 - )
22.135 - static void loadJSON(
22.136 - String url, Object[] jsonResult, Runnable whenDone, String method, String data
22.137 - ) {
22.138 - }
22.139 -
22.140 - @JavaScriptBody(args = { "url", "jsonp" }, body =
22.141 - "var scrpt = window.document.createElement('script');\n "
22.142 - + "scrpt.setAttribute('src', url);\n "
22.143 - + "scrpt.setAttribute('id', jsonp);\n "
22.144 - + "scrpt.setAttribute('type', 'text/javascript');\n "
22.145 - + "var body = document.getElementsByTagName('body')[0];\n "
22.146 - + "body.appendChild(scrpt);\n"
22.147 - )
22.148 - static void loadJSONP(String url, String jsonp) {
22.149 -
22.150 - }
22.151 -
22.152 - public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
22.153 - for (int i = 0; i < props.length; i++) {
22.154 - values[i] = getProperty(jsonObject, props[i]);
22.155 - }
22.156 - }
22.157 -
22.158 -}
23.1 --- a/ko-bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/Knockout.java Mon Jun 24 17:49:27 2013 +0200
23.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
23.3 @@ -1,134 +0,0 @@
23.4 -/**
23.5 - * HTML via Java(tm) Language Bindings
23.6 - * Copyright (C) 2013 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. apidesign.org
23.16 - * designates this particular file as subject to the
23.17 - * "Classpath" exception as provided by apidesign.org
23.18 - * in the License file that accompanied this code.
23.19 - *
23.20 - * You should have received a copy of the GNU General Public License
23.21 - * along with this program. Look for COPYING file in the top folder.
23.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
23.23 - */
23.24 -package org.apidesign.html.ko2brwsr;
23.25 -
23.26 -import java.lang.reflect.Method;
23.27 -import java.util.List;
23.28 -import org.apidesign.bck2brwsr.core.ExtraJavaScript;
23.29 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
23.30 -
23.31 -/** Provides binding between models and bck2brwsr VM.
23.32 - *
23.33 - * @author Jaroslav Tulach <jtulach@netbeans.org>
23.34 - */
23.35 -@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
23.36 -final class Knockout {
23.37 - /** used by tests */
23.38 - static Knockout next;
23.39 - private final Object model;
23.40 -
23.41 - Knockout(Object model) {
23.42 - this.model = model == null ? this : model;
23.43 - }
23.44 -
23.45 - public static <M> Knockout applyBindings(
23.46 - Object model, String[] propsGettersAndSetters,
23.47 - String[] methodsAndSignatures
23.48 - ) {
23.49 - applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
23.50 - return new Knockout(model);
23.51 - }
23.52 - public static <M> Knockout applyBindings(
23.53 - Class<M> modelClass, M model, String[] propsGettersAndSetters,
23.54 - String[] methodsAndSignatures
23.55 - ) {
23.56 - Knockout bindings = next;
23.57 - next = null;
23.58 - if (bindings == null) {
23.59 - bindings = new Knockout(null);
23.60 - }
23.61 - applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
23.62 - applyBindings(bindings);
23.63 - return bindings;
23.64 - }
23.65 -
23.66 - public void valueHasMutated(String prop) {
23.67 - valueHasMutated(model, prop);
23.68 - }
23.69 - @JavaScriptBody(args = { "self", "prop" }, body =
23.70 - "var p = self[prop]; if (p) p.valueHasMutated();"
23.71 - )
23.72 - public static void valueHasMutated(Object self, String prop) {
23.73 - }
23.74 -
23.75 -
23.76 - @JavaScriptBody(args = { "id", "ev" }, body = "ko.utils.triggerEvent(window.document.getElementById(id), ev.substring(2));")
23.77 - public static void triggerEvent(String id, String ev) {
23.78 - }
23.79 -
23.80 - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
23.81 - "var bnd = {\n"
23.82 - + " 'read': function() {\n"
23.83 - + " var v = model[getter]();\n"
23.84 - + " if (array) v = v.koArray(); else if (v !== null) v = v.valueOf();\n"
23.85 - + " return v;\n"
23.86 - + " },\n"
23.87 - + " 'owner': bindings\n"
23.88 - + "};\n"
23.89 - + "if (setter != null) {\n"
23.90 - + " bnd['write'] = function(val) {\n"
23.91 - + " var v = val === null ? null : val.valueOf();"
23.92 - + " model[setter](v);\n"
23.93 - + " };\n"
23.94 - + "}\n"
23.95 - + "bindings[prop] = ko['computed'](bnd);"
23.96 - )
23.97 - static void bind(
23.98 - Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
23.99 - ) {
23.100 - }
23.101 -
23.102 - @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
23.103 - "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
23.104 - )
23.105 - static void expose(
23.106 - Object bindings, Object model, String prop, String sig
23.107 - ) {
23.108 - }
23.109 -
23.110 - @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
23.111 - static void applyBindings(Object bindings) {}
23.112 -
23.113 - private static void applyImpl(
23.114 - String[] propsGettersAndSetters,
23.115 - Class<?> modelClass,
23.116 - Object bindings,
23.117 - Object model,
23.118 - String[] methodsAndSignatures
23.119 - ) throws IllegalStateException, SecurityException {
23.120 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
23.121 - try {
23.122 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
23.123 - bind(bindings, model, propsGettersAndSetters[i],
23.124 - propsGettersAndSetters[i + 1],
23.125 - propsGettersAndSetters[i + 2],
23.126 - getter.getReturnType().isPrimitive(),
23.127 - List.class.isAssignableFrom(getter.getReturnType()));
23.128 - } catch (NoSuchMethodException ex) {
23.129 - throw new IllegalStateException(ex.getMessage());
23.130 - }
23.131 - }
23.132 - for (int i = 0; i < methodsAndSignatures.length; i += 2) {
23.133 - expose(
23.134 - bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
23.135 - }
23.136 - }
23.137 -}
24.1 --- a/ko-bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Mon Jun 24 17:49:27 2013 +0200
24.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
24.3 @@ -1,3614 +0,0 @@
24.4 -/*
24.5 - * HTML via Java(tm) Language Bindings
24.6 - * Copyright (C) 2013 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. apidesign.org
24.16 - * designates this particular file as subject to the
24.17 - * "Classpath" exception as provided by apidesign.org
24.18 - * in the License file that accompanied this code.
24.19 - *
24.20 - * You should have received a copy of the GNU General Public License
24.21 - * along with this program. Look for COPYING file in the top folder.
24.22 - * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
24.23 - */
24.24 -// Knockout JavaScript library v2.2.1
24.25 -// (c) Steven Sanderson - http://knockoutjs.com/
24.26 -// License: MIT (http://www.opensource.org/licenses/mit-license.php)
24.27 -
24.28 -(function(){
24.29 -var DEBUG=true;
24.30 -(function(window,document,navigator,jQuery,undefined){
24.31 -!function(factory) {
24.32 - // Support three module loading scenarios
24.33 - if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
24.34 - // [1] CommonJS/Node.js
24.35 - var target = module['exports'] || exports; // module.exports is for Node.js
24.36 - factory(target);
24.37 - } else if (typeof define === 'function' && define['amd']) {
24.38 - // [2] AMD anonymous module
24.39 - define(['exports'], factory);
24.40 - } else {
24.41 - // [3] No module loader (plain <script> tag) - put directly in global namespace
24.42 - factory(window['ko'] = {});
24.43 - }
24.44 -}(function(koExports){
24.45 -// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
24.46 -// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
24.47 -var ko = typeof koExports !== 'undefined' ? koExports : {};
24.48 -// Google Closure Compiler helpers (used only to make the minified file smaller)
24.49 -ko.exportSymbol = function(koPath, object) {
24.50 - var tokens = koPath.split(".");
24.51 -
24.52 - // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
24.53 - // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
24.54 - var target = ko;
24.55 -
24.56 - for (var i = 0; i < tokens.length - 1; i++)
24.57 - target = target[tokens[i]];
24.58 - target[tokens[tokens.length - 1]] = object;
24.59 -};
24.60 -ko.exportProperty = function(owner, publicName, object) {
24.61 - owner[publicName] = object;
24.62 -};
24.63 -ko.version = "2.2.1";
24.64 -
24.65 -ko.exportSymbol('version', ko.version);
24.66 -ko.utils = new (function () {
24.67 - var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
24.68 -
24.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)
24.70 - var knownEvents = {}, knownEventTypesByEventName = {};
24.71 - var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
24.72 - knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
24.73 - knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
24.74 - for (var eventType in knownEvents) {
24.75 - var knownEventsForType = knownEvents[eventType];
24.76 - if (knownEventsForType.length) {
24.77 - for (var i = 0, j = knownEventsForType.length; i < j; i++)
24.78 - knownEventTypesByEventName[knownEventsForType[i]] = eventType;
24.79 - }
24.80 - }
24.81 - var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
24.82 -
24.83 - // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
24.84 - // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
24.85 - // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
24.86 - // If there is a future need to detect specific versions of IE10+, we will amend this.
24.87 - var ieVersion = (function() {
24.88 - var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
24.89 -
24.90 - // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
24.91 - while (
24.92 - div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
24.93 - iElems[0]
24.94 - );
24.95 - return version > 4 ? version : undefined;
24.96 - }());
24.97 - var isIe6 = ieVersion === 6,
24.98 - isIe7 = ieVersion === 7;
24.99 -
24.100 - function isClickOnCheckableElement(element, eventType) {
24.101 - if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
24.102 - if (eventType.toLowerCase() != "click") return false;
24.103 - var inputType = element.type;
24.104 - return (inputType == "checkbox") || (inputType == "radio");
24.105 - }
24.106 -
24.107 - return {
24.108 - fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
24.109 -
24.110 - arrayForEach: function (array, action) {
24.111 - for (var i = 0, j = array.length; i < j; i++)
24.112 - action(array[i]);
24.113 - },
24.114 -
24.115 - arrayIndexOf: function (array, item) {
24.116 - if (typeof Array.prototype.indexOf == "function")
24.117 - return Array.prototype.indexOf.call(array, item);
24.118 - for (var i = 0, j = array.length; i < j; i++)
24.119 - if (array[i] === item)
24.120 - return i;
24.121 - return -1;
24.122 - },
24.123 -
24.124 - arrayFirst: function (array, predicate, predicateOwner) {
24.125 - for (var i = 0, j = array.length; i < j; i++)
24.126 - if (predicate.call(predicateOwner, array[i]))
24.127 - return array[i];
24.128 - return null;
24.129 - },
24.130 -
24.131 - arrayRemoveItem: function (array, itemToRemove) {
24.132 - var index = ko.utils.arrayIndexOf(array, itemToRemove);
24.133 - if (index >= 0)
24.134 - array.splice(index, 1);
24.135 - },
24.136 -
24.137 - arrayGetDistinctValues: function (array) {
24.138 - array = array || [];
24.139 - var result = [];
24.140 - for (var i = 0, j = array.length; i < j; i++) {
24.141 - if (ko.utils.arrayIndexOf(result, array[i]) < 0)
24.142 - result.push(array[i]);
24.143 - }
24.144 - return result;
24.145 - },
24.146 -
24.147 - arrayMap: function (array, mapping) {
24.148 - array = array || [];
24.149 - var result = [];
24.150 - for (var i = 0, j = array.length; i < j; i++)
24.151 - result.push(mapping(array[i]));
24.152 - return result;
24.153 - },
24.154 -
24.155 - arrayFilter: function (array, predicate) {
24.156 - array = array || [];
24.157 - var result = [];
24.158 - for (var i = 0, j = array.length; i < j; i++)
24.159 - if (predicate(array[i]))
24.160 - result.push(array[i]);
24.161 - return result;
24.162 - },
24.163 -
24.164 - arrayPushAll: function (array, valuesToPush) {
24.165 - if (valuesToPush instanceof Array)
24.166 - array.push.apply(array, valuesToPush);
24.167 - else
24.168 - for (var i = 0, j = valuesToPush.length; i < j; i++)
24.169 - array.push(valuesToPush[i]);
24.170 - return array;
24.171 - },
24.172 -
24.173 - extend: function (target, source) {
24.174 - if (source) {
24.175 - for(var prop in source) {
24.176 - if(source.hasOwnProperty(prop)) {
24.177 - target[prop] = source[prop];
24.178 - }
24.179 - }
24.180 - }
24.181 - return target;
24.182 - },
24.183 -
24.184 - emptyDomNode: function (domNode) {
24.185 - while (domNode.firstChild) {
24.186 - ko.removeNode(domNode.firstChild);
24.187 - }
24.188 - },
24.189 -
24.190 - moveCleanedNodesToContainerElement: function(nodes) {
24.191 - // Ensure it's a real array, as we're about to reparent the nodes and
24.192 - // we don't want the underlying collection to change while we're doing that.
24.193 - var nodesArray = ko.utils.makeArray(nodes);
24.194 -
24.195 - var container = document.createElement('div');
24.196 - for (var i = 0, j = nodesArray.length; i < j; i++) {
24.197 - container.appendChild(ko.cleanNode(nodesArray[i]));
24.198 - }
24.199 - return container;
24.200 - },
24.201 -
24.202 - cloneNodes: function (nodesArray, shouldCleanNodes) {
24.203 - for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
24.204 - var clonedNode = nodesArray[i].cloneNode(true);
24.205 - newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
24.206 - }
24.207 - return newNodesArray;
24.208 - },
24.209 -
24.210 - setDomNodeChildren: function (domNode, childNodes) {
24.211 - ko.utils.emptyDomNode(domNode);
24.212 - if (childNodes) {
24.213 - for (var i = 0, j = childNodes.length; i < j; i++)
24.214 - domNode.appendChild(childNodes[i]);
24.215 - }
24.216 - },
24.217 -
24.218 - replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
24.219 - var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
24.220 - if (nodesToReplaceArray.length > 0) {
24.221 - var insertionPoint = nodesToReplaceArray[0];
24.222 - var parent = insertionPoint.parentNode;
24.223 - for (var i = 0, j = newNodesArray.length; i < j; i++)
24.224 - parent.insertBefore(newNodesArray[i], insertionPoint);
24.225 - for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
24.226 - ko.removeNode(nodesToReplaceArray[i]);
24.227 - }
24.228 - }
24.229 - },
24.230 -
24.231 - setOptionNodeSelectionState: function (optionNode, isSelected) {
24.232 - // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
24.233 - if (ieVersion < 7)
24.234 - optionNode.setAttribute("selected", isSelected);
24.235 - else
24.236 - optionNode.selected = isSelected;
24.237 - },
24.238 -
24.239 - stringTrim: function (string) {
24.240 - return (string || "").replace(stringTrimRegex, "");
24.241 - },
24.242 -
24.243 - stringTokenize: function (string, delimiter) {
24.244 - var result = [];
24.245 - var tokens = (string || "").split(delimiter);
24.246 - for (var i = 0, j = tokens.length; i < j; i++) {
24.247 - var trimmed = ko.utils.stringTrim(tokens[i]);
24.248 - if (trimmed !== "")
24.249 - result.push(trimmed);
24.250 - }
24.251 - return result;
24.252 - },
24.253 -
24.254 - stringStartsWith: function (string, startsWith) {
24.255 - string = string || "";
24.256 - if (startsWith.length > string.length)
24.257 - return false;
24.258 - return string.substring(0, startsWith.length) === startsWith;
24.259 - },
24.260 -
24.261 - domNodeIsContainedBy: function (node, containedByNode) {
24.262 - if (containedByNode.compareDocumentPosition)
24.263 - return (containedByNode.compareDocumentPosition(node) & 16) == 16;
24.264 - while (node != null) {
24.265 - if (node == containedByNode)
24.266 - return true;
24.267 - node = node.parentNode;
24.268 - }
24.269 - return false;
24.270 - },
24.271 -
24.272 - domNodeIsAttachedToDocument: function (node) {
24.273 - return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
24.274 - },
24.275 -
24.276 - tagNameLower: function(element) {
24.277 - // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
24.278 - // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
24.279 - // we don't need to do the .toLowerCase() as it will always be lower case anyway.
24.280 - return element && element.tagName && element.tagName.toLowerCase();
24.281 - },
24.282 -
24.283 - registerEventHandler: function (element, eventType, handler) {
24.284 - var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
24.285 - if (!mustUseAttachEvent && typeof jQuery != "undefined") {
24.286 - if (isClickOnCheckableElement(element, eventType)) {
24.287 - // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
24.288 - // it toggles the element checked state *after* the click event handlers run, whereas native
24.289 - // click events toggle the checked state *before* the event handler.
24.290 - // Fix this by intecepting the handler and applying the correct checkedness before it runs.
24.291 - var originalHandler = handler;
24.292 - handler = function(event, eventData) {
24.293 - var jQuerySuppliedCheckedState = this.checked;
24.294 - if (eventData)
24.295 - this.checked = eventData.checkedStateBeforeEvent !== true;
24.296 - originalHandler.call(this, event);
24.297 - this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
24.298 - };
24.299 - }
24.300 - jQuery(element)['bind'](eventType, handler);
24.301 - } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
24.302 - element.addEventListener(eventType, handler, false);
24.303 - else if (typeof element.attachEvent != "undefined")
24.304 - element.attachEvent("on" + eventType, function (event) {
24.305 - handler.call(element, event);
24.306 - });
24.307 - else
24.308 - throw new Error("Browser doesn't support addEventListener or attachEvent");
24.309 - },
24.310 -
24.311 - triggerEvent: function (element, eventType) {
24.312 - if (!(element && element.nodeType))
24.313 - throw new Error("element must be a DOM node when calling triggerEvent");
24.314 -
24.315 - if (typeof jQuery != "undefined") {
24.316 - var eventData = [];
24.317 - if (isClickOnCheckableElement(element, eventType)) {
24.318 - // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
24.319 - eventData.push({ checkedStateBeforeEvent: element.checked });
24.320 - }
24.321 - jQuery(element)['trigger'](eventType, eventData);
24.322 - } else if (typeof document.createEvent == "function") {
24.323 - if (typeof element.dispatchEvent == "function") {
24.324 - var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
24.325 - var event = document.createEvent(eventCategory);
24.326 - event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
24.327 - element.dispatchEvent(event);
24.328 - }
24.329 - else
24.330 - throw new Error("The supplied element doesn't support dispatchEvent");
24.331 - } else if (typeof element.fireEvent != "undefined") {
24.332 - // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
24.333 - // so to make it consistent, we'll do it manually here
24.334 - if (isClickOnCheckableElement(element, eventType))
24.335 - element.checked = element.checked !== true;
24.336 - element.fireEvent("on" + eventType);
24.337 - }
24.338 - else
24.339 - throw new Error("Browser doesn't support triggering events");
24.340 - },
24.341 -
24.342 - unwrapObservable: function (value) {
24.343 - return ko.isObservable(value) ? value() : value;
24.344 - },
24.345 -
24.346 - peekObservable: function (value) {
24.347 - return ko.isObservable(value) ? value.peek() : value;
24.348 - },
24.349 -
24.350 - toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
24.351 - if (classNames) {
24.352 - var cssClassNameRegex = /[\w-]+/g,
24.353 - currentClassNames = node.className.match(cssClassNameRegex) || [];
24.354 - ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
24.355 - var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
24.356 - if (indexOfClass >= 0) {
24.357 - if (!shouldHaveClass)
24.358 - currentClassNames.splice(indexOfClass, 1);
24.359 - } else {
24.360 - if (shouldHaveClass)
24.361 - currentClassNames.push(className);
24.362 - }
24.363 - });
24.364 - node.className = currentClassNames.join(" ");
24.365 - }
24.366 - },
24.367 -
24.368 - setTextContent: function(element, textContent) {
24.369 - var value = ko.utils.unwrapObservable(textContent);
24.370 - if ((value === null) || (value === undefined))
24.371 - value = "";
24.372 -
24.373 - if (element.nodeType === 3) {
24.374 - element.data = value;
24.375 - } else {
24.376 - // We need there to be exactly one child: a text node.
24.377 - // If there are no children, more than one, or if it's not a text node,
24.378 - // we'll clear everything and create a single text node.
24.379 - var innerTextNode = ko.virtualElements.firstChild(element);
24.380 - if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
24.381 - ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
24.382 - } else {
24.383 - innerTextNode.data = value;
24.384 - }
24.385 -
24.386 - ko.utils.forceRefresh(element);
24.387 - }
24.388 - },
24.389 -
24.390 - setElementName: function(element, name) {
24.391 - element.name = name;
24.392 -
24.393 - // Workaround IE 6/7 issue
24.394 - // - https://github.com/SteveSanderson/knockout/issues/197
24.395 - // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
24.396 - if (ieVersion <= 7) {
24.397 - try {
24.398 - element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
24.399 - }
24.400 - catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
24.401 - }
24.402 - },
24.403 -
24.404 - forceRefresh: function(node) {
24.405 - // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
24.406 - if (ieVersion >= 9) {
24.407 - // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
24.408 - var elem = node.nodeType == 1 ? node : node.parentNode;
24.409 - if (elem.style)
24.410 - elem.style.zoom = elem.style.zoom;
24.411 - }
24.412 - },
24.413 -
24.414 - ensureSelectElementIsRenderedCorrectly: function(selectElement) {
24.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.
24.416 - // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
24.417 - if (ieVersion >= 9) {
24.418 - var originalWidth = selectElement.style.width;
24.419 - selectElement.style.width = 0;
24.420 - selectElement.style.width = originalWidth;
24.421 - }
24.422 - },
24.423 -
24.424 - range: function (min, max) {
24.425 - min = ko.utils.unwrapObservable(min);
24.426 - max = ko.utils.unwrapObservable(max);
24.427 - var result = [];
24.428 - for (var i = min; i <= max; i++)
24.429 - result.push(i);
24.430 - return result;
24.431 - },
24.432 -
24.433 - makeArray: function(arrayLikeObject) {
24.434 - var result = [];
24.435 - for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
24.436 - result.push(arrayLikeObject[i]);
24.437 - };
24.438 - return result;
24.439 - },
24.440 -
24.441 - isIe6 : isIe6,
24.442 - isIe7 : isIe7,
24.443 - ieVersion : ieVersion,
24.444 -
24.445 - getFormFields: function(form, fieldName) {
24.446 - var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
24.447 - var isMatchingField = (typeof fieldName == 'string')
24.448 - ? function(field) { return field.name === fieldName }
24.449 - : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
24.450 - var matches = [];
24.451 - for (var i = fields.length - 1; i >= 0; i--) {
24.452 - if (isMatchingField(fields[i]))
24.453 - matches.push(fields[i]);
24.454 - };
24.455 - return matches;
24.456 - },
24.457 -
24.458 - parseJson: function (jsonString) {
24.459 - if (typeof jsonString == "string") {
24.460 - jsonString = ko.utils.stringTrim(jsonString);
24.461 - if (jsonString) {
24.462 - if (window.JSON && window.JSON.parse) // Use native parsing where available
24.463 - return window.JSON.parse(jsonString);
24.464 - return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
24.465 - }
24.466 - }
24.467 - return null;
24.468 - },
24.469 -
24.470 - stringifyJson: function (data, replacer, space) { // replacer and space are optional
24.471 - if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
24.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");
24.473 - return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
24.474 - },
24.475 -
24.476 - postJson: function (urlOrForm, data, options) {
24.477 - options = options || {};
24.478 - var params = options['params'] || {};
24.479 - var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
24.480 - var url = urlOrForm;
24.481 -
24.482 - // If we were given a form, use its 'action' URL and pick out any requested field values
24.483 - if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
24.484 - var originalForm = urlOrForm;
24.485 - url = originalForm.action;
24.486 - for (var i = includeFields.length - 1; i >= 0; i--) {
24.487 - var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
24.488 - for (var j = fields.length - 1; j >= 0; j--)
24.489 - params[fields[j].name] = fields[j].value;
24.490 - }
24.491 - }
24.492 -
24.493 - data = ko.utils.unwrapObservable(data);
24.494 - var form = document.createElement("form");
24.495 - form.style.display = "none";
24.496 - form.action = url;
24.497 - form.method = "post";
24.498 - for (var key in data) {
24.499 - var input = document.createElement("input");
24.500 - input.name = key;
24.501 - input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
24.502 - form.appendChild(input);
24.503 - }
24.504 - for (var key in params) {
24.505 - var input = document.createElement("input");
24.506 - input.name = key;
24.507 - input.value = params[key];
24.508 - form.appendChild(input);
24.509 - }
24.510 - document.body.appendChild(form);
24.511 - options['submitter'] ? options['submitter'](form) : form.submit();
24.512 - setTimeout(function () { form.parentNode.removeChild(form); }, 0);
24.513 - }
24.514 - }
24.515 -})();
24.516 -
24.517 -ko.exportSymbol('utils', ko.utils);
24.518 -ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
24.519 -ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
24.520 -ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
24.521 -ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
24.522 -ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
24.523 -ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
24.524 -ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
24.525 -ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
24.526 -ko.exportSymbol('utils.extend', ko.utils.extend);
24.527 -ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
24.528 -ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
24.529 -ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
24.530 -ko.exportSymbol('utils.postJson', ko.utils.postJson);
24.531 -ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
24.532 -ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
24.533 -ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
24.534 -ko.exportSymbol('utils.range', ko.utils.range);
24.535 -ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
24.536 -ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
24.537 -ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
24.538 -
24.539 -if (!Function.prototype['bind']) {
24.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)
24.541 - // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
24.542 - Function.prototype['bind'] = function (object) {
24.543 - var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
24.544 - return function () {
24.545 - return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
24.546 - };
24.547 - };
24.548 -}
24.549 -
24.550 -ko.utils.domData = new (function () {
24.551 - var uniqueId = 0;
24.552 - var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
24.553 - var dataStore = {};
24.554 - return {
24.555 - get: function (node, key) {
24.556 - var allDataForNode = ko.utils.domData.getAll(node, false);
24.557 - return allDataForNode === undefined ? undefined : allDataForNode[key];
24.558 - },
24.559 - set: function (node, key, value) {
24.560 - if (value === undefined) {
24.561 - // Make sure we don't actually create a new domData key if we are actually deleting a value
24.562 - if (ko.utils.domData.getAll(node, false) === undefined)
24.563 - return;
24.564 - }
24.565 - var allDataForNode = ko.utils.domData.getAll(node, true);
24.566 - allDataForNode[key] = value;
24.567 - },
24.568 - getAll: function (node, createIfNotFound) {
24.569 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
24.570 - var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
24.571 - if (!hasExistingDataStore) {
24.572 - if (!createIfNotFound)
24.573 - return undefined;
24.574 - dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
24.575 - dataStore[dataStoreKey] = {};
24.576 - }
24.577 - return dataStore[dataStoreKey];
24.578 - },
24.579 - clear: function (node) {
24.580 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
24.581 - if (dataStoreKey) {
24.582 - delete dataStore[dataStoreKey];
24.583 - node[dataStoreKeyExpandoPropertyName] = null;
24.584 - return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
24.585 - }
24.586 - return false;
24.587 - }
24.588 - }
24.589 -})();
24.590 -
24.591 -ko.exportSymbol('utils.domData', ko.utils.domData);
24.592 -ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
24.593 -
24.594 -ko.utils.domNodeDisposal = new (function () {
24.595 - var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
24.596 - var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
24.597 - var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
24.598 -
24.599 - function getDisposeCallbacksCollection(node, createIfNotFound) {
24.600 - var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
24.601 - if ((allDisposeCallbacks === undefined) && createIfNotFound) {
24.602 - allDisposeCallbacks = [];
24.603 - ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
24.604 - }
24.605 - return allDisposeCallbacks;
24.606 - }
24.607 - function destroyCallbacksCollection(node) {
24.608 - ko.utils.domData.set(node, domDataKey, undefined);
24.609 - }
24.610 -
24.611 - function cleanSingleNode(node) {
24.612 - // Run all the dispose callbacks
24.613 - var callbacks = getDisposeCallbacksCollection(node, false);
24.614 - if (callbacks) {
24.615 - callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
24.616 - for (var i = 0; i < callbacks.length; i++)
24.617 - callbacks[i](node);
24.618 - }
24.619 -
24.620 - // Also erase the DOM data
24.621 - ko.utils.domData.clear(node);
24.622 -
24.623 - // Special support for jQuery here because it's so commonly used.
24.624 - // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
24.625 - // so notify it to tear down any resources associated with the node & descendants here.
24.626 - if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
24.627 - jQuery['cleanData']([node]);
24.628 -
24.629 - // Also clear any immediate-child comment nodes, as these wouldn't have been found by
24.630 - // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
24.631 - if (cleanableNodeTypesWithDescendants[node.nodeType])
24.632 - cleanImmediateCommentTypeChildren(node);
24.633 - }
24.634 -
24.635 - function cleanImmediateCommentTypeChildren(nodeWithChildren) {
24.636 - var child, nextChild = nodeWithChildren.firstChild;
24.637 - while (child = nextChild) {
24.638 - nextChild = child.nextSibling;
24.639 - if (child.nodeType === 8)
24.640 - cleanSingleNode(child);
24.641 - }
24.642 - }
24.643 -
24.644 - return {
24.645 - addDisposeCallback : function(node, callback) {
24.646 - if (typeof callback != "function")
24.647 - throw new Error("Callback must be a function");
24.648 - getDisposeCallbacksCollection(node, true).push(callback);
24.649 - },
24.650 -
24.651 - removeDisposeCallback : function(node, callback) {
24.652 - var callbacksCollection = getDisposeCallbacksCollection(node, false);
24.653 - if (callbacksCollection) {
24.654 - ko.utils.arrayRemoveItem(callbacksCollection, callback);
24.655 - if (callbacksCollection.length == 0)
24.656 - destroyCallbacksCollection(node);
24.657 - }
24.658 - },
24.659 -
24.660 - cleanNode : function(node) {
24.661 - // First clean this node, where applicable
24.662 - if (cleanableNodeTypes[node.nodeType]) {
24.663 - cleanSingleNode(node);
24.664 -
24.665 - // ... then its descendants, where applicable
24.666 - if (cleanableNodeTypesWithDescendants[node.nodeType]) {
24.667 - // Clone the descendants list in case it changes during iteration
24.668 - var descendants = [];
24.669 - ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
24.670 - for (var i = 0, j = descendants.length; i < j; i++)
24.671 - cleanSingleNode(descendants[i]);
24.672 - }
24.673 - }
24.674 - return node;
24.675 - },
24.676 -
24.677 - removeNode : function(node) {
24.678 - ko.cleanNode(node);
24.679 - if (node.parentNode)
24.680 - node.parentNode.removeChild(node);
24.681 - }
24.682 - }
24.683 -})();
24.684 -ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
24.685 -ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
24.686 -ko.exportSymbol('cleanNode', ko.cleanNode);
24.687 -ko.exportSymbol('removeNode', ko.removeNode);
24.688 -ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
24.689 -ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
24.690 -ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
24.691 -(function () {
24.692 - var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
24.693 -
24.694 - function simpleHtmlParse(html) {
24.695 - // Based on jQuery's "clean" function, but only accounting for table-related elements.
24.696 - // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
24.697 -
24.698 - // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
24.699 - // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
24.700 - // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
24.701 - // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
24.702 -
24.703 - // Trim whitespace, otherwise indexOf won't work as expected
24.704 - var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
24.705 -
24.706 - // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
24.707 - var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
24.708 - !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
24.709 - (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
24.710 - /* anything else */ [0, "", ""];
24.711 -
24.712 - // Go to html and back, then peel off extra wrappers
24.713 - // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
24.714 - var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
24.715 - if (typeof window['innerShiv'] == "function") {
24.716 - div.appendChild(window['innerShiv'](markup));
24.717 - } else {
24.718 - div.innerHTML = markup;
24.719 - }
24.720 -
24.721 - // Move to the right depth
24.722 - while (wrap[0]--)
24.723 - div = div.lastChild;
24.724 -
24.725 - return ko.utils.makeArray(div.lastChild.childNodes);
24.726 - }
24.727 -
24.728 - function jQueryHtmlParse(html) {
24.729 - // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
24.730 - if (jQuery['parseHTML']) {
24.731 - return jQuery['parseHTML'](html);
24.732 - } else {
24.733 - // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
24.734 - var elems = jQuery['clean']([html]);
24.735 -
24.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.
24.737 - // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
24.738 - // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
24.739 - if (elems && elems[0]) {
24.740 - // Find the top-most parent element that's a direct child of a document fragment
24.741 - var elem = elems[0];
24.742 - while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
24.743 - elem = elem.parentNode;
24.744 - // ... then detach it
24.745 - if (elem.parentNode)
24.746 - elem.parentNode.removeChild(elem);
24.747 - }
24.748 -
24.749 - return elems;
24.750 - }
24.751 - }
24.752 -
24.753 - ko.utils.parseHtmlFragment = function(html) {
24.754 - return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
24.755 - : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
24.756 - };
24.757 -
24.758 - ko.utils.setHtml = function(node, html) {
24.759 - ko.utils.emptyDomNode(node);
24.760 -
24.761 - // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
24.762 - html = ko.utils.unwrapObservable(html);
24.763 -
24.764 - if ((html !== null) && (html !== undefined)) {
24.765 - if (typeof html != 'string')
24.766 - html = html.toString();
24.767 -
24.768 - // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
24.769 - // for example <tr> elements which are not normally allowed to exist on their own.
24.770 - // If you've referenced jQuery we'll use that rather than duplicating its code.
24.771 - if (typeof jQuery != 'undefined') {
24.772 - jQuery(node)['html'](html);
24.773 - } else {
24.774 - // ... otherwise, use KO's own parsing logic.
24.775 - var parsedNodes = ko.utils.parseHtmlFragment(html);
24.776 - for (var i = 0; i < parsedNodes.length; i++)
24.777 - node.appendChild(parsedNodes[i]);
24.778 - }
24.779 - }
24.780 - };
24.781 -})();
24.782 -
24.783 -ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
24.784 -ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
24.785 -
24.786 -ko.memoization = (function () {
24.787 - var memos = {};
24.788 -
24.789 - function randomMax8HexChars() {
24.790 - return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
24.791 - }
24.792 - function generateRandomId() {
24.793 - return randomMax8HexChars() + randomMax8HexChars();
24.794 - }
24.795 - function findMemoNodes(rootNode, appendToArray) {
24.796 - if (!rootNode)
24.797 - return;
24.798 - if (rootNode.nodeType == 8) {
24.799 - var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
24.800 - if (memoId != null)
24.801 - appendToArray.push({ domNode: rootNode, memoId: memoId });
24.802 - } else if (rootNode.nodeType == 1) {
24.803 - for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
24.804 - findMemoNodes(childNodes[i], appendToArray);
24.805 - }
24.806 - }
24.807 -
24.808 - return {
24.809 - memoize: function (callback) {
24.810 - if (typeof callback != "function")
24.811 - throw new Error("You can only pass a function to ko.memoization.memoize()");
24.812 - var memoId = generateRandomId();
24.813 - memos[memoId] = callback;
24.814 - return "<!--[ko_memo:" + memoId + "]-->";
24.815 - },
24.816 -
24.817 - unmemoize: function (memoId, callbackParams) {
24.818 - var callback = memos[memoId];
24.819 - if (callback === undefined)
24.820 - throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
24.821 - try {
24.822 - callback.apply(null, callbackParams || []);
24.823 - return true;
24.824 - }
24.825 - finally { delete memos[memoId]; }
24.826 - },
24.827 -
24.828 - unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
24.829 - var memos = [];
24.830 - findMemoNodes(domNode, memos);
24.831 - for (var i = 0, j = memos.length; i < j; i++) {
24.832 - var node = memos[i].domNode;
24.833 - var combinedParams = [node];
24.834 - if (extraCallbackParamsArray)
24.835 - ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
24.836 - ko.memoization.unmemoize(memos[i].memoId, combinedParams);
24.837 - node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
24.838 - if (node.parentNode)
24.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)
24.840 - }
24.841 - },
24.842 -
24.843 - parseMemoText: function (memoText) {
24.844 - var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
24.845 - return match ? match[1] : null;
24.846 - }
24.847 - };
24.848 -})();
24.849 -
24.850 -ko.exportSymbol('memoization', ko.memoization);
24.851 -ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
24.852 -ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
24.853 -ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
24.854 -ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
24.855 -ko.extenders = {
24.856 - 'throttle': function(target, timeout) {
24.857 - // Throttling means two things:
24.858 -
24.859 - // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
24.860 - // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
24.861 - target['throttleEvaluation'] = timeout;
24.862 -
24.863 - // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
24.864 - // so the target cannot change value synchronously or faster than a certain rate
24.865 - var writeTimeoutInstance = null;
24.866 - return ko.dependentObservable({
24.867 - 'read': target,
24.868 - 'write': function(value) {
24.869 - clearTimeout(writeTimeoutInstance);
24.870 - writeTimeoutInstance = setTimeout(function() {
24.871 - target(value);
24.872 - }, timeout);
24.873 - }
24.874 - });
24.875 - },
24.876 -
24.877 - 'notify': function(target, notifyWhen) {
24.878 - target["equalityComparer"] = notifyWhen == "always"
24.879 - ? function() { return false } // Treat all values as not equal
24.880 - : ko.observable["fn"]["equalityComparer"];
24.881 - return target;
24.882 - }
24.883 -};
24.884 -
24.885 -function applyExtenders(requestedExtenders) {
24.886 - var target = this;
24.887 - if (requestedExtenders) {
24.888 - for (var key in requestedExtenders) {
24.889 - var extenderHandler = ko.extenders[key];
24.890 - if (typeof extenderHandler == 'function') {
24.891 - target = extenderHandler(target, requestedExtenders[key]);
24.892 - }
24.893 - }
24.894 - }
24.895 - return target;
24.896 -}
24.897 -
24.898 -ko.exportSymbol('extenders', ko.extenders);
24.899 -
24.900 -ko.subscription = function (target, callback, disposeCallback) {
24.901 - this.target = target;
24.902 - this.callback = callback;
24.903 - this.disposeCallback = disposeCallback;
24.904 - ko.exportProperty(this, 'dispose', this.dispose);
24.905 -};
24.906 -ko.subscription.prototype.dispose = function () {
24.907 - this.isDisposed = true;
24.908 - this.disposeCallback();
24.909 -};
24.910 -
24.911 -ko.subscribable = function () {
24.912 - this._subscriptions = {};
24.913 -
24.914 - ko.utils.extend(this, ko.subscribable['fn']);
24.915 - ko.exportProperty(this, 'subscribe', this.subscribe);
24.916 - ko.exportProperty(this, 'extend', this.extend);
24.917 - ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
24.918 -}
24.919 -
24.920 -var defaultEvent = "change";
24.921 -
24.922 -ko.subscribable['fn'] = {
24.923 - subscribe: function (callback, callbackTarget, event) {
24.924 - event = event || defaultEvent;
24.925 - var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
24.926 -
24.927 - var subscription = new ko.subscription(this, boundCallback, function () {
24.928 - ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
24.929 - }.bind(this));
24.930 -
24.931 - if (!this._subscriptions[event])
24.932 - this._subscriptions[event] = [];
24.933 - this._subscriptions[event].push(subscription);
24.934 - return subscription;
24.935 - },
24.936 -
24.937 - "notifySubscribers": function (valueToNotify, event) {
24.938 - event = event || defaultEvent;
24.939 - if (this._subscriptions[event]) {
24.940 - ko.dependencyDetection.ignore(function() {
24.941 - ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
24.942 - // In case a subscription was disposed during the arrayForEach cycle, check
24.943 - // for isDisposed on each subscription before invoking its callback
24.944 - if (subscription && (subscription.isDisposed !== true))
24.945 - subscription.callback(valueToNotify);
24.946 - });
24.947 - }, this);
24.948 - }
24.949 - },
24.950 -
24.951 - getSubscriptionsCount: function () {
24.952 - var total = 0;
24.953 - for (var eventName in this._subscriptions) {
24.954 - if (this._subscriptions.hasOwnProperty(eventName))
24.955 - total += this._subscriptions[eventName].length;
24.956 - }
24.957 - return total;
24.958 - },
24.959 -
24.960 - extend: applyExtenders
24.961 -};
24.962 -
24.963 -
24.964 -ko.isSubscribable = function (instance) {
24.965 - return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
24.966 -};
24.967 -
24.968 -ko.exportSymbol('subscribable', ko.subscribable);
24.969 -ko.exportSymbol('isSubscribable', ko.isSubscribable);
24.970 -
24.971 -ko.dependencyDetection = (function () {
24.972 - var _frames = [];
24.973 -
24.974 - return {
24.975 - begin: function (callback) {
24.976 - _frames.push({ callback: callback, distinctDependencies:[] });
24.977 - },
24.978 -
24.979 - end: function () {
24.980 - _frames.pop();
24.981 - },
24.982 -
24.983 - registerDependency: function (subscribable) {
24.984 - if (!ko.isSubscribable(subscribable))
24.985 - throw new Error("Only subscribable things can act as dependencies");
24.986 - if (_frames.length > 0) {
24.987 - var topFrame = _frames[_frames.length - 1];
24.988 - if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
24.989 - return;
24.990 - topFrame.distinctDependencies.push(subscribable);
24.991 - topFrame.callback(subscribable);
24.992 - }
24.993 - },
24.994 -
24.995 - ignore: function(callback, callbackTarget, callbackArgs) {
24.996 - try {
24.997 - _frames.push(null);
24.998 - return callback.apply(callbackTarget, callbackArgs || []);
24.999 - } finally {
24.1000 - _frames.pop();
24.1001 - }
24.1002 - }
24.1003 - };
24.1004 -})();
24.1005 -var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
24.1006 -
24.1007 -ko.observable = function (initialValue) {
24.1008 - var _latestValue = initialValue;
24.1009 -
24.1010 - function observable() {
24.1011 - if (arguments.length > 0) {
24.1012 - // Write
24.1013 -
24.1014 - // Ignore writes if the value hasn't changed
24.1015 - if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
24.1016 - observable.valueWillMutate();
24.1017 - _latestValue = arguments[0];
24.1018 - if (DEBUG) observable._latestValue = _latestValue;
24.1019 - observable.valueHasMutated();
24.1020 - }
24.1021 - return this; // Permits chained assignments
24.1022 - }
24.1023 - else {
24.1024 - // Read
24.1025 - ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
24.1026 - return _latestValue;
24.1027 - }
24.1028 - }
24.1029 - if (DEBUG) observable._latestValue = _latestValue;
24.1030 - ko.subscribable.call(observable);
24.1031 - observable.peek = function() { return _latestValue };
24.1032 - observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
24.1033 - observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
24.1034 - ko.utils.extend(observable, ko.observable['fn']);
24.1035 -
24.1036 - ko.exportProperty(observable, 'peek', observable.peek);
24.1037 - ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
24.1038 - ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
24.1039 -
24.1040 - return observable;
24.1041 -}
24.1042 -
24.1043 -ko.observable['fn'] = {
24.1044 - "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
24.1045 - var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
24.1046 - return oldValueIsPrimitive ? (a === b) : false;
24.1047 - }
24.1048 -};
24.1049 -
24.1050 -var protoProperty = ko.observable.protoProperty = "__ko_proto__";
24.1051 -ko.observable['fn'][protoProperty] = ko.observable;
24.1052 -
24.1053 -ko.hasPrototype = function(instance, prototype) {
24.1054 - if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
24.1055 - if (instance[protoProperty] === prototype) return true;
24.1056 - return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
24.1057 -};
24.1058 -
24.1059 -ko.isObservable = function (instance) {
24.1060 - return ko.hasPrototype(instance, ko.observable);
24.1061 -}
24.1062 -ko.isWriteableObservable = function (instance) {
24.1063 - // Observable
24.1064 - if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
24.1065 - return true;
24.1066 - // Writeable dependent observable
24.1067 - if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
24.1068 - return true;
24.1069 - // Anything else
24.1070 - return false;
24.1071 -}
24.1072 -
24.1073 -
24.1074 -ko.exportSymbol('observable', ko.observable);
24.1075 -ko.exportSymbol('isObservable', ko.isObservable);
24.1076 -ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
24.1077 -ko.observableArray = function (initialValues) {
24.1078 - if (arguments.length == 0) {
24.1079 - // Zero-parameter constructor initializes to empty array
24.1080 - initialValues = [];
24.1081 - }
24.1082 - if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
24.1083 - throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
24.1084 -
24.1085 - var result = ko.observable(initialValues);
24.1086 - ko.utils.extend(result, ko.observableArray['fn']);
24.1087 - return result;
24.1088 -}
24.1089 -
24.1090 -ko.observableArray['fn'] = {
24.1091 - 'remove': function (valueOrPredicate) {
24.1092 - var underlyingArray = this.peek();
24.1093 - var removedValues = [];
24.1094 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
24.1095 - for (var i = 0; i < underlyingArray.length; i++) {
24.1096 - var value = underlyingArray[i];
24.1097 - if (predicate(value)) {
24.1098 - if (removedValues.length === 0) {
24.1099 - this.valueWillMutate();
24.1100 - }
24.1101 - removedValues.push(value);
24.1102 - underlyingArray.splice(i, 1);
24.1103 - i--;
24.1104 - }
24.1105 - }
24.1106 - if (removedValues.length) {
24.1107 - this.valueHasMutated();
24.1108 - }
24.1109 - return removedValues;
24.1110 - },
24.1111 -
24.1112 - 'removeAll': function (arrayOfValues) {
24.1113 - // If you passed zero args, we remove everything
24.1114 - if (arrayOfValues === undefined) {
24.1115 - var underlyingArray = this.peek();
24.1116 - var allValues = underlyingArray.slice(0);
24.1117 - this.valueWillMutate();
24.1118 - underlyingArray.splice(0, underlyingArray.length);
24.1119 - this.valueHasMutated();
24.1120 - return allValues;
24.1121 - }
24.1122 - // If you passed an arg, we interpret it as an array of entries to remove
24.1123 - if (!arrayOfValues)
24.1124 - return [];
24.1125 - return this['remove'](function (value) {
24.1126 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
24.1127 - });
24.1128 - },
24.1129 -
24.1130 - 'destroy': function (valueOrPredicate) {
24.1131 - var underlyingArray = this.peek();
24.1132 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
24.1133 - this.valueWillMutate();
24.1134 - for (var i = underlyingArray.length - 1; i >= 0; i--) {
24.1135 - var value = underlyingArray[i];
24.1136 - if (predicate(value))
24.1137 - underlyingArray[i]["_destroy"] = true;
24.1138 - }
24.1139 - this.valueHasMutated();
24.1140 - },
24.1141 -
24.1142 - 'destroyAll': function (arrayOfValues) {
24.1143 - // If you passed zero args, we destroy everything
24.1144 - if (arrayOfValues === undefined)
24.1145 - return this['destroy'](function() { return true });
24.1146 -
24.1147 - // If you passed an arg, we interpret it as an array of entries to destroy
24.1148 - if (!arrayOfValues)
24.1149 - return [];
24.1150 - return this['destroy'](function (value) {
24.1151 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
24.1152 - });
24.1153 - },
24.1154 -
24.1155 - 'indexOf': function (item) {
24.1156 - var underlyingArray = this();
24.1157 - return ko.utils.arrayIndexOf(underlyingArray, item);
24.1158 - },
24.1159 -
24.1160 - 'replace': function(oldItem, newItem) {
24.1161 - var index = this['indexOf'](oldItem);
24.1162 - if (index >= 0) {
24.1163 - this.valueWillMutate();
24.1164 - this.peek()[index] = newItem;
24.1165 - this.valueHasMutated();
24.1166 - }
24.1167 - }
24.1168 -}
24.1169 -
24.1170 -// Populate ko.observableArray.fn with read/write functions from native arrays
24.1171 -// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
24.1172 -// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
24.1173 -ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
24.1174 - ko.observableArray['fn'][methodName] = function () {
24.1175 - // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
24.1176 - // (for consistency with mutating regular observables)
24.1177 - var underlyingArray = this.peek();
24.1178 - this.valueWillMutate();
24.1179 - var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
24.1180 - this.valueHasMutated();
24.1181 - return methodCallResult;
24.1182 - };
24.1183 -});
24.1184 -
24.1185 -// Populate ko.observableArray.fn with read-only functions from native arrays
24.1186 -ko.utils.arrayForEach(["slice"], function (methodName) {
24.1187 - ko.observableArray['fn'][methodName] = function () {
24.1188 - var underlyingArray = this();
24.1189 - return underlyingArray[methodName].apply(underlyingArray, arguments);
24.1190 - };
24.1191 -});
24.1192 -
24.1193 -ko.exportSymbol('observableArray', ko.observableArray);
24.1194 -ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
24.1195 - var _latestValue,
24.1196 - _hasBeenEvaluated = false,
24.1197 - _isBeingEvaluated = false,
24.1198 - readFunction = evaluatorFunctionOrOptions;
24.1199 -
24.1200 - if (readFunction && typeof readFunction == "object") {
24.1201 - // Single-parameter syntax - everything is on this "options" param
24.1202 - options = readFunction;
24.1203 - readFunction = options["read"];
24.1204 - } else {
24.1205 - // Multi-parameter syntax - construct the options according to the params passed
24.1206 - options = options || {};
24.1207 - if (!readFunction)
24.1208 - readFunction = options["read"];
24.1209 - }
24.1210 - if (typeof readFunction != "function")
24.1211 - throw new Error("Pass a function that returns the value of the ko.computed");
24.1212 -
24.1213 - function addSubscriptionToDependency(subscribable) {
24.1214 - _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
24.1215 - }
24.1216 -
24.1217 - function disposeAllSubscriptionsToDependencies() {
24.1218 - ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
24.1219 - subscription.dispose();
24.1220 - });
24.1221 - _subscriptionsToDependencies = [];
24.1222 - }
24.1223 -
24.1224 - function evaluatePossiblyAsync() {
24.1225 - var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
24.1226 - if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
24.1227 - clearTimeout(evaluationTimeoutInstance);
24.1228 - evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
24.1229 - } else
24.1230 - evaluateImmediate();
24.1231 - }
24.1232 -
24.1233 - function evaluateImmediate() {
24.1234 - if (_isBeingEvaluated) {
24.1235 - // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
24.1236 - // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
24.1237 - // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
24.1238 - // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
24.1239 - return;
24.1240 - }
24.1241 -
24.1242 - // Don't dispose on first evaluation, because the "disposeWhen" callback might
24.1243 - // e.g., dispose when the associated DOM element isn't in the doc, and it's not
24.1244 - // going to be in the doc until *after* the first evaluation
24.1245 - if (_hasBeenEvaluated && disposeWhen()) {
24.1246 - dispose();
24.1247 - return;
24.1248 - }
24.1249 -
24.1250 - _isBeingEvaluated = true;
24.1251 - try {
24.1252 - // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
24.1253 - // Then, during evaluation, we cross off any that are in fact still being used.
24.1254 - var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
24.1255 -
24.1256 - ko.dependencyDetection.begin(function(subscribable) {
24.1257 - var inOld;
24.1258 - if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
24.1259 - disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
24.1260 - else
24.1261 - addSubscriptionToDependency(subscribable); // Brand new subscription - add it
24.1262 - });
24.1263 -
24.1264 - var newValue = readFunction.call(evaluatorFunctionTarget);
24.1265 -
24.1266 - // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
24.1267 - for (var i = disposalCandidates.length - 1; i >= 0; i--) {
24.1268 - if (disposalCandidates[i])
24.1269 - _subscriptionsToDependencies.splice(i, 1)[0].dispose();
24.1270 - }
24.1271 - _hasBeenEvaluated = true;
24.1272 -
24.1273 - dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
24.1274 - _latestValue = newValue;
24.1275 - if (DEBUG) dependentObservable._latestValue = _latestValue;
24.1276 - } finally {
24.1277 - ko.dependencyDetection.end();
24.1278 - }
24.1279 -
24.1280 - dependentObservable["notifySubscribers"](_latestValue);
24.1281 - _isBeingEvaluated = false;
24.1282 - if (!_subscriptionsToDependencies.length)
24.1283 - dispose();
24.1284 - }
24.1285 -
24.1286 - function dependentObservable() {
24.1287 - if (arguments.length > 0) {
24.1288 - if (typeof writeFunction === "function") {
24.1289 - // Writing a value
24.1290 - writeFunction.apply(evaluatorFunctionTarget, arguments);
24.1291 - } else {
24.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.");
24.1293 - }
24.1294 - return this; // Permits chained assignments
24.1295 - } else {
24.1296 - // Reading the value
24.1297 - if (!_hasBeenEvaluated)
24.1298 - evaluateImmediate();
24.1299 - ko.dependencyDetection.registerDependency(dependentObservable);
24.1300 - return _latestValue;
24.1301 - }
24.1302 - }
24.1303 -
24.1304 - function peek() {
24.1305 - if (!_hasBeenEvaluated)
24.1306 - evaluateImmediate();
24.1307 - return _latestValue;
24.1308 - }
24.1309 -
24.1310 - function isActive() {
24.1311 - return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
24.1312 - }
24.1313 -
24.1314 - // By here, "options" is always non-null
24.1315 - var writeFunction = options["write"],
24.1316 - disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
24.1317 - disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
24.1318 - dispose = disposeAllSubscriptionsToDependencies,
24.1319 - _subscriptionsToDependencies = [],
24.1320 - evaluationTimeoutInstance = null;
24.1321 -
24.1322 - if (!evaluatorFunctionTarget)
24.1323 - evaluatorFunctionTarget = options["owner"];
24.1324 -
24.1325 - dependentObservable.peek = peek;
24.1326 - dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
24.1327 - dependentObservable.hasWriteFunction = typeof options["write"] === "function";
24.1328 - dependentObservable.dispose = function () { dispose(); };
24.1329 - dependentObservable.isActive = isActive;
24.1330 - dependentObservable.valueHasMutated = function() {
24.1331 - _hasBeenEvaluated = false;
24.1332 - evaluateImmediate();
24.1333 - };
24.1334 -
24.1335 - ko.subscribable.call(dependentObservable);
24.1336 - ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
24.1337 -
24.1338 - ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
24.1339 - ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
24.1340 - ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
24.1341 - ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
24.1342 -
24.1343 - // Evaluate, unless deferEvaluation is true
24.1344 - if (options['deferEvaluation'] !== true)
24.1345 - evaluateImmediate();
24.1346 -
24.1347 - // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
24.1348 - // But skip if isActive is false (there will never be any dependencies to dispose).
24.1349 - // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
24.1350 - // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
24.1351 - if (disposeWhenNodeIsRemoved && isActive()) {
24.1352 - dispose = function() {
24.1353 - ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
24.1354 - disposeAllSubscriptionsToDependencies();
24.1355 - };
24.1356 - ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
24.1357 - var existingDisposeWhenFunction = disposeWhen;
24.1358 - disposeWhen = function () {
24.1359 - return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
24.1360 - }
24.1361 - }
24.1362 -
24.1363 - return dependentObservable;
24.1364 -};
24.1365 -
24.1366 -ko.isComputed = function(instance) {
24.1367 - return ko.hasPrototype(instance, ko.dependentObservable);
24.1368 -};
24.1369 -
24.1370 -var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
24.1371 -ko.dependentObservable[protoProp] = ko.observable;
24.1372 -
24.1373 -ko.dependentObservable['fn'] = {};
24.1374 -ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
24.1375 -
24.1376 -ko.exportSymbol('dependentObservable', ko.dependentObservable);
24.1377 -ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
24.1378 -ko.exportSymbol('isComputed', ko.isComputed);
24.1379 -
24.1380 -(function() {
24.1381 - var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
24.1382 -
24.1383 - ko.toJS = function(rootObject) {
24.1384 - if (arguments.length == 0)
24.1385 - throw new Error("When calling ko.toJS, pass the object you want to convert.");
24.1386 -
24.1387 - // We just unwrap everything at every level in the object graph
24.1388 - return mapJsObjectGraph(rootObject, function(valueToMap) {
24.1389 - // Loop because an observable's value might in turn be another observable wrapper
24.1390 - for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
24.1391 - valueToMap = valueToMap();
24.1392 - return valueToMap;
24.1393 - });
24.1394 - };
24.1395 -
24.1396 - ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
24.1397 - var plainJavaScriptObject = ko.toJS(rootObject);
24.1398 - return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
24.1399 - };
24.1400 -
24.1401 - function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
24.1402 - visitedObjects = visitedObjects || new objectLookup();
24.1403 -
24.1404 - rootObject = mapInputCallback(rootObject);
24.1405 - var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
24.1406 - if (!canHaveProperties)
24.1407 - return rootObject;
24.1408 -
24.1409 - var outputProperties = rootObject instanceof Array ? [] : {};
24.1410 - visitedObjects.save(rootObject, outputProperties);
24.1411 -
24.1412 - visitPropertiesOrArrayEntries(rootObject, function(indexer) {
24.1413 - var propertyValue = mapInputCallback(rootObject[indexer]);
24.1414 -
24.1415 - switch (typeof propertyValue) {
24.1416 - case "boolean":
24.1417 - case "number":
24.1418 - case "string":
24.1419 - case "function":
24.1420 - outputProperties[indexer] = propertyValue;
24.1421 - break;
24.1422 - case "object":
24.1423 - case "undefined":
24.1424 - var previouslyMappedValue = visitedObjects.get(propertyValue);
24.1425 - outputProperties[indexer] = (previouslyMappedValue !== undefined)
24.1426 - ? previouslyMappedValue
24.1427 - : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
24.1428 - break;
24.1429 - }
24.1430 - });
24.1431 -
24.1432 - return outputProperties;
24.1433 - }
24.1434 -
24.1435 - function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
24.1436 - if (rootObject instanceof Array) {
24.1437 - for (var i = 0; i < rootObject.length; i++)
24.1438 - visitorCallback(i);
24.1439 -
24.1440 - // For arrays, also respect toJSON property for custom mappings (fixes #278)
24.1441 - if (typeof rootObject['toJSON'] == 'function')
24.1442 - visitorCallback('toJSON');
24.1443 - } else {
24.1444 - for (var propertyName in rootObject)
24.1445 - visitorCallback(propertyName);
24.1446 - }
24.1447 - };
24.1448 -
24.1449 - function objectLookup() {
24.1450 - var keys = [];
24.1451 - var values = [];
24.1452 - this.save = function(key, value) {
24.1453 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
24.1454 - if (existingIndex >= 0)
24.1455 - values[existingIndex] = value;
24.1456 - else {
24.1457 - keys.push(key);
24.1458 - values.push(value);
24.1459 - }
24.1460 - };
24.1461 - this.get = function(key) {
24.1462 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
24.1463 - return (existingIndex >= 0) ? values[existingIndex] : undefined;
24.1464 - };
24.1465 - };
24.1466 -})();
24.1467 -
24.1468 -ko.exportSymbol('toJS', ko.toJS);
24.1469 -ko.exportSymbol('toJSON', ko.toJSON);
24.1470 -(function () {
24.1471 - var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
24.1472 -
24.1473 - // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
24.1474 - // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
24.1475 - // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
24.1476 - ko.selectExtensions = {
24.1477 - readValue : function(element) {
24.1478 - switch (ko.utils.tagNameLower(element)) {
24.1479 - case 'option':
24.1480 - if (element[hasDomDataExpandoProperty] === true)
24.1481 - return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
24.1482 - return ko.utils.ieVersion <= 7
24.1483 - ? (element.getAttributeNode('value').specified ? element.value : element.text)
24.1484 - : element.value;
24.1485 - case 'select':
24.1486 - return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
24.1487 - default:
24.1488 - return element.value;
24.1489 - }
24.1490 - },
24.1491 -
24.1492 - writeValue: function(element, value) {
24.1493 - switch (ko.utils.tagNameLower(element)) {
24.1494 - case 'option':
24.1495 - switch(typeof value) {
24.1496 - case "string":
24.1497 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
24.1498 - if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
24.1499 - delete element[hasDomDataExpandoProperty];
24.1500 - }
24.1501 - element.value = value;
24.1502 - break;
24.1503 - default:
24.1504 - // Store arbitrary object using DomData
24.1505 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
24.1506 - element[hasDomDataExpandoProperty] = true;
24.1507 -
24.1508 - // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
24.1509 - element.value = typeof value === "number" ? value : "";
24.1510 - break;
24.1511 - }
24.1512 - break;
24.1513 - case 'select':
24.1514 - for (var i = element.options.length - 1; i >= 0; i--) {
24.1515 - if (ko.selectExtensions.readValue(element.options[i]) == value) {
24.1516 - element.selectedIndex = i;
24.1517 - break;
24.1518 - }
24.1519 - }
24.1520 - break;
24.1521 - default:
24.1522 - if ((value === null) || (value === undefined))
24.1523 - value = "";
24.1524 - element.value = value;
24.1525 - break;
24.1526 - }
24.1527 - }
24.1528 - };
24.1529 -})();
24.1530 -
24.1531 -ko.exportSymbol('selectExtensions', ko.selectExtensions);
24.1532 -ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
24.1533 -ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
24.1534 -ko.expressionRewriting = (function () {
24.1535 - var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
24.1536 - var javaScriptReservedWords = ["true", "false"];
24.1537 -
24.1538 - // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
24.1539 - // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
24.1540 - var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
24.1541 -
24.1542 - function restoreTokens(string, tokens) {
24.1543 - var prevValue = null;
24.1544 - while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
24.1545 - prevValue = string;
24.1546 - string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
24.1547 - return tokens[tokenIndex];
24.1548 - });
24.1549 - }
24.1550 - return string;
24.1551 - }
24.1552 -
24.1553 - function getWriteableValue(expression) {
24.1554 - if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
24.1555 - return false;
24.1556 - var match = expression.match(javaScriptAssignmentTarget);
24.1557 - return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
24.1558 - }
24.1559 -
24.1560 - function ensureQuoted(key) {
24.1561 - var trimmedKey = ko.utils.stringTrim(key);
24.1562 - switch (trimmedKey.length && trimmedKey.charAt(0)) {
24.1563 - case "'":
24.1564 - case '"':
24.1565 - return key;
24.1566 - default:
24.1567 - return "'" + trimmedKey + "'";
24.1568 - }
24.1569 - }
24.1570 -
24.1571 - return {
24.1572 - bindingRewriteValidators: [],
24.1573 -
24.1574 - parseObjectLiteral: function(objectLiteralString) {
24.1575 - // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
24.1576 - // that is sufficient just to split an object literal string into a set of top-level key-value pairs
24.1577 -
24.1578 - var str = ko.utils.stringTrim(objectLiteralString);
24.1579 - if (str.length < 3)
24.1580 - return [];
24.1581 - if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
24.1582 - str = str.substring(1, str.length - 1);
24.1583 -
24.1584 - // Pull out any string literals and regex literals
24.1585 - var tokens = [];
24.1586 - var tokenStart = null, tokenEndChar;
24.1587 - for (var position = 0; position < str.length; position++) {
24.1588 - var c = str.charAt(position);
24.1589 - if (tokenStart === null) {
24.1590 - switch (c) {
24.1591 - case '"':
24.1592 - case "'":
24.1593 - case "/":
24.1594 - tokenStart = position;
24.1595 - tokenEndChar = c;
24.1596 - break;
24.1597 - }
24.1598 - } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
24.1599 - var token = str.substring(tokenStart, position + 1);
24.1600 - tokens.push(token);
24.1601 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
24.1602 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
24.1603 - position -= (token.length - replacement.length);
24.1604 - tokenStart = null;
24.1605 - }
24.1606 - }
24.1607 -
24.1608 - // Next pull out balanced paren, brace, and bracket blocks
24.1609 - tokenStart = null;
24.1610 - tokenEndChar = null;
24.1611 - var tokenDepth = 0, tokenStartChar = null;
24.1612 - for (var position = 0; position < str.length; position++) {
24.1613 - var c = str.charAt(position);
24.1614 - if (tokenStart === null) {
24.1615 - switch (c) {
24.1616 - case "{": tokenStart = position; tokenStartChar = c;
24.1617 - tokenEndChar = "}";
24.1618 - break;
24.1619 - case "(": tokenStart = position; tokenStartChar = c;
24.1620 - tokenEndChar = ")";
24.1621 - break;
24.1622 - case "[": tokenStart = position; tokenStartChar = c;
24.1623 - tokenEndChar = "]";
24.1624 - break;
24.1625 - }
24.1626 - }
24.1627 -
24.1628 - if (c === tokenStartChar)
24.1629 - tokenDepth++;
24.1630 - else if (c === tokenEndChar) {
24.1631 - tokenDepth--;
24.1632 - if (tokenDepth === 0) {
24.1633 - var token = str.substring(tokenStart, position + 1);
24.1634 - tokens.push(token);
24.1635 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
24.1636 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
24.1637 - position -= (token.length - replacement.length);
24.1638 - tokenStart = null;
24.1639 - }
24.1640 - }
24.1641 - }
24.1642 -
24.1643 - // Now we can safely split on commas to get the key/value pairs
24.1644 - var result = [];
24.1645 - var keyValuePairs = str.split(",");
24.1646 - for (var i = 0, j = keyValuePairs.length; i < j; i++) {
24.1647 - var pair = keyValuePairs[i];
24.1648 - var colonPos = pair.indexOf(":");
24.1649 - if ((colonPos > 0) && (colonPos < pair.length - 1)) {
24.1650 - var key = pair.substring(0, colonPos);
24.1651 - var value = pair.substring(colonPos + 1);
24.1652 - result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
24.1653 - } else {
24.1654 - result.push({ 'unknown': restoreTokens(pair, tokens) });
24.1655 - }
24.1656 - }
24.1657 - return result;
24.1658 - },
24.1659 -
24.1660 - preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
24.1661 - var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
24.1662 - ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
24.1663 - : objectLiteralStringOrKeyValueArray;
24.1664 - var resultStrings = [], propertyAccessorResultStrings = [];
24.1665 -
24.1666 - var keyValueEntry;
24.1667 - for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
24.1668 - if (resultStrings.length > 0)
24.1669 - resultStrings.push(",");
24.1670 -
24.1671 - if (keyValueEntry['key']) {
24.1672 - var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
24.1673 - resultStrings.push(quotedKey);
24.1674 - resultStrings.push(":");
24.1675 - resultStrings.push(val);
24.1676 -
24.1677 - if (val = getWriteableValue(ko.utils.stringTrim(val))) {
24.1678 - if (propertyAccessorResultStrings.length > 0)
24.1679 - propertyAccessorResultStrings.push(", ");
24.1680 - propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
24.1681 - }
24.1682 - } else if (keyValueEntry['unknown']) {
24.1683 - resultStrings.push(keyValueEntry['unknown']);
24.1684 - }
24.1685 - }
24.1686 -
24.1687 - var combinedResult = resultStrings.join("");
24.1688 - if (propertyAccessorResultStrings.length > 0) {
24.1689 - var allPropertyAccessors = propertyAccessorResultStrings.join("");
24.1690 - combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
24.1691 - }
24.1692 -
24.1693 - return combinedResult;
24.1694 - },
24.1695 -
24.1696 - keyValueArrayContainsKey: function(keyValueArray, key) {
24.1697 - for (var i = 0; i < keyValueArray.length; i++)
24.1698 - if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
24.1699 - return true;
24.1700 - return false;
24.1701 - },
24.1702 -
24.1703 - // Internal, private KO utility for updating model properties from within bindings
24.1704 - // property: If the property being updated is (or might be) an observable, pass it here
24.1705 - // If it turns out to be a writable observable, it will be written to directly
24.1706 - // allBindingsAccessor: All bindings in the current execution context.
24.1707 - // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
24.1708 - // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
24.1709 - // value: The value to be written
24.1710 - // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
24.1711 - // it is !== existing value on that writable observable
24.1712 - writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
24.1713 - if (!property || !ko.isWriteableObservable(property)) {
24.1714 - var propWriters = allBindingsAccessor()['_ko_property_writers'];
24.1715 - if (propWriters && propWriters[key])
24.1716 - propWriters[key](value);
24.1717 - } else if (!checkIfDifferent || property.peek() !== value) {
24.1718 - property(value);
24.1719 - }
24.1720 - }
24.1721 - };
24.1722 -})();
24.1723 -
24.1724 -ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
24.1725 -ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
24.1726 -ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
24.1727 -ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
24.1728 -
24.1729 -// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
24.1730 -// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
24.1731 -ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
24.1732 -ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
24.1733 - // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
24.1734 - // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
24.1735 - // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
24.1736 - // of that virtual hierarchy
24.1737 - //
24.1738 - // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
24.1739 - // without having to scatter special cases all over the binding and templating code.
24.1740 -
24.1741 - // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
24.1742 - // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
24.1743 - // So, use node.text where available, and node.nodeValue elsewhere
24.1744 - var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
24.1745 -
24.1746 - var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
24.1747 - var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
24.1748 - var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
24.1749 -
24.1750 - function isStartComment(node) {
24.1751 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
24.1752 - }
24.1753 -
24.1754 - function isEndComment(node) {
24.1755 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
24.1756 - }
24.1757 -
24.1758 - function getVirtualChildren(startComment, allowUnbalanced) {
24.1759 - var currentNode = startComment;
24.1760 - var depth = 1;
24.1761 - var children = [];
24.1762 - while (currentNode = currentNode.nextSibling) {
24.1763 - if (isEndComment(currentNode)) {
24.1764 - depth--;
24.1765 - if (depth === 0)
24.1766 - return children;
24.1767 - }
24.1768 -
24.1769 - children.push(currentNode);
24.1770 -
24.1771 - if (isStartComment(currentNode))
24.1772 - depth++;
24.1773 - }
24.1774 - if (!allowUnbalanced)
24.1775 - throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
24.1776 - return null;
24.1777 - }
24.1778 -
24.1779 - function getMatchingEndComment(startComment, allowUnbalanced) {
24.1780 - var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
24.1781 - if (allVirtualChildren) {
24.1782 - if (allVirtualChildren.length > 0)
24.1783 - return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
24.1784 - return startComment.nextSibling;
24.1785 - } else
24.1786 - return null; // Must have no matching end comment, and allowUnbalanced is true
24.1787 - }
24.1788 -
24.1789 - function getUnbalancedChildTags(node) {
24.1790 - // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
24.1791 - // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
24.1792 - var childNode = node.firstChild, captureRemaining = null;
24.1793 - if (childNode) {
24.1794 - do {
24.1795 - if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
24.1796 - captureRemaining.push(childNode);
24.1797 - else if (isStartComment(childNode)) {
24.1798 - var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
24.1799 - if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
24.1800 - childNode = matchingEndComment;
24.1801 - else
24.1802 - captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
24.1803 - } else if (isEndComment(childNode)) {
24.1804 - captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
24.1805 - }
24.1806 - } while (childNode = childNode.nextSibling);
24.1807 - }
24.1808 - return captureRemaining;
24.1809 - }
24.1810 -
24.1811 - ko.virtualElements = {
24.1812 - allowedBindings: {},
24.1813 -
24.1814 - childNodes: function(node) {
24.1815 - return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
24.1816 - },
24.1817 -
24.1818 - emptyNode: function(node) {
24.1819 - if (!isStartComment(node))
24.1820 - ko.utils.emptyDomNode(node);
24.1821 - else {
24.1822 - var virtualChildren = ko.virtualElements.childNodes(node);
24.1823 - for (var i = 0, j = virtualChildren.length; i < j; i++)
24.1824 - ko.removeNode(virtualChildren[i]);
24.1825 - }
24.1826 - },
24.1827 -
24.1828 - setDomNodeChildren: function(node, childNodes) {
24.1829 - if (!isStartComment(node))
24.1830 - ko.utils.setDomNodeChildren(node, childNodes);
24.1831 - else {
24.1832 - ko.virtualElements.emptyNode(node);
24.1833 - var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
24.1834 - for (var i = 0, j = childNodes.length; i < j; i++)
24.1835 - endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
24.1836 - }
24.1837 - },
24.1838 -
24.1839 - prepend: function(containerNode, nodeToPrepend) {
24.1840 - if (!isStartComment(containerNode)) {
24.1841 - if (containerNode.firstChild)
24.1842 - containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
24.1843 - else
24.1844 - containerNode.appendChild(nodeToPrepend);
24.1845 - } else {
24.1846 - // Start comments must always have a parent and at least one following sibling (the end comment)
24.1847 - containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
24.1848 - }
24.1849 - },
24.1850 -
24.1851 - insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
24.1852 - if (!insertAfterNode) {
24.1853 - ko.virtualElements.prepend(containerNode, nodeToInsert);
24.1854 - } else if (!isStartComment(containerNode)) {
24.1855 - // Insert after insertion point
24.1856 - if (insertAfterNode.nextSibling)
24.1857 - containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
24.1858 - else
24.1859 - containerNode.appendChild(nodeToInsert);
24.1860 - } else {
24.1861 - // Children of start comments must always have a parent and at least one following sibling (the end comment)
24.1862 - containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
24.1863 - }
24.1864 - },
24.1865 -
24.1866 - firstChild: function(node) {
24.1867 - if (!isStartComment(node))
24.1868 - return node.firstChild;
24.1869 - if (!node.nextSibling || isEndComment(node.nextSibling))
24.1870 - return null;
24.1871 - return node.nextSibling;
24.1872 - },
24.1873 -
24.1874 - nextSibling: function(node) {
24.1875 - if (isStartComment(node))
24.1876 - node = getMatchingEndComment(node);
24.1877 - if (node.nextSibling && isEndComment(node.nextSibling))
24.1878 - return null;
24.1879 - return node.nextSibling;
24.1880 - },
24.1881 -
24.1882 - virtualNodeBindingValue: function(node) {
24.1883 - var regexMatch = isStartComment(node);
24.1884 - return regexMatch ? regexMatch[1] : null;
24.1885 - },
24.1886 -
24.1887 - normaliseVirtualElementDomStructure: function(elementVerified) {
24.1888 - // Workaround for https://github.com/SteveSanderson/knockout/issues/155
24.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
24.1890 - // that are direct descendants of <ul> into the preceding <li>)
24.1891 - if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
24.1892 - return;
24.1893 -
24.1894 - // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
24.1895 - // must be intended to appear *after* that child, so move them there.
24.1896 - var childNode = elementVerified.firstChild;
24.1897 - if (childNode) {
24.1898 - do {
24.1899 - if (childNode.nodeType === 1) {
24.1900 - var unbalancedTags = getUnbalancedChildTags(childNode);
24.1901 - if (unbalancedTags) {
24.1902 - // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
24.1903 - var nodeToInsertBefore = childNode.nextSibling;
24.1904 - for (var i = 0; i < unbalancedTags.length; i++) {
24.1905 - if (nodeToInsertBefore)
24.1906 - elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
24.1907 - else
24.1908 - elementVerified.appendChild(unbalancedTags[i]);
24.1909 - }
24.1910 - }
24.1911 - }
24.1912 - } while (childNode = childNode.nextSibling);
24.1913 - }
24.1914 - }
24.1915 - };
24.1916 -})();
24.1917 -ko.exportSymbol('virtualElements', ko.virtualElements);
24.1918 -ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
24.1919 -ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
24.1920 -//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
24.1921 -ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
24.1922 -//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
24.1923 -ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
24.1924 -ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
24.1925 -(function() {
24.1926 - var defaultBindingAttributeName = "data-bind";
24.1927 -
24.1928 - ko.bindingProvider = function() {
24.1929 - this.bindingCache = {};
24.1930 - };
24.1931 -
24.1932 - ko.utils.extend(ko.bindingProvider.prototype, {
24.1933 - 'nodeHasBindings': function(node) {
24.1934 - switch (node.nodeType) {
24.1935 - case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
24.1936 - case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
24.1937 - default: return false;
24.1938 - }
24.1939 - },
24.1940 -
24.1941 - 'getBindings': function(node, bindingContext) {
24.1942 - var bindingsString = this['getBindingsString'](node, bindingContext);
24.1943 - return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
24.1944 - },
24.1945 -
24.1946 - // The following function is only used internally by this default provider.
24.1947 - // It's not part of the interface definition for a general binding provider.
24.1948 - 'getBindingsString': function(node, bindingContext) {
24.1949 - switch (node.nodeType) {
24.1950 - case 1: return node.getAttribute(defaultBindingAttributeName); // Element
24.1951 - case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
24.1952 - default: return null;
24.1953 - }
24.1954 - },
24.1955 -
24.1956 - // The following function is only used internally by this default provider.
24.1957 - // It's not part of the interface definition for a general binding provider.
24.1958 - 'parseBindingsString': function(bindingsString, bindingContext, node) {
24.1959 - try {
24.1960 - var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
24.1961 - return bindingFunction(bindingContext, node);
24.1962 - } catch (ex) {
24.1963 - throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
24.1964 - }
24.1965 - }
24.1966 - });
24.1967 -
24.1968 - ko.bindingProvider['instance'] = new ko.bindingProvider();
24.1969 -
24.1970 - function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
24.1971 - var cacheKey = bindingsString;
24.1972 - return cache[cacheKey]
24.1973 - || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
24.1974 - }
24.1975 -
24.1976 - function createBindingsStringEvaluator(bindingsString) {
24.1977 - // Build the source for a function that evaluates "expression"
24.1978 - // For each scope variable, add an extra level of "with" nesting
24.1979 - // Example result: with(sc1) { with(sc0) { return (expression) } }
24.1980 - var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
24.1981 - functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
24.1982 - return new Function("$context", "$element", functionBody);
24.1983 - }
24.1984 -})();
24.1985 -
24.1986 -ko.exportSymbol('bindingProvider', ko.bindingProvider);
24.1987 -(function () {
24.1988 - ko.bindingHandlers = {};
24.1989 -
24.1990 - ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
24.1991 - if (parentBindingContext) {
24.1992 - ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
24.1993 - this['$parentContext'] = parentBindingContext;
24.1994 - this['$parent'] = parentBindingContext['$data'];
24.1995 - this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
24.1996 - this['$parents'].unshift(this['$parent']);
24.1997 - } else {
24.1998 - this['$parents'] = [];
24.1999 - this['$root'] = dataItem;
24.2000 - // Export 'ko' in the binding context so it will be available in bindings and templates
24.2001 - // even if 'ko' isn't exported as a global, such as when using an AMD loader.
24.2002 - // See https://github.com/SteveSanderson/knockout/issues/490
24.2003 - this['ko'] = ko;
24.2004 - }
24.2005 - this['$data'] = dataItem;
24.2006 - if (dataItemAlias)
24.2007 - this[dataItemAlias] = dataItem;
24.2008 - }
24.2009 - ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
24.2010 - return new ko.bindingContext(dataItem, this, dataItemAlias);
24.2011 - };
24.2012 - ko.bindingContext.prototype['extend'] = function(properties) {
24.2013 - var clone = ko.utils.extend(new ko.bindingContext(), this);
24.2014 - return ko.utils.extend(clone, properties);
24.2015 - };
24.2016 -
24.2017 - function validateThatBindingIsAllowedForVirtualElements(bindingName) {
24.2018 - var validator = ko.virtualElements.allowedBindings[bindingName];
24.2019 - if (!validator)
24.2020 - throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
24.2021 - }
24.2022 -
24.2023 - function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
24.2024 - var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
24.2025 - while (currentChild = nextInQueue) {
24.2026 - // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
24.2027 - nextInQueue = ko.virtualElements.nextSibling(currentChild);
24.2028 - applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
24.2029 - }
24.2030 - }
24.2031 -
24.2032 - function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
24.2033 - var shouldBindDescendants = true;
24.2034 -
24.2035 - // Perf optimisation: Apply bindings only if...
24.2036 - // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
24.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
24.2038 - // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
24.2039 - var isElement = (nodeVerified.nodeType === 1);
24.2040 - if (isElement) // Workaround IE <= 8 HTML parsing weirdness
24.2041 - ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
24.2042 -
24.2043 - var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
24.2044 - || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
24.2045 - if (shouldApplyBindings)
24.2046 - shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
24.2047 -
24.2048 - if (shouldBindDescendants) {
24.2049 - // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
24.2050 - // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
24.2051 - // hence bindingContextsMayDifferFromDomParentElement is false
24.2052 - // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
24.2053 - // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
24.2054 - // hence bindingContextsMayDifferFromDomParentElement is true
24.2055 - applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
24.2056 - }
24.2057 - }
24.2058 -
24.2059 - function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
24.2060 - // Need to be sure that inits are only run once, and updates never run until all the inits have been run
24.2061 - var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
24.2062 -
24.2063 - // Each time the dependentObservable is evaluated (after data changes),
24.2064 - // the binding attribute is reparsed so that it can pick out the correct
24.2065 - // model properties in the context of the changed data.
24.2066 - // DOM event callbacks need to be able to access this changed data,
24.2067 - // so we need a single parsedBindings variable (shared by all callbacks
24.2068 - // associated with this node's bindings) that all the closures can access.
24.2069 - var parsedBindings;
24.2070 - function makeValueAccessor(bindingKey) {
24.2071 - return function () { return parsedBindings[bindingKey] }
24.2072 - }
24.2073 - function parsedBindingsAccessor() {
24.2074 - return parsedBindings;
24.2075 - }
24.2076 -
24.2077 - var bindingHandlerThatControlsDescendantBindings;
24.2078 - ko.dependentObservable(
24.2079 - function () {
24.2080 - // Ensure we have a nonnull binding context to work with
24.2081 - var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
24.2082 - ? viewModelOrBindingContext
24.2083 - : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
24.2084 - var viewModel = bindingContextInstance['$data'];
24.2085 -
24.2086 - // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
24.2087 - // we can easily recover it just by scanning up the node's ancestors in the DOM
24.2088 - // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
24.2089 - if (bindingContextMayDifferFromDomParentElement)
24.2090 - ko.storedBindingContextForNode(node, bindingContextInstance);
24.2091 -
24.2092 - // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
24.2093 - var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
24.2094 - parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
24.2095 -
24.2096 - if (parsedBindings) {
24.2097 - // First run all the inits, so bindings can register for notification on changes
24.2098 - if (initPhase === 0) {
24.2099 - initPhase = 1;
24.2100 - for (var bindingKey in parsedBindings) {
24.2101 - var binding = ko.bindingHandlers[bindingKey];
24.2102 - if (binding && node.nodeType === 8)
24.2103 - validateThatBindingIsAllowedForVirtualElements(bindingKey);
24.2104 -
24.2105 - if (binding && typeof binding["init"] == "function") {
24.2106 - var handlerInitFn = binding["init"];
24.2107 - var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
24.2108 -
24.2109 - // If this binding handler claims to control descendant bindings, make a note of this
24.2110 - if (initResult && initResult['controlsDescendantBindings']) {
24.2111 - if (bindingHandlerThatControlsDescendantBindings !== undefined)
24.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.");
24.2113 - bindingHandlerThatControlsDescendantBindings = bindingKey;
24.2114 - }
24.2115 - }
24.2116 - }
24.2117 - initPhase = 2;
24.2118 - }
24.2119 -
24.2120 - // ... then run all the updates, which might trigger changes even on the first evaluation
24.2121 - if (initPhase === 2) {
24.2122 - for (var bindingKey in parsedBindings) {
24.2123 - var binding = ko.bindingHandlers[bindingKey];
24.2124 - if (binding && typeof binding["update"] == "function") {
24.2125 - var handlerUpdateFn = binding["update"];
24.2126 - handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
24.2127 - }
24.2128 - }
24.2129 - }
24.2130 - }
24.2131 - },
24.2132 - null,
24.2133 - { disposeWhenNodeIsRemoved : node }
24.2134 - );
24.2135 -
24.2136 - return {
24.2137 - shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
24.2138 - };
24.2139 - };
24.2140 -
24.2141 - var storedBindingContextDomDataKey = "__ko_bindingContext__";
24.2142 - ko.storedBindingContextForNode = function (node, bindingContext) {
24.2143 - if (arguments.length == 2)
24.2144 - ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
24.2145 - else
24.2146 - return ko.utils.domData.get(node, storedBindingContextDomDataKey);
24.2147 - }
24.2148 -
24.2149 - ko.applyBindingsToNode = function (node, bindings, viewModel) {
24.2150 - if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
24.2151 - ko.virtualElements.normaliseVirtualElementDomStructure(node);
24.2152 - return applyBindingsToNodeInternal(node, bindings, viewModel, true);
24.2153 - };
24.2154 -
24.2155 - ko.applyBindingsToDescendants = function(viewModel, rootNode) {
24.2156 - if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
24.2157 - applyBindingsToDescendantsInternal(viewModel, rootNode, true);
24.2158 - };
24.2159 -
24.2160 - ko.applyBindings = function (viewModel, rootNode) {
24.2161 - if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
24.2162 - throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
24.2163 - rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
24.2164 -
24.2165 - applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
24.2166 - };
24.2167 -
24.2168 - // Retrieving binding context from arbitrary nodes
24.2169 - ko.contextFor = function(node) {
24.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)
24.2171 - switch (node.nodeType) {
24.2172 - case 1:
24.2173 - case 8:
24.2174 - var context = ko.storedBindingContextForNode(node);
24.2175 - if (context) return context;
24.2176 - if (node.parentNode) return ko.contextFor(node.parentNode);
24.2177 - break;
24.2178 - }
24.2179 - return undefined;
24.2180 - };
24.2181 - ko.dataFor = function(node) {
24.2182 - var context = ko.contextFor(node);
24.2183 - return context ? context['$data'] : undefined;
24.2184 - };
24.2185 -
24.2186 - ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
24.2187 - ko.exportSymbol('applyBindings', ko.applyBindings);
24.2188 - ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
24.2189 - ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
24.2190 - ko.exportSymbol('contextFor', ko.contextFor);
24.2191 - ko.exportSymbol('dataFor', ko.dataFor);
24.2192 -})();
24.2193 -var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
24.2194 -ko.bindingHandlers['attr'] = {
24.2195 - 'update': function(element, valueAccessor, allBindingsAccessor) {
24.2196 - var value = ko.utils.unwrapObservable(valueAccessor()) || {};
24.2197 - for (var attrName in value) {
24.2198 - if (typeof attrName == "string") {
24.2199 - var attrValue = ko.utils.unwrapObservable(value[attrName]);
24.2200 -
24.2201 - // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
24.2202 - // when someProp is a "no value"-like value (strictly null, false, or undefined)
24.2203 - // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
24.2204 - var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
24.2205 - if (toRemove)
24.2206 - element.removeAttribute(attrName);
24.2207 -
24.2208 - // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
24.2209 - // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
24.2210 - // but instead of figuring out the mode, we'll just set the attribute through the Javascript
24.2211 - // property for IE <= 8.
24.2212 - if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
24.2213 - attrName = attrHtmlToJavascriptMap[attrName];
24.2214 - if (toRemove)
24.2215 - element.removeAttribute(attrName);
24.2216 - else
24.2217 - element[attrName] = attrValue;
24.2218 - } else if (!toRemove) {
24.2219 - try {
24.2220 - element.setAttribute(attrName, attrValue.toString());
24.2221 - } catch (err) {
24.2222 - // ignore for now
24.2223 - if (console) {
24.2224 - console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
24.2225 - }
24.2226 - }
24.2227 - }
24.2228 -
24.2229 - // Treat "name" specially - although you can think of it as an attribute, it also needs
24.2230 - // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
24.2231 - // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
24.2232 - // entirely, and there's no strong reason to allow for such casing in HTML.
24.2233 - if (attrName === "name") {
24.2234 - ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
24.2235 - }
24.2236 - }
24.2237 - }
24.2238 - }
24.2239 -};
24.2240 -ko.bindingHandlers['checked'] = {
24.2241 - 'init': function (element, valueAccessor, allBindingsAccessor) {
24.2242 - var updateHandler = function() {
24.2243 - var valueToWrite;
24.2244 - if (element.type == "checkbox") {
24.2245 - valueToWrite = element.checked;
24.2246 - } else if ((element.type == "radio") && (element.checked)) {
24.2247 - valueToWrite = element.value;
24.2248 - } else {
24.2249 - return; // "checked" binding only responds to checkboxes and selected radio buttons
24.2250 - }
24.2251 -
24.2252 - var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
24.2253 - if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
24.2254 - // For checkboxes bound to an array, we add/remove the checkbox value to that array
24.2255 - // This works for both observable and non-observable arrays
24.2256 - var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
24.2257 - if (element.checked && (existingEntryIndex < 0))
24.2258 - modelValue.push(element.value);
24.2259 - else if ((!element.checked) && (existingEntryIndex >= 0))
24.2260 - modelValue.splice(existingEntryIndex, 1);
24.2261 - } else {
24.2262 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
24.2263 - }
24.2264 - };
24.2265 - ko.utils.registerEventHandler(element, "click", updateHandler);
24.2266 -
24.2267 - // IE 6 won't allow radio buttons to be selected unless they have a name
24.2268 - if ((element.type == "radio") && !element.name)
24.2269 - ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
24.2270 - },
24.2271 - 'update': function (element, valueAccessor) {
24.2272 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2273 -
24.2274 - if (element.type == "checkbox") {
24.2275 - if (value instanceof Array) {
24.2276 - // When bound to an array, the checkbox being checked represents its value being present in that array
24.2277 - element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
24.2278 - } else {
24.2279 - // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
24.2280 - element.checked = value;
24.2281 - }
24.2282 - } else if (element.type == "radio") {
24.2283 - element.checked = (element.value == value);
24.2284 - }
24.2285 - }
24.2286 -};
24.2287 -var classesWrittenByBindingKey = '__ko__cssValue';
24.2288 -ko.bindingHandlers['css'] = {
24.2289 - 'update': function (element, valueAccessor) {
24.2290 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2291 - if (typeof value == "object") {
24.2292 - for (var className in value) {
24.2293 - var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
24.2294 - ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
24.2295 - }
24.2296 - } else {
24.2297 - value = String(value || ''); // Make sure we don't try to store or set a non-string value
24.2298 - ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
24.2299 - element[classesWrittenByBindingKey] = value;
24.2300 - ko.utils.toggleDomNodeCssClass(element, value, true);
24.2301 - }
24.2302 - }
24.2303 -};
24.2304 -ko.bindingHandlers['enable'] = {
24.2305 - 'update': function (element, valueAccessor) {
24.2306 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2307 - if (value && element.disabled)
24.2308 - element.removeAttribute("disabled");
24.2309 - else if ((!value) && (!element.disabled))
24.2310 - element.disabled = true;
24.2311 - }
24.2312 -};
24.2313 -
24.2314 -ko.bindingHandlers['disable'] = {
24.2315 - 'update': function (element, valueAccessor) {
24.2316 - ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
24.2317 - }
24.2318 -};
24.2319 -// For certain common events (currently just 'click'), allow a simplified data-binding syntax
24.2320 -// e.g. click:handler instead of the usual full-length event:{click:handler}
24.2321 -function makeEventHandlerShortcut(eventName) {
24.2322 - ko.bindingHandlers[eventName] = {
24.2323 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
24.2324 - var newValueAccessor = function () {
24.2325 - var result = {};
24.2326 - result[eventName] = valueAccessor();
24.2327 - return result;
24.2328 - };
24.2329 - return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
24.2330 - }
24.2331 - }
24.2332 -}
24.2333 -
24.2334 -ko.bindingHandlers['event'] = {
24.2335 - 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
24.2336 - var eventsToHandle = valueAccessor() || {};
24.2337 - for(var eventNameOutsideClosure in eventsToHandle) {
24.2338 - (function() {
24.2339 - var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
24.2340 - if (typeof eventName == "string") {
24.2341 - ko.utils.registerEventHandler(element, eventName, function (event) {
24.2342 - var handlerReturnValue;
24.2343 - var handlerFunction = valueAccessor()[eventName];
24.2344 - if (!handlerFunction)
24.2345 - return;
24.2346 - var allBindings = allBindingsAccessor();
24.2347 -
24.2348 - try {
24.2349 - // Take all the event args, and prefix with the viewmodel
24.2350 - var argsForHandler = ko.utils.makeArray(arguments);
24.2351 - argsForHandler.unshift(viewModel);
24.2352 - handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
24.2353 - } finally {
24.2354 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
24.2355 - if (event.preventDefault)
24.2356 - event.preventDefault();
24.2357 - else
24.2358 - event.returnValue = false;
24.2359 - }
24.2360 - }
24.2361 -
24.2362 - var bubble = allBindings[eventName + 'Bubble'] !== false;
24.2363 - if (!bubble) {
24.2364 - event.cancelBubble = true;
24.2365 - if (event.stopPropagation)
24.2366 - event.stopPropagation();
24.2367 - }
24.2368 - });
24.2369 - }
24.2370 - })();
24.2371 - }
24.2372 - }
24.2373 -};
24.2374 -// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
24.2375 -// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
24.2376 -ko.bindingHandlers['foreach'] = {
24.2377 - makeTemplateValueAccessor: function(valueAccessor) {
24.2378 - return function() {
24.2379 - var modelValue = valueAccessor(),
24.2380 - unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
24.2381 -
24.2382 - // If unwrappedValue is the array, pass in the wrapped value on its own
24.2383 - // The value will be unwrapped and tracked within the template binding
24.2384 - // (See https://github.com/SteveSanderson/knockout/issues/523)
24.2385 - if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
24.2386 - return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
24.2387 -
24.2388 - // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
24.2389 - ko.utils.unwrapObservable(modelValue);
24.2390 - return {
24.2391 - 'foreach': unwrappedValue['data'],
24.2392 - 'as': unwrappedValue['as'],
24.2393 - 'includeDestroyed': unwrappedValue['includeDestroyed'],
24.2394 - 'afterAdd': unwrappedValue['afterAdd'],
24.2395 - 'beforeRemove': unwrappedValue['beforeRemove'],
24.2396 - 'afterRender': unwrappedValue['afterRender'],
24.2397 - 'beforeMove': unwrappedValue['beforeMove'],
24.2398 - 'afterMove': unwrappedValue['afterMove'],
24.2399 - 'templateEngine': ko.nativeTemplateEngine.instance
24.2400 - };
24.2401 - };
24.2402 - },
24.2403 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24.2404 - return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
24.2405 - },
24.2406 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24.2407 - return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
24.2408 - }
24.2409 -};
24.2410 -ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
24.2411 -ko.virtualElements.allowedBindings['foreach'] = true;
24.2412 -var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
24.2413 -ko.bindingHandlers['hasfocus'] = {
24.2414 - 'init': function(element, valueAccessor, allBindingsAccessor) {
24.2415 - var handleElementFocusChange = function(isFocused) {
24.2416 - // Where possible, ignore which event was raised and determine focus state using activeElement,
24.2417 - // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
24.2418 - // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
24.2419 - // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
24.2420 - // from calling 'blur()' on the element when it loses focus.
24.2421 - // Discussion at https://github.com/SteveSanderson/knockout/pull/352
24.2422 - element[hasfocusUpdatingProperty] = true;
24.2423 - var ownerDoc = element.ownerDocument;
24.2424 - if ("activeElement" in ownerDoc) {
24.2425 - isFocused = (ownerDoc.activeElement === element);
24.2426 - }
24.2427 - var modelValue = valueAccessor();
24.2428 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
24.2429 - element[hasfocusUpdatingProperty] = false;
24.2430 - };
24.2431 - var handleElementFocusIn = handleElementFocusChange.bind(null, true);
24.2432 - var handleElementFocusOut = handleElementFocusChange.bind(null, false);
24.2433 -
24.2434 - ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
24.2435 - ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
24.2436 - ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
24.2437 - ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
24.2438 - },
24.2439 - 'update': function(element, valueAccessor) {
24.2440 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2441 - if (!element[hasfocusUpdatingProperty]) {
24.2442 - value ? element.focus() : element.blur();
24.2443 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
24.2444 - }
24.2445 - }
24.2446 -};
24.2447 -ko.bindingHandlers['html'] = {
24.2448 - 'init': function() {
24.2449 - // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
24.2450 - return { 'controlsDescendantBindings': true };
24.2451 - },
24.2452 - 'update': function (element, valueAccessor) {
24.2453 - // setHtml will unwrap the value if needed
24.2454 - ko.utils.setHtml(element, valueAccessor());
24.2455 - }
24.2456 -};
24.2457 -var withIfDomDataKey = '__ko_withIfBindingData';
24.2458 -// Makes a binding like with or if
24.2459 -function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
24.2460 - ko.bindingHandlers[bindingKey] = {
24.2461 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24.2462 - ko.utils.domData.set(element, withIfDomDataKey, {});
24.2463 - return { 'controlsDescendantBindings': true };
24.2464 - },
24.2465 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24.2466 - var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
24.2467 - dataValue = ko.utils.unwrapObservable(valueAccessor()),
24.2468 - shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
24.2469 - isFirstRender = !withIfData.savedNodes,
24.2470 - needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
24.2471 -
24.2472 - if (needsRefresh) {
24.2473 - if (isFirstRender) {
24.2474 - withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
24.2475 - }
24.2476 -
24.2477 - if (shouldDisplay) {
24.2478 - if (!isFirstRender) {
24.2479 - ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
24.2480 - }
24.2481 - ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
24.2482 - } else {
24.2483 - ko.virtualElements.emptyNode(element);
24.2484 - }
24.2485 -
24.2486 - withIfData.didDisplayOnLastUpdate = shouldDisplay;
24.2487 - }
24.2488 - }
24.2489 - };
24.2490 - ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
24.2491 - ko.virtualElements.allowedBindings[bindingKey] = true;
24.2492 -}
24.2493 -
24.2494 -// Construct the actual binding handlers
24.2495 -makeWithIfBinding('if');
24.2496 -makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
24.2497 -makeWithIfBinding('with', true /* isWith */, false /* isNot */,
24.2498 - function(bindingContext, dataValue) {
24.2499 - return bindingContext['createChildContext'](dataValue);
24.2500 - }
24.2501 -);
24.2502 -function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
24.2503 - if (preferModelValue) {
24.2504 - if (modelValue !== ko.selectExtensions.readValue(element))
24.2505 - ko.selectExtensions.writeValue(element, modelValue);
24.2506 - }
24.2507 -
24.2508 - // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
24.2509 - // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
24.2510 - // change the model value to match the dropdown.
24.2511 - if (modelValue !== ko.selectExtensions.readValue(element))
24.2512 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
24.2513 -};
24.2514 -
24.2515 -ko.bindingHandlers['options'] = {
24.2516 - 'update': function (element, valueAccessor, allBindingsAccessor) {
24.2517 - if (ko.utils.tagNameLower(element) !== "select")
24.2518 - throw new Error("options binding applies only to SELECT elements");
24.2519 -
24.2520 - var selectWasPreviouslyEmpty = element.length == 0;
24.2521 - var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
24.2522 - return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
24.2523 - }), function (node) {
24.2524 - return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
24.2525 - });
24.2526 - var previousScrollTop = element.scrollTop;
24.2527 -
24.2528 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2529 - var selectedValue = element.value;
24.2530 -
24.2531 - // Remove all existing <option>s.
24.2532 - // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
24.2533 - while (element.length > 0) {
24.2534 - ko.cleanNode(element.options[0]);
24.2535 - element.remove(0);
24.2536 - }
24.2537 -
24.2538 - if (value) {
24.2539 - var allBindings = allBindingsAccessor(),
24.2540 - includeDestroyed = allBindings['optionsIncludeDestroyed'];
24.2541 -
24.2542 - if (typeof value.length != "number")
24.2543 - value = [value];
24.2544 - if (allBindings['optionsCaption']) {
24.2545 - var option = document.createElement("option");
24.2546 - ko.utils.setHtml(option, allBindings['optionsCaption']);
24.2547 - ko.selectExtensions.writeValue(option, undefined);
24.2548 - element.appendChild(option);
24.2549 - }
24.2550 -
24.2551 - for (var i = 0, j = value.length; i < j; i++) {
24.2552 - // Skip destroyed items
24.2553 - var arrayEntry = value[i];
24.2554 - if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
24.2555 - continue;
24.2556 -
24.2557 - var option = document.createElement("option");
24.2558 -
24.2559 - function applyToObject(object, predicate, defaultValue) {
24.2560 - var predicateType = typeof predicate;
24.2561 - if (predicateType == "function") // Given a function; run it against the data value
24.2562 - return predicate(object);
24.2563 - else if (predicateType == "string") // Given a string; treat it as a property name on the data value
24.2564 - return object[predicate];
24.2565 - else // Given no optionsText arg; use the data value itself
24.2566 - return defaultValue;
24.2567 - }
24.2568 -
24.2569 - // Apply a value to the option element
24.2570 - var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
24.2571 - ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
24.2572 -
24.2573 - // Apply some text to the option element
24.2574 - var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
24.2575 - ko.utils.setTextContent(option, optionText);
24.2576 -
24.2577 - element.appendChild(option);
24.2578 - }
24.2579 -
24.2580 - // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
24.2581 - // That's why we first added them without selection. Now it's time to set the selection.
24.2582 - var newOptions = element.getElementsByTagName("option");
24.2583 - var countSelectionsRetained = 0;
24.2584 - for (var i = 0, j = newOptions.length; i < j; i++) {
24.2585 - if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
24.2586 - ko.utils.setOptionNodeSelectionState(newOptions[i], true);
24.2587 - countSelectionsRetained++;
24.2588 - }
24.2589 - }
24.2590 -
24.2591 - element.scrollTop = previousScrollTop;
24.2592 -
24.2593 - if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
24.2594 - // Ensure consistency between model value and selected option.
24.2595 - // If the dropdown is being populated for the first time here (or was otherwise previously empty),
24.2596 - // the dropdown selection state is meaningless, so we preserve the model value.
24.2597 - ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
24.2598 - }
24.2599 -
24.2600 - // Workaround for IE9 bug
24.2601 - ko.utils.ensureSelectElementIsRenderedCorrectly(element);
24.2602 - }
24.2603 - }
24.2604 -};
24.2605 -ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
24.2606 -ko.bindingHandlers['selectedOptions'] = {
24.2607 - 'init': function (element, valueAccessor, allBindingsAccessor) {
24.2608 - ko.utils.registerEventHandler(element, "change", function () {
24.2609 - var value = valueAccessor(), valueToWrite = [];
24.2610 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
24.2611 - if (node.selected)
24.2612 - valueToWrite.push(ko.selectExtensions.readValue(node));
24.2613 - });
24.2614 - ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
24.2615 - });
24.2616 - },
24.2617 - 'update': function (element, valueAccessor) {
24.2618 - if (ko.utils.tagNameLower(element) != "select")
24.2619 - throw new Error("values binding applies only to SELECT elements");
24.2620 -
24.2621 - var newValue = ko.utils.unwrapObservable(valueAccessor());
24.2622 - if (newValue && typeof newValue.length == "number") {
24.2623 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
24.2624 - var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
24.2625 - ko.utils.setOptionNodeSelectionState(node, isSelected);
24.2626 - });
24.2627 - }
24.2628 - }
24.2629 -};
24.2630 -ko.bindingHandlers['style'] = {
24.2631 - 'update': function (element, valueAccessor) {
24.2632 - var value = ko.utils.unwrapObservable(valueAccessor() || {});
24.2633 - for (var styleName in value) {
24.2634 - if (typeof styleName == "string") {
24.2635 - var styleValue = ko.utils.unwrapObservable(value[styleName]);
24.2636 - element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
24.2637 - }
24.2638 - }
24.2639 - }
24.2640 -};
24.2641 -ko.bindingHandlers['submit'] = {
24.2642 - 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
24.2643 - if (typeof valueAccessor() != "function")
24.2644 - throw new Error("The value for a submit binding must be a function");
24.2645 - ko.utils.registerEventHandler(element, "submit", function (event) {
24.2646 - var handlerReturnValue;
24.2647 - var value = valueAccessor();
24.2648 - try { handlerReturnValue = value.call(viewModel, element); }
24.2649 - finally {
24.2650 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
24.2651 - if (event.preventDefault)
24.2652 - event.preventDefault();
24.2653 - else
24.2654 - event.returnValue = false;
24.2655 - }
24.2656 - }
24.2657 - });
24.2658 - }
24.2659 -};
24.2660 -ko.bindingHandlers['text'] = {
24.2661 - 'update': function (element, valueAccessor) {
24.2662 - ko.utils.setTextContent(element, valueAccessor());
24.2663 - }
24.2664 -};
24.2665 -ko.virtualElements.allowedBindings['text'] = true;
24.2666 -ko.bindingHandlers['uniqueName'] = {
24.2667 - 'init': function (element, valueAccessor) {
24.2668 - if (valueAccessor()) {
24.2669 - var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
24.2670 - ko.utils.setElementName(element, name);
24.2671 - }
24.2672 - }
24.2673 -};
24.2674 -ko.bindingHandlers['uniqueName'].currentIndex = 0;
24.2675 -ko.bindingHandlers['value'] = {
24.2676 - 'init': function (element, valueAccessor, allBindingsAccessor) {
24.2677 - // Always catch "change" event; possibly other events too if asked
24.2678 - var eventsToCatch = ["change"];
24.2679 - var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
24.2680 - var propertyChangedFired = false;
24.2681 - if (requestedEventsToCatch) {
24.2682 - if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
24.2683 - requestedEventsToCatch = [requestedEventsToCatch];
24.2684 - ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
24.2685 - eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
24.2686 - }
24.2687 -
24.2688 - var valueUpdateHandler = function() {
24.2689 - propertyChangedFired = false;
24.2690 - var modelValue = valueAccessor();
24.2691 - var elementValue = ko.selectExtensions.readValue(element);
24.2692 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
24.2693 - }
24.2694 -
24.2695 - // Workaround for https://github.com/SteveSanderson/knockout/issues/122
24.2696 - // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
24.2697 - var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
24.2698 - && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
24.2699 - if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
24.2700 - ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
24.2701 - ko.utils.registerEventHandler(element, "blur", function() {
24.2702 - if (propertyChangedFired) {
24.2703 - valueUpdateHandler();
24.2704 - }
24.2705 - });
24.2706 - }
24.2707 -
24.2708 - ko.utils.arrayForEach(eventsToCatch, function(eventName) {
24.2709 - // The syntax "after<eventname>" means "run the handler asynchronously after the event"
24.2710 - // This is useful, for example, to catch "keydown" events after the browser has updated the control
24.2711 - // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
24.2712 - var handler = valueUpdateHandler;
24.2713 - if (ko.utils.stringStartsWith(eventName, "after")) {
24.2714 - handler = function() { setTimeout(valueUpdateHandler, 0) };
24.2715 - eventName = eventName.substring("after".length);
24.2716 - }
24.2717 - ko.utils.registerEventHandler(element, eventName, handler);
24.2718 - });
24.2719 - },
24.2720 - 'update': function (element, valueAccessor) {
24.2721 - var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
24.2722 - var newValue = ko.utils.unwrapObservable(valueAccessor());
24.2723 - var elementValue = ko.selectExtensions.readValue(element);
24.2724 - var valueHasChanged = (newValue != elementValue);
24.2725 -
24.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).
24.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.
24.2728 - if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
24.2729 - valueHasChanged = true;
24.2730 -
24.2731 - if (valueHasChanged) {
24.2732 - var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
24.2733 - applyValueAction();
24.2734 -
24.2735 - // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
24.2736 - // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
24.2737 - // to apply the value as well.
24.2738 - var alsoApplyAsynchronously = valueIsSelectOption;
24.2739 - if (alsoApplyAsynchronously)
24.2740 - setTimeout(applyValueAction, 0);
24.2741 - }
24.2742 -
24.2743 - // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
24.2744 - // because you're not allowed to have a model value that disagrees with a visible UI selection.
24.2745 - if (valueIsSelectOption && (element.length > 0))
24.2746 - ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
24.2747 - }
24.2748 -};
24.2749 -ko.bindingHandlers['visible'] = {
24.2750 - 'update': function (element, valueAccessor) {
24.2751 - var value = ko.utils.unwrapObservable(valueAccessor());
24.2752 - var isCurrentlyVisible = !(element.style.display == "none");
24.2753 - if (value && !isCurrentlyVisible)
24.2754 - element.style.display = "";
24.2755 - else if ((!value) && isCurrentlyVisible)
24.2756 - element.style.display = "none";
24.2757 - }
24.2758 -};
24.2759 -// 'click' is just a shorthand for the usual full-length event:{click:handler}
24.2760 -makeEventHandlerShortcut('click');
24.2761 -// If you want to make a custom template engine,
24.2762 -//
24.2763 -// [1] Inherit from this class (like ko.nativeTemplateEngine does)
24.2764 -// [2] Override 'renderTemplateSource', supplying a function with this signature:
24.2765 -//
24.2766 -// function (templateSource, bindingContext, options) {
24.2767 -// // - templateSource.text() is the text of the template you should render
24.2768 -// // - bindingContext.$data is the data you should pass into the template
24.2769 -// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
24.2770 -// // and bindingContext.$root available in the template too
24.2771 -// // - options gives you access to any other properties set on "data-bind: { template: options }"
24.2772 -// //
24.2773 -// // Return value: an array of DOM nodes
24.2774 -// }
24.2775 -//
24.2776 -// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
24.2777 -//
24.2778 -// function (script) {
24.2779 -// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
24.2780 -// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
24.2781 -// }
24.2782 -//
24.2783 -// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
24.2784 -// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
24.2785 -// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
24.2786 -
24.2787 -ko.templateEngine = function () { };
24.2788 -
24.2789 -ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
24.2790 - throw new Error("Override renderTemplateSource");
24.2791 -};
24.2792 -
24.2793 -ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
24.2794 - throw new Error("Override createJavaScriptEvaluatorBlock");
24.2795 -};
24.2796 -
24.2797 -ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
24.2798 - // Named template
24.2799 - if (typeof template == "string") {
24.2800 - templateDocument = templateDocument || document;
24.2801 - var elem = templateDocument.getElementById(template);
24.2802 - if (!elem)
24.2803 - throw new Error("Cannot find template with ID " + template);
24.2804 - return new ko.templateSources.domElement(elem);
24.2805 - } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
24.2806 - // Anonymous template
24.2807 - return new ko.templateSources.anonymousTemplate(template);
24.2808 - } else
24.2809 - throw new Error("Unknown template type: " + template);
24.2810 -};
24.2811 -
24.2812 -ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
24.2813 - var templateSource = this['makeTemplateSource'](template, templateDocument);
24.2814 - return this['renderTemplateSource'](templateSource, bindingContext, options);
24.2815 -};
24.2816 -
24.2817 -ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
24.2818 - // Skip rewriting if requested
24.2819 - if (this['allowTemplateRewriting'] === false)
24.2820 - return true;
24.2821 - return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
24.2822 -};
24.2823 -
24.2824 -ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
24.2825 - var templateSource = this['makeTemplateSource'](template, templateDocument);
24.2826 - var rewritten = rewriterCallback(templateSource['text']());
24.2827 - templateSource['text'](rewritten);
24.2828 - templateSource['data']("isRewritten", true);
24.2829 -};
24.2830 -
24.2831 -ko.exportSymbol('templateEngine', ko.templateEngine);
24.2832 -
24.2833 -ko.templateRewriting = (function () {
24.2834 - var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
24.2835 - var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
24.2836 -
24.2837 - function validateDataBindValuesForRewriting(keyValueArray) {
24.2838 - var allValidators = ko.expressionRewriting.bindingRewriteValidators;
24.2839 - for (var i = 0; i < keyValueArray.length; i++) {
24.2840 - var key = keyValueArray[i]['key'];
24.2841 - if (allValidators.hasOwnProperty(key)) {
24.2842 - var validator = allValidators[key];
24.2843 -
24.2844 - if (typeof validator === "function") {
24.2845 - var possibleErrorMessage = validator(keyValueArray[i]['value']);
24.2846 - if (possibleErrorMessage)
24.2847 - throw new Error(possibleErrorMessage);
24.2848 - } else if (!validator) {
24.2849 - throw new Error("This template engine does not support the '" + key + "' binding within its templates");
24.2850 - }
24.2851 - }
24.2852 - }
24.2853 - }
24.2854 -
24.2855 - function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
24.2856 - var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
24.2857 - validateDataBindValuesForRewriting(dataBindKeyValueArray);
24.2858 - var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
24.2859 -
24.2860 - // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
24.2861 - // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
24.2862 - // extra indirection.
24.2863 - var applyBindingsToNextSiblingScript =
24.2864 - "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
24.2865 - return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
24.2866 - }
24.2867 -
24.2868 - return {
24.2869 - ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
24.2870 - if (!templateEngine['isTemplateRewritten'](template, templateDocument))
24.2871 - templateEngine['rewriteTemplate'](template, function (htmlString) {
24.2872 - return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
24.2873 - }, templateDocument);
24.2874 - },
24.2875 -
24.2876 - memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
24.2877 - return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
24.2878 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
24.2879 - }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
24.2880 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
24.2881 - });
24.2882 - },
24.2883 -
24.2884 - applyMemoizedBindingsToNextSibling: function (bindings) {
24.2885 - return ko.memoization.memoize(function (domNode, bindingContext) {
24.2886 - if (domNode.nextSibling)
24.2887 - ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
24.2888 - });
24.2889 - }
24.2890 - }
24.2891 -})();
24.2892 -
24.2893 -
24.2894 -// Exported only because it has to be referenced by string lookup from within rewritten template
24.2895 -ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
24.2896 -(function() {
24.2897 - // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
24.2898 - // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
24.2899 - //
24.2900 - // Two are provided by default:
24.2901 - // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
24.2902 - // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
24.2903 - // without reading/writing the actual element text content, since it will be overwritten
24.2904 - // with the rendered template output.
24.2905 - // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
24.2906 - // Template sources need to have the following functions:
24.2907 - // text() - returns the template text from your storage location
24.2908 - // text(value) - writes the supplied template text to your storage location
24.2909 - // data(key) - reads values stored using data(key, value) - see below
24.2910 - // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
24.2911 - //
24.2912 - // Optionally, template sources can also have the following functions:
24.2913 - // nodes() - returns a DOM element containing the nodes of this template, where available
24.2914 - // nodes(value) - writes the given DOM element to your storage location
24.2915 - // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
24.2916 - // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
24.2917 - //
24.2918 - // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
24.2919 - // using and overriding "makeTemplateSource" to return an instance of your custom template source.
24.2920 -
24.2921 - ko.templateSources = {};
24.2922 -
24.2923 - // ---- ko.templateSources.domElement -----
24.2924 -
24.2925 - ko.templateSources.domElement = function(element) {
24.2926 - this.domElement = element;
24.2927 - }
24.2928 -
24.2929 - ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
24.2930 - var tagNameLower = ko.utils.tagNameLower(this.domElement),
24.2931 - elemContentsProperty = tagNameLower === "script" ? "text"
24.2932 - : tagNameLower === "textarea" ? "value"
24.2933 - : "innerHTML";
24.2934 -
24.2935 - if (arguments.length == 0) {
24.2936 - return this.domElement[elemContentsProperty];
24.2937 - } else {
24.2938 - var valueToWrite = arguments[0];
24.2939 - if (elemContentsProperty === "innerHTML")
24.2940 - ko.utils.setHtml(this.domElement, valueToWrite);
24.2941 - else
24.2942 - this.domElement[elemContentsProperty] = valueToWrite;
24.2943 - }
24.2944 - };
24.2945 -
24.2946 - ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
24.2947 - if (arguments.length === 1) {
24.2948 - return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
24.2949 - } else {
24.2950 - ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
24.2951 - }
24.2952 - };
24.2953 -
24.2954 - // ---- ko.templateSources.anonymousTemplate -----
24.2955 - // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
24.2956 - // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
24.2957 - // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
24.2958 -
24.2959 - var anonymousTemplatesDomDataKey = "__ko_anon_template__";
24.2960 - ko.templateSources.anonymousTemplate = function(element) {
24.2961 - this.domElement = element;
24.2962 - }
24.2963 - ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
24.2964 - ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
24.2965 - if (arguments.length == 0) {
24.2966 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
24.2967 - if (templateData.textData === undefined && templateData.containerData)
24.2968 - templateData.textData = templateData.containerData.innerHTML;
24.2969 - return templateData.textData;
24.2970 - } else {
24.2971 - var valueToWrite = arguments[0];
24.2972 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
24.2973 - }
24.2974 - };
24.2975 - ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
24.2976 - if (arguments.length == 0) {
24.2977 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
24.2978 - return templateData.containerData;
24.2979 - } else {
24.2980 - var valueToWrite = arguments[0];
24.2981 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
24.2982 - }
24.2983 - };
24.2984 -
24.2985 - ko.exportSymbol('templateSources', ko.templateSources);
24.2986 - ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
24.2987 - ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
24.2988 -})();
24.2989 -(function () {
24.2990 - var _templateEngine;
24.2991 - ko.setTemplateEngine = function (templateEngine) {
24.2992 - if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
24.2993 - throw new Error("templateEngine must inherit from ko.templateEngine");
24.2994 - _templateEngine = templateEngine;
24.2995 - }
24.2996 -
24.2997 - function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
24.2998 - var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
24.2999 - while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
24.3000 - nextInQueue = ko.virtualElements.nextSibling(node);
24.3001 - if (node.nodeType === 1 || node.nodeType === 8)
24.3002 - action(node);
24.3003 - }
24.3004 - }
24.3005 -
24.3006 - function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
24.3007 - // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
24.3008 - // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
24.3009 - // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
24.3010 - // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
24.3011 - // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
24.3012 -
24.3013 - if (continuousNodeArray.length) {
24.3014 - var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
24.3015 -
24.3016 - // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
24.3017 - // whereas a regular applyBindings won't introduce new memoized nodes
24.3018 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
24.3019 - ko.applyBindings(bindingContext, node);
24.3020 - });
24.3021 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
24.3022 - ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
24.3023 - });
24.3024 - }
24.3025 - }
24.3026 -
24.3027 - function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
24.3028 - return nodeOrNodeArray.nodeType ? nodeOrNodeArray
24.3029 - : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
24.3030 - : null;
24.3031 - }
24.3032 -
24.3033 - function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
24.3034 - options = options || {};
24.3035 - var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
24.3036 - var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
24.3037 - var templateEngineToUse = (options['templateEngine'] || _templateEngine);
24.3038 - ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
24.3039 - var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
24.3040 -
24.3041 - // Loosely check result is an array of DOM nodes
24.3042 - if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
24.3043 - throw new Error("Template engine must return an array of DOM nodes");
24.3044 -
24.3045 - var haveAddedNodesToParent = false;
24.3046 - switch (renderMode) {
24.3047 - case "replaceChildren":
24.3048 - ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
24.3049 - haveAddedNodesToParent = true;
24.3050 - break;
24.3051 - case "replaceNode":
24.3052 - ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
24.3053 - haveAddedNodesToParent = true;
24.3054 - break;
24.3055 - case "ignoreTargetNode": break;
24.3056 - default:
24.3057 - throw new Error("Unknown renderMode: " + renderMode);
24.3058 - }
24.3059 -
24.3060 - if (haveAddedNodesToParent) {
24.3061 - activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
24.3062 - if (options['afterRender'])
24.3063 - ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
24.3064 - }
24.3065 -
24.3066 - return renderedNodesArray;
24.3067 - }
24.3068 -
24.3069 - ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
24.3070 - options = options || {};
24.3071 - if ((options['templateEngine'] || _templateEngine) == undefined)
24.3072 - throw new Error("Set a template engine before calling renderTemplate");
24.3073 - renderMode = renderMode || "replaceChildren";
24.3074 -
24.3075 - if (targetNodeOrNodeArray) {
24.3076 - var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
24.3077 -
24.3078 - var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
24.3079 - var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
24.3080 -
24.3081 - return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
24.3082 - function () {
24.3083 - // Ensure we've got a proper binding context to work with
24.3084 - var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
24.3085 - ? dataOrBindingContext
24.3086 - : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
24.3087 -
24.3088 - // Support selecting template as a function of the data being rendered
24.3089 - var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
24.3090 -
24.3091 - var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
24.3092 - if (renderMode == "replaceNode") {
24.3093 - targetNodeOrNodeArray = renderedNodesArray;
24.3094 - firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
24.3095 - }
24.3096 - },
24.3097 - null,
24.3098 - { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
24.3099 - );
24.3100 - } else {
24.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
24.3102 - return ko.memoization.memoize(function (domNode) {
24.3103 - ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
24.3104 - });
24.3105 - }
24.3106 - };
24.3107 -
24.3108 - ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
24.3109 - // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
24.3110 - // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
24.3111 - var arrayItemContext;
24.3112 -
24.3113 - // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
24.3114 - var executeTemplateForArrayItem = function (arrayValue, index) {
24.3115 - // Support selecting template as a function of the data being rendered
24.3116 - arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
24.3117 - arrayItemContext['$index'] = index;
24.3118 - var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
24.3119 - return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
24.3120 - }
24.3121 -
24.3122 - // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
24.3123 - var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
24.3124 - activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
24.3125 - if (options['afterRender'])
24.3126 - options['afterRender'](addedNodesArray, arrayValue);
24.3127 - };
24.3128 -
24.3129 - return ko.dependentObservable(function () {
24.3130 - var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
24.3131 - if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
24.3132 - unwrappedArray = [unwrappedArray];
24.3133 -
24.3134 - // Filter out any entries marked as destroyed
24.3135 - var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
24.3136 - return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
24.3137 - });
24.3138 -
24.3139 - // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
24.3140 - // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
24.3141 - ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
24.3142 -
24.3143 - }, null, { disposeWhenNodeIsRemoved: targetNode });
24.3144 - };
24.3145 -
24.3146 - var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
24.3147 - function disposeOldComputedAndStoreNewOne(element, newComputed) {
24.3148 - var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
24.3149 - if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
24.3150 - oldComputed.dispose();
24.3151 - ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
24.3152 - }
24.3153 -
24.3154 - ko.bindingHandlers['template'] = {
24.3155 - 'init': function(element, valueAccessor) {
24.3156 - // Support anonymous templates
24.3157 - var bindingValue = ko.utils.unwrapObservable(valueAccessor());
24.3158 - if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
24.3159 - // It's an anonymous template - store the element contents, then clear the element
24.3160 - var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
24.3161 - container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
24.3162 - new ko.templateSources.anonymousTemplate(element)['nodes'](container);
24.3163 - }
24.3164 - return { 'controlsDescendantBindings': true };
24.3165 - },
24.3166 - 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24.3167 - var templateName = ko.utils.unwrapObservable(valueAccessor()),
24.3168 - options = {},
24.3169 - shouldDisplay = true,
24.3170 - dataValue,
24.3171 - templateComputed = null;
24.3172 -
24.3173 - if (typeof templateName != "string") {
24.3174 - options = templateName;
24.3175 - templateName = options['name'];
24.3176 -
24.3177 - // Support "if"/"ifnot" conditions
24.3178 - if ('if' in options)
24.3179 - shouldDisplay = ko.utils.unwrapObservable(options['if']);
24.3180 - if (shouldDisplay && 'ifnot' in options)
24.3181 - shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
24.3182 -
24.3183 - dataValue = ko.utils.unwrapObservable(options['data']);
24.3184 - }
24.3185 -
24.3186 - if ('foreach' in options) {
24.3187 - // Render once for each data point (treating data set as empty if shouldDisplay==false)
24.3188 - var dataArray = (shouldDisplay && options['foreach']) || [];
24.3189 - templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
24.3190 - } else if (!shouldDisplay) {
24.3191 - ko.virtualElements.emptyNode(element);
24.3192 - } else {
24.3193 - // Render once for this single data point (or use the viewModel if no data was provided)
24.3194 - var innerBindingContext = ('data' in options) ?
24.3195 - bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
24.3196 - bindingContext; // Given no explicit 'data' value, we retain the same binding context
24.3197 - templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
24.3198 - }
24.3199 -
24.3200 - // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
24.3201 - disposeOldComputedAndStoreNewOne(element, templateComputed);
24.3202 - }
24.3203 - };
24.3204 -
24.3205 - // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
24.3206 - ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
24.3207 - var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
24.3208 -
24.3209 - if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
24.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)
24.3211 -
24.3212 - if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
24.3213 - return null; // Named templates can be rewritten, so return "no error"
24.3214 - return "This template engine does not support anonymous templates nested within its templates";
24.3215 - };
24.3216 -
24.3217 - ko.virtualElements.allowedBindings['template'] = true;
24.3218 -})();
24.3219 -
24.3220 -ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
24.3221 -ko.exportSymbol('renderTemplate', ko.renderTemplate);
24.3222 -
24.3223 -ko.utils.compareArrays = (function () {
24.3224 - var statusNotInOld = 'added', statusNotInNew = 'deleted';
24.3225 -
24.3226 - // Simple calculation based on Levenshtein distance.
24.3227 - function compareArrays(oldArray, newArray, dontLimitMoves) {
24.3228 - oldArray = oldArray || [];
24.3229 - newArray = newArray || [];
24.3230 -
24.3231 - if (oldArray.length <= newArray.length)
24.3232 - return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
24.3233 - else
24.3234 - return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
24.3235 - }
24.3236 -
24.3237 - function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
24.3238 - var myMin = Math.min,
24.3239 - myMax = Math.max,
24.3240 - editDistanceMatrix = [],
24.3241 - smlIndex, smlIndexMax = smlArray.length,
24.3242 - bigIndex, bigIndexMax = bigArray.length,
24.3243 - compareRange = (bigIndexMax - smlIndexMax) || 1,
24.3244 - maxDistance = smlIndexMax + bigIndexMax + 1,
24.3245 - thisRow, lastRow,
24.3246 - bigIndexMaxForRow, bigIndexMinForRow;
24.3247 -
24.3248 - for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
24.3249 - lastRow = thisRow;
24.3250 - editDistanceMatrix.push(thisRow = []);
24.3251 - bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
24.3252 - bigIndexMinForRow = myMax(0, smlIndex - 1);
24.3253 - for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
24.3254 - if (!bigIndex)
24.3255 - thisRow[bigIndex] = smlIndex + 1;
24.3256 - else if (!smlIndex) // Top row - transform empty array into new array via additions
24.3257 - thisRow[bigIndex] = bigIndex + 1;
24.3258 - else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
24.3259 - thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
24.3260 - else {
24.3261 - var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
24.3262 - var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
24.3263 - thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
24.3264 - }
24.3265 - }
24.3266 - }
24.3267 -
24.3268 - var editScript = [], meMinusOne, notInSml = [], notInBig = [];
24.3269 - for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
24.3270 - meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
24.3271 - if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
24.3272 - notInSml.push(editScript[editScript.length] = { // added
24.3273 - 'status': statusNotInSml,
24.3274 - 'value': bigArray[--bigIndex],
24.3275 - 'index': bigIndex });
24.3276 - } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
24.3277 - notInBig.push(editScript[editScript.length] = { // deleted
24.3278 - 'status': statusNotInBig,
24.3279 - 'value': smlArray[--smlIndex],
24.3280 - 'index': smlIndex });
24.3281 - } else {
24.3282 - editScript.push({
24.3283 - 'status': "retained",
24.3284 - 'value': bigArray[--bigIndex] });
24.3285 - --smlIndex;
24.3286 - }
24.3287 - }
24.3288 -
24.3289 - if (notInSml.length && notInBig.length) {
24.3290 - // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
24.3291 - // smlIndexMax keeps the time complexity of this algorithm linear.
24.3292 - var limitFailedCompares = smlIndexMax * 10, failedCompares,
24.3293 - a, d, notInSmlItem, notInBigItem;
24.3294 - // Go through the items that have been added and deleted and try to find matches between them.
24.3295 - for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
24.3296 - for (d = 0; notInBigItem = notInBig[d]; d++) {
24.3297 - if (notInSmlItem['value'] === notInBigItem['value']) {
24.3298 - notInSmlItem['moved'] = notInBigItem['index'];
24.3299 - notInBigItem['moved'] = notInSmlItem['index'];
24.3300 - notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
24.3301 - failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
24.3302 - break;
24.3303 - }
24.3304 - }
24.3305 - failedCompares += d;
24.3306 - }
24.3307 - }
24.3308 - return editScript.reverse();
24.3309 - }
24.3310 -
24.3311 - return compareArrays;
24.3312 -})();
24.3313 -
24.3314 -ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
24.3315 -
24.3316 -(function () {
24.3317 - // Objective:
24.3318 - // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
24.3319 - // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
24.3320 - // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
24.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
24.3322 - // previously mapped - retain those nodes, and just insert/delete other ones
24.3323 -
24.3324 - // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
24.3325 - // You can use this, for example, to activate bindings on those nodes.
24.3326 -
24.3327 - function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
24.3328 - // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
24.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,
24.3330 - // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
24.3331 - // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
24.3332 - // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
24.3333 - //
24.3334 - // Rules:
24.3335 - // [A] Any leading nodes that aren't in the document any more should be ignored
24.3336 - // These most likely correspond to memoization nodes that were already removed during binding
24.3337 - // See https://github.com/SteveSanderson/knockout/pull/440
24.3338 - // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
24.3339 - // have already been removed, and include any nodes that have been inserted among the previous collection
24.3340 -
24.3341 - // Rule [A]
24.3342 - while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
24.3343 - contiguousNodeArray.splice(0, 1);
24.3344 -
24.3345 - // Rule [B]
24.3346 - if (contiguousNodeArray.length > 1) {
24.3347 - // Build up the actual new contiguous node set
24.3348 - var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
24.3349 - while (current !== last) {
24.3350 - current = current.nextSibling;
24.3351 - if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
24.3352 - return;
24.3353 - newContiguousSet.push(current);
24.3354 - }
24.3355 -
24.3356 - // ... then mutate the input array to match this.
24.3357 - // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
24.3358 - Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
24.3359 - }
24.3360 - return contiguousNodeArray;
24.3361 - }
24.3362 -
24.3363 - function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
24.3364 - // Map this array value inside a dependentObservable so we re-map when any dependency changes
24.3365 - var mappedNodes = [];
24.3366 - var dependentObservable = ko.dependentObservable(function() {
24.3367 - var newMappedNodes = mapping(valueToMap, index) || [];
24.3368 -
24.3369 - // On subsequent evaluations, just replace the previously-inserted DOM nodes
24.3370 - if (mappedNodes.length > 0) {
24.3371 - ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
24.3372 - if (callbackAfterAddingNodes)
24.3373 - ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
24.3374 - }
24.3375 -
24.3376 - // Replace the contents of the mappedNodes array, thereby updating the record
24.3377 - // of which nodes would be deleted if valueToMap was itself later removed
24.3378 - mappedNodes.splice(0, mappedNodes.length);
24.3379 - ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
24.3380 - }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
24.3381 - return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
24.3382 - }
24.3383 -
24.3384 - var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
24.3385 -
24.3386 - ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
24.3387 - // Compare the provided array against the previous one
24.3388 - array = array || [];
24.3389 - options = options || {};
24.3390 - var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
24.3391 - var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
24.3392 - var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
24.3393 - var editScript = ko.utils.compareArrays(lastArray, array);
24.3394 -
24.3395 - // Build the new mapping result
24.3396 - var newMappingResult = [];
24.3397 - var lastMappingResultIndex = 0;
24.3398 - var newMappingResultIndex = 0;
24.3399 -
24.3400 - var nodesToDelete = [];
24.3401 - var itemsToProcess = [];
24.3402 - var itemsForBeforeRemoveCallbacks = [];
24.3403 - var itemsForMoveCallbacks = [];
24.3404 - var itemsForAfterAddCallbacks = [];
24.3405 - var mapData;
24.3406 -
24.3407 - function itemMovedOrRetained(editScriptIndex, oldPosition) {
24.3408 - mapData = lastMappingResult[oldPosition];
24.3409 - if (newMappingResultIndex !== oldPosition)
24.3410 - itemsForMoveCallbacks[editScriptIndex] = mapData;
24.3411 - // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
24.3412 - mapData.indexObservable(newMappingResultIndex++);
24.3413 - fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
24.3414 - newMappingResult.push(mapData);
24.3415 - itemsToProcess.push(mapData);
24.3416 - }
24.3417 -
24.3418 - function callCallback(callback, items) {
24.3419 - if (callback) {
24.3420 - for (var i = 0, n = items.length; i < n; i++) {
24.3421 - if (items[i]) {
24.3422 - ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
24.3423 - callback(node, i, items[i].arrayEntry);
24.3424 - });
24.3425 - }
24.3426 - }
24.3427 - }
24.3428 - }
24.3429 -
24.3430 - for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
24.3431 - movedIndex = editScriptItem['moved'];
24.3432 - switch (editScriptItem['status']) {
24.3433 - case "deleted":
24.3434 - if (movedIndex === undefined) {
24.3435 - mapData = lastMappingResult[lastMappingResultIndex];
24.3436 -
24.3437 - // Stop tracking changes to the mapping for these nodes
24.3438 - if (mapData.dependentObservable)
24.3439 - mapData.dependentObservable.dispose();
24.3440 -
24.3441 - // Queue these nodes for later removal
24.3442 - nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
24.3443 - if (options['beforeRemove']) {
24.3444 - itemsForBeforeRemoveCallbacks[i] = mapData;
24.3445 - itemsToProcess.push(mapData);
24.3446 - }
24.3447 - }
24.3448 - lastMappingResultIndex++;
24.3449 - break;
24.3450 -
24.3451 - case "retained":
24.3452 - itemMovedOrRetained(i, lastMappingResultIndex++);
24.3453 - break;
24.3454 -
24.3455 - case "added":
24.3456 - if (movedIndex !== undefined) {
24.3457 - itemMovedOrRetained(i, movedIndex);
24.3458 - } else {
24.3459 - mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
24.3460 - newMappingResult.push(mapData);
24.3461 - itemsToProcess.push(mapData);
24.3462 - if (!isFirstExecution)
24.3463 - itemsForAfterAddCallbacks[i] = mapData;
24.3464 - }
24.3465 - break;
24.3466 - }
24.3467 - }
24.3468 -
24.3469 - // Call beforeMove first before any changes have been made to the DOM
24.3470 - callCallback(options['beforeMove'], itemsForMoveCallbacks);
24.3471 -
24.3472 - // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
24.3473 - ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
24.3474 -
24.3475 - // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
24.3476 - for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
24.3477 - // Get nodes for newly added items
24.3478 - if (!mapData.mappedNodes)
24.3479 - ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
24.3480 -
24.3481 - // Put nodes in the right place if they aren't there already
24.3482 - for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
24.3483 - if (node !== nextNode)
24.3484 - ko.virtualElements.insertAfter(domNode, node, lastNode);
24.3485 - }
24.3486 -
24.3487 - // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
24.3488 - if (!mapData.initialized && callbackAfterAddingNodes) {
24.3489 - callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
24.3490 - mapData.initialized = true;
24.3491 - }
24.3492 - }
24.3493 -
24.3494 - // If there's a beforeRemove callback, call it after reordering.
24.3495 - // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
24.3496 - // some sort of animation, which is why we first reorder the nodes that will be removed. If the
24.3497 - // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
24.3498 - // Perhaps we'll make that change in the future if this scenario becomes more common.
24.3499 - callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
24.3500 -
24.3501 - // Finally call afterMove and afterAdd callbacks
24.3502 - callCallback(options['afterMove'], itemsForMoveCallbacks);
24.3503 - callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
24.3504 -
24.3505 - // Store a copy of the array items we just considered so we can difference it next time
24.3506 - ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
24.3507 - }
24.3508 -})();
24.3509 -
24.3510 -ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
24.3511 -ko.nativeTemplateEngine = function () {
24.3512 - this['allowTemplateRewriting'] = false;
24.3513 -}
24.3514 -
24.3515 -ko.nativeTemplateEngine.prototype = new ko.templateEngine();
24.3516 -ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
24.3517 - var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
24.3518 - templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
24.3519 - templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
24.3520 -
24.3521 - if (templateNodes) {
24.3522 - return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
24.3523 - } else {
24.3524 - var templateText = templateSource['text']();
24.3525 - return ko.utils.parseHtmlFragment(templateText);
24.3526 - }
24.3527 -};
24.3528 -
24.3529 -ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
24.3530 -ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
24.3531 -
24.3532 -ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
24.3533 -(function() {
24.3534 - ko.jqueryTmplTemplateEngine = function () {
24.3535 - // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
24.3536 - // doesn't expose a version number, so we have to infer it.
24.3537 - // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
24.3538 - // which KO internally refers to as version "2", so older versions are no longer detected.
24.3539 - var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
24.3540 - if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
24.3541 - return 0;
24.3542 - // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
24.3543 - try {
24.3544 - if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
24.3545 - // Since 1.0.0pre, custom tags should append markup to an array called "__"
24.3546 - return 2; // Final version of jquery.tmpl
24.3547 - }
24.3548 - } catch(ex) { /* Apparently not the version we were looking for */ }
24.3549 -
24.3550 - return 1; // Any older version that we don't support
24.3551 - })();
24.3552 -
24.3553 - function ensureHasReferencedJQueryTemplates() {
24.3554 - if (jQueryTmplVersion < 2)
24.3555 - throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
24.3556 - }
24.3557 -
24.3558 - function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
24.3559 - return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
24.3560 - }
24.3561 -
24.3562 - this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
24.3563 - options = options || {};
24.3564 - ensureHasReferencedJQueryTemplates();
24.3565 -
24.3566 - // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
24.3567 - var precompiled = templateSource['data']('precompiled');
24.3568 - if (!precompiled) {
24.3569 - var templateText = templateSource['text']() || "";
24.3570 - // Wrap in "with($whatever.koBindingContext) { ... }"
24.3571 - templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
24.3572 -
24.3573 - precompiled = jQuery['template'](null, templateText);
24.3574 - templateSource['data']('precompiled', precompiled);
24.3575 - }
24.3576 -
24.3577 - var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
24.3578 - var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
24.3579 -
24.3580 - var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
24.3581 - resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
24.3582 -
24.3583 - jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
24.3584 - return resultNodes;
24.3585 - };
24.3586 -
24.3587 - this['createJavaScriptEvaluatorBlock'] = function(script) {
24.3588 - return "{{ko_code ((function() { return " + script + " })()) }}";
24.3589 - };
24.3590 -
24.3591 - this['addTemplate'] = function(templateName, templateMarkup) {
24.3592 - document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
24.3593 - };
24.3594 -
24.3595 - if (jQueryTmplVersion > 0) {
24.3596 - jQuery['tmpl']['tag']['ko_code'] = {
24.3597 - open: "__.push($1 || '');"
24.3598 - };
24.3599 - jQuery['tmpl']['tag']['ko_with'] = {
24.3600 - open: "with($1) {",
24.3601 - close: "} "
24.3602 - };
24.3603 - }
24.3604 - };
24.3605 -
24.3606 - ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
24.3607 -
24.3608 - // Use this one by default *only if jquery.tmpl is referenced*
24.3609 - var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
24.3610 - if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
24.3611 - ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
24.3612 -
24.3613 - ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
24.3614 -})();
24.3615 -});
24.3616 -})(window,document,navigator,window["jQuery"]);
24.3617 -})();
24.3618 \ No newline at end of file
25.1 --- a/ko-bck2brwsr/src/test/java/org/apidesign/html/ko2brwsr/Bck2BrwsrKnockoutTest.java Mon Jun 24 17:49:27 2013 +0200
25.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
25.3 @@ -1,124 +0,0 @@
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 -package org.apidesign.html.ko2brwsr;
25.25 -
25.26 -import java.io.BufferedReader;
25.27 -import java.io.IOException;
25.28 -import java.io.InputStreamReader;
25.29 -import java.net.URI;
25.30 -import java.net.URISyntaxException;
25.31 -import java.net.URL;
25.32 -import java.net.URLConnection;
25.33 -import java.util.Map;
25.34 -import net.java.html.BrwsrCtx;
25.35 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
25.36 -import org.apidesign.bck2brwsr.vmtest.VMTest;
25.37 -import org.apidesign.html.context.spi.Contexts;
25.38 -import org.apidesign.html.json.spi.Technology;
25.39 -import org.apidesign.html.json.spi.Transfer;
25.40 -import org.apidesign.html.json.tck.KOTest;
25.41 -import org.apidesign.html.json.tck.KnockoutTCK;
25.42 -import org.openide.util.lookup.ServiceProvider;
25.43 -import org.testng.annotations.Factory;
25.44 -
25.45 -/**
25.46 - *
25.47 - * @author Jaroslav Tulach <jtulach@netbeans.org>
25.48 - */
25.49 -@ServiceProvider(service = KnockoutTCK.class)
25.50 -public final class Bck2BrwsrKnockoutTest extends KnockoutTCK {
25.51 - @Factory public static Object[] create() {
25.52 - return VMTest.newTests().
25.53 - withClasses(testClasses()).
25.54 - withLaunchers("bck2brwsr").
25.55 - withTestAnnotation(KOTest.class).
25.56 - build();
25.57 - }
25.58 -
25.59 - @Override
25.60 - public BrwsrCtx createContext() {
25.61 - return Contexts.newBuilder().
25.62 - register(Transfer.class, BrwsrCtxImpl.DEFAULT, 9).
25.63 - register(Technology.class, BrwsrCtxImpl.DEFAULT, 9).build();
25.64 - }
25.65 -
25.66 -
25.67 -
25.68 - @Override
25.69 - public Object createJSON(Map<String, Object> values) {
25.70 - Object json = createJSON();
25.71 -
25.72 - for (Map.Entry<String, Object> entry : values.entrySet()) {
25.73 - putValue(json, entry.getKey(), entry.getValue());
25.74 - }
25.75 - return json;
25.76 - }
25.77 -
25.78 - @JavaScriptBody(args = {}, body = "return new Object();")
25.79 - private static native Object createJSON();
25.80 -
25.81 - @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
25.82 - private static native void putValue(Object json, String key, Object value);
25.83 -
25.84 - @Override
25.85 - public Object executeScript(String script, Object[] arguments) {
25.86 - return execScript(script, arguments);
25.87 - }
25.88 -
25.89 - @JavaScriptBody(args = { "s", "args" }, body =
25.90 - "var f = new Function(s); return f.apply(null, args);"
25.91 - )
25.92 - private static native Object execScript(String s, Object[] arguments);
25.93 -
25.94 - @JavaScriptBody(args = { }, body =
25.95 - "var h;"
25.96 - + "if (!!window && !!window.location && !!window.location.href)\n"
25.97 - + " h = window.location.href;\n"
25.98 - + "else "
25.99 - + " h = null;"
25.100 - + "return h;\n"
25.101 - )
25.102 - private static native String findBaseURL();
25.103 -
25.104 - @Override
25.105 - public URI prepareURL(String content, String mimeType, String[] parameters) {
25.106 - try {
25.107 - final URL baseURL = new URL(findBaseURL());
25.108 - StringBuilder sb = new StringBuilder();
25.109 - sb.append("/dynamic?mimeType=").append(mimeType);
25.110 - for (int i = 0; i < parameters.length; i++) {
25.111 - sb.append("¶m" + i).append("=").append(parameters[i]);
25.112 - }
25.113 - String mangle = content.replace("\n", "%0a")
25.114 - .replace("\"", "\\\"").replace(" ", "%20");
25.115 - sb.append("&content=").append(mangle);
25.116 -
25.117 - URL query = new URL(baseURL, sb.toString());
25.118 - String uri = (String) query.getContent(new Class[] { String.class });
25.119 - URI connectTo = new URI(uri.trim());
25.120 - return connectTo;
25.121 - } catch (IOException ex) {
25.122 - throw new IllegalStateException(ex);
25.123 - } catch (URISyntaxException ex) {
25.124 - throw new IllegalStateException(ex);
25.125 - }
25.126 - }
25.127 -}
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
26.2 +++ b/ko/archetype-test/pom.xml Mon Jun 24 17:50:44 2013 +0200
26.3 @@ -0,0 +1,48 @@
26.4 +<?xml version="1.0"?>
26.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">
26.6 + <modelVersion>4.0.0</modelVersion>
26.7 + <parent>
26.8 + <groupId>org.apidesign</groupId>
26.9 + <artifactId>html</artifactId>
26.10 + <version>0.4-SNAPSHOT</version>
26.11 + </parent>
26.12 + <groupId>org.apidesign.html</groupId>
26.13 + <artifactId>ko-archetype-test</artifactId>
26.14 + <version>0.4-SNAPSHOT</version>
26.15 + <name>Knockout 4 Java Archetype Test</name>
26.16 + <url>http://maven.apache.org</url>
26.17 + <description>Verifies the Knockout & net.java.html.json archetype behaves properly.</description>
26.18 + <properties>
26.19 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
26.20 + </properties>
26.21 + <dependencies>
26.22 + <dependency>
26.23 + <groupId>${project.groupId}</groupId>
26.24 + <artifactId>knockout4j-archetype</artifactId>
26.25 + <version>0.4-SNAPSHOT</version>
26.26 + </dependency>
26.27 + <dependency>
26.28 + <groupId>org.testng</groupId>
26.29 + <artifactId>testng</artifactId>
26.30 + <scope>test</scope>
26.31 + </dependency>
26.32 + <dependency>
26.33 + <groupId>org.apache.maven.shared</groupId>
26.34 + <artifactId>maven-verifier</artifactId>
26.35 + <version>1.4</version>
26.36 + <scope>test</scope>
26.37 + </dependency>
26.38 + <dependency>
26.39 + <groupId>${project.groupId}</groupId>
26.40 + <artifactId>ko-fx</artifactId>
26.41 + <version>${project.version}</version>
26.42 + <scope>provided</scope>
26.43 + </dependency>
26.44 + <dependency>
26.45 + <groupId>${project.groupId}</groupId>
26.46 + <artifactId>ko-bck2brwsr</artifactId>
26.47 + <version>${project.version}</version>
26.48 + <scope>provided</scope>
26.49 + </dependency>
26.50 + </dependencies>
26.51 +</project>
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
27.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/html/archetype/test/ArchetypeVersionTest.java Mon Jun 24 17:50:44 2013 +0200
27.3 @@ -0,0 +1,139 @@
27.4 +/**
27.5 + * HTML via Java(tm) Language Bindings
27.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
27.7 + *
27.8 + * This program is free software: you can redistribute it and/or modify
27.9 + * it under the terms of the GNU General Public License as published by
27.10 + * the Free Software Foundation, version 2 of the License.
27.11 + *
27.12 + * This program is distributed in the hope that it will be useful,
27.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
27.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27.15 + * GNU General Public License for more details. apidesign.org
27.16 + * designates this particular file as subject to the
27.17 + * "Classpath" exception as provided by apidesign.org
27.18 + * in the License file that accompanied this code.
27.19 + *
27.20 + * You should have received a copy of the GNU General Public License
27.21 + * along with this program. Look for COPYING file in the top folder.
27.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
27.23 + */
27.24 +package org.apidesign.html.archetype.test;
27.25 +
27.26 +import java.io.IOException;
27.27 +import java.net.URL;
27.28 +import javax.xml.XMLConstants;
27.29 +import javax.xml.parsers.DocumentBuilderFactory;
27.30 +import javax.xml.parsers.ParserConfigurationException;
27.31 +import javax.xml.xpath.XPathConstants;
27.32 +import javax.xml.xpath.XPathExpression;
27.33 +import javax.xml.xpath.XPathExpressionException;
27.34 +import javax.xml.xpath.XPathFactory;
27.35 +import javax.xml.xpath.XPathFactoryConfigurationException;
27.36 +import org.testng.annotations.Test;
27.37 +import static org.testng.Assert.*;
27.38 +import org.testng.annotations.BeforeClass;
27.39 +import org.w3c.dom.Document;
27.40 +import org.w3c.dom.NodeList;
27.41 +import org.xml.sax.SAXException;
27.42 +
27.43 +/**
27.44 + *
27.45 + * @author Jaroslav Tulach <jtulach@netbeans.org>
27.46 + */
27.47 +public class ArchetypeVersionTest {
27.48 + private String version;
27.49 +
27.50 + public ArchetypeVersionTest() {
27.51 + }
27.52 +
27.53 + @BeforeClass public void readCurrentVersion() throws Exception {
27.54 + version = findCurrentVersion();
27.55 + assertFalse(version.isEmpty(), "There should be some version string");
27.56 + }
27.57 +
27.58 +
27.59 + @Test public void testComparePomDepsVersions() throws Exception {
27.60 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
27.61 + URL r = l.getResource("archetype-resources/pom.xml");
27.62 + assertNotNull(r, "Archetype pom found");
27.63 +
27.64 + final XPathFactory fact = XPathFactory.newInstance();
27.65 + XPathExpression xp2 = fact.newXPath().compile(
27.66 + "//properties/net.java.html.version/text()"
27.67 + );
27.68 +
27.69 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
27.70 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
27.71 +
27.72 + assertEquals(arch, version, "net.java.html.json dependency needs to be on latest version");
27.73 + }
27.74 +
27.75 + @Test public void testCheckLauncher() throws Exception {
27.76 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
27.77 + URL r = l.getResource("archetype-resources/pom.xml");
27.78 + assertNotNull(r, "Archetype pom found");
27.79 +
27.80 + final XPathFactory fact = XPathFactory.newInstance();
27.81 + XPathExpression xp2 = fact.newXPath().compile(
27.82 + "//properties/bck2brwsr.launcher.version/text()"
27.83 + );
27.84 +
27.85 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
27.86 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
27.87 +
27.88 +
27.89 + assertTrue(arch.matches("[0-9\\.]+"), "launcher version seems valid: " + arch);
27.90 + }
27.91 +
27.92 + @Test public void testCheckBck2Brwsr() throws Exception {
27.93 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
27.94 + URL r = l.getResource("archetype-resources/pom.xml");
27.95 + assertNotNull(r, "Archetype pom found");
27.96 +
27.97 + final XPathFactory fact = XPathFactory.newInstance();
27.98 + XPathExpression xp2 = fact.newXPath().compile(
27.99 + "//properties/bck2brwsr.version/text()"
27.100 + );
27.101 +
27.102 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
27.103 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
27.104 +
27.105 + assertTrue(arch.matches("[0-9\\.]+"), "bck2brwsr version seems valid: " + arch);
27.106 + }
27.107 +
27.108 + @Test public void testNbActions() throws Exception {
27.109 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
27.110 + URL r = l.getResource("archetype-resources/nbactions.xml");
27.111 + assertNotNull(r, "Archetype nb file found");
27.112 +
27.113 + final XPathFactory fact = XPathFactory.newInstance();
27.114 + XPathExpression xp2 = fact.newXPath().compile(
27.115 + "//goal/text()"
27.116 + );
27.117 +
27.118 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
27.119 + NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET);
27.120 +
27.121 + for (int i = 0; i < goals.getLength(); i++) {
27.122 + String s = goals.item(i).getTextContent();
27.123 + if (s.contains("apidesign")) {
27.124 + assertFalse(s.matches(".*apidesign.*[0-9].*"), "No numbers: " + s);
27.125 + }
27.126 + }
27.127 + }
27.128 +
27.129 + static String findCurrentVersion() throws XPathExpressionException, IOException, ParserConfigurationException, SAXException, XPathFactoryConfigurationException {
27.130 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
27.131 + URL u = l.getResource("META-INF/maven/org.apidesign.html/knockout4j-archetype/pom.xml");
27.132 + assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path"));
27.133 +
27.134 + final XPathFactory fact = XPathFactory.newInstance();
27.135 + fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
27.136 +
27.137 + XPathExpression xp = fact.newXPath().compile("project/version/text()");
27.138 +
27.139 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream());
27.140 + return xp.evaluate(dom);
27.141 + }
27.142 +}
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
28.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/html/archetype/test/VerifyArchetypeTest.java Mon Jun 24 17:50:44 2013 +0200
28.3 @@ -0,0 +1,119 @@
28.4 +/**
28.5 + * HTML via Java(tm) Language Bindings
28.6 + * Copyright (C) 2013 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. apidesign.org
28.16 + * designates this particular file as subject to the
28.17 + * "Classpath" exception as provided by apidesign.org
28.18 + * in the License file that accompanied this code.
28.19 + *
28.20 + * You should have received a copy of the GNU General Public License
28.21 + * along with this program. Look for COPYING file in the top folder.
28.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
28.23 + */
28.24 +package org.apidesign.html.archetype.test;
28.25 +
28.26 +import java.io.File;
28.27 +import java.util.Properties;
28.28 +import java.util.zip.ZipFile;
28.29 +import org.apache.maven.it.Verifier;
28.30 +import org.testng.annotations.Test;
28.31 +import static org.testng.Assert.*;
28.32 +
28.33 +/**
28.34 + *
28.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
28.36 + */
28.37 +public class VerifyArchetypeTest {
28.38 + @Test public void fxBrwsrCompiles() throws Exception {
28.39 + final File dir = new File("target/tests/fxcompile/").getAbsoluteFile();
28.40 + generateFromArchetype(dir);
28.41 +
28.42 + File created = new File(dir, "o-a-test");
28.43 + assertTrue(created.isDirectory(), "Project created");
28.44 + assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
28.45 +
28.46 + Verifier v = new Verifier(created.getAbsolutePath());
28.47 + v.executeGoal("verify");
28.48 +
28.49 + v.verifyErrorFreeLog();
28.50 +
28.51 + for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
28.52 + if (l.contains("j2js")) {
28.53 + fail("No pre-compilaton:\n" + l);
28.54 + }
28.55 + }
28.56 +
28.57 + v.verifyTextInLog("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher");
28.58 + v.verifyTextInLog("fxcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-fxbrwsr.zip");
28.59 + }
28.60 +
28.61 + @Test public void bck2BrwsrCompiles() throws Exception {
28.62 + final File dir = new File("target/tests/b2bcompile/").getAbsoluteFile();
28.63 + generateFromArchetype(dir);
28.64 +
28.65 + File created = new File(dir, "o-a-test");
28.66 + assertTrue(created.isDirectory(), "Project created");
28.67 + assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
28.68 +
28.69 + Verifier v = new Verifier(created.getAbsolutePath());
28.70 + Properties sysProp = v.getSystemProperties();
28.71 + if (Boolean.getBoolean("java.awt.headless")) {
28.72 + sysProp.put("java.awt.headless", "true");
28.73 + }
28.74 + v.addCliOption("-Pbck2brwsr");
28.75 + v.executeGoal("verify");
28.76 +
28.77 + v.verifyErrorFreeLog();
28.78 +
28.79 + // does pre-compilation to JavaScript
28.80 + v.verifyTextInLog("j2js");
28.81 + // uses Bck2BrwsrLauncher
28.82 + v.verifyTextInLog("BaseHTTPLauncher stopServerAndBrwsr");
28.83 + // building zip:
28.84 + v.verifyTextInLog("b2bcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
28.85 +
28.86 + for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
28.87 + if (l.contains("fxbrwsr")) {
28.88 + fail("No fxbrwsr:\n" + l);
28.89 + }
28.90 + }
28.91 +
28.92 + File zip = new File(new File(created, "target"), "o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
28.93 + assertTrue(zip.isFile(), "Zip file with website was created");
28.94 +
28.95 + ZipFile zf = new ZipFile(zip);
28.96 + assertNotNull(zf.getEntry("public_html/index.html"), "index.html found");
28.97 + assertNotNull(zf.getEntry("public_html/twitterExample.css"), "css file found");
28.98 +
28.99 + }
28.100 +
28.101 + private Verifier generateFromArchetype(final File dir, String... params) throws Exception {
28.102 + Verifier v = new Verifier(dir.getAbsolutePath());
28.103 + v.setAutoclean(false);
28.104 + v.setLogFileName("generate.log");
28.105 + v.deleteDirectory("");
28.106 + dir.mkdirs();
28.107 + Properties sysProp = v.getSystemProperties();
28.108 + sysProp.put("groupId", "org.apidesign.test");
28.109 + sysProp.put("artifactId", "o-a-test");
28.110 + sysProp.put("package", "org.apidesign.test.oat");
28.111 + sysProp.put("archetypeGroupId", "org.apidesign.html");
28.112 + sysProp.put("archetypeArtifactId", "knockout4j-archetype");
28.113 + sysProp.put("archetypeVersion", ArchetypeVersionTest.findCurrentVersion());
28.114 +
28.115 + for (String p : params) {
28.116 + v.addCliOption(p);
28.117 + }
28.118 + v.executeGoal("archetype:generate");
28.119 + v.verifyErrorFreeLog();
28.120 + return v;
28.121 + }
28.122 +}
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
29.2 +++ b/ko/archetype/pom.xml Mon Jun 24 17:50:44 2013 +0200
29.3 @@ -0,0 +1,58 @@
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 + <parent>
29.8 + <artifactId>html</artifactId>
29.9 + <groupId>org.apidesign</groupId>
29.10 + <version>0.4-SNAPSHOT</version>
29.11 + </parent>
29.12 + <groupId>org.apidesign.html</groupId>
29.13 + <artifactId>knockout4j-archetype</artifactId>
29.14 + <version>0.4-SNAPSHOT</version>
29.15 + <packaging>jar</packaging>
29.16 + <name>Knockout 4 Java Maven Archetype</name>
29.17 + <description>
29.18 + HTML page with Knockout.js bindings driven by application model
29.19 + written in Java. Use your favorite language to code. Use
29.20 + HTML as a lightweight rendering toolkit. Deploy using JavaFX or
29.21 + bck2brwsr virtual machine.
29.22 + </description>
29.23 + <build>
29.24 + <resources>
29.25 + <resource>
29.26 + <directory>src/main/resources</directory>
29.27 + <filtering>true</filtering>
29.28 + <includes>
29.29 + <include>**/pom.xml</include>
29.30 + </includes>
29.31 + </resource>
29.32 + <resource>
29.33 + <directory>src/main/resources</directory>
29.34 + <filtering>false</filtering>
29.35 + <excludes>
29.36 + <exclude>**/pom.xml</exclude>
29.37 + </excludes>
29.38 + </resource>
29.39 + </resources>
29.40 + <plugins>
29.41 + <plugin>
29.42 + <groupId>org.apache.maven.plugins</groupId>
29.43 + <artifactId>maven-compiler-plugin</artifactId>
29.44 + <version>2.3.2</version>
29.45 + <configuration>
29.46 + <source>1.6</source>
29.47 + <target>1.6</target>
29.48 + </configuration>
29.49 + </plugin>
29.50 + <plugin>
29.51 + <groupId>org.apache.maven.plugins</groupId>
29.52 + <artifactId>maven-resources-plugin</artifactId>
29.53 + <version>2.6</version>
29.54 + <configuration>
29.55 + <escapeString>\</escapeString>
29.56 + <target>1.6</target>
29.57 + </configuration>
29.58 + </plugin>
29.59 + </plugins>
29.60 + </build>
29.61 +</project>
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
30.2 +++ b/ko/archetype/src/main/java/org/apidesign/html/archetype/package-info.java Mon Jun 24 17:50:44 2013 +0200
30.3 @@ -0,0 +1,21 @@
30.4 +/**
30.5 + * HTML via Java(tm) Language Bindings
30.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
30.7 + *
30.8 + * This program is free software: you can redistribute it and/or modify
30.9 + * it under the terms of the GNU General Public License as published by
30.10 + * the Free Software Foundation, version 2 of the License.
30.11 + *
30.12 + * This program is distributed in the hope that it will be useful,
30.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
30.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30.15 + * GNU General Public License for more details. apidesign.org
30.16 + * designates this particular file as subject to the
30.17 + * "Classpath" exception as provided by apidesign.org
30.18 + * in the License file that accompanied this code.
30.19 + *
30.20 + * You should have received a copy of the GNU General Public License
30.21 + * along with this program. Look for COPYING file in the top folder.
30.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
30.23 + */
30.24 +package org.apidesign.html.archetype;
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
31.2 +++ b/ko/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml Mon Jun 24 17:50:44 2013 +0200
31.3 @@ -0,0 +1,44 @@
31.4 +<?xml version="1.0" encoding="UTF-8"?>
31.5 +<archetype-descriptor name="FX/Bck2Brwsr Example">
31.6 + <fileSets>
31.7 + <fileSet filtered="true" packaged="true">
31.8 + <directory>src/main/java</directory>
31.9 + <includes>
31.10 + <include>**/*.java</include>
31.11 + </includes>
31.12 + </fileSet>
31.13 + <fileSet filtered="true" packaged="true">
31.14 + <directory>src/main/resources</directory>
31.15 + <includes>
31.16 + <include>**/*.xhtml</include>
31.17 + <include>**/*.html</include>
31.18 + <include>**/*.css</include>
31.19 + </includes>
31.20 + </fileSet>
31.21 + <fileSet filtered="true" packaged="true">
31.22 + <directory>src/test/java</directory>
31.23 + <includes>
31.24 + <include>**/*Test.java</include>
31.25 + </includes>
31.26 + </fileSet>
31.27 + <fileSet filtered="true" packaged="false">
31.28 + <directory>src/main/assembly</directory>
31.29 + <includes>
31.30 + <include>**/*.xml</include>
31.31 + </includes>
31.32 + </fileSet>
31.33 + <fileSet filtered="false" packaged="false">
31.34 + <directory></directory>
31.35 + <includes>
31.36 + <include>nbactions*.xml</include>
31.37 + </includes>
31.38 + </fileSet>
31.39 + <fileSet filtered="true" packaged="false">
31.40 + <directory>assembly</directory>
31.41 + <includes>
31.42 + <include>fxbrwsr-assembly.xml</include>
31.43 + <include>bck2brwsr-assembly.xml</include>
31.44 + </includes>
31.45 + </fileSet>
31.46 + </fileSets>
31.47 +</archetype-descriptor>
31.48 \ No newline at end of file
32.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
32.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions-bck2brwsr.xml Mon Jun 24 17:50:44 2013 +0200
32.3 @@ -0,0 +1,14 @@
32.4 +<?xml version="1.0" encoding="UTF-8"?>
32.5 +<actions>
32.6 + <action>
32.7 + <actionName>run</actionName>
32.8 + <goals>
32.9 + <goal>package</goal>
32.10 + <goal>bck2brwsr:brwsr</goal>
32.11 + </goals>
32.12 + <properties>
32.13 + <skipTests>true</skipTests>
32.14 + <bck2brwsr.obfuscationlevel>NONE</bck2brwsr.obfuscationlevel>
32.15 + </properties>
32.16 + </action>
32.17 +</actions>
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
33.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions-fxbrwsr.xml Mon Jun 24 17:50:44 2013 +0200
33.3 @@ -0,0 +1,20 @@
33.4 +<?xml version="1.0" encoding="UTF-8"?>
33.5 +<actions>
33.6 + <action>
33.7 + <actionName>run</actionName>
33.8 + <goals>
33.9 + <goal>process-classes</goal>
33.10 + <goal>bck2brwsr:brwsr</goal>
33.11 + </goals>
33.12 + </action>
33.13 + <action>
33.14 + <actionName>debug</actionName>
33.15 + <goals>
33.16 + <goal>process-classes</goal>
33.17 + <goal>bck2brwsr:brwsr</goal>
33.18 + </goals>
33.19 + <properties>
33.20 + <jpda.listen>maven</jpda.listen>
33.21 + </properties>
33.22 + </action>
33.23 +</actions>
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
34.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions.xml Mon Jun 24 17:50:44 2013 +0200
34.3 @@ -0,0 +1,20 @@
34.4 +<?xml version="1.0" encoding="UTF-8"?>
34.5 +<actions>
34.6 + <action>
34.7 + <actionName>run</actionName>
34.8 + <goals>
34.9 + <goal>process-classes</goal>
34.10 + <goal>bck2brwsr:brwsr</goal>
34.11 + </goals>
34.12 + </action>
34.13 + <action>
34.14 + <actionName>debug</actionName>
34.15 + <goals>
34.16 + <goal>process-classes</goal>
34.17 + <goal>bck2brwsr:brwsr</goal>
34.18 + </goals>
34.19 + <properties>
34.20 + <jpda.listen>maven</jpda.listen>
34.21 + </properties>
34.22 + </action>
34.23 +</actions>
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
35.2 +++ b/ko/archetype/src/main/resources/archetype-resources/pom.xml Mon Jun 24 17:50:44 2013 +0200
35.3 @@ -0,0 +1,266 @@
35.4 +<?xml version="1.0"?>
35.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
35.6 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
35.7 + <modelVersion>4.0.0</modelVersion>
35.8 +
35.9 + <groupId>\${groupId}</groupId>
35.10 + <artifactId>\${artifactId}</artifactId>
35.11 + <version>\${version}</version>
35.12 + <packaging>jar</packaging>
35.13 +
35.14 + <name>\${artifactId}</name>
35.15 +
35.16 + <repositories>
35.17 + <repository>
35.18 + <id>java.net</id>
35.19 + <name>Java.net</name>
35.20 + <url>https://maven.java.net/content/repositories/releases/</url>
35.21 + <snapshots>
35.22 + <enabled>true</enabled>
35.23 + </snapshots>
35.24 + </repository>
35.25 + <repository>
35.26 + <id>netbeans</id>
35.27 + <name>NetBeans</name>
35.28 + <url>http://bits.netbeans.org/maven2/</url>
35.29 + </repository>
35.30 + </repositories>
35.31 + <pluginRepositories>
35.32 + <pluginRepository>
35.33 + <id>java.net</id>
35.34 + <name>Java.net</name>
35.35 + <url>https://maven.java.net/content/repositories/releases/</url>
35.36 + <snapshots>
35.37 + <enabled>true</enabled>
35.38 + </snapshots>
35.39 + </pluginRepository>
35.40 + </pluginRepositories>
35.41 +
35.42 + <properties>
35.43 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
35.44 + <net.java.html.version>${project.version}</net.java.html.version>
35.45 + <bck2brwsr.version>${bck2brwsr.version}</bck2brwsr.version>
35.46 + <bck2brwsr.launcher.version>${bck2brwsr.launcher.version}</bck2brwsr.launcher.version>
35.47 + <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
35.48 + <brwsr.startpage>\${package.replace('.','/')}/index.html</brwsr.startpage>
35.49 + </properties>
35.50 + <build>
35.51 + <plugins>
35.52 + <plugin>
35.53 + <groupId>org.apidesign.bck2brwsr</groupId>
35.54 + <artifactId>bck2brwsr-maven-plugin</artifactId>
35.55 + <version>\${bck2brwsr.launcher.version}</version>
35.56 + <executions>
35.57 + <execution>
35.58 + <goals>
35.59 + <goal>brwsr</goal>
35.60 + </goals>
35.61 + </execution>
35.62 + </executions>
35.63 + <configuration>
35.64 + <startpage>\${brwsr.startpage}</startpage>
35.65 + <launcher>\${brwsr}</launcher>
35.66 + </configuration>
35.67 + </plugin>
35.68 + <plugin>
35.69 + <groupId>org.apache.maven.plugins</groupId>
35.70 + <artifactId>maven-compiler-plugin</artifactId>
35.71 + <version>2.3.2</version>
35.72 + <configuration>
35.73 + <source>1.7</source>
35.74 + <target>1.7</target>
35.75 + </configuration>
35.76 + </plugin>
35.77 + <plugin>
35.78 + <groupId>org.apache.maven.plugins</groupId>
35.79 + <artifactId>maven-surefire-plugin</artifactId>
35.80 + <version>2.14.1</version>
35.81 + <configuration>
35.82 + <systemPropertyVariables>
35.83 + <vmtest.brwsrs>\${brwsr}</vmtest.brwsrs>
35.84 + </systemPropertyVariables>
35.85 + </configuration>
35.86 + </plugin>
35.87 + <plugin>
35.88 + <groupId>org.apache.maven.plugins</groupId>
35.89 + <artifactId>maven-jar-plugin</artifactId>
35.90 + <version>2.4</version>
35.91 + <configuration>
35.92 + <archive>
35.93 + <manifest>
35.94 + <addClasspath>true</addClasspath>
35.95 + <classpathPrefix>lib/</classpathPrefix>
35.96 + </manifest>
35.97 + </archive>
35.98 + </configuration>
35.99 + </plugin>
35.100 + <plugin>
35.101 + <groupId>org.apache.maven.plugins</groupId>
35.102 + <artifactId>maven-deploy-plugin</artifactId>
35.103 + <version>2.7</version>
35.104 + <configuration>
35.105 + <skip>true</skip>
35.106 + </configuration>
35.107 + </plugin>
35.108 + </plugins>
35.109 + </build>
35.110 +
35.111 + <dependencies>
35.112 + <dependency>
35.113 + <groupId>org.testng</groupId>
35.114 + <artifactId>testng</artifactId>
35.115 + <version>6.5.2</version>
35.116 + <scope>test</scope>
35.117 + </dependency>
35.118 + <dependency>
35.119 + <groupId>org.apidesign.bck2brwsr</groupId>
35.120 + <artifactId>vmtest</artifactId>
35.121 + <version>\${bck2brwsr.version}</version>
35.122 + <scope>test</scope>
35.123 + </dependency>
35.124 + <dependency>
35.125 + <groupId>org.apidesign.html</groupId>
35.126 + <artifactId>net.java.html.json</artifactId>
35.127 + <version>\${net.java.html.version}</version>
35.128 + <type>jar</type>
35.129 + </dependency>
35.130 + </dependencies>
35.131 + <profiles>
35.132 + <profile>
35.133 + <id>fxbrwsr</id>
35.134 + <activation>
35.135 + <activeByDefault>true</activeByDefault>
35.136 + </activation>
35.137 + <properties>
35.138 + <brwsr>fxbrwsr</brwsr>
35.139 + </properties>
35.140 + <build>
35.141 + <plugins>
35.142 + <plugin>
35.143 + <groupId>org.apache.maven.plugins</groupId>
35.144 + <artifactId>maven-jar-plugin</artifactId>
35.145 + <version>2.4</version>
35.146 + <configuration>
35.147 + <archive>
35.148 + <manifest>
35.149 + <mainClass>org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher</mainClass>
35.150 + <addClasspath>true</addClasspath>
35.151 + <classpathPrefix>lib/</classpathPrefix>
35.152 + </manifest>
35.153 + <manifestEntries>
35.154 + <StartPage>\${brwsr.startpage}</StartPage>
35.155 + </manifestEntries>
35.156 + </archive>
35.157 + </configuration>
35.158 + </plugin>
35.159 + <plugin>
35.160 + <artifactId>maven-assembly-plugin</artifactId>
35.161 + <version>2.4</version>
35.162 + <executions>
35.163 + <execution>
35.164 + <id>distro-assembly</id>
35.165 + <phase>package</phase>
35.166 + <goals>
35.167 + <goal>single</goal>
35.168 + </goals>
35.169 + <configuration>
35.170 + <descriptors>
35.171 + <descriptor>src/main/assembly/fxbrwsr.xml</descriptor>
35.172 + </descriptors>
35.173 + </configuration>
35.174 + </execution>
35.175 + </executions>
35.176 + </plugin>
35.177 + </plugins>
35.178 + </build>
35.179 + <dependencies>
35.180 + <dependency>
35.181 + <groupId>org.apidesign.html</groupId>
35.182 + <artifactId>ko-fx</artifactId>
35.183 + <version>\${net.java.html.version}</version>
35.184 + </dependency>
35.185 + <dependency>
35.186 + <groupId>org.apidesign.bck2brwsr</groupId>
35.187 + <artifactId>launcher.fx</artifactId>
35.188 + <version>\${bck2brwsr.launcher.version}</version>
35.189 + <scope>runtime</scope>
35.190 + </dependency>
35.191 + </dependencies>
35.192 + </profile>
35.193 + <profile>
35.194 + <id>bck2brwsr</id>
35.195 + <activation>
35.196 + <property>
35.197 + <name>brwsr</name>
35.198 + <value>bck2brwsr</value>
35.199 + </property>
35.200 + </activation>
35.201 + <build>
35.202 + <plugins>
35.203 + <plugin>
35.204 + <groupId>org.apidesign.bck2brwsr</groupId>
35.205 + <artifactId>bck2brwsr-maven-plugin</artifactId>
35.206 + <executions>
35.207 + <execution>
35.208 + <goals>
35.209 + <goal>j2js</goal>
35.210 + </goals>
35.211 + </execution>
35.212 + </executions>
35.213 + <configuration>
35.214 + <javascript>\${project.build.directory}/bck2brwsr.js</javascript>
35.215 + <obfuscation>\${bck2brwsr.obfuscationlevel}</obfuscation>
35.216 + </configuration>
35.217 + </plugin>
35.218 + <plugin>
35.219 + <groupId>org.apache.maven.plugins</groupId>
35.220 + <artifactId>maven-compiler-plugin</artifactId>
35.221 + <configuration>
35.222 + <compilerArguments>
35.223 + <bootclasspath>netbeans.ignore.jdk.bootclasspath</bootclasspath>
35.224 + </compilerArguments>
35.225 + </configuration>
35.226 + </plugin>
35.227 + <plugin>
35.228 + <artifactId>maven-assembly-plugin</artifactId>
35.229 + <version>2.4</version>
35.230 + <executions>
35.231 + <execution>
35.232 + <id>distro-assembly</id>
35.233 + <phase>package</phase>
35.234 + <goals>
35.235 + <goal>single</goal>
35.236 + </goals>
35.237 + <configuration>
35.238 + <descriptors>
35.239 + <descriptor>src/main/assembly/bck2brwsr.xml</descriptor>
35.240 + </descriptors>
35.241 + </configuration>
35.242 + </execution>
35.243 + </executions>
35.244 + </plugin>
35.245 + </plugins>
35.246 + </build>
35.247 + <dependencies>
35.248 + <dependency>
35.249 + <groupId>org.apidesign.bck2brwsr</groupId>
35.250 + <artifactId>emul</artifactId>
35.251 + <version>\${bck2brwsr.version}</version>
35.252 + <classifier>rt</classifier>
35.253 + </dependency>
35.254 + <dependency>
35.255 + <groupId>org.apidesign.html</groupId>
35.256 + <artifactId>ko-bck2brwsr</artifactId>
35.257 + <version>\${net.java.html.version}</version>
35.258 + <scope>runtime</scope>
35.259 + </dependency>
35.260 + <dependency>
35.261 + <groupId>org.apidesign.bck2brwsr</groupId>
35.262 + <artifactId>launcher.http</artifactId>
35.263 + <version>\${bck2brwsr.launcher.version}</version>
35.264 + <scope>test</scope>
35.265 + </dependency>
35.266 + </dependencies>
35.267 + </profile>
35.268 + </profiles>
35.269 +</project>
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
36.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml Mon Jun 24 17:50:44 2013 +0200
36.3 @@ -0,0 +1,43 @@
36.4 +<?xml version="1.0"?>
36.5 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
36.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">
36.7 +
36.8 + <id>bck2brwsr</id>
36.9 + <formats>
36.10 + <format>zip</format>
36.11 + </formats>
36.12 + <baseDirectory>public_html</baseDirectory>
36.13 + <dependencySets>
36.14 + <dependencySet>
36.15 + <useProjectArtifact>false</useProjectArtifact>
36.16 + <scope>runtime</scope>
36.17 + <outputDirectory>lib</outputDirectory>
36.18 + <includes>
36.19 + <include>*:jar</include>
36.20 + <include>*:rt</include>
36.21 + </includes>
36.22 + </dependencySet>
36.23 + </dependencySets>
36.24 + <fileSets>
36.25 + <fileSet>
36.26 + <directory>${project.build.directory}/classes/${package.replace('.','/')}/</directory>
36.27 + <includes>
36.28 + <include>**/*</include>
36.29 + </includes>
36.30 + <excludes>
36.31 + <exclude>**/*.class</exclude>
36.32 + </excludes>
36.33 + <outputDirectory>/</outputDirectory>
36.34 + </fileSet>
36.35 + </fileSets>
36.36 + <files>
36.37 + <file>
36.38 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
36.39 + <outputDirectory>/</outputDirectory>
36.40 + </file>
36.41 + <file>
36.42 + <source>${project.build.directory}/bck2brwsr.js</source>
36.43 + <outputDirectory>/</outputDirectory>
36.44 + </file>
36.45 + </files>
36.46 +</assembly>
36.47 \ No newline at end of file
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
37.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml Mon Jun 24 17:50:44 2013 +0200
37.3 @@ -0,0 +1,23 @@
37.4 +<?xml version="1.0"?>
37.5 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
37.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">
37.7 +
37.8 + <id>fxbrwsr</id>
37.9 + <formats>
37.10 + <format>zip</format>
37.11 + </formats>
37.12 + <baseDirectory>${project.build.finalName}-fxbrwsr</baseDirectory>
37.13 + <dependencySets>
37.14 + <dependencySet>
37.15 + <useProjectArtifact>false</useProjectArtifact>
37.16 + <scope>runtime</scope>
37.17 + <outputDirectory>lib</outputDirectory>
37.18 + </dependencySet>
37.19 + </dependencySets>
37.20 + <files>
37.21 + <file>
37.22 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
37.23 + <outputDirectory>/</outputDirectory>
37.24 + </file>
37.25 + </files>
37.26 +</assembly>
37.27 \ No newline at end of file
38.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
38.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java Mon Jun 24 17:50:44 2013 +0200
38.3 @@ -0,0 +1,178 @@
38.4 +package ${package};
38.5 +
38.6 +import java.util.Arrays;
38.7 +import java.util.List;
38.8 +import net.java.html.json.ComputedProperty;
38.9 +import net.java.html.json.Function;
38.10 +import net.java.html.json.Model;
38.11 +import net.java.html.json.OnPropertyChange;
38.12 +import net.java.html.json.OnReceive;
38.13 +import net.java.html.json.Property;
38.14 +
38.15 +@Model(className="TwitterModel", properties={
38.16 + @Property(name="savedLists", type=Tweeters.class, array = true),
38.17 + @Property(name="activeTweetersName", type=String.class),
38.18 + @Property(name="activeTweeters", type=String.class, array = true),
38.19 + @Property(name="userNameToAdd", type=String.class),
38.20 + @Property(name="loading", type=boolean.class),
38.21 + @Property(name="currentTweets", type=Tweet.class, array = true)
38.22 +})
38.23 +public class TwitterClient {
38.24 + @Model(className = "Tweeters", properties = {
38.25 + @Property(name="name", type = String.class),
38.26 + @Property(name="userNames", type = String.class, array = true)
38.27 + })
38.28 + static class Twttrs {
38.29 + }
38.30 + @Model(className = "Tweet", properties = {
38.31 + @Property(name = "from_user", type = String.class),
38.32 + @Property(name = "from_user_id", type = int.class),
38.33 + @Property(name = "profile_image_url", type = String.class),
38.34 + @Property(name = "text", type = String.class),
38.35 + @Property(name = "created_at", type = String.class),
38.36 + })
38.37 + static final class Twt {
38.38 + @ComputedProperty static String html(String text) {
38.39 + StringBuilder sb = new StringBuilder(320);
38.40 + for (int pos = 0;;) {
38.41 + int http = text.indexOf("http", pos);
38.42 + if (http == -1) {
38.43 + sb.append(text.substring(pos));
38.44 + return sb.toString();
38.45 + }
38.46 + int spc = text.indexOf(' ', http);
38.47 + if (spc == -1) {
38.48 + spc = text.length();
38.49 + }
38.50 + sb.append(text.substring(pos, http));
38.51 + String url = text.substring(http, spc);
38.52 + sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
38.53 + pos = spc;
38.54 + }
38.55 + }
38.56 +
38.57 + @ComputedProperty static String userUrl(String from_user) {
38.58 + return "http://twitter.com/" + from_user;
38.59 + }
38.60 + }
38.61 + @Model(className = "TwitterQuery", properties = {
38.62 + @Property(array = true, name = "results", type = Twt.class)
38.63 + })
38.64 + public static final class TwttrQr {
38.65 + }
38.66 +
38.67 + @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
38.68 + static void queryTweets(TwitterModel page, TwitterQuery q) {
38.69 + page.getCurrentTweets().clear();
38.70 + page.getCurrentTweets().addAll(q.getResults());
38.71 + page.setLoading(false);
38.72 + }
38.73 +
38.74 + @OnPropertyChange("activeTweetersName")
38.75 + static void changeTweetersList(TwitterModel model) {
38.76 + Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());
38.77 + model.getActiveTweeters().clear();
38.78 + model.getActiveTweeters().addAll(people.getUserNames());
38.79 + }
38.80 +
38.81 + @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
38.82 + static void refreshTweets(TwitterModel model) {
38.83 + StringBuilder sb = new StringBuilder();
38.84 + sb.append("rpp=25&q=");
38.85 + String sep = "";
38.86 + for (String p : model.getActiveTweeters()) {
38.87 + sb.append(sep);
38.88 + sb.append("from:");
38.89 + sb.append(p);
38.90 + sep = " OR ";
38.91 + }
38.92 + model.setLoading(true);
38.93 + model.queryTweets("http://search.twitter.com", sb.toString());
38.94 + }
38.95 +
38.96 + static {
38.97 + final TwitterModel model = new TwitterModel();
38.98 + final List<Tweeters> svdLst = model.getSavedLists();
38.99 + svdLst.add(newTweeters("API Design", "JaroslavTulach"));
38.100 + svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
38.101 + svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
38.102 + svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
38.103 + svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
38.104 +
38.105 + model.setActiveTweetersName("NetBeans");
38.106 +
38.107 + model.applyBindings();
38.108 + }
38.109 +
38.110 + @ComputedProperty
38.111 + static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
38.112 + Tweeters tw = findByName(savedLists, activeTweetersName);
38.113 + if (activeTweeters == null) {
38.114 + return false;
38.115 + }
38.116 + return !tw.getUserNames().equals(activeTweeters);
38.117 + }
38.118 +
38.119 + @ComputedProperty
38.120 + static int activeTweetersCount(List<String> activeTweeters) {
38.121 + return activeTweeters.size();
38.122 + }
38.123 +
38.124 + @ComputedProperty
38.125 + static boolean userNameToAddIsValid(
38.126 + String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
38.127 + ) {
38.128 + return userNameToAdd != null &&
38.129 + userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
38.130 + !activeTweeters.contains(userNameToAdd);
38.131 + }
38.132 +
38.133 + @Function
38.134 + static void deleteList(TwitterModel model) {
38.135 + final List<Tweeters> sl = model.getSavedLists();
38.136 + sl.remove(findByName(sl, model.getActiveTweetersName()));
38.137 + if (sl.isEmpty()) {
38.138 + final Tweeters t = new Tweeters();
38.139 + t.setName("New");
38.140 + sl.add(t);
38.141 + }
38.142 + model.setActiveTweetersName(sl.get(0).getName());
38.143 + }
38.144 +
38.145 + @Function
38.146 + static void saveChanges(TwitterModel model) {
38.147 + Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
38.148 + int indx = model.getSavedLists().indexOf(t);
38.149 + if (indx != -1) {
38.150 + t.setName(model.getActiveTweetersName());
38.151 + t.getUserNames().clear();
38.152 + t.getUserNames().addAll(model.getActiveTweeters());
38.153 + }
38.154 + }
38.155 +
38.156 + @Function
38.157 + static void addUser(TwitterModel model) {
38.158 + String n = model.getUserNameToAdd();
38.159 + model.getActiveTweeters().add(n);
38.160 + }
38.161 + @Function
38.162 + static void removeUser(String data, TwitterModel model) {
38.163 + model.getActiveTweeters().remove(data);
38.164 + }
38.165 +
38.166 + private static Tweeters findByName(List<Tweeters> list, String name) {
38.167 + for (Tweeters l : list) {
38.168 + if (l.getName() != null && l.getName().equals(name)) {
38.169 + return l;
38.170 + }
38.171 + }
38.172 + return list.isEmpty() ? new Tweeters() : list.get(0);
38.173 + }
38.174 +
38.175 + private static Tweeters newTweeters(String listName, String... userNames) {
38.176 + Tweeters t = new Tweeters();
38.177 + t.setName(listName);
38.178 + t.getUserNames().addAll(Arrays.asList(userNames));
38.179 + return t;
38.180 + }
38.181 +}
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
39.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/resources/index.html Mon Jun 24 17:50:44 2013 +0200
39.3 @@ -0,0 +1,90 @@
39.4 +<?xml version="1.0" encoding="UTF-8"?>
39.5 +
39.6 +<!--
39.7 + Copied from knockout.js Twitter example:
39.8 + http://knockoutjs.com/examples/twitter.html
39.9 +-->
39.10 +
39.11 +<!DOCTYPE html>
39.12 +<html xmlns="http://www.w3.org/1999/xhtml">
39.13 + <head>
39.14 + <title>Bck2Brwsr's Twitter</title>
39.15 + </head>
39.16 + <body>
39.17 + <link href='twitterExample.css' rel='Stylesheet' ></link>
39.18 +
39.19 + <style type='text/css'>
39.20 + .liveExample select { height: 1.7em; }
39.21 + .liveExample button { height: 2em; }
39.22 + </style>
39.23 +
39.24 +
39.25 + <h2>Bck2Brwsr's Twitter</h2>
39.26 +
39.27 + <p>
39.28 + This code is based on original
39.29 + <a href="http://knockoutjs.com/examples/twitter.html">knockout.js
39.30 + Twitter example</a> and
39.31 + uses almost unmodified HTML page. It just changes the model. The model
39.32 + is written in Java language with the help of
39.33 + <a href="http://bck2brwsr.apidesign.org/javadoc/net.java.html.json/">
39.34 + Knockout/Java binding library
39.35 + </a>. The Java source code has about 180 lines and seems more
39.36 + dense and shorter than the original JavaScript model.
39.37 + </p>
39.38 + <p>
39.39 + The project has two profiles. Either it executes in real Java virtual
39.40 + machine and renders using JavaFX's WebView (use <code>fxbrwsr</code> profile
39.41 + - the default). It can also run directly in a browser via
39.42 + <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> virtual machine
39.43 + (use <code>bck2brwsr</code> profile).
39.44 + </p>
39.45 +
39.46 + <div class='liveExample'>
39.47 + <div class='configuration'>
39.48 + <div class='listChooser'>
39.49 + <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
39.50 + <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button>
39.51 + <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
39.52 + </div>
39.53 +
39.54 + <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
39.55 + <div class='currentUsers' >
39.56 + <ul data-bind='foreach: activeTweeters'>
39.57 + <li>
39.58 + <button data-bind='click: $root.removeUser'>Remove</button>
39.59 + <div data-bind='text: $data'> </div>
39.60 + </li>
39.61 + </ul>
39.62 + </div>
39.63 +
39.64 + <form data-bind='submit: addUser'>
39.65 + <label>Add user:</label>
39.66 + <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
39.67 + <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
39.68 + </form>
39.69 + </div>
39.70 + <div class='tweets'>
39.71 + <div class='loadingIndicator' data-bind="visible: loading">Loading...</div>
39.72 + <table data-bind='foreach: currentTweets' width='100%'>
39.73 + <tr>
39.74 + <td><img data-bind='attr: { src: profile_image_url }' /></td>
39.75 + <td>
39.76 + <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
39.77 + <span data-bind='html: html'> </span>
39.78 + <div class='tweetInfo' data-bind='text: created_at'> </div>
39.79 + </td>
39.80 + </tr>
39.81 + </table>
39.82 + </div>
39.83 + </div>
39.84 +
39.85 + <script src="bck2brwsr.js"></script>
39.86 + <script type="text/javascript">
39.87 + var vm = bck2brwsr('${artifactId}-${version}.jar');
39.88 + vm.loadClass('${package}.TwitterClient');
39.89 + </script>
39.90 +
39.91 +
39.92 + </body>
39.93 +</html>
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
40.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css Mon Jun 24 17:50:44 2013 +0200
40.3 @@ -0,0 +1,32 @@
40.4 +/*
40.5 + Copied from knockout.js Twitter example:
40.6 + http://knockoutjs.com/examples/twitter.html
40.7 +*/
40.8 +
40.9 +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
40.10 +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
40.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; }
40.12 +.tweets table { border-width: 0;}
40.13 +.tweets tr { vertical-align: top; }
40.14 +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
40.15 +.tweets img { width: 4em; }
40.16 +.tweetInfo { color: Gray; font-size: 0.9em; }
40.17 +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
40.18 +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
40.19 +
40.20 +.listChooser select, .listChooser button { vertical-align:top; }
40.21 +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
40.22 +.listChooser button { width: 19%; height:1.68em; float:right; }
40.23 +
40.24 +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
40.25 +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
40.26 +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
40.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; }
40.28 +.currentUsers li div { padding: 0.6em; }
40.29 +.currentUsers li:hover { background-color: #EEC; }
40.30 +
40.31 +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
40.32 +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
40.33 +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
40.34 +
40.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; }
41.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
41.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java Mon Jun 24 17:50:44 2013 +0200
41.3 @@ -0,0 +1,31 @@
41.4 +package ${package};
41.5 +
41.6 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
41.7 +import org.apidesign.bck2brwsr.vmtest.HtmlFragment;
41.8 +import org.apidesign.bck2brwsr.vmtest.VMTest;
41.9 +import org.testng.annotations.Factory;
41.10 +
41.11 +/** Sometimes it is useful to run tests inside of the real browser.
41.12 + * To do that just annotate your method with {@link org.apidesign.bck2brwsr.vmtest.BrwsrTest}
41.13 + * and that is it. If your code references elements on the HTML page,
41.14 + * you can pass in an {@link org.apidesign.bck2brwsr.vmtest.HtmlFragment} which
41.15 + * will be made available on the page before your test starts.
41.16 + */
41.17 +public class IntegrationTest {
41.18 +
41.19 + /** Write to testing code here. Use <code>assert</code> (but not TestNG's
41.20 + * Assert, as TestNG is not compiled with target 1.6 yet).
41.21 + */
41.22 + @HtmlFragment(
41.23 + "<h1>Put this snippet on the HTML page</h1>\n"
41.24 + )
41.25 + @BrwsrTest
41.26 + public void runThisTestInABrowser() {
41.27 + }
41.28 +
41.29 + @Factory
41.30 + public static Object[] create() {
41.31 + return VMTest.create(IntegrationTest.class);
41.32 + }
41.33 +
41.34 +}
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
42.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java Mon Jun 24 17:50:44 2013 +0200
42.3 @@ -0,0 +1,50 @@
42.4 +package ${package};
42.5 +
42.6 +import java.util.List;
42.7 +import net.java.html.BrwsrCtx;
42.8 +import net.java.html.json.Models;
42.9 +import static org.testng.Assert.*;
42.10 +import org.testng.annotations.BeforeMethod;
42.11 +import org.testng.annotations.Test;
42.12 +
42.13 +/** We can unit test the TwitterModel smoothly.
42.14 + */
42.15 +public class TwitterClientTest {
42.16 + private TwitterModel model;
42.17 +
42.18 +
42.19 + @BeforeMethod
42.20 + public void initModel() {
42.21 + model = Models.bind(new TwitterModel(), BrwsrCtx.EMPTY);
42.22 + }
42.23 +
42.24 + @Test public void testIsValidToAdd() {
42.25 + model.setUserNameToAdd("Joe");
42.26 + Tweeters t = Models.bind(new Tweeters(), BrwsrCtx.EMPTY);
42.27 + t.setName("test");
42.28 + model.getSavedLists().add(t);
42.29 + model.setActiveTweetersName("test");
42.30 +
42.31 + assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
42.32 + TwitterClient.addUser(model);
42.33 + assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
42.34 + assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
42.35 +
42.36 + List<String> mod = model.getActiveTweeters();
42.37 + assertTrue(model.isHasUnsavedChanges(), "We have modifications");
42.38 + assertEquals(mod.size(), 1, "One element in the list");
42.39 + assertEquals(mod.get(0), "Joe", "Its name is Joe");
42.40 +
42.41 + assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
42.42 +
42.43 + TwitterClient.saveChanges(model);
42.44 + assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
42.45 +
42.46 + assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
42.47 + }
42.48 +
42.49 + @Test public void httpAtTheEnd() {
42.50 + String res = TwitterClient.Twt.html("Ahoj http://kuk");
42.51 + assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
42.52 + }
42.53 +}
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
43.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java Mon Jun 24 17:50:44 2013 +0200
43.3 @@ -0,0 +1,73 @@
43.4 +package ${package};
43.5 +
43.6 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
43.7 +import org.apidesign.bck2brwsr.vmtest.Http;
43.8 +import org.apidesign.bck2brwsr.vmtest.VMTest;
43.9 +import org.testng.annotations.Factory;
43.10 +
43.11 +public class TwitterProtocolTest {
43.12 + private TwitterModel page;
43.13 + @Http(@Http.Resource(
43.14 + path = "/search.json",
43.15 + mimeType = "application/json",
43.16 + parameters = {"callback"},
43.17 + content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
43.18 + + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
43.19 + + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
43.20 + + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
43.21 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
43.22 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
43.23 + + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
43.24 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.25 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.26 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
43.27 + + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
43.28 + + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
43.29 + + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
43.30 + + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
43.31 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
43.32 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
43.33 + + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
43.34 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.35 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.36 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
43.37 + + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
43.38 + + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
43.39 + + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
43.40 + + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
43.41 + + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
43.42 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.43 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.44 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
43.45 + + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
43.46 + + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
43.47 + + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
43.48 + + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
43.49 + + "{\"result_type\":\"recent\"},\"profile_image_url\":"
43.50 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.51 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
43.52 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
43.53 + + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
43.54 + + "\"since_id\":0,\"since_id_str\":\"0\"})"
43.55 + ))
43.56 + @BrwsrTest public void readFromTwttr() throws InterruptedException {
43.57 + if (page == null) {
43.58 + page = new TwitterModel();
43.59 + page.applyBindings();
43.60 + page.queryTweets("", "q=xyz");
43.61 + }
43.62 +
43.63 + if (page.getCurrentTweets().isEmpty()) {
43.64 + throw new InterruptedException();
43.65 + }
43.66 +
43.67 + assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
43.68 +
43.69 + String firstDate = page.getCurrentTweets().get(0).getCreated_at();
43.70 + assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
43.71 + }
43.72 +
43.73 + @Factory public static Object[] create() {
43.74 + return VMTest.create(TwitterProtocolTest.class);
43.75 + }
43.76 +}
44.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
44.2 +++ b/ko/bck2brwsr/pom.xml Mon Jun 24 17:50:44 2013 +0200
44.3 @@ -0,0 +1,96 @@
44.4 +<?xml version="1.0"?>
44.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">
44.6 + <modelVersion>4.0.0</modelVersion>
44.7 + <parent>
44.8 + <groupId>org.apidesign</groupId>
44.9 + <artifactId>html</artifactId>
44.10 + <version>0.4-SNAPSHOT</version>
44.11 + </parent>
44.12 + <groupId>org.apidesign.html</groupId>
44.13 + <artifactId>ko-bck2brwsr</artifactId>
44.14 + <version>0.4-SNAPSHOT</version>
44.15 + <name>Knockout.b2b</name>
44.16 + <url>http://maven.apache.org</url>
44.17 + <build>
44.18 + <plugins>
44.19 + <plugin>
44.20 + <groupId>org.apache.maven.plugins</groupId>
44.21 + <artifactId>maven-compiler-plugin</artifactId>
44.22 + <version>2.3.2</version>
44.23 + <configuration>
44.24 + <source>1.7</source>
44.25 + <target>1.7</target>
44.26 + </configuration>
44.27 + </plugin>
44.28 + <plugin>
44.29 + <groupId>org.apache.maven.plugins</groupId>
44.30 + <artifactId>maven-javadoc-plugin</artifactId>
44.31 + <configuration>
44.32 + <skip>false</skip>
44.33 + </configuration>
44.34 + </plugin>
44.35 + </plugins>
44.36 + </build>
44.37 + <dependencies>
44.38 + <dependency>
44.39 + <groupId>org.testng</groupId>
44.40 + <artifactId>testng</artifactId>
44.41 + <scope>test</scope>
44.42 + <exclusions>
44.43 + <exclusion>
44.44 + <artifactId>junit</artifactId>
44.45 + <groupId>junit</groupId>
44.46 + </exclusion>
44.47 + </exclusions>
44.48 + </dependency>
44.49 + <dependency>
44.50 + <groupId>org.netbeans.api</groupId>
44.51 + <artifactId>org-openide-util-lookup</artifactId>
44.52 + <scope>provided</scope>
44.53 + </dependency>
44.54 + <dependency>
44.55 + <groupId>org.apidesign.bck2brwsr</groupId>
44.56 + <artifactId>emul</artifactId>
44.57 + <version>${bck2brwsr.version}</version>
44.58 + <classifier>rt</classifier>
44.59 + <type>jar</type>
44.60 + <scope>compile</scope>
44.61 + </dependency>
44.62 + <dependency>
44.63 + <groupId>org.apidesign.bck2brwsr</groupId>
44.64 + <artifactId>vm4brwsr</artifactId>
44.65 + <version>${bck2brwsr.version}</version>
44.66 + <type>jar</type>
44.67 + <scope>test</scope>
44.68 + </dependency>
44.69 + <dependency>
44.70 + <groupId>org.apidesign.bck2brwsr</groupId>
44.71 + <artifactId>vmtest</artifactId>
44.72 + <version>${bck2brwsr.version}</version>
44.73 + <scope>test</scope>
44.74 + </dependency>
44.75 + <dependency>
44.76 + <groupId>org.apidesign.bck2brwsr</groupId>
44.77 + <artifactId>launcher.http</artifactId>
44.78 + <version>${bck2brwsr.launcher.version}</version>
44.79 + <scope>test</scope>
44.80 + </dependency>
44.81 + <dependency>
44.82 + <groupId>org.apidesign.html</groupId>
44.83 + <artifactId>net.java.html.json</artifactId>
44.84 + <version>0.4-SNAPSHOT</version>
44.85 + </dependency>
44.86 + <dependency>
44.87 + <groupId>org.apidesign.html</groupId>
44.88 + <artifactId>net.java.html.json.tck</artifactId>
44.89 + <version>0.4-SNAPSHOT</version>
44.90 + <scope>test</scope>
44.91 + </dependency>
44.92 + <dependency>
44.93 + <groupId>org.apidesign.bck2brwsr</groupId>
44.94 + <artifactId>core</artifactId>
44.95 + <version>${bck2brwsr.version}</version>
44.96 + <type>jar</type>
44.97 + </dependency>
44.98 + </dependencies>
44.99 +</project>
45.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
45.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/BrwsrCtxImpl.java Mon Jun 24 17:50:44 2013 +0200
45.3 @@ -0,0 +1,129 @@
45.4 +/**
45.5 + * HTML via Java(tm) Language Bindings
45.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
45.7 + *
45.8 + * This program is free software: you can redistribute it and/or modify
45.9 + * it under the terms of the GNU General Public License as published by
45.10 + * the Free Software Foundation, version 2 of the License.
45.11 + *
45.12 + * This program is distributed in the hope that it will be useful,
45.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
45.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45.15 + * GNU General Public License for more details. apidesign.org
45.16 + * designates this particular file as subject to the
45.17 + * "Classpath" exception as provided by apidesign.org
45.18 + * in the License file that accompanied this code.
45.19 + *
45.20 + * You should have received a copy of the GNU General Public License
45.21 + * along with this program. Look for COPYING file in the top folder.
45.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
45.23 + */
45.24 +package org.apidesign.html.ko2brwsr;
45.25 +
45.26 +import java.io.ByteArrayOutputStream;
45.27 +import java.io.IOException;
45.28 +import java.io.InputStream;
45.29 +import java.io.InputStreamReader;
45.30 +import net.java.html.BrwsrCtx;
45.31 +import org.apidesign.html.context.spi.Contexts;
45.32 +import org.apidesign.html.json.spi.FunctionBinding;
45.33 +import org.apidesign.html.json.spi.JSONCall;
45.34 +import org.apidesign.html.json.spi.PropertyBinding;
45.35 +import org.apidesign.html.json.spi.Technology;
45.36 +import org.apidesign.html.json.spi.Transfer;
45.37 +
45.38 +/**
45.39 + *
45.40 + * @author Jaroslav Tulach <jtulach@netbeans.org>
45.41 + */
45.42 +final class BrwsrCtxImpl implements Technology<Object>, Transfer {
45.43 + private BrwsrCtxImpl() {}
45.44 +
45.45 + public static final BrwsrCtxImpl DEFAULT = new BrwsrCtxImpl();
45.46 +
45.47 + @Override
45.48 + public void extract(Object obj, String[] props, Object[] values) {
45.49 + ConvertTypes.extractJSON(obj, props, values);
45.50 + }
45.51 +
45.52 + @Override
45.53 + public void loadJSON(final JSONCall call) {
45.54 + class R implements Runnable {
45.55 + Object[] arr = { null };
45.56 + @Override
45.57 + public void run() {
45.58 + call.notifySuccess(arr[0]);
45.59 + }
45.60 + }
45.61 + R r = new R();
45.62 + if (call.isJSONP()) {
45.63 + String me = ConvertTypes.createJSONP(r.arr, r);
45.64 + ConvertTypes.loadJSONP(call.composeURL(me), me);
45.65 + } else {
45.66 + String data = null;
45.67 + if (call.isDoOutput()) {
45.68 + try {
45.69 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
45.70 + call.writeData(bos);
45.71 + data = new String(bos.toByteArray(), "UTF-8");
45.72 + } catch (IOException ex) {
45.73 + call.notifyError(ex);
45.74 + }
45.75 + }
45.76 + ConvertTypes.loadJSON(call.composeURL(null), r.arr, r, call.getMethod(), data);
45.77 + }
45.78 + }
45.79 +
45.80 + @Override
45.81 + public Object wrapModel(Object model) {
45.82 + return model;
45.83 + }
45.84 +
45.85 + @Override
45.86 + public void bind(PropertyBinding b, Object model, Object data) {
45.87 + Knockout.bind(data, b, b.getPropertyName(),
45.88 + "getValue__Ljava_lang_Object_2",
45.89 + b.isReadOnly() ? null : "setValue__VLjava_lang_Object_2",
45.90 + false, false
45.91 + );
45.92 + }
45.93 +
45.94 + @Override
45.95 + public void valueHasMutated(Object data, String propertyName) {
45.96 + Knockout.valueHasMutated(data, propertyName);
45.97 + }
45.98 +
45.99 + @Override
45.100 + public void expose(FunctionBinding fb, Object model, Object d) {
45.101 + Knockout.expose(d, fb, fb.getFunctionName(), "call__VLjava_lang_Object_2Ljava_lang_Object_2");
45.102 + }
45.103 +
45.104 + @Override
45.105 + public void applyBindings(Object data) {
45.106 + Knockout.applyBindings(data);
45.107 + }
45.108 +
45.109 + @Override
45.110 + public Object wrapArray(Object[] arr) {
45.111 + return arr;
45.112 + }
45.113 +
45.114 + @Override
45.115 + public <M> M toModel(Class<M> modelClass, Object data) {
45.116 + return modelClass.cast(data);
45.117 + }
45.118 +
45.119 + @Override
45.120 + public Object toJSON(InputStream is) throws IOException {
45.121 + StringBuilder sb = new StringBuilder();
45.122 + InputStreamReader r = new InputStreamReader(is);
45.123 + for (;;) {
45.124 + int ch = r.read();
45.125 + if (ch == -1) {
45.126 + break;
45.127 + }
45.128 + sb.append((char)ch);
45.129 + }
45.130 + return ConvertTypes.parse(sb.toString());
45.131 + }
45.132 +}
46.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
46.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/BrwsrCtxPrvdr.java Mon Jun 24 17:50:44 2013 +0200
46.3 @@ -0,0 +1,54 @@
46.4 +/**
46.5 + * HTML via Java(tm) Language Bindings
46.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
46.7 + *
46.8 + * This program is free software: you can redistribute it and/or modify
46.9 + * it under the terms of the GNU General Public License as published by
46.10 + * the Free Software Foundation, version 2 of the License.
46.11 + *
46.12 + * This program is distributed in the hope that it will be useful,
46.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
46.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46.15 + * GNU General Public License for more details. apidesign.org
46.16 + * designates this particular file as subject to the
46.17 + * "Classpath" exception as provided by apidesign.org
46.18 + * in the License file that accompanied this code.
46.19 + *
46.20 + * You should have received a copy of the GNU General Public License
46.21 + * along with this program. Look for COPYING file in the top folder.
46.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
46.23 + */
46.24 +package org.apidesign.html.ko2brwsr;
46.25 +
46.26 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
46.27 +import org.apidesign.html.context.spi.Contexts;
46.28 +import org.apidesign.html.json.spi.Technology;
46.29 +import org.apidesign.html.json.spi.Transfer;
46.30 +import org.openide.util.lookup.ServiceProvider;
46.31 +
46.32 +/** This is an implementation package - just
46.33 + * include its JAR on classpath and use official {@link Context} API
46.34 + * to access the functionality.
46.35 + * <p>
46.36 + * Provides binding between models and <a href="http://bck2brwsr.apidesign.org">
46.37 + * Bck2Brwsr</a> VM.
46.38 + * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
46.39 + *
46.40 + * @author Jaroslav Tulach <jtulach@netbeans.org>
46.41 + */
46.42 +@ServiceProvider(service = Contexts.Provider.class)
46.43 +public final class BrwsrCtxPrvdr implements Contexts.Provider {
46.44 +
46.45 + @Override
46.46 + public void fillContext(Contexts.Builder context, Class<?> requestor) {
46.47 + if (bck2BrwsrVM()) {
46.48 + context.register(Technology.class, BrwsrCtxImpl.DEFAULT, 50).
46.49 + register(Transfer.class, BrwsrCtxImpl.DEFAULT, 50);
46.50 + }
46.51 + }
46.52 +
46.53 + @JavaScriptBody(args = { }, body = "return true;")
46.54 + private static boolean bck2BrwsrVM() {
46.55 + return false;
46.56 + }
46.57 +}
47.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
47.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/ConvertTypes.java Mon Jun 24 17:50:44 2013 +0200
47.3 @@ -0,0 +1,155 @@
47.4 +/**
47.5 + * HTML via Java(tm) Language Bindings
47.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
47.7 + *
47.8 + * This program is free software: you can redistribute it and/or modify
47.9 + * it under the terms of the GNU General Public License as published by
47.10 + * the Free Software Foundation, version 2 of the License.
47.11 + *
47.12 + * This program is distributed in the hope that it will be useful,
47.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
47.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47.15 + * GNU General Public License for more details. apidesign.org
47.16 + * designates this particular file as subject to the
47.17 + * "Classpath" exception as provided by apidesign.org
47.18 + * in the License file that accompanied this code.
47.19 + *
47.20 + * You should have received a copy of the GNU General Public License
47.21 + * along with this program. Look for COPYING file in the top folder.
47.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
47.23 + */
47.24 +package org.apidesign.html.ko2brwsr;
47.25 +
47.26 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
47.27 +
47.28 +/**
47.29 + *
47.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
47.31 + */
47.32 +final class ConvertTypes {
47.33 + ConvertTypes() {
47.34 + }
47.35 +
47.36 + public static String toString(Object object, String property) {
47.37 + Object ret = getProperty(object, property);
47.38 + return ret == null ? null : ret.toString();
47.39 + }
47.40 +
47.41 + public static double toDouble(Object object, String property) {
47.42 + Object ret = getProperty(object, property);
47.43 + return ret instanceof Number ? ((Number)ret).doubleValue() : Double.NaN;
47.44 + }
47.45 +
47.46 + public static int toInt(Object object, String property) {
47.47 + Object ret = getProperty(object, property);
47.48 + return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
47.49 + }
47.50 +
47.51 + public static <T> T toModel(Class<T> modelClass, Object object, String property) {
47.52 + Object ret = getProperty(object, property);
47.53 + if (ret == null || modelClass.isInstance(ret)) {
47.54 + return modelClass.cast(ret);
47.55 + }
47.56 + throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
47.57 + }
47.58 +
47.59 + public static String toJSON(Object value) {
47.60 + if (value == null) {
47.61 + return "null";
47.62 + }
47.63 + if (value instanceof Enum) {
47.64 + value = value.toString();
47.65 + }
47.66 + if (value instanceof String) {
47.67 + return '"' +
47.68 + ((String)value).
47.69 + replace("\"", "\\\"").
47.70 + replace("\n", "\\n").
47.71 + replace("\r", "\\r").
47.72 + replace("\t", "\\t")
47.73 + + '"';
47.74 + }
47.75 + return value.toString();
47.76 + }
47.77 +
47.78 + @JavaScriptBody(args = { "object", "property" },
47.79 + body = "if (property === null) return object;\n"
47.80 + + "var p = object[property]; return p ? p : null;"
47.81 + )
47.82 + private static Object getProperty(Object object, String property) {
47.83 + return null;
47.84 + }
47.85 +
47.86 + public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
47.87 + int h = whenDone.hashCode();
47.88 + String name;
47.89 + for (;;) {
47.90 + name = "jsonp" + Integer.toHexString(h);
47.91 + if (defineIfUnused(name, jsonResult, whenDone)) {
47.92 + return name;
47.93 + }
47.94 + h++;
47.95 + }
47.96 + }
47.97 +
47.98 + @JavaScriptBody(args = { "name", "arr", "run" }, body =
47.99 + "if (window[name]) return false;\n "
47.100 + + "window[name] = function(data) {\n "
47.101 + + " delete window[name];\n"
47.102 + + " var el = window.document.getElementById(name);\n"
47.103 + + " el.parentNode.removeChild(el);\n"
47.104 + + " arr[0] = data;\n"
47.105 + + " run.run__V();\n"
47.106 + + "};\n"
47.107 + + "return true;\n"
47.108 + )
47.109 + private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
47.110 + return true;
47.111 + }
47.112 +
47.113 + @JavaScriptBody(args = { "s" }, body = "return eval('(' + s + ')');")
47.114 + static Object parse(String s) {
47.115 + return s;
47.116 + }
47.117 +
47.118 + @JavaScriptBody(args = { "url", "arr", "callback", "method", "data" }, body = ""
47.119 + + "var request = new XMLHttpRequest();\n"
47.120 + + "if (!method) method = 'GET';\n"
47.121 + + "request.open(method, url, true);\n"
47.122 + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
47.123 + + "request.onreadystatechange = function() {\n"
47.124 + + " if (this.readyState!==4) return;\n"
47.125 + + " try {\n"
47.126 + + " arr[0] = eval('(' + this.response + ')');\n"
47.127 + + " } catch (error) {;\n"
47.128 + + " arr[0] = this.response;\n"
47.129 + + " }\n"
47.130 + + " callback.run__V();\n"
47.131 + + "};"
47.132 + + "if (data) request.send(data);"
47.133 + + "else request.send();"
47.134 + )
47.135 + static void loadJSON(
47.136 + String url, Object[] jsonResult, Runnable whenDone, String method, String data
47.137 + ) {
47.138 + }
47.139 +
47.140 + @JavaScriptBody(args = { "url", "jsonp" }, body =
47.141 + "var scrpt = window.document.createElement('script');\n "
47.142 + + "scrpt.setAttribute('src', url);\n "
47.143 + + "scrpt.setAttribute('id', jsonp);\n "
47.144 + + "scrpt.setAttribute('type', 'text/javascript');\n "
47.145 + + "var body = document.getElementsByTagName('body')[0];\n "
47.146 + + "body.appendChild(scrpt);\n"
47.147 + )
47.148 + static void loadJSONP(String url, String jsonp) {
47.149 +
47.150 + }
47.151 +
47.152 + public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
47.153 + for (int i = 0; i < props.length; i++) {
47.154 + values[i] = getProperty(jsonObject, props[i]);
47.155 + }
47.156 + }
47.157 +
47.158 +}
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
48.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/html/ko2brwsr/Knockout.java Mon Jun 24 17:50:44 2013 +0200
48.3 @@ -0,0 +1,134 @@
48.4 +/**
48.5 + * HTML via Java(tm) Language Bindings
48.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
48.7 + *
48.8 + * This program is free software: you can redistribute it and/or modify
48.9 + * it under the terms of the GNU General Public License as published by
48.10 + * the Free Software Foundation, version 2 of the License.
48.11 + *
48.12 + * This program is distributed in the hope that it will be useful,
48.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
48.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48.15 + * GNU General Public License for more details. apidesign.org
48.16 + * designates this particular file as subject to the
48.17 + * "Classpath" exception as provided by apidesign.org
48.18 + * in the License file that accompanied this code.
48.19 + *
48.20 + * You should have received a copy of the GNU General Public License
48.21 + * along with this program. Look for COPYING file in the top folder.
48.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
48.23 + */
48.24 +package org.apidesign.html.ko2brwsr;
48.25 +
48.26 +import java.lang.reflect.Method;
48.27 +import java.util.List;
48.28 +import org.apidesign.bck2brwsr.core.ExtraJavaScript;
48.29 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
48.30 +
48.31 +/** Provides binding between models and bck2brwsr VM.
48.32 + *
48.33 + * @author Jaroslav Tulach <jtulach@netbeans.org>
48.34 + */
48.35 +@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
48.36 +final class Knockout {
48.37 + /** used by tests */
48.38 + static Knockout next;
48.39 + private final Object model;
48.40 +
48.41 + Knockout(Object model) {
48.42 + this.model = model == null ? this : model;
48.43 + }
48.44 +
48.45 + public static <M> Knockout applyBindings(
48.46 + Object model, String[] propsGettersAndSetters,
48.47 + String[] methodsAndSignatures
48.48 + ) {
48.49 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
48.50 + return new Knockout(model);
48.51 + }
48.52 + public static <M> Knockout applyBindings(
48.53 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
48.54 + String[] methodsAndSignatures
48.55 + ) {
48.56 + Knockout bindings = next;
48.57 + next = null;
48.58 + if (bindings == null) {
48.59 + bindings = new Knockout(null);
48.60 + }
48.61 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
48.62 + applyBindings(bindings);
48.63 + return bindings;
48.64 + }
48.65 +
48.66 + public void valueHasMutated(String prop) {
48.67 + valueHasMutated(model, prop);
48.68 + }
48.69 + @JavaScriptBody(args = { "self", "prop" }, body =
48.70 + "var p = self[prop]; if (p) p.valueHasMutated();"
48.71 + )
48.72 + public static void valueHasMutated(Object self, String prop) {
48.73 + }
48.74 +
48.75 +
48.76 + @JavaScriptBody(args = { "id", "ev" }, body = "ko.utils.triggerEvent(window.document.getElementById(id), ev.substring(2));")
48.77 + public static void triggerEvent(String id, String ev) {
48.78 + }
48.79 +
48.80 + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
48.81 + "var bnd = {\n"
48.82 + + " 'read': function() {\n"
48.83 + + " var v = model[getter]();\n"
48.84 + + " if (array) v = v.koArray(); else if (v !== null) v = v.valueOf();\n"
48.85 + + " return v;\n"
48.86 + + " },\n"
48.87 + + " 'owner': bindings\n"
48.88 + + "};\n"
48.89 + + "if (setter != null) {\n"
48.90 + + " bnd['write'] = function(val) {\n"
48.91 + + " var v = val === null ? null : val.valueOf();"
48.92 + + " model[setter](v);\n"
48.93 + + " };\n"
48.94 + + "}\n"
48.95 + + "bindings[prop] = ko['computed'](bnd);"
48.96 + )
48.97 + static void bind(
48.98 + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
48.99 + ) {
48.100 + }
48.101 +
48.102 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
48.103 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
48.104 + )
48.105 + static void expose(
48.106 + Object bindings, Object model, String prop, String sig
48.107 + ) {
48.108 + }
48.109 +
48.110 + @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
48.111 + static void applyBindings(Object bindings) {}
48.112 +
48.113 + private static void applyImpl(
48.114 + String[] propsGettersAndSetters,
48.115 + Class<?> modelClass,
48.116 + Object bindings,
48.117 + Object model,
48.118 + String[] methodsAndSignatures
48.119 + ) throws IllegalStateException, SecurityException {
48.120 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
48.121 + try {
48.122 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
48.123 + bind(bindings, model, propsGettersAndSetters[i],
48.124 + propsGettersAndSetters[i + 1],
48.125 + propsGettersAndSetters[i + 2],
48.126 + getter.getReturnType().isPrimitive(),
48.127 + List.class.isAssignableFrom(getter.getReturnType()));
48.128 + } catch (NoSuchMethodException ex) {
48.129 + throw new IllegalStateException(ex.getMessage());
48.130 + }
48.131 + }
48.132 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
48.133 + expose(
48.134 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
48.135 + }
48.136 + }
48.137 +}
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
49.2 +++ b/ko/bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Mon Jun 24 17:50:44 2013 +0200
49.3 @@ -0,0 +1,3614 @@
49.4 +/*
49.5 + * HTML via Java(tm) Language Bindings
49.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
49.7 + *
49.8 + * This program is free software: you can redistribute it and/or modify
49.9 + * it under the terms of the GNU General Public License as published by
49.10 + * the Free Software Foundation, version 2 of the License.
49.11 + *
49.12 + * This program is distributed in the hope that it will be useful,
49.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
49.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49.15 + * GNU General Public License for more details. apidesign.org
49.16 + * designates this particular file as subject to the
49.17 + * "Classpath" exception as provided by apidesign.org
49.18 + * in the License file that accompanied this code.
49.19 + *
49.20 + * You should have received a copy of the GNU General Public License
49.21 + * along with this program. Look for COPYING file in the top folder.
49.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
49.23 + */
49.24 +// Knockout JavaScript library v2.2.1
49.25 +// (c) Steven Sanderson - http://knockoutjs.com/
49.26 +// License: MIT (http://www.opensource.org/licenses/mit-license.php)
49.27 +
49.28 +(function(){
49.29 +var DEBUG=true;
49.30 +(function(window,document,navigator,jQuery,undefined){
49.31 +!function(factory) {
49.32 + // Support three module loading scenarios
49.33 + if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
49.34 + // [1] CommonJS/Node.js
49.35 + var target = module['exports'] || exports; // module.exports is for Node.js
49.36 + factory(target);
49.37 + } else if (typeof define === 'function' && define['amd']) {
49.38 + // [2] AMD anonymous module
49.39 + define(['exports'], factory);
49.40 + } else {
49.41 + // [3] No module loader (plain <script> tag) - put directly in global namespace
49.42 + factory(window['ko'] = {});
49.43 + }
49.44 +}(function(koExports){
49.45 +// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
49.46 +// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
49.47 +var ko = typeof koExports !== 'undefined' ? koExports : {};
49.48 +// Google Closure Compiler helpers (used only to make the minified file smaller)
49.49 +ko.exportSymbol = function(koPath, object) {
49.50 + var tokens = koPath.split(".");
49.51 +
49.52 + // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
49.53 + // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
49.54 + var target = ko;
49.55 +
49.56 + for (var i = 0; i < tokens.length - 1; i++)
49.57 + target = target[tokens[i]];
49.58 + target[tokens[tokens.length - 1]] = object;
49.59 +};
49.60 +ko.exportProperty = function(owner, publicName, object) {
49.61 + owner[publicName] = object;
49.62 +};
49.63 +ko.version = "2.2.1";
49.64 +
49.65 +ko.exportSymbol('version', ko.version);
49.66 +ko.utils = new (function () {
49.67 + var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
49.68 +
49.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)
49.70 + var knownEvents = {}, knownEventTypesByEventName = {};
49.71 + var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
49.72 + knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
49.73 + knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
49.74 + for (var eventType in knownEvents) {
49.75 + var knownEventsForType = knownEvents[eventType];
49.76 + if (knownEventsForType.length) {
49.77 + for (var i = 0, j = knownEventsForType.length; i < j; i++)
49.78 + knownEventTypesByEventName[knownEventsForType[i]] = eventType;
49.79 + }
49.80 + }
49.81 + var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
49.82 +
49.83 + // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
49.84 + // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
49.85 + // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
49.86 + // If there is a future need to detect specific versions of IE10+, we will amend this.
49.87 + var ieVersion = (function() {
49.88 + var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
49.89 +
49.90 + // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
49.91 + while (
49.92 + div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
49.93 + iElems[0]
49.94 + );
49.95 + return version > 4 ? version : undefined;
49.96 + }());
49.97 + var isIe6 = ieVersion === 6,
49.98 + isIe7 = ieVersion === 7;
49.99 +
49.100 + function isClickOnCheckableElement(element, eventType) {
49.101 + if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
49.102 + if (eventType.toLowerCase() != "click") return false;
49.103 + var inputType = element.type;
49.104 + return (inputType == "checkbox") || (inputType == "radio");
49.105 + }
49.106 +
49.107 + return {
49.108 + fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
49.109 +
49.110 + arrayForEach: function (array, action) {
49.111 + for (var i = 0, j = array.length; i < j; i++)
49.112 + action(array[i]);
49.113 + },
49.114 +
49.115 + arrayIndexOf: function (array, item) {
49.116 + if (typeof Array.prototype.indexOf == "function")
49.117 + return Array.prototype.indexOf.call(array, item);
49.118 + for (var i = 0, j = array.length; i < j; i++)
49.119 + if (array[i] === item)
49.120 + return i;
49.121 + return -1;
49.122 + },
49.123 +
49.124 + arrayFirst: function (array, predicate, predicateOwner) {
49.125 + for (var i = 0, j = array.length; i < j; i++)
49.126 + if (predicate.call(predicateOwner, array[i]))
49.127 + return array[i];
49.128 + return null;
49.129 + },
49.130 +
49.131 + arrayRemoveItem: function (array, itemToRemove) {
49.132 + var index = ko.utils.arrayIndexOf(array, itemToRemove);
49.133 + if (index >= 0)
49.134 + array.splice(index, 1);
49.135 + },
49.136 +
49.137 + arrayGetDistinctValues: function (array) {
49.138 + array = array || [];
49.139 + var result = [];
49.140 + for (var i = 0, j = array.length; i < j; i++) {
49.141 + if (ko.utils.arrayIndexOf(result, array[i]) < 0)
49.142 + result.push(array[i]);
49.143 + }
49.144 + return result;
49.145 + },
49.146 +
49.147 + arrayMap: function (array, mapping) {
49.148 + array = array || [];
49.149 + var result = [];
49.150 + for (var i = 0, j = array.length; i < j; i++)
49.151 + result.push(mapping(array[i]));
49.152 + return result;
49.153 + },
49.154 +
49.155 + arrayFilter: function (array, predicate) {
49.156 + array = array || [];
49.157 + var result = [];
49.158 + for (var i = 0, j = array.length; i < j; i++)
49.159 + if (predicate(array[i]))
49.160 + result.push(array[i]);
49.161 + return result;
49.162 + },
49.163 +
49.164 + arrayPushAll: function (array, valuesToPush) {
49.165 + if (valuesToPush instanceof Array)
49.166 + array.push.apply(array, valuesToPush);
49.167 + else
49.168 + for (var i = 0, j = valuesToPush.length; i < j; i++)
49.169 + array.push(valuesToPush[i]);
49.170 + return array;
49.171 + },
49.172 +
49.173 + extend: function (target, source) {
49.174 + if (source) {
49.175 + for(var prop in source) {
49.176 + if(source.hasOwnProperty(prop)) {
49.177 + target[prop] = source[prop];
49.178 + }
49.179 + }
49.180 + }
49.181 + return target;
49.182 + },
49.183 +
49.184 + emptyDomNode: function (domNode) {
49.185 + while (domNode.firstChild) {
49.186 + ko.removeNode(domNode.firstChild);
49.187 + }
49.188 + },
49.189 +
49.190 + moveCleanedNodesToContainerElement: function(nodes) {
49.191 + // Ensure it's a real array, as we're about to reparent the nodes and
49.192 + // we don't want the underlying collection to change while we're doing that.
49.193 + var nodesArray = ko.utils.makeArray(nodes);
49.194 +
49.195 + var container = document.createElement('div');
49.196 + for (var i = 0, j = nodesArray.length; i < j; i++) {
49.197 + container.appendChild(ko.cleanNode(nodesArray[i]));
49.198 + }
49.199 + return container;
49.200 + },
49.201 +
49.202 + cloneNodes: function (nodesArray, shouldCleanNodes) {
49.203 + for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
49.204 + var clonedNode = nodesArray[i].cloneNode(true);
49.205 + newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
49.206 + }
49.207 + return newNodesArray;
49.208 + },
49.209 +
49.210 + setDomNodeChildren: function (domNode, childNodes) {
49.211 + ko.utils.emptyDomNode(domNode);
49.212 + if (childNodes) {
49.213 + for (var i = 0, j = childNodes.length; i < j; i++)
49.214 + domNode.appendChild(childNodes[i]);
49.215 + }
49.216 + },
49.217 +
49.218 + replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
49.219 + var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
49.220 + if (nodesToReplaceArray.length > 0) {
49.221 + var insertionPoint = nodesToReplaceArray[0];
49.222 + var parent = insertionPoint.parentNode;
49.223 + for (var i = 0, j = newNodesArray.length; i < j; i++)
49.224 + parent.insertBefore(newNodesArray[i], insertionPoint);
49.225 + for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
49.226 + ko.removeNode(nodesToReplaceArray[i]);
49.227 + }
49.228 + }
49.229 + },
49.230 +
49.231 + setOptionNodeSelectionState: function (optionNode, isSelected) {
49.232 + // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
49.233 + if (ieVersion < 7)
49.234 + optionNode.setAttribute("selected", isSelected);
49.235 + else
49.236 + optionNode.selected = isSelected;
49.237 + },
49.238 +
49.239 + stringTrim: function (string) {
49.240 + return (string || "").replace(stringTrimRegex, "");
49.241 + },
49.242 +
49.243 + stringTokenize: function (string, delimiter) {
49.244 + var result = [];
49.245 + var tokens = (string || "").split(delimiter);
49.246 + for (var i = 0, j = tokens.length; i < j; i++) {
49.247 + var trimmed = ko.utils.stringTrim(tokens[i]);
49.248 + if (trimmed !== "")
49.249 + result.push(trimmed);
49.250 + }
49.251 + return result;
49.252 + },
49.253 +
49.254 + stringStartsWith: function (string, startsWith) {
49.255 + string = string || "";
49.256 + if (startsWith.length > string.length)
49.257 + return false;
49.258 + return string.substring(0, startsWith.length) === startsWith;
49.259 + },
49.260 +
49.261 + domNodeIsContainedBy: function (node, containedByNode) {
49.262 + if (containedByNode.compareDocumentPosition)
49.263 + return (containedByNode.compareDocumentPosition(node) & 16) == 16;
49.264 + while (node != null) {
49.265 + if (node == containedByNode)
49.266 + return true;
49.267 + node = node.parentNode;
49.268 + }
49.269 + return false;
49.270 + },
49.271 +
49.272 + domNodeIsAttachedToDocument: function (node) {
49.273 + return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
49.274 + },
49.275 +
49.276 + tagNameLower: function(element) {
49.277 + // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
49.278 + // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
49.279 + // we don't need to do the .toLowerCase() as it will always be lower case anyway.
49.280 + return element && element.tagName && element.tagName.toLowerCase();
49.281 + },
49.282 +
49.283 + registerEventHandler: function (element, eventType, handler) {
49.284 + var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
49.285 + if (!mustUseAttachEvent && typeof jQuery != "undefined") {
49.286 + if (isClickOnCheckableElement(element, eventType)) {
49.287 + // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
49.288 + // it toggles the element checked state *after* the click event handlers run, whereas native
49.289 + // click events toggle the checked state *before* the event handler.
49.290 + // Fix this by intecepting the handler and applying the correct checkedness before it runs.
49.291 + var originalHandler = handler;
49.292 + handler = function(event, eventData) {
49.293 + var jQuerySuppliedCheckedState = this.checked;
49.294 + if (eventData)
49.295 + this.checked = eventData.checkedStateBeforeEvent !== true;
49.296 + originalHandler.call(this, event);
49.297 + this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
49.298 + };
49.299 + }
49.300 + jQuery(element)['bind'](eventType, handler);
49.301 + } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
49.302 + element.addEventListener(eventType, handler, false);
49.303 + else if (typeof element.attachEvent != "undefined")
49.304 + element.attachEvent("on" + eventType, function (event) {
49.305 + handler.call(element, event);
49.306 + });
49.307 + else
49.308 + throw new Error("Browser doesn't support addEventListener or attachEvent");
49.309 + },
49.310 +
49.311 + triggerEvent: function (element, eventType) {
49.312 + if (!(element && element.nodeType))
49.313 + throw new Error("element must be a DOM node when calling triggerEvent");
49.314 +
49.315 + if (typeof jQuery != "undefined") {
49.316 + var eventData = [];
49.317 + if (isClickOnCheckableElement(element, eventType)) {
49.318 + // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
49.319 + eventData.push({ checkedStateBeforeEvent: element.checked });
49.320 + }
49.321 + jQuery(element)['trigger'](eventType, eventData);
49.322 + } else if (typeof document.createEvent == "function") {
49.323 + if (typeof element.dispatchEvent == "function") {
49.324 + var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
49.325 + var event = document.createEvent(eventCategory);
49.326 + event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
49.327 + element.dispatchEvent(event);
49.328 + }
49.329 + else
49.330 + throw new Error("The supplied element doesn't support dispatchEvent");
49.331 + } else if (typeof element.fireEvent != "undefined") {
49.332 + // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
49.333 + // so to make it consistent, we'll do it manually here
49.334 + if (isClickOnCheckableElement(element, eventType))
49.335 + element.checked = element.checked !== true;
49.336 + element.fireEvent("on" + eventType);
49.337 + }
49.338 + else
49.339 + throw new Error("Browser doesn't support triggering events");
49.340 + },
49.341 +
49.342 + unwrapObservable: function (value) {
49.343 + return ko.isObservable(value) ? value() : value;
49.344 + },
49.345 +
49.346 + peekObservable: function (value) {
49.347 + return ko.isObservable(value) ? value.peek() : value;
49.348 + },
49.349 +
49.350 + toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
49.351 + if (classNames) {
49.352 + var cssClassNameRegex = /[\w-]+/g,
49.353 + currentClassNames = node.className.match(cssClassNameRegex) || [];
49.354 + ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
49.355 + var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
49.356 + if (indexOfClass >= 0) {
49.357 + if (!shouldHaveClass)
49.358 + currentClassNames.splice(indexOfClass, 1);
49.359 + } else {
49.360 + if (shouldHaveClass)
49.361 + currentClassNames.push(className);
49.362 + }
49.363 + });
49.364 + node.className = currentClassNames.join(" ");
49.365 + }
49.366 + },
49.367 +
49.368 + setTextContent: function(element, textContent) {
49.369 + var value = ko.utils.unwrapObservable(textContent);
49.370 + if ((value === null) || (value === undefined))
49.371 + value = "";
49.372 +
49.373 + if (element.nodeType === 3) {
49.374 + element.data = value;
49.375 + } else {
49.376 + // We need there to be exactly one child: a text node.
49.377 + // If there are no children, more than one, or if it's not a text node,
49.378 + // we'll clear everything and create a single text node.
49.379 + var innerTextNode = ko.virtualElements.firstChild(element);
49.380 + if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
49.381 + ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
49.382 + } else {
49.383 + innerTextNode.data = value;
49.384 + }
49.385 +
49.386 + ko.utils.forceRefresh(element);
49.387 + }
49.388 + },
49.389 +
49.390 + setElementName: function(element, name) {
49.391 + element.name = name;
49.392 +
49.393 + // Workaround IE 6/7 issue
49.394 + // - https://github.com/SteveSanderson/knockout/issues/197
49.395 + // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
49.396 + if (ieVersion <= 7) {
49.397 + try {
49.398 + element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
49.399 + }
49.400 + catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
49.401 + }
49.402 + },
49.403 +
49.404 + forceRefresh: function(node) {
49.405 + // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
49.406 + if (ieVersion >= 9) {
49.407 + // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
49.408 + var elem = node.nodeType == 1 ? node : node.parentNode;
49.409 + if (elem.style)
49.410 + elem.style.zoom = elem.style.zoom;
49.411 + }
49.412 + },
49.413 +
49.414 + ensureSelectElementIsRenderedCorrectly: function(selectElement) {
49.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.
49.416 + // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
49.417 + if (ieVersion >= 9) {
49.418 + var originalWidth = selectElement.style.width;
49.419 + selectElement.style.width = 0;
49.420 + selectElement.style.width = originalWidth;
49.421 + }
49.422 + },
49.423 +
49.424 + range: function (min, max) {
49.425 + min = ko.utils.unwrapObservable(min);
49.426 + max = ko.utils.unwrapObservable(max);
49.427 + var result = [];
49.428 + for (var i = min; i <= max; i++)
49.429 + result.push(i);
49.430 + return result;
49.431 + },
49.432 +
49.433 + makeArray: function(arrayLikeObject) {
49.434 + var result = [];
49.435 + for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
49.436 + result.push(arrayLikeObject[i]);
49.437 + };
49.438 + return result;
49.439 + },
49.440 +
49.441 + isIe6 : isIe6,
49.442 + isIe7 : isIe7,
49.443 + ieVersion : ieVersion,
49.444 +
49.445 + getFormFields: function(form, fieldName) {
49.446 + var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
49.447 + var isMatchingField = (typeof fieldName == 'string')
49.448 + ? function(field) { return field.name === fieldName }
49.449 + : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
49.450 + var matches = [];
49.451 + for (var i = fields.length - 1; i >= 0; i--) {
49.452 + if (isMatchingField(fields[i]))
49.453 + matches.push(fields[i]);
49.454 + };
49.455 + return matches;
49.456 + },
49.457 +
49.458 + parseJson: function (jsonString) {
49.459 + if (typeof jsonString == "string") {
49.460 + jsonString = ko.utils.stringTrim(jsonString);
49.461 + if (jsonString) {
49.462 + if (window.JSON && window.JSON.parse) // Use native parsing where available
49.463 + return window.JSON.parse(jsonString);
49.464 + return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
49.465 + }
49.466 + }
49.467 + return null;
49.468 + },
49.469 +
49.470 + stringifyJson: function (data, replacer, space) { // replacer and space are optional
49.471 + if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
49.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");
49.473 + return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
49.474 + },
49.475 +
49.476 + postJson: function (urlOrForm, data, options) {
49.477 + options = options || {};
49.478 + var params = options['params'] || {};
49.479 + var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
49.480 + var url = urlOrForm;
49.481 +
49.482 + // If we were given a form, use its 'action' URL and pick out any requested field values
49.483 + if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
49.484 + var originalForm = urlOrForm;
49.485 + url = originalForm.action;
49.486 + for (var i = includeFields.length - 1; i >= 0; i--) {
49.487 + var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
49.488 + for (var j = fields.length - 1; j >= 0; j--)
49.489 + params[fields[j].name] = fields[j].value;
49.490 + }
49.491 + }
49.492 +
49.493 + data = ko.utils.unwrapObservable(data);
49.494 + var form = document.createElement("form");
49.495 + form.style.display = "none";
49.496 + form.action = url;
49.497 + form.method = "post";
49.498 + for (var key in data) {
49.499 + var input = document.createElement("input");
49.500 + input.name = key;
49.501 + input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
49.502 + form.appendChild(input);
49.503 + }
49.504 + for (var key in params) {
49.505 + var input = document.createElement("input");
49.506 + input.name = key;
49.507 + input.value = params[key];
49.508 + form.appendChild(input);
49.509 + }
49.510 + document.body.appendChild(form);
49.511 + options['submitter'] ? options['submitter'](form) : form.submit();
49.512 + setTimeout(function () { form.parentNode.removeChild(form); }, 0);
49.513 + }
49.514 + }
49.515 +})();
49.516 +
49.517 +ko.exportSymbol('utils', ko.utils);
49.518 +ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
49.519 +ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
49.520 +ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
49.521 +ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
49.522 +ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
49.523 +ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
49.524 +ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
49.525 +ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
49.526 +ko.exportSymbol('utils.extend', ko.utils.extend);
49.527 +ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
49.528 +ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
49.529 +ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
49.530 +ko.exportSymbol('utils.postJson', ko.utils.postJson);
49.531 +ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
49.532 +ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
49.533 +ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
49.534 +ko.exportSymbol('utils.range', ko.utils.range);
49.535 +ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
49.536 +ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
49.537 +ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
49.538 +
49.539 +if (!Function.prototype['bind']) {
49.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)
49.541 + // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
49.542 + Function.prototype['bind'] = function (object) {
49.543 + var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
49.544 + return function () {
49.545 + return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
49.546 + };
49.547 + };
49.548 +}
49.549 +
49.550 +ko.utils.domData = new (function () {
49.551 + var uniqueId = 0;
49.552 + var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
49.553 + var dataStore = {};
49.554 + return {
49.555 + get: function (node, key) {
49.556 + var allDataForNode = ko.utils.domData.getAll(node, false);
49.557 + return allDataForNode === undefined ? undefined : allDataForNode[key];
49.558 + },
49.559 + set: function (node, key, value) {
49.560 + if (value === undefined) {
49.561 + // Make sure we don't actually create a new domData key if we are actually deleting a value
49.562 + if (ko.utils.domData.getAll(node, false) === undefined)
49.563 + return;
49.564 + }
49.565 + var allDataForNode = ko.utils.domData.getAll(node, true);
49.566 + allDataForNode[key] = value;
49.567 + },
49.568 + getAll: function (node, createIfNotFound) {
49.569 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
49.570 + var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
49.571 + if (!hasExistingDataStore) {
49.572 + if (!createIfNotFound)
49.573 + return undefined;
49.574 + dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
49.575 + dataStore[dataStoreKey] = {};
49.576 + }
49.577 + return dataStore[dataStoreKey];
49.578 + },
49.579 + clear: function (node) {
49.580 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
49.581 + if (dataStoreKey) {
49.582 + delete dataStore[dataStoreKey];
49.583 + node[dataStoreKeyExpandoPropertyName] = null;
49.584 + return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
49.585 + }
49.586 + return false;
49.587 + }
49.588 + }
49.589 +})();
49.590 +
49.591 +ko.exportSymbol('utils.domData', ko.utils.domData);
49.592 +ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
49.593 +
49.594 +ko.utils.domNodeDisposal = new (function () {
49.595 + var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
49.596 + var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
49.597 + var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
49.598 +
49.599 + function getDisposeCallbacksCollection(node, createIfNotFound) {
49.600 + var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
49.601 + if ((allDisposeCallbacks === undefined) && createIfNotFound) {
49.602 + allDisposeCallbacks = [];
49.603 + ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
49.604 + }
49.605 + return allDisposeCallbacks;
49.606 + }
49.607 + function destroyCallbacksCollection(node) {
49.608 + ko.utils.domData.set(node, domDataKey, undefined);
49.609 + }
49.610 +
49.611 + function cleanSingleNode(node) {
49.612 + // Run all the dispose callbacks
49.613 + var callbacks = getDisposeCallbacksCollection(node, false);
49.614 + if (callbacks) {
49.615 + callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
49.616 + for (var i = 0; i < callbacks.length; i++)
49.617 + callbacks[i](node);
49.618 + }
49.619 +
49.620 + // Also erase the DOM data
49.621 + ko.utils.domData.clear(node);
49.622 +
49.623 + // Special support for jQuery here because it's so commonly used.
49.624 + // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
49.625 + // so notify it to tear down any resources associated with the node & descendants here.
49.626 + if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
49.627 + jQuery['cleanData']([node]);
49.628 +
49.629 + // Also clear any immediate-child comment nodes, as these wouldn't have been found by
49.630 + // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
49.631 + if (cleanableNodeTypesWithDescendants[node.nodeType])
49.632 + cleanImmediateCommentTypeChildren(node);
49.633 + }
49.634 +
49.635 + function cleanImmediateCommentTypeChildren(nodeWithChildren) {
49.636 + var child, nextChild = nodeWithChildren.firstChild;
49.637 + while (child = nextChild) {
49.638 + nextChild = child.nextSibling;
49.639 + if (child.nodeType === 8)
49.640 + cleanSingleNode(child);
49.641 + }
49.642 + }
49.643 +
49.644 + return {
49.645 + addDisposeCallback : function(node, callback) {
49.646 + if (typeof callback != "function")
49.647 + throw new Error("Callback must be a function");
49.648 + getDisposeCallbacksCollection(node, true).push(callback);
49.649 + },
49.650 +
49.651 + removeDisposeCallback : function(node, callback) {
49.652 + var callbacksCollection = getDisposeCallbacksCollection(node, false);
49.653 + if (callbacksCollection) {
49.654 + ko.utils.arrayRemoveItem(callbacksCollection, callback);
49.655 + if (callbacksCollection.length == 0)
49.656 + destroyCallbacksCollection(node);
49.657 + }
49.658 + },
49.659 +
49.660 + cleanNode : function(node) {
49.661 + // First clean this node, where applicable
49.662 + if (cleanableNodeTypes[node.nodeType]) {
49.663 + cleanSingleNode(node);
49.664 +
49.665 + // ... then its descendants, where applicable
49.666 + if (cleanableNodeTypesWithDescendants[node.nodeType]) {
49.667 + // Clone the descendants list in case it changes during iteration
49.668 + var descendants = [];
49.669 + ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
49.670 + for (var i = 0, j = descendants.length; i < j; i++)
49.671 + cleanSingleNode(descendants[i]);
49.672 + }
49.673 + }
49.674 + return node;
49.675 + },
49.676 +
49.677 + removeNode : function(node) {
49.678 + ko.cleanNode(node);
49.679 + if (node.parentNode)
49.680 + node.parentNode.removeChild(node);
49.681 + }
49.682 + }
49.683 +})();
49.684 +ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
49.685 +ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
49.686 +ko.exportSymbol('cleanNode', ko.cleanNode);
49.687 +ko.exportSymbol('removeNode', ko.removeNode);
49.688 +ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
49.689 +ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
49.690 +ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
49.691 +(function () {
49.692 + var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
49.693 +
49.694 + function simpleHtmlParse(html) {
49.695 + // Based on jQuery's "clean" function, but only accounting for table-related elements.
49.696 + // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
49.697 +
49.698 + // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
49.699 + // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
49.700 + // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
49.701 + // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
49.702 +
49.703 + // Trim whitespace, otherwise indexOf won't work as expected
49.704 + var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
49.705 +
49.706 + // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
49.707 + var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
49.708 + !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
49.709 + (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
49.710 + /* anything else */ [0, "", ""];
49.711 +
49.712 + // Go to html and back, then peel off extra wrappers
49.713 + // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
49.714 + var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
49.715 + if (typeof window['innerShiv'] == "function") {
49.716 + div.appendChild(window['innerShiv'](markup));
49.717 + } else {
49.718 + div.innerHTML = markup;
49.719 + }
49.720 +
49.721 + // Move to the right depth
49.722 + while (wrap[0]--)
49.723 + div = div.lastChild;
49.724 +
49.725 + return ko.utils.makeArray(div.lastChild.childNodes);
49.726 + }
49.727 +
49.728 + function jQueryHtmlParse(html) {
49.729 + // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
49.730 + if (jQuery['parseHTML']) {
49.731 + return jQuery['parseHTML'](html);
49.732 + } else {
49.733 + // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
49.734 + var elems = jQuery['clean']([html]);
49.735 +
49.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.
49.737 + // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
49.738 + // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
49.739 + if (elems && elems[0]) {
49.740 + // Find the top-most parent element that's a direct child of a document fragment
49.741 + var elem = elems[0];
49.742 + while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
49.743 + elem = elem.parentNode;
49.744 + // ... then detach it
49.745 + if (elem.parentNode)
49.746 + elem.parentNode.removeChild(elem);
49.747 + }
49.748 +
49.749 + return elems;
49.750 + }
49.751 + }
49.752 +
49.753 + ko.utils.parseHtmlFragment = function(html) {
49.754 + return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
49.755 + : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
49.756 + };
49.757 +
49.758 + ko.utils.setHtml = function(node, html) {
49.759 + ko.utils.emptyDomNode(node);
49.760 +
49.761 + // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
49.762 + html = ko.utils.unwrapObservable(html);
49.763 +
49.764 + if ((html !== null) && (html !== undefined)) {
49.765 + if (typeof html != 'string')
49.766 + html = html.toString();
49.767 +
49.768 + // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
49.769 + // for example <tr> elements which are not normally allowed to exist on their own.
49.770 + // If you've referenced jQuery we'll use that rather than duplicating its code.
49.771 + if (typeof jQuery != 'undefined') {
49.772 + jQuery(node)['html'](html);
49.773 + } else {
49.774 + // ... otherwise, use KO's own parsing logic.
49.775 + var parsedNodes = ko.utils.parseHtmlFragment(html);
49.776 + for (var i = 0; i < parsedNodes.length; i++)
49.777 + node.appendChild(parsedNodes[i]);
49.778 + }
49.779 + }
49.780 + };
49.781 +})();
49.782 +
49.783 +ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
49.784 +ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
49.785 +
49.786 +ko.memoization = (function () {
49.787 + var memos = {};
49.788 +
49.789 + function randomMax8HexChars() {
49.790 + return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
49.791 + }
49.792 + function generateRandomId() {
49.793 + return randomMax8HexChars() + randomMax8HexChars();
49.794 + }
49.795 + function findMemoNodes(rootNode, appendToArray) {
49.796 + if (!rootNode)
49.797 + return;
49.798 + if (rootNode.nodeType == 8) {
49.799 + var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
49.800 + if (memoId != null)
49.801 + appendToArray.push({ domNode: rootNode, memoId: memoId });
49.802 + } else if (rootNode.nodeType == 1) {
49.803 + for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
49.804 + findMemoNodes(childNodes[i], appendToArray);
49.805 + }
49.806 + }
49.807 +
49.808 + return {
49.809 + memoize: function (callback) {
49.810 + if (typeof callback != "function")
49.811 + throw new Error("You can only pass a function to ko.memoization.memoize()");
49.812 + var memoId = generateRandomId();
49.813 + memos[memoId] = callback;
49.814 + return "<!--[ko_memo:" + memoId + "]-->";
49.815 + },
49.816 +
49.817 + unmemoize: function (memoId, callbackParams) {
49.818 + var callback = memos[memoId];
49.819 + if (callback === undefined)
49.820 + throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
49.821 + try {
49.822 + callback.apply(null, callbackParams || []);
49.823 + return true;
49.824 + }
49.825 + finally { delete memos[memoId]; }
49.826 + },
49.827 +
49.828 + unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
49.829 + var memos = [];
49.830 + findMemoNodes(domNode, memos);
49.831 + for (var i = 0, j = memos.length; i < j; i++) {
49.832 + var node = memos[i].domNode;
49.833 + var combinedParams = [node];
49.834 + if (extraCallbackParamsArray)
49.835 + ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
49.836 + ko.memoization.unmemoize(memos[i].memoId, combinedParams);
49.837 + node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
49.838 + if (node.parentNode)
49.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)
49.840 + }
49.841 + },
49.842 +
49.843 + parseMemoText: function (memoText) {
49.844 + var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
49.845 + return match ? match[1] : null;
49.846 + }
49.847 + };
49.848 +})();
49.849 +
49.850 +ko.exportSymbol('memoization', ko.memoization);
49.851 +ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
49.852 +ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
49.853 +ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
49.854 +ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
49.855 +ko.extenders = {
49.856 + 'throttle': function(target, timeout) {
49.857 + // Throttling means two things:
49.858 +
49.859 + // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
49.860 + // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
49.861 + target['throttleEvaluation'] = timeout;
49.862 +
49.863 + // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
49.864 + // so the target cannot change value synchronously or faster than a certain rate
49.865 + var writeTimeoutInstance = null;
49.866 + return ko.dependentObservable({
49.867 + 'read': target,
49.868 + 'write': function(value) {
49.869 + clearTimeout(writeTimeoutInstance);
49.870 + writeTimeoutInstance = setTimeout(function() {
49.871 + target(value);
49.872 + }, timeout);
49.873 + }
49.874 + });
49.875 + },
49.876 +
49.877 + 'notify': function(target, notifyWhen) {
49.878 + target["equalityComparer"] = notifyWhen == "always"
49.879 + ? function() { return false } // Treat all values as not equal
49.880 + : ko.observable["fn"]["equalityComparer"];
49.881 + return target;
49.882 + }
49.883 +};
49.884 +
49.885 +function applyExtenders(requestedExtenders) {
49.886 + var target = this;
49.887 + if (requestedExtenders) {
49.888 + for (var key in requestedExtenders) {
49.889 + var extenderHandler = ko.extenders[key];
49.890 + if (typeof extenderHandler == 'function') {
49.891 + target = extenderHandler(target, requestedExtenders[key]);
49.892 + }
49.893 + }
49.894 + }
49.895 + return target;
49.896 +}
49.897 +
49.898 +ko.exportSymbol('extenders', ko.extenders);
49.899 +
49.900 +ko.subscription = function (target, callback, disposeCallback) {
49.901 + this.target = target;
49.902 + this.callback = callback;
49.903 + this.disposeCallback = disposeCallback;
49.904 + ko.exportProperty(this, 'dispose', this.dispose);
49.905 +};
49.906 +ko.subscription.prototype.dispose = function () {
49.907 + this.isDisposed = true;
49.908 + this.disposeCallback();
49.909 +};
49.910 +
49.911 +ko.subscribable = function () {
49.912 + this._subscriptions = {};
49.913 +
49.914 + ko.utils.extend(this, ko.subscribable['fn']);
49.915 + ko.exportProperty(this, 'subscribe', this.subscribe);
49.916 + ko.exportProperty(this, 'extend', this.extend);
49.917 + ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
49.918 +}
49.919 +
49.920 +var defaultEvent = "change";
49.921 +
49.922 +ko.subscribable['fn'] = {
49.923 + subscribe: function (callback, callbackTarget, event) {
49.924 + event = event || defaultEvent;
49.925 + var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
49.926 +
49.927 + var subscription = new ko.subscription(this, boundCallback, function () {
49.928 + ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
49.929 + }.bind(this));
49.930 +
49.931 + if (!this._subscriptions[event])
49.932 + this._subscriptions[event] = [];
49.933 + this._subscriptions[event].push(subscription);
49.934 + return subscription;
49.935 + },
49.936 +
49.937 + "notifySubscribers": function (valueToNotify, event) {
49.938 + event = event || defaultEvent;
49.939 + if (this._subscriptions[event]) {
49.940 + ko.dependencyDetection.ignore(function() {
49.941 + ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
49.942 + // In case a subscription was disposed during the arrayForEach cycle, check
49.943 + // for isDisposed on each subscription before invoking its callback
49.944 + if (subscription && (subscription.isDisposed !== true))
49.945 + subscription.callback(valueToNotify);
49.946 + });
49.947 + }, this);
49.948 + }
49.949 + },
49.950 +
49.951 + getSubscriptionsCount: function () {
49.952 + var total = 0;
49.953 + for (var eventName in this._subscriptions) {
49.954 + if (this._subscriptions.hasOwnProperty(eventName))
49.955 + total += this._subscriptions[eventName].length;
49.956 + }
49.957 + return total;
49.958 + },
49.959 +
49.960 + extend: applyExtenders
49.961 +};
49.962 +
49.963 +
49.964 +ko.isSubscribable = function (instance) {
49.965 + return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
49.966 +};
49.967 +
49.968 +ko.exportSymbol('subscribable', ko.subscribable);
49.969 +ko.exportSymbol('isSubscribable', ko.isSubscribable);
49.970 +
49.971 +ko.dependencyDetection = (function () {
49.972 + var _frames = [];
49.973 +
49.974 + return {
49.975 + begin: function (callback) {
49.976 + _frames.push({ callback: callback, distinctDependencies:[] });
49.977 + },
49.978 +
49.979 + end: function () {
49.980 + _frames.pop();
49.981 + },
49.982 +
49.983 + registerDependency: function (subscribable) {
49.984 + if (!ko.isSubscribable(subscribable))
49.985 + throw new Error("Only subscribable things can act as dependencies");
49.986 + if (_frames.length > 0) {
49.987 + var topFrame = _frames[_frames.length - 1];
49.988 + if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
49.989 + return;
49.990 + topFrame.distinctDependencies.push(subscribable);
49.991 + topFrame.callback(subscribable);
49.992 + }
49.993 + },
49.994 +
49.995 + ignore: function(callback, callbackTarget, callbackArgs) {
49.996 + try {
49.997 + _frames.push(null);
49.998 + return callback.apply(callbackTarget, callbackArgs || []);
49.999 + } finally {
49.1000 + _frames.pop();
49.1001 + }
49.1002 + }
49.1003 + };
49.1004 +})();
49.1005 +var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
49.1006 +
49.1007 +ko.observable = function (initialValue) {
49.1008 + var _latestValue = initialValue;
49.1009 +
49.1010 + function observable() {
49.1011 + if (arguments.length > 0) {
49.1012 + // Write
49.1013 +
49.1014 + // Ignore writes if the value hasn't changed
49.1015 + if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
49.1016 + observable.valueWillMutate();
49.1017 + _latestValue = arguments[0];
49.1018 + if (DEBUG) observable._latestValue = _latestValue;
49.1019 + observable.valueHasMutated();
49.1020 + }
49.1021 + return this; // Permits chained assignments
49.1022 + }
49.1023 + else {
49.1024 + // Read
49.1025 + ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
49.1026 + return _latestValue;
49.1027 + }
49.1028 + }
49.1029 + if (DEBUG) observable._latestValue = _latestValue;
49.1030 + ko.subscribable.call(observable);
49.1031 + observable.peek = function() { return _latestValue };
49.1032 + observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
49.1033 + observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
49.1034 + ko.utils.extend(observable, ko.observable['fn']);
49.1035 +
49.1036 + ko.exportProperty(observable, 'peek', observable.peek);
49.1037 + ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
49.1038 + ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
49.1039 +
49.1040 + return observable;
49.1041 +}
49.1042 +
49.1043 +ko.observable['fn'] = {
49.1044 + "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
49.1045 + var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
49.1046 + return oldValueIsPrimitive ? (a === b) : false;
49.1047 + }
49.1048 +};
49.1049 +
49.1050 +var protoProperty = ko.observable.protoProperty = "__ko_proto__";
49.1051 +ko.observable['fn'][protoProperty] = ko.observable;
49.1052 +
49.1053 +ko.hasPrototype = function(instance, prototype) {
49.1054 + if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
49.1055 + if (instance[protoProperty] === prototype) return true;
49.1056 + return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
49.1057 +};
49.1058 +
49.1059 +ko.isObservable = function (instance) {
49.1060 + return ko.hasPrototype(instance, ko.observable);
49.1061 +}
49.1062 +ko.isWriteableObservable = function (instance) {
49.1063 + // Observable
49.1064 + if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
49.1065 + return true;
49.1066 + // Writeable dependent observable
49.1067 + if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
49.1068 + return true;
49.1069 + // Anything else
49.1070 + return false;
49.1071 +}
49.1072 +
49.1073 +
49.1074 +ko.exportSymbol('observable', ko.observable);
49.1075 +ko.exportSymbol('isObservable', ko.isObservable);
49.1076 +ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
49.1077 +ko.observableArray = function (initialValues) {
49.1078 + if (arguments.length == 0) {
49.1079 + // Zero-parameter constructor initializes to empty array
49.1080 + initialValues = [];
49.1081 + }
49.1082 + if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
49.1083 + throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
49.1084 +
49.1085 + var result = ko.observable(initialValues);
49.1086 + ko.utils.extend(result, ko.observableArray['fn']);
49.1087 + return result;
49.1088 +}
49.1089 +
49.1090 +ko.observableArray['fn'] = {
49.1091 + 'remove': function (valueOrPredicate) {
49.1092 + var underlyingArray = this.peek();
49.1093 + var removedValues = [];
49.1094 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
49.1095 + for (var i = 0; i < underlyingArray.length; i++) {
49.1096 + var value = underlyingArray[i];
49.1097 + if (predicate(value)) {
49.1098 + if (removedValues.length === 0) {
49.1099 + this.valueWillMutate();
49.1100 + }
49.1101 + removedValues.push(value);
49.1102 + underlyingArray.splice(i, 1);
49.1103 + i--;
49.1104 + }
49.1105 + }
49.1106 + if (removedValues.length) {
49.1107 + this.valueHasMutated();
49.1108 + }
49.1109 + return removedValues;
49.1110 + },
49.1111 +
49.1112 + 'removeAll': function (arrayOfValues) {
49.1113 + // If you passed zero args, we remove everything
49.1114 + if (arrayOfValues === undefined) {
49.1115 + var underlyingArray = this.peek();
49.1116 + var allValues = underlyingArray.slice(0);
49.1117 + this.valueWillMutate();
49.1118 + underlyingArray.splice(0, underlyingArray.length);
49.1119 + this.valueHasMutated();
49.1120 + return allValues;
49.1121 + }
49.1122 + // If you passed an arg, we interpret it as an array of entries to remove
49.1123 + if (!arrayOfValues)
49.1124 + return [];
49.1125 + return this['remove'](function (value) {
49.1126 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
49.1127 + });
49.1128 + },
49.1129 +
49.1130 + 'destroy': function (valueOrPredicate) {
49.1131 + var underlyingArray = this.peek();
49.1132 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
49.1133 + this.valueWillMutate();
49.1134 + for (var i = underlyingArray.length - 1; i >= 0; i--) {
49.1135 + var value = underlyingArray[i];
49.1136 + if (predicate(value))
49.1137 + underlyingArray[i]["_destroy"] = true;
49.1138 + }
49.1139 + this.valueHasMutated();
49.1140 + },
49.1141 +
49.1142 + 'destroyAll': function (arrayOfValues) {
49.1143 + // If you passed zero args, we destroy everything
49.1144 + if (arrayOfValues === undefined)
49.1145 + return this['destroy'](function() { return true });
49.1146 +
49.1147 + // If you passed an arg, we interpret it as an array of entries to destroy
49.1148 + if (!arrayOfValues)
49.1149 + return [];
49.1150 + return this['destroy'](function (value) {
49.1151 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
49.1152 + });
49.1153 + },
49.1154 +
49.1155 + 'indexOf': function (item) {
49.1156 + var underlyingArray = this();
49.1157 + return ko.utils.arrayIndexOf(underlyingArray, item);
49.1158 + },
49.1159 +
49.1160 + 'replace': function(oldItem, newItem) {
49.1161 + var index = this['indexOf'](oldItem);
49.1162 + if (index >= 0) {
49.1163 + this.valueWillMutate();
49.1164 + this.peek()[index] = newItem;
49.1165 + this.valueHasMutated();
49.1166 + }
49.1167 + }
49.1168 +}
49.1169 +
49.1170 +// Populate ko.observableArray.fn with read/write functions from native arrays
49.1171 +// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
49.1172 +// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
49.1173 +ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
49.1174 + ko.observableArray['fn'][methodName] = function () {
49.1175 + // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
49.1176 + // (for consistency with mutating regular observables)
49.1177 + var underlyingArray = this.peek();
49.1178 + this.valueWillMutate();
49.1179 + var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
49.1180 + this.valueHasMutated();
49.1181 + return methodCallResult;
49.1182 + };
49.1183 +});
49.1184 +
49.1185 +// Populate ko.observableArray.fn with read-only functions from native arrays
49.1186 +ko.utils.arrayForEach(["slice"], function (methodName) {
49.1187 + ko.observableArray['fn'][methodName] = function () {
49.1188 + var underlyingArray = this();
49.1189 + return underlyingArray[methodName].apply(underlyingArray, arguments);
49.1190 + };
49.1191 +});
49.1192 +
49.1193 +ko.exportSymbol('observableArray', ko.observableArray);
49.1194 +ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
49.1195 + var _latestValue,
49.1196 + _hasBeenEvaluated = false,
49.1197 + _isBeingEvaluated = false,
49.1198 + readFunction = evaluatorFunctionOrOptions;
49.1199 +
49.1200 + if (readFunction && typeof readFunction == "object") {
49.1201 + // Single-parameter syntax - everything is on this "options" param
49.1202 + options = readFunction;
49.1203 + readFunction = options["read"];
49.1204 + } else {
49.1205 + // Multi-parameter syntax - construct the options according to the params passed
49.1206 + options = options || {};
49.1207 + if (!readFunction)
49.1208 + readFunction = options["read"];
49.1209 + }
49.1210 + if (typeof readFunction != "function")
49.1211 + throw new Error("Pass a function that returns the value of the ko.computed");
49.1212 +
49.1213 + function addSubscriptionToDependency(subscribable) {
49.1214 + _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
49.1215 + }
49.1216 +
49.1217 + function disposeAllSubscriptionsToDependencies() {
49.1218 + ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
49.1219 + subscription.dispose();
49.1220 + });
49.1221 + _subscriptionsToDependencies = [];
49.1222 + }
49.1223 +
49.1224 + function evaluatePossiblyAsync() {
49.1225 + var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
49.1226 + if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
49.1227 + clearTimeout(evaluationTimeoutInstance);
49.1228 + evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
49.1229 + } else
49.1230 + evaluateImmediate();
49.1231 + }
49.1232 +
49.1233 + function evaluateImmediate() {
49.1234 + if (_isBeingEvaluated) {
49.1235 + // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
49.1236 + // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
49.1237 + // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
49.1238 + // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
49.1239 + return;
49.1240 + }
49.1241 +
49.1242 + // Don't dispose on first evaluation, because the "disposeWhen" callback might
49.1243 + // e.g., dispose when the associated DOM element isn't in the doc, and it's not
49.1244 + // going to be in the doc until *after* the first evaluation
49.1245 + if (_hasBeenEvaluated && disposeWhen()) {
49.1246 + dispose();
49.1247 + return;
49.1248 + }
49.1249 +
49.1250 + _isBeingEvaluated = true;
49.1251 + try {
49.1252 + // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
49.1253 + // Then, during evaluation, we cross off any that are in fact still being used.
49.1254 + var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
49.1255 +
49.1256 + ko.dependencyDetection.begin(function(subscribable) {
49.1257 + var inOld;
49.1258 + if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
49.1259 + disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
49.1260 + else
49.1261 + addSubscriptionToDependency(subscribable); // Brand new subscription - add it
49.1262 + });
49.1263 +
49.1264 + var newValue = readFunction.call(evaluatorFunctionTarget);
49.1265 +
49.1266 + // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
49.1267 + for (var i = disposalCandidates.length - 1; i >= 0; i--) {
49.1268 + if (disposalCandidates[i])
49.1269 + _subscriptionsToDependencies.splice(i, 1)[0].dispose();
49.1270 + }
49.1271 + _hasBeenEvaluated = true;
49.1272 +
49.1273 + dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
49.1274 + _latestValue = newValue;
49.1275 + if (DEBUG) dependentObservable._latestValue = _latestValue;
49.1276 + } finally {
49.1277 + ko.dependencyDetection.end();
49.1278 + }
49.1279 +
49.1280 + dependentObservable["notifySubscribers"](_latestValue);
49.1281 + _isBeingEvaluated = false;
49.1282 + if (!_subscriptionsToDependencies.length)
49.1283 + dispose();
49.1284 + }
49.1285 +
49.1286 + function dependentObservable() {
49.1287 + if (arguments.length > 0) {
49.1288 + if (typeof writeFunction === "function") {
49.1289 + // Writing a value
49.1290 + writeFunction.apply(evaluatorFunctionTarget, arguments);
49.1291 + } else {
49.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.");
49.1293 + }
49.1294 + return this; // Permits chained assignments
49.1295 + } else {
49.1296 + // Reading the value
49.1297 + if (!_hasBeenEvaluated)
49.1298 + evaluateImmediate();
49.1299 + ko.dependencyDetection.registerDependency(dependentObservable);
49.1300 + return _latestValue;
49.1301 + }
49.1302 + }
49.1303 +
49.1304 + function peek() {
49.1305 + if (!_hasBeenEvaluated)
49.1306 + evaluateImmediate();
49.1307 + return _latestValue;
49.1308 + }
49.1309 +
49.1310 + function isActive() {
49.1311 + return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
49.1312 + }
49.1313 +
49.1314 + // By here, "options" is always non-null
49.1315 + var writeFunction = options["write"],
49.1316 + disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
49.1317 + disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
49.1318 + dispose = disposeAllSubscriptionsToDependencies,
49.1319 + _subscriptionsToDependencies = [],
49.1320 + evaluationTimeoutInstance = null;
49.1321 +
49.1322 + if (!evaluatorFunctionTarget)
49.1323 + evaluatorFunctionTarget = options["owner"];
49.1324 +
49.1325 + dependentObservable.peek = peek;
49.1326 + dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
49.1327 + dependentObservable.hasWriteFunction = typeof options["write"] === "function";
49.1328 + dependentObservable.dispose = function () { dispose(); };
49.1329 + dependentObservable.isActive = isActive;
49.1330 + dependentObservable.valueHasMutated = function() {
49.1331 + _hasBeenEvaluated = false;
49.1332 + evaluateImmediate();
49.1333 + };
49.1334 +
49.1335 + ko.subscribable.call(dependentObservable);
49.1336 + ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
49.1337 +
49.1338 + ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
49.1339 + ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
49.1340 + ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
49.1341 + ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
49.1342 +
49.1343 + // Evaluate, unless deferEvaluation is true
49.1344 + if (options['deferEvaluation'] !== true)
49.1345 + evaluateImmediate();
49.1346 +
49.1347 + // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
49.1348 + // But skip if isActive is false (there will never be any dependencies to dispose).
49.1349 + // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
49.1350 + // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
49.1351 + if (disposeWhenNodeIsRemoved && isActive()) {
49.1352 + dispose = function() {
49.1353 + ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
49.1354 + disposeAllSubscriptionsToDependencies();
49.1355 + };
49.1356 + ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
49.1357 + var existingDisposeWhenFunction = disposeWhen;
49.1358 + disposeWhen = function () {
49.1359 + return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
49.1360 + }
49.1361 + }
49.1362 +
49.1363 + return dependentObservable;
49.1364 +};
49.1365 +
49.1366 +ko.isComputed = function(instance) {
49.1367 + return ko.hasPrototype(instance, ko.dependentObservable);
49.1368 +};
49.1369 +
49.1370 +var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
49.1371 +ko.dependentObservable[protoProp] = ko.observable;
49.1372 +
49.1373 +ko.dependentObservable['fn'] = {};
49.1374 +ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
49.1375 +
49.1376 +ko.exportSymbol('dependentObservable', ko.dependentObservable);
49.1377 +ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
49.1378 +ko.exportSymbol('isComputed', ko.isComputed);
49.1379 +
49.1380 +(function() {
49.1381 + var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
49.1382 +
49.1383 + ko.toJS = function(rootObject) {
49.1384 + if (arguments.length == 0)
49.1385 + throw new Error("When calling ko.toJS, pass the object you want to convert.");
49.1386 +
49.1387 + // We just unwrap everything at every level in the object graph
49.1388 + return mapJsObjectGraph(rootObject, function(valueToMap) {
49.1389 + // Loop because an observable's value might in turn be another observable wrapper
49.1390 + for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
49.1391 + valueToMap = valueToMap();
49.1392 + return valueToMap;
49.1393 + });
49.1394 + };
49.1395 +
49.1396 + ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
49.1397 + var plainJavaScriptObject = ko.toJS(rootObject);
49.1398 + return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
49.1399 + };
49.1400 +
49.1401 + function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
49.1402 + visitedObjects = visitedObjects || new objectLookup();
49.1403 +
49.1404 + rootObject = mapInputCallback(rootObject);
49.1405 + var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
49.1406 + if (!canHaveProperties)
49.1407 + return rootObject;
49.1408 +
49.1409 + var outputProperties = rootObject instanceof Array ? [] : {};
49.1410 + visitedObjects.save(rootObject, outputProperties);
49.1411 +
49.1412 + visitPropertiesOrArrayEntries(rootObject, function(indexer) {
49.1413 + var propertyValue = mapInputCallback(rootObject[indexer]);
49.1414 +
49.1415 + switch (typeof propertyValue) {
49.1416 + case "boolean":
49.1417 + case "number":
49.1418 + case "string":
49.1419 + case "function":
49.1420 + outputProperties[indexer] = propertyValue;
49.1421 + break;
49.1422 + case "object":
49.1423 + case "undefined":
49.1424 + var previouslyMappedValue = visitedObjects.get(propertyValue);
49.1425 + outputProperties[indexer] = (previouslyMappedValue !== undefined)
49.1426 + ? previouslyMappedValue
49.1427 + : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
49.1428 + break;
49.1429 + }
49.1430 + });
49.1431 +
49.1432 + return outputProperties;
49.1433 + }
49.1434 +
49.1435 + function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
49.1436 + if (rootObject instanceof Array) {
49.1437 + for (var i = 0; i < rootObject.length; i++)
49.1438 + visitorCallback(i);
49.1439 +
49.1440 + // For arrays, also respect toJSON property for custom mappings (fixes #278)
49.1441 + if (typeof rootObject['toJSON'] == 'function')
49.1442 + visitorCallback('toJSON');
49.1443 + } else {
49.1444 + for (var propertyName in rootObject)
49.1445 + visitorCallback(propertyName);
49.1446 + }
49.1447 + };
49.1448 +
49.1449 + function objectLookup() {
49.1450 + var keys = [];
49.1451 + var values = [];
49.1452 + this.save = function(key, value) {
49.1453 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
49.1454 + if (existingIndex >= 0)
49.1455 + values[existingIndex] = value;
49.1456 + else {
49.1457 + keys.push(key);
49.1458 + values.push(value);
49.1459 + }
49.1460 + };
49.1461 + this.get = function(key) {
49.1462 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
49.1463 + return (existingIndex >= 0) ? values[existingIndex] : undefined;
49.1464 + };
49.1465 + };
49.1466 +})();
49.1467 +
49.1468 +ko.exportSymbol('toJS', ko.toJS);
49.1469 +ko.exportSymbol('toJSON', ko.toJSON);
49.1470 +(function () {
49.1471 + var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
49.1472 +
49.1473 + // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
49.1474 + // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
49.1475 + // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
49.1476 + ko.selectExtensions = {
49.1477 + readValue : function(element) {
49.1478 + switch (ko.utils.tagNameLower(element)) {
49.1479 + case 'option':
49.1480 + if (element[hasDomDataExpandoProperty] === true)
49.1481 + return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
49.1482 + return ko.utils.ieVersion <= 7
49.1483 + ? (element.getAttributeNode('value').specified ? element.value : element.text)
49.1484 + : element.value;
49.1485 + case 'select':
49.1486 + return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
49.1487 + default:
49.1488 + return element.value;
49.1489 + }
49.1490 + },
49.1491 +
49.1492 + writeValue: function(element, value) {
49.1493 + switch (ko.utils.tagNameLower(element)) {
49.1494 + case 'option':
49.1495 + switch(typeof value) {
49.1496 + case "string":
49.1497 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
49.1498 + if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
49.1499 + delete element[hasDomDataExpandoProperty];
49.1500 + }
49.1501 + element.value = value;
49.1502 + break;
49.1503 + default:
49.1504 + // Store arbitrary object using DomData
49.1505 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
49.1506 + element[hasDomDataExpandoProperty] = true;
49.1507 +
49.1508 + // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
49.1509 + element.value = typeof value === "number" ? value : "";
49.1510 + break;
49.1511 + }
49.1512 + break;
49.1513 + case 'select':
49.1514 + for (var i = element.options.length - 1; i >= 0; i--) {
49.1515 + if (ko.selectExtensions.readValue(element.options[i]) == value) {
49.1516 + element.selectedIndex = i;
49.1517 + break;
49.1518 + }
49.1519 + }
49.1520 + break;
49.1521 + default:
49.1522 + if ((value === null) || (value === undefined))
49.1523 + value = "";
49.1524 + element.value = value;
49.1525 + break;
49.1526 + }
49.1527 + }
49.1528 + };
49.1529 +})();
49.1530 +
49.1531 +ko.exportSymbol('selectExtensions', ko.selectExtensions);
49.1532 +ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
49.1533 +ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
49.1534 +ko.expressionRewriting = (function () {
49.1535 + var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
49.1536 + var javaScriptReservedWords = ["true", "false"];
49.1537 +
49.1538 + // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
49.1539 + // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
49.1540 + var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
49.1541 +
49.1542 + function restoreTokens(string, tokens) {
49.1543 + var prevValue = null;
49.1544 + while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
49.1545 + prevValue = string;
49.1546 + string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
49.1547 + return tokens[tokenIndex];
49.1548 + });
49.1549 + }
49.1550 + return string;
49.1551 + }
49.1552 +
49.1553 + function getWriteableValue(expression) {
49.1554 + if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
49.1555 + return false;
49.1556 + var match = expression.match(javaScriptAssignmentTarget);
49.1557 + return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
49.1558 + }
49.1559 +
49.1560 + function ensureQuoted(key) {
49.1561 + var trimmedKey = ko.utils.stringTrim(key);
49.1562 + switch (trimmedKey.length && trimmedKey.charAt(0)) {
49.1563 + case "'":
49.1564 + case '"':
49.1565 + return key;
49.1566 + default:
49.1567 + return "'" + trimmedKey + "'";
49.1568 + }
49.1569 + }
49.1570 +
49.1571 + return {
49.1572 + bindingRewriteValidators: [],
49.1573 +
49.1574 + parseObjectLiteral: function(objectLiteralString) {
49.1575 + // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
49.1576 + // that is sufficient just to split an object literal string into a set of top-level key-value pairs
49.1577 +
49.1578 + var str = ko.utils.stringTrim(objectLiteralString);
49.1579 + if (str.length < 3)
49.1580 + return [];
49.1581 + if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
49.1582 + str = str.substring(1, str.length - 1);
49.1583 +
49.1584 + // Pull out any string literals and regex literals
49.1585 + var tokens = [];
49.1586 + var tokenStart = null, tokenEndChar;
49.1587 + for (var position = 0; position < str.length; position++) {
49.1588 + var c = str.charAt(position);
49.1589 + if (tokenStart === null) {
49.1590 + switch (c) {
49.1591 + case '"':
49.1592 + case "'":
49.1593 + case "/":
49.1594 + tokenStart = position;
49.1595 + tokenEndChar = c;
49.1596 + break;
49.1597 + }
49.1598 + } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
49.1599 + var token = str.substring(tokenStart, position + 1);
49.1600 + tokens.push(token);
49.1601 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
49.1602 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
49.1603 + position -= (token.length - replacement.length);
49.1604 + tokenStart = null;
49.1605 + }
49.1606 + }
49.1607 +
49.1608 + // Next pull out balanced paren, brace, and bracket blocks
49.1609 + tokenStart = null;
49.1610 + tokenEndChar = null;
49.1611 + var tokenDepth = 0, tokenStartChar = null;
49.1612 + for (var position = 0; position < str.length; position++) {
49.1613 + var c = str.charAt(position);
49.1614 + if (tokenStart === null) {
49.1615 + switch (c) {
49.1616 + case "{": tokenStart = position; tokenStartChar = c;
49.1617 + tokenEndChar = "}";
49.1618 + break;
49.1619 + case "(": tokenStart = position; tokenStartChar = c;
49.1620 + tokenEndChar = ")";
49.1621 + break;
49.1622 + case "[": tokenStart = position; tokenStartChar = c;
49.1623 + tokenEndChar = "]";
49.1624 + break;
49.1625 + }
49.1626 + }
49.1627 +
49.1628 + if (c === tokenStartChar)
49.1629 + tokenDepth++;
49.1630 + else if (c === tokenEndChar) {
49.1631 + tokenDepth--;
49.1632 + if (tokenDepth === 0) {
49.1633 + var token = str.substring(tokenStart, position + 1);
49.1634 + tokens.push(token);
49.1635 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
49.1636 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
49.1637 + position -= (token.length - replacement.length);
49.1638 + tokenStart = null;
49.1639 + }
49.1640 + }
49.1641 + }
49.1642 +
49.1643 + // Now we can safely split on commas to get the key/value pairs
49.1644 + var result = [];
49.1645 + var keyValuePairs = str.split(",");
49.1646 + for (var i = 0, j = keyValuePairs.length; i < j; i++) {
49.1647 + var pair = keyValuePairs[i];
49.1648 + var colonPos = pair.indexOf(":");
49.1649 + if ((colonPos > 0) && (colonPos < pair.length - 1)) {
49.1650 + var key = pair.substring(0, colonPos);
49.1651 + var value = pair.substring(colonPos + 1);
49.1652 + result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
49.1653 + } else {
49.1654 + result.push({ 'unknown': restoreTokens(pair, tokens) });
49.1655 + }
49.1656 + }
49.1657 + return result;
49.1658 + },
49.1659 +
49.1660 + preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
49.1661 + var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
49.1662 + ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
49.1663 + : objectLiteralStringOrKeyValueArray;
49.1664 + var resultStrings = [], propertyAccessorResultStrings = [];
49.1665 +
49.1666 + var keyValueEntry;
49.1667 + for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
49.1668 + if (resultStrings.length > 0)
49.1669 + resultStrings.push(",");
49.1670 +
49.1671 + if (keyValueEntry['key']) {
49.1672 + var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
49.1673 + resultStrings.push(quotedKey);
49.1674 + resultStrings.push(":");
49.1675 + resultStrings.push(val);
49.1676 +
49.1677 + if (val = getWriteableValue(ko.utils.stringTrim(val))) {
49.1678 + if (propertyAccessorResultStrings.length > 0)
49.1679 + propertyAccessorResultStrings.push(", ");
49.1680 + propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
49.1681 + }
49.1682 + } else if (keyValueEntry['unknown']) {
49.1683 + resultStrings.push(keyValueEntry['unknown']);
49.1684 + }
49.1685 + }
49.1686 +
49.1687 + var combinedResult = resultStrings.join("");
49.1688 + if (propertyAccessorResultStrings.length > 0) {
49.1689 + var allPropertyAccessors = propertyAccessorResultStrings.join("");
49.1690 + combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
49.1691 + }
49.1692 +
49.1693 + return combinedResult;
49.1694 + },
49.1695 +
49.1696 + keyValueArrayContainsKey: function(keyValueArray, key) {
49.1697 + for (var i = 0; i < keyValueArray.length; i++)
49.1698 + if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
49.1699 + return true;
49.1700 + return false;
49.1701 + },
49.1702 +
49.1703 + // Internal, private KO utility for updating model properties from within bindings
49.1704 + // property: If the property being updated is (or might be) an observable, pass it here
49.1705 + // If it turns out to be a writable observable, it will be written to directly
49.1706 + // allBindingsAccessor: All bindings in the current execution context.
49.1707 + // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
49.1708 + // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
49.1709 + // value: The value to be written
49.1710 + // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
49.1711 + // it is !== existing value on that writable observable
49.1712 + writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
49.1713 + if (!property || !ko.isWriteableObservable(property)) {
49.1714 + var propWriters = allBindingsAccessor()['_ko_property_writers'];
49.1715 + if (propWriters && propWriters[key])
49.1716 + propWriters[key](value);
49.1717 + } else if (!checkIfDifferent || property.peek() !== value) {
49.1718 + property(value);
49.1719 + }
49.1720 + }
49.1721 + };
49.1722 +})();
49.1723 +
49.1724 +ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
49.1725 +ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
49.1726 +ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
49.1727 +ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
49.1728 +
49.1729 +// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
49.1730 +// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
49.1731 +ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
49.1732 +ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
49.1733 + // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
49.1734 + // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
49.1735 + // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
49.1736 + // of that virtual hierarchy
49.1737 + //
49.1738 + // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
49.1739 + // without having to scatter special cases all over the binding and templating code.
49.1740 +
49.1741 + // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
49.1742 + // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
49.1743 + // So, use node.text where available, and node.nodeValue elsewhere
49.1744 + var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
49.1745 +
49.1746 + var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
49.1747 + var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
49.1748 + var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
49.1749 +
49.1750 + function isStartComment(node) {
49.1751 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
49.1752 + }
49.1753 +
49.1754 + function isEndComment(node) {
49.1755 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
49.1756 + }
49.1757 +
49.1758 + function getVirtualChildren(startComment, allowUnbalanced) {
49.1759 + var currentNode = startComment;
49.1760 + var depth = 1;
49.1761 + var children = [];
49.1762 + while (currentNode = currentNode.nextSibling) {
49.1763 + if (isEndComment(currentNode)) {
49.1764 + depth--;
49.1765 + if (depth === 0)
49.1766 + return children;
49.1767 + }
49.1768 +
49.1769 + children.push(currentNode);
49.1770 +
49.1771 + if (isStartComment(currentNode))
49.1772 + depth++;
49.1773 + }
49.1774 + if (!allowUnbalanced)
49.1775 + throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
49.1776 + return null;
49.1777 + }
49.1778 +
49.1779 + function getMatchingEndComment(startComment, allowUnbalanced) {
49.1780 + var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
49.1781 + if (allVirtualChildren) {
49.1782 + if (allVirtualChildren.length > 0)
49.1783 + return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
49.1784 + return startComment.nextSibling;
49.1785 + } else
49.1786 + return null; // Must have no matching end comment, and allowUnbalanced is true
49.1787 + }
49.1788 +
49.1789 + function getUnbalancedChildTags(node) {
49.1790 + // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
49.1791 + // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
49.1792 + var childNode = node.firstChild, captureRemaining = null;
49.1793 + if (childNode) {
49.1794 + do {
49.1795 + if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
49.1796 + captureRemaining.push(childNode);
49.1797 + else if (isStartComment(childNode)) {
49.1798 + var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
49.1799 + if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
49.1800 + childNode = matchingEndComment;
49.1801 + else
49.1802 + captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
49.1803 + } else if (isEndComment(childNode)) {
49.1804 + captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
49.1805 + }
49.1806 + } while (childNode = childNode.nextSibling);
49.1807 + }
49.1808 + return captureRemaining;
49.1809 + }
49.1810 +
49.1811 + ko.virtualElements = {
49.1812 + allowedBindings: {},
49.1813 +
49.1814 + childNodes: function(node) {
49.1815 + return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
49.1816 + },
49.1817 +
49.1818 + emptyNode: function(node) {
49.1819 + if (!isStartComment(node))
49.1820 + ko.utils.emptyDomNode(node);
49.1821 + else {
49.1822 + var virtualChildren = ko.virtualElements.childNodes(node);
49.1823 + for (var i = 0, j = virtualChildren.length; i < j; i++)
49.1824 + ko.removeNode(virtualChildren[i]);
49.1825 + }
49.1826 + },
49.1827 +
49.1828 + setDomNodeChildren: function(node, childNodes) {
49.1829 + if (!isStartComment(node))
49.1830 + ko.utils.setDomNodeChildren(node, childNodes);
49.1831 + else {
49.1832 + ko.virtualElements.emptyNode(node);
49.1833 + var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
49.1834 + for (var i = 0, j = childNodes.length; i < j; i++)
49.1835 + endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
49.1836 + }
49.1837 + },
49.1838 +
49.1839 + prepend: function(containerNode, nodeToPrepend) {
49.1840 + if (!isStartComment(containerNode)) {
49.1841 + if (containerNode.firstChild)
49.1842 + containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
49.1843 + else
49.1844 + containerNode.appendChild(nodeToPrepend);
49.1845 + } else {
49.1846 + // Start comments must always have a parent and at least one following sibling (the end comment)
49.1847 + containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
49.1848 + }
49.1849 + },
49.1850 +
49.1851 + insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
49.1852 + if (!insertAfterNode) {
49.1853 + ko.virtualElements.prepend(containerNode, nodeToInsert);
49.1854 + } else if (!isStartComment(containerNode)) {
49.1855 + // Insert after insertion point
49.1856 + if (insertAfterNode.nextSibling)
49.1857 + containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
49.1858 + else
49.1859 + containerNode.appendChild(nodeToInsert);
49.1860 + } else {
49.1861 + // Children of start comments must always have a parent and at least one following sibling (the end comment)
49.1862 + containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
49.1863 + }
49.1864 + },
49.1865 +
49.1866 + firstChild: function(node) {
49.1867 + if (!isStartComment(node))
49.1868 + return node.firstChild;
49.1869 + if (!node.nextSibling || isEndComment(node.nextSibling))
49.1870 + return null;
49.1871 + return node.nextSibling;
49.1872 + },
49.1873 +
49.1874 + nextSibling: function(node) {
49.1875 + if (isStartComment(node))
49.1876 + node = getMatchingEndComment(node);
49.1877 + if (node.nextSibling && isEndComment(node.nextSibling))
49.1878 + return null;
49.1879 + return node.nextSibling;
49.1880 + },
49.1881 +
49.1882 + virtualNodeBindingValue: function(node) {
49.1883 + var regexMatch = isStartComment(node);
49.1884 + return regexMatch ? regexMatch[1] : null;
49.1885 + },
49.1886 +
49.1887 + normaliseVirtualElementDomStructure: function(elementVerified) {
49.1888 + // Workaround for https://github.com/SteveSanderson/knockout/issues/155
49.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
49.1890 + // that are direct descendants of <ul> into the preceding <li>)
49.1891 + if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
49.1892 + return;
49.1893 +
49.1894 + // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
49.1895 + // must be intended to appear *after* that child, so move them there.
49.1896 + var childNode = elementVerified.firstChild;
49.1897 + if (childNode) {
49.1898 + do {
49.1899 + if (childNode.nodeType === 1) {
49.1900 + var unbalancedTags = getUnbalancedChildTags(childNode);
49.1901 + if (unbalancedTags) {
49.1902 + // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
49.1903 + var nodeToInsertBefore = childNode.nextSibling;
49.1904 + for (var i = 0; i < unbalancedTags.length; i++) {
49.1905 + if (nodeToInsertBefore)
49.1906 + elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
49.1907 + else
49.1908 + elementVerified.appendChild(unbalancedTags[i]);
49.1909 + }
49.1910 + }
49.1911 + }
49.1912 + } while (childNode = childNode.nextSibling);
49.1913 + }
49.1914 + }
49.1915 + };
49.1916 +})();
49.1917 +ko.exportSymbol('virtualElements', ko.virtualElements);
49.1918 +ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
49.1919 +ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
49.1920 +//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
49.1921 +ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
49.1922 +//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
49.1923 +ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
49.1924 +ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
49.1925 +(function() {
49.1926 + var defaultBindingAttributeName = "data-bind";
49.1927 +
49.1928 + ko.bindingProvider = function() {
49.1929 + this.bindingCache = {};
49.1930 + };
49.1931 +
49.1932 + ko.utils.extend(ko.bindingProvider.prototype, {
49.1933 + 'nodeHasBindings': function(node) {
49.1934 + switch (node.nodeType) {
49.1935 + case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
49.1936 + case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
49.1937 + default: return false;
49.1938 + }
49.1939 + },
49.1940 +
49.1941 + 'getBindings': function(node, bindingContext) {
49.1942 + var bindingsString = this['getBindingsString'](node, bindingContext);
49.1943 + return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
49.1944 + },
49.1945 +
49.1946 + // The following function is only used internally by this default provider.
49.1947 + // It's not part of the interface definition for a general binding provider.
49.1948 + 'getBindingsString': function(node, bindingContext) {
49.1949 + switch (node.nodeType) {
49.1950 + case 1: return node.getAttribute(defaultBindingAttributeName); // Element
49.1951 + case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
49.1952 + default: return null;
49.1953 + }
49.1954 + },
49.1955 +
49.1956 + // The following function is only used internally by this default provider.
49.1957 + // It's not part of the interface definition for a general binding provider.
49.1958 + 'parseBindingsString': function(bindingsString, bindingContext, node) {
49.1959 + try {
49.1960 + var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
49.1961 + return bindingFunction(bindingContext, node);
49.1962 + } catch (ex) {
49.1963 + throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
49.1964 + }
49.1965 + }
49.1966 + });
49.1967 +
49.1968 + ko.bindingProvider['instance'] = new ko.bindingProvider();
49.1969 +
49.1970 + function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
49.1971 + var cacheKey = bindingsString;
49.1972 + return cache[cacheKey]
49.1973 + || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
49.1974 + }
49.1975 +
49.1976 + function createBindingsStringEvaluator(bindingsString) {
49.1977 + // Build the source for a function that evaluates "expression"
49.1978 + // For each scope variable, add an extra level of "with" nesting
49.1979 + // Example result: with(sc1) { with(sc0) { return (expression) } }
49.1980 + var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
49.1981 + functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
49.1982 + return new Function("$context", "$element", functionBody);
49.1983 + }
49.1984 +})();
49.1985 +
49.1986 +ko.exportSymbol('bindingProvider', ko.bindingProvider);
49.1987 +(function () {
49.1988 + ko.bindingHandlers = {};
49.1989 +
49.1990 + ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
49.1991 + if (parentBindingContext) {
49.1992 + ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
49.1993 + this['$parentContext'] = parentBindingContext;
49.1994 + this['$parent'] = parentBindingContext['$data'];
49.1995 + this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
49.1996 + this['$parents'].unshift(this['$parent']);
49.1997 + } else {
49.1998 + this['$parents'] = [];
49.1999 + this['$root'] = dataItem;
49.2000 + // Export 'ko' in the binding context so it will be available in bindings and templates
49.2001 + // even if 'ko' isn't exported as a global, such as when using an AMD loader.
49.2002 + // See https://github.com/SteveSanderson/knockout/issues/490
49.2003 + this['ko'] = ko;
49.2004 + }
49.2005 + this['$data'] = dataItem;
49.2006 + if (dataItemAlias)
49.2007 + this[dataItemAlias] = dataItem;
49.2008 + }
49.2009 + ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
49.2010 + return new ko.bindingContext(dataItem, this, dataItemAlias);
49.2011 + };
49.2012 + ko.bindingContext.prototype['extend'] = function(properties) {
49.2013 + var clone = ko.utils.extend(new ko.bindingContext(), this);
49.2014 + return ko.utils.extend(clone, properties);
49.2015 + };
49.2016 +
49.2017 + function validateThatBindingIsAllowedForVirtualElements(bindingName) {
49.2018 + var validator = ko.virtualElements.allowedBindings[bindingName];
49.2019 + if (!validator)
49.2020 + throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
49.2021 + }
49.2022 +
49.2023 + function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
49.2024 + var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
49.2025 + while (currentChild = nextInQueue) {
49.2026 + // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
49.2027 + nextInQueue = ko.virtualElements.nextSibling(currentChild);
49.2028 + applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
49.2029 + }
49.2030 + }
49.2031 +
49.2032 + function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
49.2033 + var shouldBindDescendants = true;
49.2034 +
49.2035 + // Perf optimisation: Apply bindings only if...
49.2036 + // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
49.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
49.2038 + // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
49.2039 + var isElement = (nodeVerified.nodeType === 1);
49.2040 + if (isElement) // Workaround IE <= 8 HTML parsing weirdness
49.2041 + ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
49.2042 +
49.2043 + var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
49.2044 + || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
49.2045 + if (shouldApplyBindings)
49.2046 + shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
49.2047 +
49.2048 + if (shouldBindDescendants) {
49.2049 + // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
49.2050 + // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
49.2051 + // hence bindingContextsMayDifferFromDomParentElement is false
49.2052 + // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
49.2053 + // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
49.2054 + // hence bindingContextsMayDifferFromDomParentElement is true
49.2055 + applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
49.2056 + }
49.2057 + }
49.2058 +
49.2059 + function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
49.2060 + // Need to be sure that inits are only run once, and updates never run until all the inits have been run
49.2061 + var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
49.2062 +
49.2063 + // Each time the dependentObservable is evaluated (after data changes),
49.2064 + // the binding attribute is reparsed so that it can pick out the correct
49.2065 + // model properties in the context of the changed data.
49.2066 + // DOM event callbacks need to be able to access this changed data,
49.2067 + // so we need a single parsedBindings variable (shared by all callbacks
49.2068 + // associated with this node's bindings) that all the closures can access.
49.2069 + var parsedBindings;
49.2070 + function makeValueAccessor(bindingKey) {
49.2071 + return function () { return parsedBindings[bindingKey] }
49.2072 + }
49.2073 + function parsedBindingsAccessor() {
49.2074 + return parsedBindings;
49.2075 + }
49.2076 +
49.2077 + var bindingHandlerThatControlsDescendantBindings;
49.2078 + ko.dependentObservable(
49.2079 + function () {
49.2080 + // Ensure we have a nonnull binding context to work with
49.2081 + var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
49.2082 + ? viewModelOrBindingContext
49.2083 + : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
49.2084 + var viewModel = bindingContextInstance['$data'];
49.2085 +
49.2086 + // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
49.2087 + // we can easily recover it just by scanning up the node's ancestors in the DOM
49.2088 + // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
49.2089 + if (bindingContextMayDifferFromDomParentElement)
49.2090 + ko.storedBindingContextForNode(node, bindingContextInstance);
49.2091 +
49.2092 + // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
49.2093 + var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
49.2094 + parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
49.2095 +
49.2096 + if (parsedBindings) {
49.2097 + // First run all the inits, so bindings can register for notification on changes
49.2098 + if (initPhase === 0) {
49.2099 + initPhase = 1;
49.2100 + for (var bindingKey in parsedBindings) {
49.2101 + var binding = ko.bindingHandlers[bindingKey];
49.2102 + if (binding && node.nodeType === 8)
49.2103 + validateThatBindingIsAllowedForVirtualElements(bindingKey);
49.2104 +
49.2105 + if (binding && typeof binding["init"] == "function") {
49.2106 + var handlerInitFn = binding["init"];
49.2107 + var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
49.2108 +
49.2109 + // If this binding handler claims to control descendant bindings, make a note of this
49.2110 + if (initResult && initResult['controlsDescendantBindings']) {
49.2111 + if (bindingHandlerThatControlsDescendantBindings !== undefined)
49.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.");
49.2113 + bindingHandlerThatControlsDescendantBindings = bindingKey;
49.2114 + }
49.2115 + }
49.2116 + }
49.2117 + initPhase = 2;
49.2118 + }
49.2119 +
49.2120 + // ... then run all the updates, which might trigger changes even on the first evaluation
49.2121 + if (initPhase === 2) {
49.2122 + for (var bindingKey in parsedBindings) {
49.2123 + var binding = ko.bindingHandlers[bindingKey];
49.2124 + if (binding && typeof binding["update"] == "function") {
49.2125 + var handlerUpdateFn = binding["update"];
49.2126 + handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
49.2127 + }
49.2128 + }
49.2129 + }
49.2130 + }
49.2131 + },
49.2132 + null,
49.2133 + { disposeWhenNodeIsRemoved : node }
49.2134 + );
49.2135 +
49.2136 + return {
49.2137 + shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
49.2138 + };
49.2139 + };
49.2140 +
49.2141 + var storedBindingContextDomDataKey = "__ko_bindingContext__";
49.2142 + ko.storedBindingContextForNode = function (node, bindingContext) {
49.2143 + if (arguments.length == 2)
49.2144 + ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
49.2145 + else
49.2146 + return ko.utils.domData.get(node, storedBindingContextDomDataKey);
49.2147 + }
49.2148 +
49.2149 + ko.applyBindingsToNode = function (node, bindings, viewModel) {
49.2150 + if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
49.2151 + ko.virtualElements.normaliseVirtualElementDomStructure(node);
49.2152 + return applyBindingsToNodeInternal(node, bindings, viewModel, true);
49.2153 + };
49.2154 +
49.2155 + ko.applyBindingsToDescendants = function(viewModel, rootNode) {
49.2156 + if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
49.2157 + applyBindingsToDescendantsInternal(viewModel, rootNode, true);
49.2158 + };
49.2159 +
49.2160 + ko.applyBindings = function (viewModel, rootNode) {
49.2161 + if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
49.2162 + throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
49.2163 + rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
49.2164 +
49.2165 + applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
49.2166 + };
49.2167 +
49.2168 + // Retrieving binding context from arbitrary nodes
49.2169 + ko.contextFor = function(node) {
49.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)
49.2171 + switch (node.nodeType) {
49.2172 + case 1:
49.2173 + case 8:
49.2174 + var context = ko.storedBindingContextForNode(node);
49.2175 + if (context) return context;
49.2176 + if (node.parentNode) return ko.contextFor(node.parentNode);
49.2177 + break;
49.2178 + }
49.2179 + return undefined;
49.2180 + };
49.2181 + ko.dataFor = function(node) {
49.2182 + var context = ko.contextFor(node);
49.2183 + return context ? context['$data'] : undefined;
49.2184 + };
49.2185 +
49.2186 + ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
49.2187 + ko.exportSymbol('applyBindings', ko.applyBindings);
49.2188 + ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
49.2189 + ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
49.2190 + ko.exportSymbol('contextFor', ko.contextFor);
49.2191 + ko.exportSymbol('dataFor', ko.dataFor);
49.2192 +})();
49.2193 +var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
49.2194 +ko.bindingHandlers['attr'] = {
49.2195 + 'update': function(element, valueAccessor, allBindingsAccessor) {
49.2196 + var value = ko.utils.unwrapObservable(valueAccessor()) || {};
49.2197 + for (var attrName in value) {
49.2198 + if (typeof attrName == "string") {
49.2199 + var attrValue = ko.utils.unwrapObservable(value[attrName]);
49.2200 +
49.2201 + // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
49.2202 + // when someProp is a "no value"-like value (strictly null, false, or undefined)
49.2203 + // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
49.2204 + var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
49.2205 + if (toRemove)
49.2206 + element.removeAttribute(attrName);
49.2207 +
49.2208 + // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
49.2209 + // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
49.2210 + // but instead of figuring out the mode, we'll just set the attribute through the Javascript
49.2211 + // property for IE <= 8.
49.2212 + if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
49.2213 + attrName = attrHtmlToJavascriptMap[attrName];
49.2214 + if (toRemove)
49.2215 + element.removeAttribute(attrName);
49.2216 + else
49.2217 + element[attrName] = attrValue;
49.2218 + } else if (!toRemove) {
49.2219 + try {
49.2220 + element.setAttribute(attrName, attrValue.toString());
49.2221 + } catch (err) {
49.2222 + // ignore for now
49.2223 + if (console) {
49.2224 + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
49.2225 + }
49.2226 + }
49.2227 + }
49.2228 +
49.2229 + // Treat "name" specially - although you can think of it as an attribute, it also needs
49.2230 + // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
49.2231 + // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
49.2232 + // entirely, and there's no strong reason to allow for such casing in HTML.
49.2233 + if (attrName === "name") {
49.2234 + ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
49.2235 + }
49.2236 + }
49.2237 + }
49.2238 + }
49.2239 +};
49.2240 +ko.bindingHandlers['checked'] = {
49.2241 + 'init': function (element, valueAccessor, allBindingsAccessor) {
49.2242 + var updateHandler = function() {
49.2243 + var valueToWrite;
49.2244 + if (element.type == "checkbox") {
49.2245 + valueToWrite = element.checked;
49.2246 + } else if ((element.type == "radio") && (element.checked)) {
49.2247 + valueToWrite = element.value;
49.2248 + } else {
49.2249 + return; // "checked" binding only responds to checkboxes and selected radio buttons
49.2250 + }
49.2251 +
49.2252 + var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
49.2253 + if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
49.2254 + // For checkboxes bound to an array, we add/remove the checkbox value to that array
49.2255 + // This works for both observable and non-observable arrays
49.2256 + var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
49.2257 + if (element.checked && (existingEntryIndex < 0))
49.2258 + modelValue.push(element.value);
49.2259 + else if ((!element.checked) && (existingEntryIndex >= 0))
49.2260 + modelValue.splice(existingEntryIndex, 1);
49.2261 + } else {
49.2262 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
49.2263 + }
49.2264 + };
49.2265 + ko.utils.registerEventHandler(element, "click", updateHandler);
49.2266 +
49.2267 + // IE 6 won't allow radio buttons to be selected unless they have a name
49.2268 + if ((element.type == "radio") && !element.name)
49.2269 + ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
49.2270 + },
49.2271 + 'update': function (element, valueAccessor) {
49.2272 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2273 +
49.2274 + if (element.type == "checkbox") {
49.2275 + if (value instanceof Array) {
49.2276 + // When bound to an array, the checkbox being checked represents its value being present in that array
49.2277 + element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
49.2278 + } else {
49.2279 + // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
49.2280 + element.checked = value;
49.2281 + }
49.2282 + } else if (element.type == "radio") {
49.2283 + element.checked = (element.value == value);
49.2284 + }
49.2285 + }
49.2286 +};
49.2287 +var classesWrittenByBindingKey = '__ko__cssValue';
49.2288 +ko.bindingHandlers['css'] = {
49.2289 + 'update': function (element, valueAccessor) {
49.2290 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2291 + if (typeof value == "object") {
49.2292 + for (var className in value) {
49.2293 + var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
49.2294 + ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
49.2295 + }
49.2296 + } else {
49.2297 + value = String(value || ''); // Make sure we don't try to store or set a non-string value
49.2298 + ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
49.2299 + element[classesWrittenByBindingKey] = value;
49.2300 + ko.utils.toggleDomNodeCssClass(element, value, true);
49.2301 + }
49.2302 + }
49.2303 +};
49.2304 +ko.bindingHandlers['enable'] = {
49.2305 + 'update': function (element, valueAccessor) {
49.2306 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2307 + if (value && element.disabled)
49.2308 + element.removeAttribute("disabled");
49.2309 + else if ((!value) && (!element.disabled))
49.2310 + element.disabled = true;
49.2311 + }
49.2312 +};
49.2313 +
49.2314 +ko.bindingHandlers['disable'] = {
49.2315 + 'update': function (element, valueAccessor) {
49.2316 + ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
49.2317 + }
49.2318 +};
49.2319 +// For certain common events (currently just 'click'), allow a simplified data-binding syntax
49.2320 +// e.g. click:handler instead of the usual full-length event:{click:handler}
49.2321 +function makeEventHandlerShortcut(eventName) {
49.2322 + ko.bindingHandlers[eventName] = {
49.2323 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
49.2324 + var newValueAccessor = function () {
49.2325 + var result = {};
49.2326 + result[eventName] = valueAccessor();
49.2327 + return result;
49.2328 + };
49.2329 + return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
49.2330 + }
49.2331 + }
49.2332 +}
49.2333 +
49.2334 +ko.bindingHandlers['event'] = {
49.2335 + 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
49.2336 + var eventsToHandle = valueAccessor() || {};
49.2337 + for(var eventNameOutsideClosure in eventsToHandle) {
49.2338 + (function() {
49.2339 + var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
49.2340 + if (typeof eventName == "string") {
49.2341 + ko.utils.registerEventHandler(element, eventName, function (event) {
49.2342 + var handlerReturnValue;
49.2343 + var handlerFunction = valueAccessor()[eventName];
49.2344 + if (!handlerFunction)
49.2345 + return;
49.2346 + var allBindings = allBindingsAccessor();
49.2347 +
49.2348 + try {
49.2349 + // Take all the event args, and prefix with the viewmodel
49.2350 + var argsForHandler = ko.utils.makeArray(arguments);
49.2351 + argsForHandler.unshift(viewModel);
49.2352 + handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
49.2353 + } finally {
49.2354 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
49.2355 + if (event.preventDefault)
49.2356 + event.preventDefault();
49.2357 + else
49.2358 + event.returnValue = false;
49.2359 + }
49.2360 + }
49.2361 +
49.2362 + var bubble = allBindings[eventName + 'Bubble'] !== false;
49.2363 + if (!bubble) {
49.2364 + event.cancelBubble = true;
49.2365 + if (event.stopPropagation)
49.2366 + event.stopPropagation();
49.2367 + }
49.2368 + });
49.2369 + }
49.2370 + })();
49.2371 + }
49.2372 + }
49.2373 +};
49.2374 +// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
49.2375 +// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
49.2376 +ko.bindingHandlers['foreach'] = {
49.2377 + makeTemplateValueAccessor: function(valueAccessor) {
49.2378 + return function() {
49.2379 + var modelValue = valueAccessor(),
49.2380 + unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
49.2381 +
49.2382 + // If unwrappedValue is the array, pass in the wrapped value on its own
49.2383 + // The value will be unwrapped and tracked within the template binding
49.2384 + // (See https://github.com/SteveSanderson/knockout/issues/523)
49.2385 + if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
49.2386 + return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
49.2387 +
49.2388 + // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
49.2389 + ko.utils.unwrapObservable(modelValue);
49.2390 + return {
49.2391 + 'foreach': unwrappedValue['data'],
49.2392 + 'as': unwrappedValue['as'],
49.2393 + 'includeDestroyed': unwrappedValue['includeDestroyed'],
49.2394 + 'afterAdd': unwrappedValue['afterAdd'],
49.2395 + 'beforeRemove': unwrappedValue['beforeRemove'],
49.2396 + 'afterRender': unwrappedValue['afterRender'],
49.2397 + 'beforeMove': unwrappedValue['beforeMove'],
49.2398 + 'afterMove': unwrappedValue['afterMove'],
49.2399 + 'templateEngine': ko.nativeTemplateEngine.instance
49.2400 + };
49.2401 + };
49.2402 + },
49.2403 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
49.2404 + return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
49.2405 + },
49.2406 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
49.2407 + return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
49.2408 + }
49.2409 +};
49.2410 +ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
49.2411 +ko.virtualElements.allowedBindings['foreach'] = true;
49.2412 +var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
49.2413 +ko.bindingHandlers['hasfocus'] = {
49.2414 + 'init': function(element, valueAccessor, allBindingsAccessor) {
49.2415 + var handleElementFocusChange = function(isFocused) {
49.2416 + // Where possible, ignore which event was raised and determine focus state using activeElement,
49.2417 + // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
49.2418 + // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
49.2419 + // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
49.2420 + // from calling 'blur()' on the element when it loses focus.
49.2421 + // Discussion at https://github.com/SteveSanderson/knockout/pull/352
49.2422 + element[hasfocusUpdatingProperty] = true;
49.2423 + var ownerDoc = element.ownerDocument;
49.2424 + if ("activeElement" in ownerDoc) {
49.2425 + isFocused = (ownerDoc.activeElement === element);
49.2426 + }
49.2427 + var modelValue = valueAccessor();
49.2428 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
49.2429 + element[hasfocusUpdatingProperty] = false;
49.2430 + };
49.2431 + var handleElementFocusIn = handleElementFocusChange.bind(null, true);
49.2432 + var handleElementFocusOut = handleElementFocusChange.bind(null, false);
49.2433 +
49.2434 + ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
49.2435 + ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
49.2436 + ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
49.2437 + ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
49.2438 + },
49.2439 + 'update': function(element, valueAccessor) {
49.2440 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2441 + if (!element[hasfocusUpdatingProperty]) {
49.2442 + value ? element.focus() : element.blur();
49.2443 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
49.2444 + }
49.2445 + }
49.2446 +};
49.2447 +ko.bindingHandlers['html'] = {
49.2448 + 'init': function() {
49.2449 + // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
49.2450 + return { 'controlsDescendantBindings': true };
49.2451 + },
49.2452 + 'update': function (element, valueAccessor) {
49.2453 + // setHtml will unwrap the value if needed
49.2454 + ko.utils.setHtml(element, valueAccessor());
49.2455 + }
49.2456 +};
49.2457 +var withIfDomDataKey = '__ko_withIfBindingData';
49.2458 +// Makes a binding like with or if
49.2459 +function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
49.2460 + ko.bindingHandlers[bindingKey] = {
49.2461 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
49.2462 + ko.utils.domData.set(element, withIfDomDataKey, {});
49.2463 + return { 'controlsDescendantBindings': true };
49.2464 + },
49.2465 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
49.2466 + var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
49.2467 + dataValue = ko.utils.unwrapObservable(valueAccessor()),
49.2468 + shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
49.2469 + isFirstRender = !withIfData.savedNodes,
49.2470 + needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
49.2471 +
49.2472 + if (needsRefresh) {
49.2473 + if (isFirstRender) {
49.2474 + withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
49.2475 + }
49.2476 +
49.2477 + if (shouldDisplay) {
49.2478 + if (!isFirstRender) {
49.2479 + ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
49.2480 + }
49.2481 + ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
49.2482 + } else {
49.2483 + ko.virtualElements.emptyNode(element);
49.2484 + }
49.2485 +
49.2486 + withIfData.didDisplayOnLastUpdate = shouldDisplay;
49.2487 + }
49.2488 + }
49.2489 + };
49.2490 + ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
49.2491 + ko.virtualElements.allowedBindings[bindingKey] = true;
49.2492 +}
49.2493 +
49.2494 +// Construct the actual binding handlers
49.2495 +makeWithIfBinding('if');
49.2496 +makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
49.2497 +makeWithIfBinding('with', true /* isWith */, false /* isNot */,
49.2498 + function(bindingContext, dataValue) {
49.2499 + return bindingContext['createChildContext'](dataValue);
49.2500 + }
49.2501 +);
49.2502 +function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
49.2503 + if (preferModelValue) {
49.2504 + if (modelValue !== ko.selectExtensions.readValue(element))
49.2505 + ko.selectExtensions.writeValue(element, modelValue);
49.2506 + }
49.2507 +
49.2508 + // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
49.2509 + // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
49.2510 + // change the model value to match the dropdown.
49.2511 + if (modelValue !== ko.selectExtensions.readValue(element))
49.2512 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
49.2513 +};
49.2514 +
49.2515 +ko.bindingHandlers['options'] = {
49.2516 + 'update': function (element, valueAccessor, allBindingsAccessor) {
49.2517 + if (ko.utils.tagNameLower(element) !== "select")
49.2518 + throw new Error("options binding applies only to SELECT elements");
49.2519 +
49.2520 + var selectWasPreviouslyEmpty = element.length == 0;
49.2521 + var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
49.2522 + return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
49.2523 + }), function (node) {
49.2524 + return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
49.2525 + });
49.2526 + var previousScrollTop = element.scrollTop;
49.2527 +
49.2528 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2529 + var selectedValue = element.value;
49.2530 +
49.2531 + // Remove all existing <option>s.
49.2532 + // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
49.2533 + while (element.length > 0) {
49.2534 + ko.cleanNode(element.options[0]);
49.2535 + element.remove(0);
49.2536 + }
49.2537 +
49.2538 + if (value) {
49.2539 + var allBindings = allBindingsAccessor(),
49.2540 + includeDestroyed = allBindings['optionsIncludeDestroyed'];
49.2541 +
49.2542 + if (typeof value.length != "number")
49.2543 + value = [value];
49.2544 + if (allBindings['optionsCaption']) {
49.2545 + var option = document.createElement("option");
49.2546 + ko.utils.setHtml(option, allBindings['optionsCaption']);
49.2547 + ko.selectExtensions.writeValue(option, undefined);
49.2548 + element.appendChild(option);
49.2549 + }
49.2550 +
49.2551 + for (var i = 0, j = value.length; i < j; i++) {
49.2552 + // Skip destroyed items
49.2553 + var arrayEntry = value[i];
49.2554 + if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
49.2555 + continue;
49.2556 +
49.2557 + var option = document.createElement("option");
49.2558 +
49.2559 + function applyToObject(object, predicate, defaultValue) {
49.2560 + var predicateType = typeof predicate;
49.2561 + if (predicateType == "function") // Given a function; run it against the data value
49.2562 + return predicate(object);
49.2563 + else if (predicateType == "string") // Given a string; treat it as a property name on the data value
49.2564 + return object[predicate];
49.2565 + else // Given no optionsText arg; use the data value itself
49.2566 + return defaultValue;
49.2567 + }
49.2568 +
49.2569 + // Apply a value to the option element
49.2570 + var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
49.2571 + ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
49.2572 +
49.2573 + // Apply some text to the option element
49.2574 + var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
49.2575 + ko.utils.setTextContent(option, optionText);
49.2576 +
49.2577 + element.appendChild(option);
49.2578 + }
49.2579 +
49.2580 + // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
49.2581 + // That's why we first added them without selection. Now it's time to set the selection.
49.2582 + var newOptions = element.getElementsByTagName("option");
49.2583 + var countSelectionsRetained = 0;
49.2584 + for (var i = 0, j = newOptions.length; i < j; i++) {
49.2585 + if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
49.2586 + ko.utils.setOptionNodeSelectionState(newOptions[i], true);
49.2587 + countSelectionsRetained++;
49.2588 + }
49.2589 + }
49.2590 +
49.2591 + element.scrollTop = previousScrollTop;
49.2592 +
49.2593 + if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
49.2594 + // Ensure consistency between model value and selected option.
49.2595 + // If the dropdown is being populated for the first time here (or was otherwise previously empty),
49.2596 + // the dropdown selection state is meaningless, so we preserve the model value.
49.2597 + ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
49.2598 + }
49.2599 +
49.2600 + // Workaround for IE9 bug
49.2601 + ko.utils.ensureSelectElementIsRenderedCorrectly(element);
49.2602 + }
49.2603 + }
49.2604 +};
49.2605 +ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
49.2606 +ko.bindingHandlers['selectedOptions'] = {
49.2607 + 'init': function (element, valueAccessor, allBindingsAccessor) {
49.2608 + ko.utils.registerEventHandler(element, "change", function () {
49.2609 + var value = valueAccessor(), valueToWrite = [];
49.2610 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
49.2611 + if (node.selected)
49.2612 + valueToWrite.push(ko.selectExtensions.readValue(node));
49.2613 + });
49.2614 + ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
49.2615 + });
49.2616 + },
49.2617 + 'update': function (element, valueAccessor) {
49.2618 + if (ko.utils.tagNameLower(element) != "select")
49.2619 + throw new Error("values binding applies only to SELECT elements");
49.2620 +
49.2621 + var newValue = ko.utils.unwrapObservable(valueAccessor());
49.2622 + if (newValue && typeof newValue.length == "number") {
49.2623 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
49.2624 + var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
49.2625 + ko.utils.setOptionNodeSelectionState(node, isSelected);
49.2626 + });
49.2627 + }
49.2628 + }
49.2629 +};
49.2630 +ko.bindingHandlers['style'] = {
49.2631 + 'update': function (element, valueAccessor) {
49.2632 + var value = ko.utils.unwrapObservable(valueAccessor() || {});
49.2633 + for (var styleName in value) {
49.2634 + if (typeof styleName == "string") {
49.2635 + var styleValue = ko.utils.unwrapObservable(value[styleName]);
49.2636 + element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
49.2637 + }
49.2638 + }
49.2639 + }
49.2640 +};
49.2641 +ko.bindingHandlers['submit'] = {
49.2642 + 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
49.2643 + if (typeof valueAccessor() != "function")
49.2644 + throw new Error("The value for a submit binding must be a function");
49.2645 + ko.utils.registerEventHandler(element, "submit", function (event) {
49.2646 + var handlerReturnValue;
49.2647 + var value = valueAccessor();
49.2648 + try { handlerReturnValue = value.call(viewModel, element); }
49.2649 + finally {
49.2650 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
49.2651 + if (event.preventDefault)
49.2652 + event.preventDefault();
49.2653 + else
49.2654 + event.returnValue = false;
49.2655 + }
49.2656 + }
49.2657 + });
49.2658 + }
49.2659 +};
49.2660 +ko.bindingHandlers['text'] = {
49.2661 + 'update': function (element, valueAccessor) {
49.2662 + ko.utils.setTextContent(element, valueAccessor());
49.2663 + }
49.2664 +};
49.2665 +ko.virtualElements.allowedBindings['text'] = true;
49.2666 +ko.bindingHandlers['uniqueName'] = {
49.2667 + 'init': function (element, valueAccessor) {
49.2668 + if (valueAccessor()) {
49.2669 + var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
49.2670 + ko.utils.setElementName(element, name);
49.2671 + }
49.2672 + }
49.2673 +};
49.2674 +ko.bindingHandlers['uniqueName'].currentIndex = 0;
49.2675 +ko.bindingHandlers['value'] = {
49.2676 + 'init': function (element, valueAccessor, allBindingsAccessor) {
49.2677 + // Always catch "change" event; possibly other events too if asked
49.2678 + var eventsToCatch = ["change"];
49.2679 + var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
49.2680 + var propertyChangedFired = false;
49.2681 + if (requestedEventsToCatch) {
49.2682 + if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
49.2683 + requestedEventsToCatch = [requestedEventsToCatch];
49.2684 + ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
49.2685 + eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
49.2686 + }
49.2687 +
49.2688 + var valueUpdateHandler = function() {
49.2689 + propertyChangedFired = false;
49.2690 + var modelValue = valueAccessor();
49.2691 + var elementValue = ko.selectExtensions.readValue(element);
49.2692 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
49.2693 + }
49.2694 +
49.2695 + // Workaround for https://github.com/SteveSanderson/knockout/issues/122
49.2696 + // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
49.2697 + var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
49.2698 + && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
49.2699 + if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
49.2700 + ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
49.2701 + ko.utils.registerEventHandler(element, "blur", function() {
49.2702 + if (propertyChangedFired) {
49.2703 + valueUpdateHandler();
49.2704 + }
49.2705 + });
49.2706 + }
49.2707 +
49.2708 + ko.utils.arrayForEach(eventsToCatch, function(eventName) {
49.2709 + // The syntax "after<eventname>" means "run the handler asynchronously after the event"
49.2710 + // This is useful, for example, to catch "keydown" events after the browser has updated the control
49.2711 + // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
49.2712 + var handler = valueUpdateHandler;
49.2713 + if (ko.utils.stringStartsWith(eventName, "after")) {
49.2714 + handler = function() { setTimeout(valueUpdateHandler, 0) };
49.2715 + eventName = eventName.substring("after".length);
49.2716 + }
49.2717 + ko.utils.registerEventHandler(element, eventName, handler);
49.2718 + });
49.2719 + },
49.2720 + 'update': function (element, valueAccessor) {
49.2721 + var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
49.2722 + var newValue = ko.utils.unwrapObservable(valueAccessor());
49.2723 + var elementValue = ko.selectExtensions.readValue(element);
49.2724 + var valueHasChanged = (newValue != elementValue);
49.2725 +
49.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).
49.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.
49.2728 + if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
49.2729 + valueHasChanged = true;
49.2730 +
49.2731 + if (valueHasChanged) {
49.2732 + var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
49.2733 + applyValueAction();
49.2734 +
49.2735 + // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
49.2736 + // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
49.2737 + // to apply the value as well.
49.2738 + var alsoApplyAsynchronously = valueIsSelectOption;
49.2739 + if (alsoApplyAsynchronously)
49.2740 + setTimeout(applyValueAction, 0);
49.2741 + }
49.2742 +
49.2743 + // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
49.2744 + // because you're not allowed to have a model value that disagrees with a visible UI selection.
49.2745 + if (valueIsSelectOption && (element.length > 0))
49.2746 + ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
49.2747 + }
49.2748 +};
49.2749 +ko.bindingHandlers['visible'] = {
49.2750 + 'update': function (element, valueAccessor) {
49.2751 + var value = ko.utils.unwrapObservable(valueAccessor());
49.2752 + var isCurrentlyVisible = !(element.style.display == "none");
49.2753 + if (value && !isCurrentlyVisible)
49.2754 + element.style.display = "";
49.2755 + else if ((!value) && isCurrentlyVisible)
49.2756 + element.style.display = "none";
49.2757 + }
49.2758 +};
49.2759 +// 'click' is just a shorthand for the usual full-length event:{click:handler}
49.2760 +makeEventHandlerShortcut('click');
49.2761 +// If you want to make a custom template engine,
49.2762 +//
49.2763 +// [1] Inherit from this class (like ko.nativeTemplateEngine does)
49.2764 +// [2] Override 'renderTemplateSource', supplying a function with this signature:
49.2765 +//
49.2766 +// function (templateSource, bindingContext, options) {
49.2767 +// // - templateSource.text() is the text of the template you should render
49.2768 +// // - bindingContext.$data is the data you should pass into the template
49.2769 +// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
49.2770 +// // and bindingContext.$root available in the template too
49.2771 +// // - options gives you access to any other properties set on "data-bind: { template: options }"
49.2772 +// //
49.2773 +// // Return value: an array of DOM nodes
49.2774 +// }
49.2775 +//
49.2776 +// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
49.2777 +//
49.2778 +// function (script) {
49.2779 +// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
49.2780 +// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
49.2781 +// }
49.2782 +//
49.2783 +// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
49.2784 +// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
49.2785 +// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
49.2786 +
49.2787 +ko.templateEngine = function () { };
49.2788 +
49.2789 +ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
49.2790 + throw new Error("Override renderTemplateSource");
49.2791 +};
49.2792 +
49.2793 +ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
49.2794 + throw new Error("Override createJavaScriptEvaluatorBlock");
49.2795 +};
49.2796 +
49.2797 +ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
49.2798 + // Named template
49.2799 + if (typeof template == "string") {
49.2800 + templateDocument = templateDocument || document;
49.2801 + var elem = templateDocument.getElementById(template);
49.2802 + if (!elem)
49.2803 + throw new Error("Cannot find template with ID " + template);
49.2804 + return new ko.templateSources.domElement(elem);
49.2805 + } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
49.2806 + // Anonymous template
49.2807 + return new ko.templateSources.anonymousTemplate(template);
49.2808 + } else
49.2809 + throw new Error("Unknown template type: " + template);
49.2810 +};
49.2811 +
49.2812 +ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
49.2813 + var templateSource = this['makeTemplateSource'](template, templateDocument);
49.2814 + return this['renderTemplateSource'](templateSource, bindingContext, options);
49.2815 +};
49.2816 +
49.2817 +ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
49.2818 + // Skip rewriting if requested
49.2819 + if (this['allowTemplateRewriting'] === false)
49.2820 + return true;
49.2821 + return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
49.2822 +};
49.2823 +
49.2824 +ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
49.2825 + var templateSource = this['makeTemplateSource'](template, templateDocument);
49.2826 + var rewritten = rewriterCallback(templateSource['text']());
49.2827 + templateSource['text'](rewritten);
49.2828 + templateSource['data']("isRewritten", true);
49.2829 +};
49.2830 +
49.2831 +ko.exportSymbol('templateEngine', ko.templateEngine);
49.2832 +
49.2833 +ko.templateRewriting = (function () {
49.2834 + var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
49.2835 + var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
49.2836 +
49.2837 + function validateDataBindValuesForRewriting(keyValueArray) {
49.2838 + var allValidators = ko.expressionRewriting.bindingRewriteValidators;
49.2839 + for (var i = 0; i < keyValueArray.length; i++) {
49.2840 + var key = keyValueArray[i]['key'];
49.2841 + if (allValidators.hasOwnProperty(key)) {
49.2842 + var validator = allValidators[key];
49.2843 +
49.2844 + if (typeof validator === "function") {
49.2845 + var possibleErrorMessage = validator(keyValueArray[i]['value']);
49.2846 + if (possibleErrorMessage)
49.2847 + throw new Error(possibleErrorMessage);
49.2848 + } else if (!validator) {
49.2849 + throw new Error("This template engine does not support the '" + key + "' binding within its templates");
49.2850 + }
49.2851 + }
49.2852 + }
49.2853 + }
49.2854 +
49.2855 + function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
49.2856 + var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
49.2857 + validateDataBindValuesForRewriting(dataBindKeyValueArray);
49.2858 + var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
49.2859 +
49.2860 + // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
49.2861 + // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
49.2862 + // extra indirection.
49.2863 + var applyBindingsToNextSiblingScript =
49.2864 + "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
49.2865 + return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
49.2866 + }
49.2867 +
49.2868 + return {
49.2869 + ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
49.2870 + if (!templateEngine['isTemplateRewritten'](template, templateDocument))
49.2871 + templateEngine['rewriteTemplate'](template, function (htmlString) {
49.2872 + return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
49.2873 + }, templateDocument);
49.2874 + },
49.2875 +
49.2876 + memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
49.2877 + return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
49.2878 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
49.2879 + }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
49.2880 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
49.2881 + });
49.2882 + },
49.2883 +
49.2884 + applyMemoizedBindingsToNextSibling: function (bindings) {
49.2885 + return ko.memoization.memoize(function (domNode, bindingContext) {
49.2886 + if (domNode.nextSibling)
49.2887 + ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
49.2888 + });
49.2889 + }
49.2890 + }
49.2891 +})();
49.2892 +
49.2893 +
49.2894 +// Exported only because it has to be referenced by string lookup from within rewritten template
49.2895 +ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
49.2896 +(function() {
49.2897 + // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
49.2898 + // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
49.2899 + //
49.2900 + // Two are provided by default:
49.2901 + // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
49.2902 + // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
49.2903 + // without reading/writing the actual element text content, since it will be overwritten
49.2904 + // with the rendered template output.
49.2905 + // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
49.2906 + // Template sources need to have the following functions:
49.2907 + // text() - returns the template text from your storage location
49.2908 + // text(value) - writes the supplied template text to your storage location
49.2909 + // data(key) - reads values stored using data(key, value) - see below
49.2910 + // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
49.2911 + //
49.2912 + // Optionally, template sources can also have the following functions:
49.2913 + // nodes() - returns a DOM element containing the nodes of this template, where available
49.2914 + // nodes(value) - writes the given DOM element to your storage location
49.2915 + // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
49.2916 + // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
49.2917 + //
49.2918 + // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
49.2919 + // using and overriding "makeTemplateSource" to return an instance of your custom template source.
49.2920 +
49.2921 + ko.templateSources = {};
49.2922 +
49.2923 + // ---- ko.templateSources.domElement -----
49.2924 +
49.2925 + ko.templateSources.domElement = function(element) {
49.2926 + this.domElement = element;
49.2927 + }
49.2928 +
49.2929 + ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
49.2930 + var tagNameLower = ko.utils.tagNameLower(this.domElement),
49.2931 + elemContentsProperty = tagNameLower === "script" ? "text"
49.2932 + : tagNameLower === "textarea" ? "value"
49.2933 + : "innerHTML";
49.2934 +
49.2935 + if (arguments.length == 0) {
49.2936 + return this.domElement[elemContentsProperty];
49.2937 + } else {
49.2938 + var valueToWrite = arguments[0];
49.2939 + if (elemContentsProperty === "innerHTML")
49.2940 + ko.utils.setHtml(this.domElement, valueToWrite);
49.2941 + else
49.2942 + this.domElement[elemContentsProperty] = valueToWrite;
49.2943 + }
49.2944 + };
49.2945 +
49.2946 + ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
49.2947 + if (arguments.length === 1) {
49.2948 + return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
49.2949 + } else {
49.2950 + ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
49.2951 + }
49.2952 + };
49.2953 +
49.2954 + // ---- ko.templateSources.anonymousTemplate -----
49.2955 + // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
49.2956 + // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
49.2957 + // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
49.2958 +
49.2959 + var anonymousTemplatesDomDataKey = "__ko_anon_template__";
49.2960 + ko.templateSources.anonymousTemplate = function(element) {
49.2961 + this.domElement = element;
49.2962 + }
49.2963 + ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
49.2964 + ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
49.2965 + if (arguments.length == 0) {
49.2966 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
49.2967 + if (templateData.textData === undefined && templateData.containerData)
49.2968 + templateData.textData = templateData.containerData.innerHTML;
49.2969 + return templateData.textData;
49.2970 + } else {
49.2971 + var valueToWrite = arguments[0];
49.2972 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
49.2973 + }
49.2974 + };
49.2975 + ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
49.2976 + if (arguments.length == 0) {
49.2977 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
49.2978 + return templateData.containerData;
49.2979 + } else {
49.2980 + var valueToWrite = arguments[0];
49.2981 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
49.2982 + }
49.2983 + };
49.2984 +
49.2985 + ko.exportSymbol('templateSources', ko.templateSources);
49.2986 + ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
49.2987 + ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
49.2988 +})();
49.2989 +(function () {
49.2990 + var _templateEngine;
49.2991 + ko.setTemplateEngine = function (templateEngine) {
49.2992 + if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
49.2993 + throw new Error("templateEngine must inherit from ko.templateEngine");
49.2994 + _templateEngine = templateEngine;
49.2995 + }
49.2996 +
49.2997 + function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
49.2998 + var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
49.2999 + while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
49.3000 + nextInQueue = ko.virtualElements.nextSibling(node);
49.3001 + if (node.nodeType === 1 || node.nodeType === 8)
49.3002 + action(node);
49.3003 + }
49.3004 + }
49.3005 +
49.3006 + function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
49.3007 + // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
49.3008 + // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
49.3009 + // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
49.3010 + // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
49.3011 + // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
49.3012 +
49.3013 + if (continuousNodeArray.length) {
49.3014 + var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
49.3015 +
49.3016 + // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
49.3017 + // whereas a regular applyBindings won't introduce new memoized nodes
49.3018 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
49.3019 + ko.applyBindings(bindingContext, node);
49.3020 + });
49.3021 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
49.3022 + ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
49.3023 + });
49.3024 + }
49.3025 + }
49.3026 +
49.3027 + function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
49.3028 + return nodeOrNodeArray.nodeType ? nodeOrNodeArray
49.3029 + : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
49.3030 + : null;
49.3031 + }
49.3032 +
49.3033 + function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
49.3034 + options = options || {};
49.3035 + var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
49.3036 + var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
49.3037 + var templateEngineToUse = (options['templateEngine'] || _templateEngine);
49.3038 + ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
49.3039 + var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
49.3040 +
49.3041 + // Loosely check result is an array of DOM nodes
49.3042 + if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
49.3043 + throw new Error("Template engine must return an array of DOM nodes");
49.3044 +
49.3045 + var haveAddedNodesToParent = false;
49.3046 + switch (renderMode) {
49.3047 + case "replaceChildren":
49.3048 + ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
49.3049 + haveAddedNodesToParent = true;
49.3050 + break;
49.3051 + case "replaceNode":
49.3052 + ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
49.3053 + haveAddedNodesToParent = true;
49.3054 + break;
49.3055 + case "ignoreTargetNode": break;
49.3056 + default:
49.3057 + throw new Error("Unknown renderMode: " + renderMode);
49.3058 + }
49.3059 +
49.3060 + if (haveAddedNodesToParent) {
49.3061 + activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
49.3062 + if (options['afterRender'])
49.3063 + ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
49.3064 + }
49.3065 +
49.3066 + return renderedNodesArray;
49.3067 + }
49.3068 +
49.3069 + ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
49.3070 + options = options || {};
49.3071 + if ((options['templateEngine'] || _templateEngine) == undefined)
49.3072 + throw new Error("Set a template engine before calling renderTemplate");
49.3073 + renderMode = renderMode || "replaceChildren";
49.3074 +
49.3075 + if (targetNodeOrNodeArray) {
49.3076 + var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
49.3077 +
49.3078 + var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
49.3079 + var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
49.3080 +
49.3081 + return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
49.3082 + function () {
49.3083 + // Ensure we've got a proper binding context to work with
49.3084 + var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
49.3085 + ? dataOrBindingContext
49.3086 + : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
49.3087 +
49.3088 + // Support selecting template as a function of the data being rendered
49.3089 + var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
49.3090 +
49.3091 + var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
49.3092 + if (renderMode == "replaceNode") {
49.3093 + targetNodeOrNodeArray = renderedNodesArray;
49.3094 + firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
49.3095 + }
49.3096 + },
49.3097 + null,
49.3098 + { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
49.3099 + );
49.3100 + } else {
49.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
49.3102 + return ko.memoization.memoize(function (domNode) {
49.3103 + ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
49.3104 + });
49.3105 + }
49.3106 + };
49.3107 +
49.3108 + ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
49.3109 + // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
49.3110 + // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
49.3111 + var arrayItemContext;
49.3112 +
49.3113 + // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
49.3114 + var executeTemplateForArrayItem = function (arrayValue, index) {
49.3115 + // Support selecting template as a function of the data being rendered
49.3116 + arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
49.3117 + arrayItemContext['$index'] = index;
49.3118 + var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
49.3119 + return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
49.3120 + }
49.3121 +
49.3122 + // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
49.3123 + var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
49.3124 + activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
49.3125 + if (options['afterRender'])
49.3126 + options['afterRender'](addedNodesArray, arrayValue);
49.3127 + };
49.3128 +
49.3129 + return ko.dependentObservable(function () {
49.3130 + var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
49.3131 + if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
49.3132 + unwrappedArray = [unwrappedArray];
49.3133 +
49.3134 + // Filter out any entries marked as destroyed
49.3135 + var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
49.3136 + return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
49.3137 + });
49.3138 +
49.3139 + // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
49.3140 + // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
49.3141 + ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
49.3142 +
49.3143 + }, null, { disposeWhenNodeIsRemoved: targetNode });
49.3144 + };
49.3145 +
49.3146 + var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
49.3147 + function disposeOldComputedAndStoreNewOne(element, newComputed) {
49.3148 + var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
49.3149 + if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
49.3150 + oldComputed.dispose();
49.3151 + ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
49.3152 + }
49.3153 +
49.3154 + ko.bindingHandlers['template'] = {
49.3155 + 'init': function(element, valueAccessor) {
49.3156 + // Support anonymous templates
49.3157 + var bindingValue = ko.utils.unwrapObservable(valueAccessor());
49.3158 + if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
49.3159 + // It's an anonymous template - store the element contents, then clear the element
49.3160 + var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
49.3161 + container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
49.3162 + new ko.templateSources.anonymousTemplate(element)['nodes'](container);
49.3163 + }
49.3164 + return { 'controlsDescendantBindings': true };
49.3165 + },
49.3166 + 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
49.3167 + var templateName = ko.utils.unwrapObservable(valueAccessor()),
49.3168 + options = {},
49.3169 + shouldDisplay = true,
49.3170 + dataValue,
49.3171 + templateComputed = null;
49.3172 +
49.3173 + if (typeof templateName != "string") {
49.3174 + options = templateName;
49.3175 + templateName = options['name'];
49.3176 +
49.3177 + // Support "if"/"ifnot" conditions
49.3178 + if ('if' in options)
49.3179 + shouldDisplay = ko.utils.unwrapObservable(options['if']);
49.3180 + if (shouldDisplay && 'ifnot' in options)
49.3181 + shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
49.3182 +
49.3183 + dataValue = ko.utils.unwrapObservable(options['data']);
49.3184 + }
49.3185 +
49.3186 + if ('foreach' in options) {
49.3187 + // Render once for each data point (treating data set as empty if shouldDisplay==false)
49.3188 + var dataArray = (shouldDisplay && options['foreach']) || [];
49.3189 + templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
49.3190 + } else if (!shouldDisplay) {
49.3191 + ko.virtualElements.emptyNode(element);
49.3192 + } else {
49.3193 + // Render once for this single data point (or use the viewModel if no data was provided)
49.3194 + var innerBindingContext = ('data' in options) ?
49.3195 + bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
49.3196 + bindingContext; // Given no explicit 'data' value, we retain the same binding context
49.3197 + templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
49.3198 + }
49.3199 +
49.3200 + // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
49.3201 + disposeOldComputedAndStoreNewOne(element, templateComputed);
49.3202 + }
49.3203 + };
49.3204 +
49.3205 + // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
49.3206 + ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
49.3207 + var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
49.3208 +
49.3209 + if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
49.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)
49.3211 +
49.3212 + if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
49.3213 + return null; // Named templates can be rewritten, so return "no error"
49.3214 + return "This template engine does not support anonymous templates nested within its templates";
49.3215 + };
49.3216 +
49.3217 + ko.virtualElements.allowedBindings['template'] = true;
49.3218 +})();
49.3219 +
49.3220 +ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
49.3221 +ko.exportSymbol('renderTemplate', ko.renderTemplate);
49.3222 +
49.3223 +ko.utils.compareArrays = (function () {
49.3224 + var statusNotInOld = 'added', statusNotInNew = 'deleted';
49.3225 +
49.3226 + // Simple calculation based on Levenshtein distance.
49.3227 + function compareArrays(oldArray, newArray, dontLimitMoves) {
49.3228 + oldArray = oldArray || [];
49.3229 + newArray = newArray || [];
49.3230 +
49.3231 + if (oldArray.length <= newArray.length)
49.3232 + return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
49.3233 + else
49.3234 + return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
49.3235 + }
49.3236 +
49.3237 + function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
49.3238 + var myMin = Math.min,
49.3239 + myMax = Math.max,
49.3240 + editDistanceMatrix = [],
49.3241 + smlIndex, smlIndexMax = smlArray.length,
49.3242 + bigIndex, bigIndexMax = bigArray.length,
49.3243 + compareRange = (bigIndexMax - smlIndexMax) || 1,
49.3244 + maxDistance = smlIndexMax + bigIndexMax + 1,
49.3245 + thisRow, lastRow,
49.3246 + bigIndexMaxForRow, bigIndexMinForRow;
49.3247 +
49.3248 + for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
49.3249 + lastRow = thisRow;
49.3250 + editDistanceMatrix.push(thisRow = []);
49.3251 + bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
49.3252 + bigIndexMinForRow = myMax(0, smlIndex - 1);
49.3253 + for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
49.3254 + if (!bigIndex)
49.3255 + thisRow[bigIndex] = smlIndex + 1;
49.3256 + else if (!smlIndex) // Top row - transform empty array into new array via additions
49.3257 + thisRow[bigIndex] = bigIndex + 1;
49.3258 + else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
49.3259 + thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
49.3260 + else {
49.3261 + var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
49.3262 + var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
49.3263 + thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
49.3264 + }
49.3265 + }
49.3266 + }
49.3267 +
49.3268 + var editScript = [], meMinusOne, notInSml = [], notInBig = [];
49.3269 + for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
49.3270 + meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
49.3271 + if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
49.3272 + notInSml.push(editScript[editScript.length] = { // added
49.3273 + 'status': statusNotInSml,
49.3274 + 'value': bigArray[--bigIndex],
49.3275 + 'index': bigIndex });
49.3276 + } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
49.3277 + notInBig.push(editScript[editScript.length] = { // deleted
49.3278 + 'status': statusNotInBig,
49.3279 + 'value': smlArray[--smlIndex],
49.3280 + 'index': smlIndex });
49.3281 + } else {
49.3282 + editScript.push({
49.3283 + 'status': "retained",
49.3284 + 'value': bigArray[--bigIndex] });
49.3285 + --smlIndex;
49.3286 + }
49.3287 + }
49.3288 +
49.3289 + if (notInSml.length && notInBig.length) {
49.3290 + // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
49.3291 + // smlIndexMax keeps the time complexity of this algorithm linear.
49.3292 + var limitFailedCompares = smlIndexMax * 10, failedCompares,
49.3293 + a, d, notInSmlItem, notInBigItem;
49.3294 + // Go through the items that have been added and deleted and try to find matches between them.
49.3295 + for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
49.3296 + for (d = 0; notInBigItem = notInBig[d]; d++) {
49.3297 + if (notInSmlItem['value'] === notInBigItem['value']) {
49.3298 + notInSmlItem['moved'] = notInBigItem['index'];
49.3299 + notInBigItem['moved'] = notInSmlItem['index'];
49.3300 + notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
49.3301 + failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
49.3302 + break;
49.3303 + }
49.3304 + }
49.3305 + failedCompares += d;
49.3306 + }
49.3307 + }
49.3308 + return editScript.reverse();
49.3309 + }
49.3310 +
49.3311 + return compareArrays;
49.3312 +})();
49.3313 +
49.3314 +ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
49.3315 +
49.3316 +(function () {
49.3317 + // Objective:
49.3318 + // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
49.3319 + // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
49.3320 + // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
49.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
49.3322 + // previously mapped - retain those nodes, and just insert/delete other ones
49.3323 +
49.3324 + // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
49.3325 + // You can use this, for example, to activate bindings on those nodes.
49.3326 +
49.3327 + function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
49.3328 + // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
49.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,
49.3330 + // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
49.3331 + // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
49.3332 + // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
49.3333 + //
49.3334 + // Rules:
49.3335 + // [A] Any leading nodes that aren't in the document any more should be ignored
49.3336 + // These most likely correspond to memoization nodes that were already removed during binding
49.3337 + // See https://github.com/SteveSanderson/knockout/pull/440
49.3338 + // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
49.3339 + // have already been removed, and include any nodes that have been inserted among the previous collection
49.3340 +
49.3341 + // Rule [A]
49.3342 + while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
49.3343 + contiguousNodeArray.splice(0, 1);
49.3344 +
49.3345 + // Rule [B]
49.3346 + if (contiguousNodeArray.length > 1) {
49.3347 + // Build up the actual new contiguous node set
49.3348 + var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
49.3349 + while (current !== last) {
49.3350 + current = current.nextSibling;
49.3351 + if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
49.3352 + return;
49.3353 + newContiguousSet.push(current);
49.3354 + }
49.3355 +
49.3356 + // ... then mutate the input array to match this.
49.3357 + // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
49.3358 + Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
49.3359 + }
49.3360 + return contiguousNodeArray;
49.3361 + }
49.3362 +
49.3363 + function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
49.3364 + // Map this array value inside a dependentObservable so we re-map when any dependency changes
49.3365 + var mappedNodes = [];
49.3366 + var dependentObservable = ko.dependentObservable(function() {
49.3367 + var newMappedNodes = mapping(valueToMap, index) || [];
49.3368 +
49.3369 + // On subsequent evaluations, just replace the previously-inserted DOM nodes
49.3370 + if (mappedNodes.length > 0) {
49.3371 + ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
49.3372 + if (callbackAfterAddingNodes)
49.3373 + ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
49.3374 + }
49.3375 +
49.3376 + // Replace the contents of the mappedNodes array, thereby updating the record
49.3377 + // of which nodes would be deleted if valueToMap was itself later removed
49.3378 + mappedNodes.splice(0, mappedNodes.length);
49.3379 + ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
49.3380 + }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
49.3381 + return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
49.3382 + }
49.3383 +
49.3384 + var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
49.3385 +
49.3386 + ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
49.3387 + // Compare the provided array against the previous one
49.3388 + array = array || [];
49.3389 + options = options || {};
49.3390 + var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
49.3391 + var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
49.3392 + var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
49.3393 + var editScript = ko.utils.compareArrays(lastArray, array);
49.3394 +
49.3395 + // Build the new mapping result
49.3396 + var newMappingResult = [];
49.3397 + var lastMappingResultIndex = 0;
49.3398 + var newMappingResultIndex = 0;
49.3399 +
49.3400 + var nodesToDelete = [];
49.3401 + var itemsToProcess = [];
49.3402 + var itemsForBeforeRemoveCallbacks = [];
49.3403 + var itemsForMoveCallbacks = [];
49.3404 + var itemsForAfterAddCallbacks = [];
49.3405 + var mapData;
49.3406 +
49.3407 + function itemMovedOrRetained(editScriptIndex, oldPosition) {
49.3408 + mapData = lastMappingResult[oldPosition];
49.3409 + if (newMappingResultIndex !== oldPosition)
49.3410 + itemsForMoveCallbacks[editScriptIndex] = mapData;
49.3411 + // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
49.3412 + mapData.indexObservable(newMappingResultIndex++);
49.3413 + fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
49.3414 + newMappingResult.push(mapData);
49.3415 + itemsToProcess.push(mapData);
49.3416 + }
49.3417 +
49.3418 + function callCallback(callback, items) {
49.3419 + if (callback) {
49.3420 + for (var i = 0, n = items.length; i < n; i++) {
49.3421 + if (items[i]) {
49.3422 + ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
49.3423 + callback(node, i, items[i].arrayEntry);
49.3424 + });
49.3425 + }
49.3426 + }
49.3427 + }
49.3428 + }
49.3429 +
49.3430 + for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
49.3431 + movedIndex = editScriptItem['moved'];
49.3432 + switch (editScriptItem['status']) {
49.3433 + case "deleted":
49.3434 + if (movedIndex === undefined) {
49.3435 + mapData = lastMappingResult[lastMappingResultIndex];
49.3436 +
49.3437 + // Stop tracking changes to the mapping for these nodes
49.3438 + if (mapData.dependentObservable)
49.3439 + mapData.dependentObservable.dispose();
49.3440 +
49.3441 + // Queue these nodes for later removal
49.3442 + nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
49.3443 + if (options['beforeRemove']) {
49.3444 + itemsForBeforeRemoveCallbacks[i] = mapData;
49.3445 + itemsToProcess.push(mapData);
49.3446 + }
49.3447 + }
49.3448 + lastMappingResultIndex++;
49.3449 + break;
49.3450 +
49.3451 + case "retained":
49.3452 + itemMovedOrRetained(i, lastMappingResultIndex++);
49.3453 + break;
49.3454 +
49.3455 + case "added":
49.3456 + if (movedIndex !== undefined) {
49.3457 + itemMovedOrRetained(i, movedIndex);
49.3458 + } else {
49.3459 + mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
49.3460 + newMappingResult.push(mapData);
49.3461 + itemsToProcess.push(mapData);
49.3462 + if (!isFirstExecution)
49.3463 + itemsForAfterAddCallbacks[i] = mapData;
49.3464 + }
49.3465 + break;
49.3466 + }
49.3467 + }
49.3468 +
49.3469 + // Call beforeMove first before any changes have been made to the DOM
49.3470 + callCallback(options['beforeMove'], itemsForMoveCallbacks);
49.3471 +
49.3472 + // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
49.3473 + ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
49.3474 +
49.3475 + // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
49.3476 + for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
49.3477 + // Get nodes for newly added items
49.3478 + if (!mapData.mappedNodes)
49.3479 + ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
49.3480 +
49.3481 + // Put nodes in the right place if they aren't there already
49.3482 + for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
49.3483 + if (node !== nextNode)
49.3484 + ko.virtualElements.insertAfter(domNode, node, lastNode);
49.3485 + }
49.3486 +
49.3487 + // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
49.3488 + if (!mapData.initialized && callbackAfterAddingNodes) {
49.3489 + callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
49.3490 + mapData.initialized = true;
49.3491 + }
49.3492 + }
49.3493 +
49.3494 + // If there's a beforeRemove callback, call it after reordering.
49.3495 + // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
49.3496 + // some sort of animation, which is why we first reorder the nodes that will be removed. If the
49.3497 + // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
49.3498 + // Perhaps we'll make that change in the future if this scenario becomes more common.
49.3499 + callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
49.3500 +
49.3501 + // Finally call afterMove and afterAdd callbacks
49.3502 + callCallback(options['afterMove'], itemsForMoveCallbacks);
49.3503 + callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
49.3504 +
49.3505 + // Store a copy of the array items we just considered so we can difference it next time
49.3506 + ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
49.3507 + }
49.3508 +})();
49.3509 +
49.3510 +ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
49.3511 +ko.nativeTemplateEngine = function () {
49.3512 + this['allowTemplateRewriting'] = false;
49.3513 +}
49.3514 +
49.3515 +ko.nativeTemplateEngine.prototype = new ko.templateEngine();
49.3516 +ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
49.3517 + var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
49.3518 + templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
49.3519 + templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
49.3520 +
49.3521 + if (templateNodes) {
49.3522 + return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
49.3523 + } else {
49.3524 + var templateText = templateSource['text']();
49.3525 + return ko.utils.parseHtmlFragment(templateText);
49.3526 + }
49.3527 +};
49.3528 +
49.3529 +ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
49.3530 +ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
49.3531 +
49.3532 +ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
49.3533 +(function() {
49.3534 + ko.jqueryTmplTemplateEngine = function () {
49.3535 + // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
49.3536 + // doesn't expose a version number, so we have to infer it.
49.3537 + // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
49.3538 + // which KO internally refers to as version "2", so older versions are no longer detected.
49.3539 + var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
49.3540 + if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
49.3541 + return 0;
49.3542 + // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
49.3543 + try {
49.3544 + if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
49.3545 + // Since 1.0.0pre, custom tags should append markup to an array called "__"
49.3546 + return 2; // Final version of jquery.tmpl
49.3547 + }
49.3548 + } catch(ex) { /* Apparently not the version we were looking for */ }
49.3549 +
49.3550 + return 1; // Any older version that we don't support
49.3551 + })();
49.3552 +
49.3553 + function ensureHasReferencedJQueryTemplates() {
49.3554 + if (jQueryTmplVersion < 2)
49.3555 + throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
49.3556 + }
49.3557 +
49.3558 + function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
49.3559 + return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
49.3560 + }
49.3561 +
49.3562 + this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
49.3563 + options = options || {};
49.3564 + ensureHasReferencedJQueryTemplates();
49.3565 +
49.3566 + // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
49.3567 + var precompiled = templateSource['data']('precompiled');
49.3568 + if (!precompiled) {
49.3569 + var templateText = templateSource['text']() || "";
49.3570 + // Wrap in "with($whatever.koBindingContext) { ... }"
49.3571 + templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
49.3572 +
49.3573 + precompiled = jQuery['template'](null, templateText);
49.3574 + templateSource['data']('precompiled', precompiled);
49.3575 + }
49.3576 +
49.3577 + var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
49.3578 + var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
49.3579 +
49.3580 + var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
49.3581 + resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
49.3582 +
49.3583 + jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
49.3584 + return resultNodes;
49.3585 + };
49.3586 +
49.3587 + this['createJavaScriptEvaluatorBlock'] = function(script) {
49.3588 + return "{{ko_code ((function() { return " + script + " })()) }}";
49.3589 + };
49.3590 +
49.3591 + this['addTemplate'] = function(templateName, templateMarkup) {
49.3592 + document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
49.3593 + };
49.3594 +
49.3595 + if (jQueryTmplVersion > 0) {
49.3596 + jQuery['tmpl']['tag']['ko_code'] = {
49.3597 + open: "__.push($1 || '');"
49.3598 + };
49.3599 + jQuery['tmpl']['tag']['ko_with'] = {
49.3600 + open: "with($1) {",
49.3601 + close: "} "
49.3602 + };
49.3603 + }
49.3604 + };
49.3605 +
49.3606 + ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
49.3607 +
49.3608 + // Use this one by default *only if jquery.tmpl is referenced*
49.3609 + var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
49.3610 + if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
49.3611 + ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
49.3612 +
49.3613 + ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
49.3614 +})();
49.3615 +});
49.3616 +})(window,document,navigator,window["jQuery"]);
49.3617 +})();
49.3618 \ No newline at end of file
50.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
50.2 +++ b/ko/bck2brwsr/src/test/java/org/apidesign/html/ko2brwsr/Bck2BrwsrKnockoutTest.java Mon Jun 24 17:50:44 2013 +0200
50.3 @@ -0,0 +1,124 @@
50.4 +/**
50.5 + * HTML via Java(tm) Language Bindings
50.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
50.7 + *
50.8 + * This program is free software: you can redistribute it and/or modify
50.9 + * it under the terms of the GNU General Public License as published by
50.10 + * the Free Software Foundation, version 2 of the License.
50.11 + *
50.12 + * This program is distributed in the hope that it will be useful,
50.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
50.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50.15 + * GNU General Public License for more details. apidesign.org
50.16 + * designates this particular file as subject to the
50.17 + * "Classpath" exception as provided by apidesign.org
50.18 + * in the License file that accompanied this code.
50.19 + *
50.20 + * You should have received a copy of the GNU General Public License
50.21 + * along with this program. Look for COPYING file in the top folder.
50.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
50.23 + */
50.24 +package org.apidesign.html.ko2brwsr;
50.25 +
50.26 +import java.io.BufferedReader;
50.27 +import java.io.IOException;
50.28 +import java.io.InputStreamReader;
50.29 +import java.net.URI;
50.30 +import java.net.URISyntaxException;
50.31 +import java.net.URL;
50.32 +import java.net.URLConnection;
50.33 +import java.util.Map;
50.34 +import net.java.html.BrwsrCtx;
50.35 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
50.36 +import org.apidesign.bck2brwsr.vmtest.VMTest;
50.37 +import org.apidesign.html.context.spi.Contexts;
50.38 +import org.apidesign.html.json.spi.Technology;
50.39 +import org.apidesign.html.json.spi.Transfer;
50.40 +import org.apidesign.html.json.tck.KOTest;
50.41 +import org.apidesign.html.json.tck.KnockoutTCK;
50.42 +import org.openide.util.lookup.ServiceProvider;
50.43 +import org.testng.annotations.Factory;
50.44 +
50.45 +/**
50.46 + *
50.47 + * @author Jaroslav Tulach <jtulach@netbeans.org>
50.48 + */
50.49 +@ServiceProvider(service = KnockoutTCK.class)
50.50 +public final class Bck2BrwsrKnockoutTest extends KnockoutTCK {
50.51 + @Factory public static Object[] create() {
50.52 + return VMTest.newTests().
50.53 + withClasses(testClasses()).
50.54 + withLaunchers("bck2brwsr").
50.55 + withTestAnnotation(KOTest.class).
50.56 + build();
50.57 + }
50.58 +
50.59 + @Override
50.60 + public BrwsrCtx createContext() {
50.61 + return Contexts.newBuilder().
50.62 + register(Transfer.class, BrwsrCtxImpl.DEFAULT, 9).
50.63 + register(Technology.class, BrwsrCtxImpl.DEFAULT, 9).build();
50.64 + }
50.65 +
50.66 +
50.67 +
50.68 + @Override
50.69 + public Object createJSON(Map<String, Object> values) {
50.70 + Object json = createJSON();
50.71 +
50.72 + for (Map.Entry<String, Object> entry : values.entrySet()) {
50.73 + putValue(json, entry.getKey(), entry.getValue());
50.74 + }
50.75 + return json;
50.76 + }
50.77 +
50.78 + @JavaScriptBody(args = {}, body = "return new Object();")
50.79 + private static native Object createJSON();
50.80 +
50.81 + @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
50.82 + private static native void putValue(Object json, String key, Object value);
50.83 +
50.84 + @Override
50.85 + public Object executeScript(String script, Object[] arguments) {
50.86 + return execScript(script, arguments);
50.87 + }
50.88 +
50.89 + @JavaScriptBody(args = { "s", "args" }, body =
50.90 + "var f = new Function(s); return f.apply(null, args);"
50.91 + )
50.92 + private static native Object execScript(String s, Object[] arguments);
50.93 +
50.94 + @JavaScriptBody(args = { }, body =
50.95 + "var h;"
50.96 + + "if (!!window && !!window.location && !!window.location.href)\n"
50.97 + + " h = window.location.href;\n"
50.98 + + "else "
50.99 + + " h = null;"
50.100 + + "return h;\n"
50.101 + )
50.102 + private static native String findBaseURL();
50.103 +
50.104 + @Override
50.105 + public URI prepareURL(String content, String mimeType, String[] parameters) {
50.106 + try {
50.107 + final URL baseURL = new URL(findBaseURL());
50.108 + StringBuilder sb = new StringBuilder();
50.109 + sb.append("/dynamic?mimeType=").append(mimeType);
50.110 + for (int i = 0; i < parameters.length; i++) {
50.111 + sb.append("¶m" + i).append("=").append(parameters[i]);
50.112 + }
50.113 + String mangle = content.replace("\n", "%0a")
50.114 + .replace("\"", "\\\"").replace(" ", "%20");
50.115 + sb.append("&content=").append(mangle);
50.116 +
50.117 + URL query = new URL(baseURL, sb.toString());
50.118 + String uri = (String) query.getContent(new Class[] { String.class });
50.119 + URI connectTo = new URI(uri.trim());
50.120 + return connectTo;
50.121 + } catch (IOException ex) {
50.122 + throw new IllegalStateException(ex);
50.123 + } catch (URISyntaxException ex) {
50.124 + throw new IllegalStateException(ex);
50.125 + }
50.126 + }
50.127 +}