diff --git a/extensions/jython/module/MOD-INF/controller.js b/extensions/jython/module/MOD-INF/controller.js
index ef26fbaa9..6c4c6de08 100644
--- a/extensions/jython/module/MOD-INF/controller.js
+++ b/extensions/jython/module/MOD-INF/controller.js
@@ -1,5 +1,5 @@
function init() {
- Packages.java.lang.System.err.println("Initializing jython extension");
+ // Packages.java.lang.System.err.println("Initializing jython extension");
Packages.com.metaweb.gridworks.expr.MetaParser.registerLanguageParser(
"jython",
diff --git a/main/.classpath b/main/.classpath
index ab04d6c81..067c64ff7 100644
--- a/main/.classpath
+++ b/main/.classpath
@@ -37,7 +37,7 @@
-
+
diff --git a/main/src/com/metaweb/gridworks/ClientSideResourceManager.java b/main/src/com/metaweb/gridworks/ClientSideResourceManager.java
new file mode 100644
index 000000000..39001c316
--- /dev/null
+++ b/main/src/com/metaweb/gridworks/ClientSideResourceManager.java
@@ -0,0 +1,67 @@
+package com.metaweb.gridworks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import edu.mit.simile.butterfly.ButterflyModule;
+import edu.mit.simile.butterfly.MountPoint;
+
+
+public class ClientSideResourceManager {
+ static public class ClientSideResourceBundle {
+ final protected Set _pathSet = new HashSet();
+ final protected List _pathList = new ArrayList();
+ }
+
+ final static protected Map s_bundles
+ = new HashMap();
+
+ static public void addPaths(
+ String bundleName,
+ ButterflyModule module,
+ String[] paths) {
+
+ ClientSideResourceBundle bundle = s_bundles.get(bundleName);
+ if (bundle == null) {
+ bundle = new ClientSideResourceBundle();
+ s_bundles.put(bundleName, bundle);
+ }
+
+ for (String path : paths) {
+ String fullPath = resolve(module, path);
+ if (!bundle._pathSet.contains(fullPath)) {
+ bundle._pathSet.add(fullPath);
+ bundle._pathList.add(fullPath);
+ }
+ }
+ }
+
+ static public String[] getPaths(String bundleName) {
+ ClientSideResourceBundle bundle = s_bundles.get(bundleName);
+ if (bundle == null) {
+ return new String[] {};
+ } else {
+ String[] paths = new String[bundle._pathList.size()];
+ bundle._pathList.toArray(paths);
+ return paths;
+ }
+ }
+
+ static protected String resolve(ButterflyModule module, String path) {
+ StringBuffer sb = new StringBuffer();
+
+ MountPoint mountPoint = module.getMountPoint();
+
+ boolean slashed = path.startsWith("/");
+ char[] mountPointChars = mountPoint.getMountPoint().toCharArray();
+
+ sb.append(mountPointChars, 0, slashed ? mountPointChars.length - 1 : mountPointChars.length);
+ sb.append(path);
+
+ return sb.toString();
+ }
+}
diff --git a/main/webapp/WEB-INF/lib-src/butterfly-trunk-r20-sources.jar b/main/webapp/WEB-INF/lib-src/butterfly-trunk-r21-sources.jar
similarity index 93%
rename from main/webapp/WEB-INF/lib-src/butterfly-trunk-r20-sources.jar
rename to main/webapp/WEB-INF/lib-src/butterfly-trunk-r21-sources.jar
index b027d44dc..ba34464a9 100644
Binary files a/main/webapp/WEB-INF/lib-src/butterfly-trunk-r20-sources.jar and b/main/webapp/WEB-INF/lib-src/butterfly-trunk-r21-sources.jar differ
diff --git a/main/webapp/WEB-INF/lib/butterfly-trunk-r20.jar b/main/webapp/WEB-INF/lib/butterfly-trunk-r21.jar
similarity index 95%
rename from main/webapp/WEB-INF/lib/butterfly-trunk-r20.jar
rename to main/webapp/WEB-INF/lib/butterfly-trunk-r21.jar
index 945992883..0ebcb5d47 100644
Binary files a/main/webapp/WEB-INF/lib/butterfly-trunk-r20.jar and b/main/webapp/WEB-INF/lib/butterfly-trunk-r21.jar differ
diff --git a/main/webapp/WEB-INF/modules.properties b/main/webapp/WEB-INF/modules.properties
index 8b06ff19a..1b05a14bb 100644
--- a/main/webapp/WEB-INF/modules.properties
+++ b/main/webapp/WEB-INF/modules.properties
@@ -3,6 +3,5 @@
#
core = /
-core.scripting-jython = jython
jython = /extensions/jython/
diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js
new file mode 100644
index 000000000..b6f876bfa
--- /dev/null
+++ b/main/webapp/modules/core/MOD-INF/controller.js
@@ -0,0 +1,131 @@
+var html = "text/html";
+var encoding = "UTF-8";
+var ClientSideResourceManager = Packages.com.metaweb.gridworks.ClientSideResourceManager;
+
+var templatedFiles = {
+ // Requests with last path segments mentioned here
+ // will get served from .vt files with the same names
+ "project" : true
+};
+
+/*
+ * This optional function is invoked from the module's init() Java function.
+ */
+function init() {
+ // Packages.java.lang.System.err.println("Initializing by script " + module);
+
+ ClientSideResourceManager.addPaths(
+ "project/scripts",
+ module,
+ [
+ "externals/jquery-1.4.2.min.js",
+ "externals/jquery.cookie.js",
+ "externals/suggest/suggest-1.2.min.js",
+ "externals/jquery-ui/jquery-ui-1.8.custom.min.js",
+ "externals/imgareaselect/jquery.imgareaselect.js",
+ "externals/date.js",
+
+ "scripts/util/misc.js",
+ "scripts/util/url.js",
+ "scripts/util/string.js",
+ "scripts/util/ajax.js",
+ "scripts/util/menu.js",
+ "scripts/util/dialog.js",
+ "scripts/util/dom.js",
+ "scripts/util/sign.js",
+ "scripts/util/freebase.js",
+ "scripts/util/custom-suggest.js",
+
+ "scripts/widgets/history-widget.js",
+ "scripts/widgets/process-widget.js",
+ "scripts/widgets/histogram-widget.js",
+ "scripts/widgets/slider-widget.js",
+
+ "scripts/project.js",
+ "scripts/project/menu-bar.js",
+ "scripts/project/browsing-engine.js",
+ "scripts/project/scripting.js",
+
+ "scripts/facets/list-facet.js",
+ "scripts/facets/range-facet.js",
+ "scripts/facets/scatterplot-facet.js",
+ "scripts/facets/text-search-facet.js",
+
+ "scripts/views/data-table-view.js",
+ "scripts/views/data-table-cell-ui.js",
+ "scripts/views/data-table-column-header-ui.js",
+
+ "scripts/dialogs/recon-dialog.js",
+ "scripts/dialogs/expression-preview-dialog.js",
+ "scripts/dialogs/freebase-loading-dialog.js",
+ "scripts/dialogs/clustering-dialog.js",
+ "scripts/dialogs/scatterplot-dialog.js",
+ "scripts/dialogs/extend-data-preview-dialog.js",
+ "scripts/dialogs/templating-exporter-dialog.js",
+
+ "scripts/protograph/schema-alignment.js",
+ "scripts/protograph/schema-alignment-ui-node.js",
+ "scripts/protograph/schema-alignment-ui-link.js"
+ ]
+ );
+
+ ClientSideResourceManager.addPaths(
+ "project/styles",
+ module,
+ [
+ "externals/suggest/css/suggest-1.2.min.css",
+ "externals/jquery-ui/css/ui-lightness/jquery-ui-1.8.custom.css",
+ "externals/imgareaselect/css/imgareaselect-default.css",
+
+ "styles/common.css",
+ "styles/jquery-ui-overrides.css",
+
+ "styles/util/menu.css",
+ "styles/util/dialog.css",
+ "styles/util/custom-suggest.css",
+
+ "styles/project.css",
+ "styles/project/browsing.css",
+ "styles/project/process.css",
+ "styles/project/menu-bar.css",
+
+ "styles/widgets/history.css",
+ "styles/widgets/histogram-widget.css",
+ "styles/widgets/slider-widget.css",
+
+ "styles/views/data-table-view.css",
+
+ "styles/dialogs/expression-preview-dialog.css",
+ "styles/dialogs/recon-dialog.css",
+ "styles/dialogs/clustering-dialog.css",
+ "styles/dialogs/scatterplot-dialog.css",
+ "styles/dialogs/freebase-loading-dialog.css",
+ "styles/dialogs/extend-data-preview-dialog.css",
+
+ "styles/protograph/schema-alignment-dialog.css"
+ ]
+ );
+}
+
+/*
+ * This is the function that is invoked by Butterfly
+ */
+function process(path, request, response) {
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length - 1);
+ }
+
+ var slash = path.lastIndexOf("/");
+ var lastSegment = slash >= 0 ? path.substring(slash + 1) : path;
+ if (lastSegment in templatedFiles) {
+ var context = {};
+ context.scripts = ClientSideResourceManager.getPaths(lastSegment + "/scripts");
+ context.styles = ClientSideResourceManager.getPaths(lastSegment + "/styles");
+
+ send(request, response, path + ".vt", context);
+ }
+}
+
+function send(request, response, template, context) {
+ butterfly.sendTextFromTemplate(request, response, context, template, encoding, html);
+}
diff --git a/main/webapp/modules/core/MOD-INF/module.properties b/main/webapp/modules/core/MOD-INF/module.properties
index 9fe6eaa69..18cd4f845 100644
--- a/main/webapp/modules/core/MOD-INF/module.properties
+++ b/main/webapp/modules/core/MOD-INF/module.properties
@@ -1,3 +1,4 @@
description = Gridworks Core Module
-templating = false
+templating.macros = macros.vm
+
diff --git a/main/webapp/modules/core/macros.vm b/main/webapp/modules/core/macros.vm
new file mode 100644
index 000000000..e69de29bb
diff --git a/main/webapp/modules/core/project.html b/main/webapp/modules/core/project.html
deleted file mode 100644
index 86aa86ca3..000000000
--- a/main/webapp/modules/core/project.html
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
- Freebase Gridworks
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
starting up ...
-
-
-
diff --git a/main/webapp/modules/core/project.vt b/main/webapp/modules/core/project.vt
new file mode 100644
index 000000000..9a83d2462
--- /dev/null
+++ b/main/webapp/modules/core/project.vt
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Freebase Gridworks
+
+
+
+
+#foreach($path in $styles)
+
+#end
+#foreach($path in $scripts)
+
+#end
+
+
+
+
+
+
+
starting up ...
+
+
+
diff --git a/main/webapp/modules/core/scripts/index.js b/main/webapp/modules/core/scripts/index.js
index 9e134eec5..3c7dd9f33 100644
--- a/main/webapp/modules/core/scripts/index.js
+++ b/main/webapp/modules/core/scripts/index.js
@@ -112,7 +112,7 @@ function renderProjects(data) {
var nameLink = $('')
.text(project.name)
- .attr("href", "/project.html?project=" + project.id)
+ .attr("href", "/project?project=" + project.id)
.appendTo(tr.insertCell(tr.cells.length));
var renameLink = $('')