Rewritting the archetype to be simpler and more easily modifiable to one's needs
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 09 Sep 2013 17:34:30 +0200
changeset 127337ad459579bc
parent 1272 3ee4ec9577bc
child 1274 c4f83529954d
Rewritting the archetype to be simpler and more easily modifiable to one's needs
ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java
ko/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
ko/archetype/src/main/resources/archetype-resources/pom.xml
ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml
ko/archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml
ko/archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java
ko/archetype/src/main/resources/archetype-resources/src/main/java/Main.java
ko/archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java
ko/archetype/src/main/resources/archetype-resources/src/main/resources/index.html
ko/archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css
ko/archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html
ko/archetype/src/main/resources/archetype-resources/src/test/java/DataModelTest.java
ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java
ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java
launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java
     1.1 --- a/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java	Mon Sep 09 15:26:12 2013 +0200
     1.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java	Mon Sep 09 17:34:30 2013 +0200
     1.3 @@ -88,7 +88,6 @@
     1.4          
     1.5          ZipFile zf = new ZipFile(zip);
     1.6          assertNotNull(zf.getEntry("public_html/index.html"), "index.html found");
     1.7 -        assertNotNull(zf.getEntry("public_html/twitterExample.css"), "css file found");
     1.8          
     1.9      }
    1.10  
     2.1 --- a/ko/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml	Mon Sep 09 15:26:12 2013 +0200
     2.2 +++ b/ko/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml	Mon Sep 09 17:34:30 2013 +0200
     2.3 @@ -26,8 +26,8 @@
     2.4          <include>**/*.java</include>
     2.5        </includes>
     2.6      </fileSet>
     2.7 -    <fileSet filtered="true" packaged="true">
     2.8 -      <directory>src/main/resources</directory>
     2.9 +    <fileSet filtered="true" packaged="false">
    2.10 +      <directory>src/main/webapp/pages</directory>
    2.11        <includes>
    2.12          <include>**/*.xhtml</include>
    2.13          <include>**/*.html</include>
     3.1 --- a/ko/archetype/src/main/resources/archetype-resources/pom.xml	Mon Sep 09 15:26:12 2013 +0200
     3.2 +++ b/ko/archetype/src/main/resources/archetype-resources/pom.xml	Mon Sep 09 17:34:30 2013 +0200
     3.3 @@ -42,7 +42,7 @@
     3.4      <bck2brwsr.version>${project.version}</bck2brwsr.version>
     3.5      <bck2brwsr.launcher.version>${project.version}</bck2brwsr.launcher.version>
     3.6      <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
     3.7 -    <brwsr.startpage>\${package.replace('.','/')}/index.html</brwsr.startpage>
     3.8 +    <brwsr.startpage>pages/index.html</brwsr.startpage>
     3.9    </properties>
    3.10    <build>
    3.11        <plugins>
    3.12 @@ -58,8 +58,9 @@
    3.13                    </execution>
    3.14                </executions>
    3.15                <configuration>
    3.16 -                  <startpage>\${brwsr.startpage}</startpage>
    3.17 -                  <launcher>\${brwsr}</launcher>
    3.18 +                  <directory>\${basedir}/src/main/webapp/</directory>
    3.19 +                  <startpage>${brwsr.startpage}</startpage>
    3.20 +                  <launcher>${brwsr}</launcher>
    3.21                </configuration>
    3.22            </plugin>
    3.23            <plugin>
     4.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml	Mon Sep 09 15:26:12 2013 +0200
     4.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml	Mon Sep 09 17:34:30 2013 +0200
     4.3 @@ -29,6 +29,10 @@
     4.4            </excludes>
     4.5            <outputDirectory>/</outputDirectory>
     4.6        </fileSet>
     4.7 +      <fileSet>
     4.8 +          <directory>src/main/webapp/pages</directory>
     4.9 +          <outputDirectory>/</outputDirectory>
    4.10 +      </fileSet>
    4.11    </fileSets>
    4.12    <files>
    4.13      <file>
     5.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml	Mon Sep 09 15:26:12 2013 +0200
     5.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml	Mon Sep 09 17:34:30 2013 +0200
     5.3 @@ -20,4 +20,13 @@
     5.4        <outputDirectory>/</outputDirectory>
     5.5      </file>
     5.6    </files>
     5.7 +  <fileSets>
     5.8 +    <fileSet>
     5.9 +       <directory>src/main/webapp/</directory>
    5.10 +       <outputDirectory>/</outputDirectory>
    5.11 +       <includes>
    5.12 +          <include>pages/**</include>
    5.13 +       </includes>
    5.14 +    </fileSet>
    5.15 +  </fileSets>
    5.16  </assembly>
    5.17 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/java/DataModel.java	Mon Sep 09 17:34:30 2013 +0200
     6.3 @@ -0,0 +1,31 @@
     6.4 +package ${package};
     6.5 +
     6.6 +import net.java.html.json.ComputedProperty;
     6.7 +import net.java.html.json.Function;
     6.8 +import net.java.html.json.Model;
     6.9 +import net.java.html.json.Property;
    6.10 +
    6.11 +/** Model annotation generates class Data with 
    6.12 + * one message property, boolean property and read only words property
    6.13 + */
    6.14 +@Model(className = "Data", properties = {
    6.15 +    @Property(name = "message", type = String.class),
    6.16 +    @Property(name = "on", type = boolean.class)
    6.17 +})
    6.18 +final class DataModel {
    6.19 +    @ComputedProperty static java.util.List<String> words(String message) {
    6.20 +        String[] arr = new String[6];
    6.21 +        String[] words = message == null ? new String[0] : message.split(" ", 6);
    6.22 +        for (int i = 0; i < 6; i++) {
    6.23 +            arr[i] = words.length > i ? words[i] : "!";
    6.24 +        }
    6.25 +        return java.util.Arrays.asList(arr);
    6.26 +    }
    6.27 +    
    6.28 +    @Function static void turnOn(Data model) {
    6.29 +        model.setOn(true);
    6.30 +    }
    6.31 +    @Function static void turnOff(Data model) {
    6.32 +        model.setOn(false);
    6.33 +    }
    6.34 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/java/Main.java	Mon Sep 09 17:34:30 2013 +0200
     7.3 @@ -0,0 +1,15 @@
     7.4 +package ${package};
     7.5 +
     7.6 +public final class Main {
     7.7 +    private Main() {
     7.8 +    }
     7.9 +    
    7.10 +    /**
    7.11 +     * Called when the page is ready.
    7.12 +     */
    7.13 +    static {
    7.14 +        Data d = new Data();
    7.15 +        d.setMessage("Hello World from HTML and Java!");
    7.16 +        d.applyBindings();
    7.17 +    }
    7.18 +}
     8.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java	Mon Sep 09 15:26:12 2013 +0200
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,178 +0,0 @@
     8.4 -package ${package};
     8.5 -
     8.6 -import java.util.Arrays;
     8.7 -import java.util.List;
     8.8 -import net.java.html.json.ComputedProperty;
     8.9 -import net.java.html.json.Function;
    8.10 -import net.java.html.json.Model;
    8.11 -import net.java.html.json.OnPropertyChange;
    8.12 -import net.java.html.json.OnReceive;
    8.13 -import net.java.html.json.Property;
    8.14 -
    8.15 -@Model(className="TwitterModel", properties={
    8.16 -    @Property(name="savedLists", type=Tweeters.class, array = true),
    8.17 -    @Property(name="activeTweetersName", type=String.class),
    8.18 -    @Property(name="activeTweeters", type=String.class, array = true),
    8.19 -    @Property(name="userNameToAdd", type=String.class),
    8.20 -    @Property(name="loading", type=boolean.class),
    8.21 -    @Property(name="currentTweets", type=Tweet.class, array = true)
    8.22 -})
    8.23 -public class TwitterClient {
    8.24 -    @Model(className = "Tweeters", properties = {
    8.25 -        @Property(name="name", type = String.class),
    8.26 -        @Property(name="userNames", type = String.class, array = true)
    8.27 -    })
    8.28 -    static class Twttrs {
    8.29 -    }
    8.30 -    @Model(className = "Tweet", properties = {
    8.31 -        @Property(name = "from_user", type = String.class),
    8.32 -        @Property(name = "from_user_id", type = int.class),
    8.33 -        @Property(name = "profile_image_url", type = String.class),
    8.34 -        @Property(name = "text", type = String.class),
    8.35 -        @Property(name = "created_at", type = String.class),
    8.36 -    })
    8.37 -    static final class Twt {
    8.38 -        @ComputedProperty static String html(String text) {
    8.39 -            StringBuilder sb = new StringBuilder(320);
    8.40 -            for (int pos = 0;;) {
    8.41 -                int http = text.indexOf("http", pos);
    8.42 -                if (http == -1) {
    8.43 -                    sb.append(text.substring(pos));
    8.44 -                    return sb.toString();
    8.45 -                }
    8.46 -                int spc = text.indexOf(' ', http);
    8.47 -                if (spc == -1) {
    8.48 -                    spc = text.length();
    8.49 -                }
    8.50 -                sb.append(text.substring(pos, http));
    8.51 -                String url = text.substring(http, spc);
    8.52 -                sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
    8.53 -                pos = spc;
    8.54 -            }
    8.55 -        }
    8.56 -        
    8.57 -        @ComputedProperty static String userUrl(String from_user) {
    8.58 -            return "http://twitter.com/" + from_user;
    8.59 -        }
    8.60 -    }
    8.61 -    @Model(className = "TwitterQuery", properties = {
    8.62 -        @Property(array = true, name = "results", type = Twt.class)
    8.63 -    })
    8.64 -    public static final class TwttrQr {
    8.65 -    }
    8.66 -    
    8.67 -    @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
    8.68 -    static void queryTweets(TwitterModel page, TwitterQuery q) {
    8.69 -        page.getCurrentTweets().clear();
    8.70 -        page.getCurrentTweets().addAll(q.getResults());
    8.71 -        page.setLoading(false);
    8.72 -    }
    8.73 -    
    8.74 -    @OnPropertyChange("activeTweetersName")
    8.75 -    static void changeTweetersList(TwitterModel model) {
    8.76 -        Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());        
    8.77 -        model.getActiveTweeters().clear();
    8.78 -        model.getActiveTweeters().addAll(people.getUserNames());
    8.79 -    }
    8.80 -    
    8.81 -    @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
    8.82 -    static void refreshTweets(TwitterModel model) {
    8.83 -        StringBuilder sb = new StringBuilder();
    8.84 -        sb.append("rpp=25&q=");
    8.85 -        String sep = "";
    8.86 -        for (String p : model.getActiveTweeters()) {
    8.87 -            sb.append(sep);
    8.88 -            sb.append("from:");
    8.89 -            sb.append(p);
    8.90 -            sep = " OR ";
    8.91 -        }
    8.92 -        model.setLoading(true);
    8.93 -        model.queryTweets("http://search.twitter.com", sb.toString());
    8.94 -    }
    8.95 -    
    8.96 -    static {
    8.97 -        final TwitterModel model = new TwitterModel();
    8.98 -        final List<Tweeters> svdLst = model.getSavedLists();
    8.99 -        svdLst.add(newTweeters("API Design", "JaroslavTulach"));
   8.100 -        svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
   8.101 -        svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
   8.102 -        svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
   8.103 -        svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
   8.104 -
   8.105 -        model.setActiveTweetersName("NetBeans");
   8.106 -
   8.107 -        model.applyBindings();
   8.108 -    }
   8.109 -    
   8.110 -    @ComputedProperty
   8.111 -    static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
   8.112 -        Tweeters tw = findByName(savedLists, activeTweetersName);
   8.113 -        if (activeTweeters == null) {
   8.114 -            return false;
   8.115 -        }
   8.116 -        return !tw.getUserNames().equals(activeTweeters);
   8.117 -    }
   8.118 -    
   8.119 -    @ComputedProperty
   8.120 -    static int activeTweetersCount(List<String> activeTweeters) {
   8.121 -        return activeTweeters.size();
   8.122 -    }
   8.123 -    
   8.124 -    @ComputedProperty
   8.125 -    static boolean userNameToAddIsValid(
   8.126 -        String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
   8.127 -    ) {
   8.128 -        return userNameToAdd != null && 
   8.129 -            userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
   8.130 -            !activeTweeters.contains(userNameToAdd);
   8.131 -    }
   8.132 -    
   8.133 -    @Function
   8.134 -    static void deleteList(TwitterModel model) {
   8.135 -        final List<Tweeters> sl = model.getSavedLists();
   8.136 -        sl.remove(findByName(sl, model.getActiveTweetersName()));
   8.137 -        if (sl.isEmpty()) {
   8.138 -            final Tweeters t = new Tweeters();
   8.139 -            t.setName("New");
   8.140 -            sl.add(t);
   8.141 -        }
   8.142 -        model.setActiveTweetersName(sl.get(0).getName());
   8.143 -    }
   8.144 -    
   8.145 -    @Function
   8.146 -    static void saveChanges(TwitterModel model) {
   8.147 -        Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
   8.148 -        int indx = model.getSavedLists().indexOf(t);
   8.149 -        if (indx != -1) {
   8.150 -            t.setName(model.getActiveTweetersName());
   8.151 -            t.getUserNames().clear();
   8.152 -            t.getUserNames().addAll(model.getActiveTweeters());
   8.153 -        }
   8.154 -    }
   8.155 -    
   8.156 -    @Function
   8.157 -    static void addUser(TwitterModel model) {
   8.158 -        String n = model.getUserNameToAdd();
   8.159 -        model.getActiveTweeters().add(n);
   8.160 -    }
   8.161 -    @Function
   8.162 -    static void removeUser(String data, TwitterModel model) {
   8.163 -        model.getActiveTweeters().remove(data);
   8.164 -    }
   8.165 -    
   8.166 -    private static Tweeters findByName(List<Tweeters> list, String name) {
   8.167 -        for (Tweeters l : list) {
   8.168 -            if (l.getName() != null && l.getName().equals(name)) {
   8.169 -                return l;
   8.170 -            }
   8.171 -        }
   8.172 -        return list.isEmpty() ? new Tweeters() : list.get(0);
   8.173 -    }
   8.174 -    
   8.175 -    private static Tweeters newTweeters(String listName, String... userNames) {
   8.176 -        Tweeters t = new Tweeters();
   8.177 -        t.setName(listName);
   8.178 -        t.getUserNames().addAll(Arrays.asList(userNames));
   8.179 -        return t;
   8.180 -    }
   8.181 -}
     9.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/resources/index.html	Mon Sep 09 15:26:12 2013 +0200
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,90 +0,0 @@
     9.4 -<?xml version="1.0" encoding="UTF-8"?>
     9.5 -
     9.6 -<!--
     9.7 -    Copied from knockout.js Twitter example:
     9.8 -    http://knockoutjs.com/examples/twitter.html
     9.9 --->
    9.10 -
    9.11 -<!DOCTYPE html>
    9.12 -<html xmlns="http://www.w3.org/1999/xhtml">
    9.13 -    <head>
    9.14 -        <title>Bck2Brwsr's Twitter</title>
    9.15 -    </head>
    9.16 -    <body>
    9.17 -        <link href='twitterExample.css' rel='Stylesheet' ></link>
    9.18 -        
    9.19 -        <style type='text/css'>
    9.20 -           .liveExample select { height: 1.7em; }
    9.21 -           .liveExample button { height: 2em; }
    9.22 -        </style>
    9.23 -        
    9.24 -        
    9.25 -        <h2>Bck2Brwsr's Twitter</h2>
    9.26 -        
    9.27 -        <p>
    9.28 -        This code is based on original 
    9.29 -        <a href="http://knockoutjs.com/examples/twitter.html">knockout.js
    9.30 -        Twitter example</a> and
    9.31 -        uses almost unmodified HTML page. It just changes the model. The model
    9.32 -        is written in Java language with the help of 
    9.33 -        <a href="http://bck2brwsr.apidesign.org/javadoc/net.java.html.json/">
    9.34 -            Knockout/Java binding library
    9.35 -        </a>. The Java source code has about 180 lines and seems more 
    9.36 -        dense and shorter than the original JavaScript model.
    9.37 -        </p>
    9.38 -        <p>
    9.39 -        The project has two profiles. Either it executes in real Java virtual
    9.40 -        machine and renders using JavaFX's WebView (use <code>fxbrwsr</code> profile
    9.41 -        - the default). It can also run directly in a browser via
    9.42 -        <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> virtual machine
    9.43 -        (use <code>bck2brwsr</code> profile).
    9.44 -        </p>
    9.45 -        
    9.46 -        <div class='liveExample'>
    9.47 -            <div class='configuration'>
    9.48 -                <div class='listChooser'>
    9.49 -                    <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
    9.50 -                    <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button> 
    9.51 -                    <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
    9.52 -                </div>
    9.53 -
    9.54 -                <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
    9.55 -                <div class='currentUsers' >
    9.56 -                    <ul data-bind='foreach: activeTweeters'>
    9.57 -                        <li>
    9.58 -                            <button data-bind='click: $root.removeUser'>Remove</button>
    9.59 -                            <div data-bind='text: $data'> </div>
    9.60 -                        </li>
    9.61 -                    </ul>
    9.62 -                </div>
    9.63 -
    9.64 -                <form data-bind='submit: addUser'>
    9.65 -                    <label>Add user:</label>
    9.66 -                    <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
    9.67 -                    <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
    9.68 -                </form>
    9.69 -            </div>
    9.70 -            <div class='tweets'>
    9.71 -                <div class='loadingIndicator' data-bind="visible: loading">Loading...</div>
    9.72 -                <table data-bind='foreach: currentTweets' width='100%'>
    9.73 -                    <tr>
    9.74 -                        <td><img data-bind='attr: { src: profile_image_url }' /></td>
    9.75 -                        <td>
    9.76 -                            <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
    9.77 -                            <span data-bind='html: html'> </span>
    9.78 -                            <div class='tweetInfo' data-bind='text: created_at'> </div>
    9.79 -                        </td>
    9.80 -                    </tr>
    9.81 -                </table>
    9.82 -            </div>
    9.83 -        </div>
    9.84 -        
    9.85 -        <script src="bck2brwsr.js"></script>
    9.86 -        <script type="text/javascript">
    9.87 -            var vm = bck2brwsr('${artifactId}-${version}.jar');
    9.88 -            vm.loadClass('${package}.TwitterClient');
    9.89 -        </script>
    9.90 -
    9.91 -
    9.92 -    </body>
    9.93 -</html>
    10.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css	Mon Sep 09 15:26:12 2013 +0200
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,32 +0,0 @@
    10.4 -/*
    10.5 -    Copied from knockout.js Twitter example:
    10.6 -    http://knockoutjs.com/examples/twitter.html
    10.7 -*/
    10.8 -
    10.9 -.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
   10.10 -.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
   10.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; }
   10.12 -.tweets table { border-width: 0;}
   10.13 -.tweets tr { vertical-align: top; }
   10.14 -.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
   10.15 -.tweets img { width: 4em; }
   10.16 -.tweetInfo { color: Gray; font-size: 0.9em; }
   10.17 -.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
   10.18 -input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
   10.19 -
   10.20 -.listChooser select, .listChooser button { vertical-align:top; }
   10.21 -.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
   10.22 -.listChooser button { width: 19%; height:1.68em; float:right; }
   10.23 -
   10.24 -.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
   10.25 -.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
   10.26 -.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
   10.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; }
   10.28 -.currentUsers li div { padding: 0.6em; }
   10.29 -.currentUsers li:hover { background-color: #EEC; }
   10.30 -
   10.31 -.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
   10.32 -.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
   10.33 -.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
   10.34 -
   10.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; }
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/webapp/pages/index.html	Mon Sep 09 17:34:30 2013 +0200
    11.3 @@ -0,0 +1,63 @@
    11.4 +<!DOCTYPE html>
    11.5 +<html>
    11.6 +    <head>
    11.7 +        <title></title>
    11.8 +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    11.9 +
   11.10 +        <style type="text/css">
   11.11 +            @-webkit-keyframes spin {
   11.12 +                0% { -webkit-transform: rotate(0deg); }
   11.13 +                100% { -webkit-transform: rotate(360deg); }
   11.14 +            }
   11.15 +
   11.16 +            .rotate {
   11.17 +                -webkit-animation-name: spin;
   11.18 +                -webkit-animation-duration: 3s;
   11.19 +                -webkit-animation-iteration-count: infinite;
   11.20 +                -webkit-animation-direction: alternate;
   11.21 +            }
   11.22 +
   11.23 +            #scene {
   11.24 +                position: relative;
   11.25 +                top: 60px;
   11.26 +                text-align: center;
   11.27 +            }
   11.28 +            
   11.29 +            #words span {
   11.30 +                border: 1px solid #ccc;
   11.31 +                background: rgba(255,255,155,0.8);
   11.32 +                text-align: center;
   11.33 +                font-size: 30px;                
   11.34 +                -webkit-box-shadow: inset 0 0 40px rgba(0,0,0,0.4);
   11.35 +                position: absolute;
   11.36 +            }
   11.37 +
   11.38 +            #words span:nth-child(1) { left: 45%; top: 0px; }
   11.39 +            #words span:nth-child(2) { left: 25%; top: 100px; }
   11.40 +            #words span:nth-child(3) { left: 65%; top: 100px; }
   11.41 +            #words span:nth-child(4) { left: 10%; top: 200px; }
   11.42 +            #words span:nth-child(5) { left: 45%; top: 200px; }
   11.43 +            #words span:nth-child(6) { left: 80%; top: 200px; }
   11.44 +            
   11.45 +        </style>
   11.46 +
   11.47 +    </head>
   11.48 +    <body>
   11.49 +        <h1>Words Demo</h1>
   11.50 +        <input data-bind="value: message, valueUpdate: 'afterkeydown'" size="80">
   11.51 +        <br>
   11.52 +        <button data-bind="enable: !on(), click: $root.turnOn">Start</button>
   11.53 +        <button data-bind="enable: on, click: $root.turnOff">Stop</button>
   11.54 +
   11.55 +        <div id="scene">
   11.56 +            <span id="words" data-bind="foreach: words">
   11.57 +                <span data-bind="text: $data, css: { 'rotate' : $root.on } "></span>
   11.58 +            </span>
   11.59 +        </div>
   11.60 +        <script type="text/javascript" src="bck2brwsr.js"></script>
   11.61 +        <script>
   11.62 +            var vm = bck2brwsr('${project.build.finalName}.jar');
   11.63 +            vm.loadClass('${package}.Main');
   11.64 +        </script>
   11.65 +    </body>
   11.66 +</html>
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/DataModelTest.java	Mon Sep 09 17:34:30 2013 +0200
    12.3 @@ -0,0 +1,16 @@
    12.4 +package ${package};
    12.5 +
    12.6 +import static org.testng.Assert.*;
    12.7 +import org.testng.annotations.Test;
    12.8 +
    12.9 +public class DataModelTest {
   12.10 +    @Test public void areHelloWorldTwoWords() {
   12.11 +        Data model = new Data();
   12.12 +        model.setMessage("Hello World!");
   12.13 +        
   12.14 +        java.util.List<String> arr = model.getWords();
   12.15 +        assertEquals(arr.size(), 6, "Six words always");
   12.16 +        assertEquals("Hello", arr.get(0), "Hello is the first word");
   12.17 +        assertEquals("World!", arr.get(1), "World is the second word");
   12.18 +    }
   12.19 +}
    13.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java	Mon Sep 09 15:26:12 2013 +0200
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,50 +0,0 @@
    13.4 -package ${package};
    13.5 -
    13.6 -import java.util.List;
    13.7 -import net.java.html.BrwsrCtx;
    13.8 -import net.java.html.json.Models;
    13.9 -import static org.testng.Assert.*;
   13.10 -import org.testng.annotations.BeforeMethod;
   13.11 -import org.testng.annotations.Test;
   13.12 -
   13.13 -/** We can unit test the TwitterModel smoothly.
   13.14 - */
   13.15 -public class TwitterClientTest {
   13.16 -    private TwitterModel model;
   13.17 -    
   13.18 -
   13.19 -    @BeforeMethod
   13.20 -    public void initModel() {
   13.21 -        model = Models.bind(new TwitterModel(), BrwsrCtx.EMPTY);
   13.22 -    }
   13.23 -
   13.24 -    @Test public void testIsValidToAdd() {
   13.25 -        model.setUserNameToAdd("Joe");
   13.26 -        Tweeters t = Models.bind(new Tweeters(), BrwsrCtx.EMPTY);
   13.27 -        t.setName("test");
   13.28 -        model.getSavedLists().add(t);
   13.29 -        model.setActiveTweetersName("test");
   13.30 -        
   13.31 -        assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
   13.32 -        TwitterClient.addUser(model);
   13.33 -        assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
   13.34 -        assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
   13.35 -        
   13.36 -        List<String> mod = model.getActiveTweeters();
   13.37 -        assertTrue(model.isHasUnsavedChanges(), "We have modifications");
   13.38 -        assertEquals(mod.size(), 1, "One element in the list");
   13.39 -        assertEquals(mod.get(0), "Joe", "Its name is Joe");
   13.40 -        
   13.41 -        assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
   13.42 -        
   13.43 -        TwitterClient.saveChanges(model);
   13.44 -        assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
   13.45 -        
   13.46 -        assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
   13.47 -    }
   13.48 -    
   13.49 -    @Test public void httpAtTheEnd() {
   13.50 -        String res = TwitterClient.Twt.html("Ahoj http://kuk");
   13.51 -        assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
   13.52 -    }
   13.53 -}
    14.1 --- a/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java	Mon Sep 09 15:26:12 2013 +0200
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,73 +0,0 @@
    14.4 -package ${package};
    14.5 -
    14.6 -import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
    14.7 -import org.apidesign.bck2brwsr.vmtest.Http;
    14.8 -import org.apidesign.bck2brwsr.vmtest.VMTest;
    14.9 -import org.testng.annotations.Factory;
   14.10 -
   14.11 -public class TwitterProtocolTest {
   14.12 -    private TwitterModel page;
   14.13 -    @Http(@Http.Resource(
   14.14 -        path = "/search.json",
   14.15 -        mimeType = "application/json",
   14.16 -        parameters = {"callback"},
   14.17 -        content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
   14.18 -        + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
   14.19 -        + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
   14.20 -        + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
   14.21 -        + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
   14.22 -        + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
   14.23 -        + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
   14.24 -        + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.25 -        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.26 -        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.27 -        + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
   14.28 -        + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
   14.29 -        + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
   14.30 -        + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
   14.31 -        + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
   14.32 -        + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
   14.33 -        + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
   14.34 -        + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.35 -        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.36 -        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.37 -        + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
   14.38 -        + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
   14.39 -        + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
   14.40 -        + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
   14.41 -        + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
   14.42 -        + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.43 -        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.44 -        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.45 -        + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
   14.46 -        + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
   14.47 -        + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
   14.48 -        + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
   14.49 -        + "{\"result_type\":\"recent\"},\"profile_image_url\":"
   14.50 -        + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.51 -        + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
   14.52 -        + "\"source\":\"&lt;a href=&quot;http:\\/\\/twitter.com\\/&quot;&gt;web&lt;\\/a&gt;\",\"text\":"
   14.53 -        + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
   14.54 -        + "\"since_id\":0,\"since_id_str\":\"0\"})"
   14.55 -    ))
   14.56 -    @BrwsrTest public void readFromTwttr() throws InterruptedException {
   14.57 -        if (page == null) {
   14.58 -            page = new TwitterModel();
   14.59 -            page.applyBindings();
   14.60 -            page.queryTweets("", "q=xyz");
   14.61 -        }
   14.62 -
   14.63 -        if (page.getCurrentTweets().isEmpty()) {
   14.64 -            throw new InterruptedException();
   14.65 -        }
   14.66 -
   14.67 -        assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
   14.68 -        
   14.69 -        String firstDate = page.getCurrentTweets().get(0).getCreated_at();
   14.70 -        assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
   14.71 -    }
   14.72 -    
   14.73 -    @Factory public static Object[] create() {
   14.74 -        return VMTest.create(TwitterProtocolTest.class);
   14.75 -    }
   14.76 -}
    15.1 --- a/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java	Mon Sep 09 15:26:12 2013 +0200
    15.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java	Mon Sep 09 17:34:30 2013 +0200
    15.3 @@ -148,7 +148,7 @@
    15.4          if (classes != null) {
    15.5              l.addClassLoader(classes);
    15.6          }
    15.7 -        l.showDirectory(directory, startpage);
    15.8 +        l.showDirectory(directory, startpage, classes != null);
    15.9          return (Closeable) l;
   15.10      }
   15.11      
   15.12 @@ -172,7 +172,7 @@
   15.13          return Launcher.class.getClassLoader().loadClass(cn);
   15.14      }
   15.15  
   15.16 -    void showDirectory(File directory, String startpage) throws IOException {
   15.17 +    void showDirectory(File directory, String startpage, boolean addClasses) throws IOException {
   15.18          throw new UnsupportedOperationException();
   15.19      }
   15.20  
    16.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Mon Sep 09 15:26:12 2013 +0200
    16.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Mon Sep 09 17:34:30 2013 +0200
    16.3 @@ -125,7 +125,7 @@
    16.4          }
    16.5      }
    16.6  
    16.7 -    void showDirectory(File dir, String startpage) throws IOException {
    16.8 +    void showDirectory(File dir, String startpage, boolean addClasses) throws IOException {
    16.9          if (!startpage.startsWith("/")) {
   16.10              startpage = "/" + startpage;
   16.11          }
   16.12 @@ -134,7 +134,7 @@
   16.13          if (last >= 0) {
   16.14              prefix = startpage.substring(0, last);
   16.15          }
   16.16 -        HttpServer s = initServer(dir.getPath(), false, prefix);
   16.17 +        HttpServer s = initServer(dir.getPath(), addClasses, prefix);
   16.18          try {
   16.19              launchServerAndBrwsr(s, startpage);
   16.20          } catch (Exception ex) {
    17.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java	Mon Sep 09 15:26:12 2013 +0200
    17.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java	Mon Sep 09 17:34:30 2013 +0200
    17.3 @@ -17,11 +17,14 @@
    17.4   */
    17.5  package org.apidesign.bck2brwsr.launcher;
    17.6  
    17.7 +import java.io.File;
    17.8  import org.apidesign.bck2brwsr.launcher.fximpl.FXBrwsr;
    17.9  import java.io.IOException;
   17.10  import java.io.InputStream;
   17.11  import java.lang.reflect.Method;
   17.12 +import java.net.JarURLConnection;
   17.13  import java.net.URI;
   17.14 +import java.net.URISyntaxException;
   17.15  import java.net.URL;
   17.16  import java.net.URLClassLoader;
   17.17  import java.util.ArrayList;
   17.18 @@ -34,6 +37,7 @@
   17.19  import java.util.logging.Logger;
   17.20  import javafx.application.Platform;
   17.21  import org.apidesign.bck2brwsr.launcher.fximpl.JVMBridge;
   17.22 +import org.openide.util.Exceptions;
   17.23  
   17.24  /**
   17.25   *
   17.26 @@ -116,15 +120,29 @@
   17.27          String startPage = null;
   17.28  
   17.29          final ClassLoader cl = FXBrwsrLauncher.class.getClassLoader();
   17.30 -        startPage = findStartPage(cl, startPage);
   17.31 +        URL[] manifestURL = { null };
   17.32 +        startPage = findStartPage(cl, startPage, manifestURL);
   17.33          if (startPage == null) {
   17.34              throw new NullPointerException("Can't find StartPage tag in manifests!");
   17.35          }
   17.36          
   17.37 -        Launcher.showURL("fxbrwsr", cl, startPage);
   17.38 +        File dir = new File(".");
   17.39 +        if (manifestURL[0].getProtocol().equals("jar")) {
   17.40 +            try {
   17.41 +                dir = new File(
   17.42 +                    ((JarURLConnection)manifestURL[0].openConnection()).getJarFileURL().toURI()
   17.43 +                ).getParentFile();
   17.44 +            } catch (URISyntaxException ex) {
   17.45 +                LOG.log(Level.WARNING, "Can't find root directory", ex);
   17.46 +            }
   17.47 +        }
   17.48 +        
   17.49 +        Launcher.showDir("fxbrwsr", dir, cl, startPage);
   17.50      }
   17.51      
   17.52 -    private static String findStartPage(final ClassLoader cl, String startPage) throws IOException {
   17.53 +    private static String findStartPage(
   17.54 +        final ClassLoader cl, String startPage, URL[] startURL
   17.55 +    ) throws IOException {
   17.56          Enumeration<URL> en = cl.getResources("META-INF/MANIFEST.MF");
   17.57          while (en.hasMoreElements()) {
   17.58              URL url = en.nextElement();
   17.59 @@ -139,6 +157,9 @@
   17.60              String sp = mf.getMainAttributes().getValue("StartPage");
   17.61              if (sp != null) {
   17.62                  startPage = sp;
   17.63 +                if (startURL != null) {
   17.64 +                    startURL[0] = url;
   17.65 +                }
   17.66                  break;
   17.67              }
   17.68          }