Index: Dev/branches/rest-dojo-ui/client/config/config.sh
===================================================================
--- Dev/branches/rest-dojo-ui/client/config/config.sh	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/config/config.sh	(revision 410)
@@ -0,0 +1,2 @@
+#!/bin/sh
+node ../dojo/dojo.js load=config/db
Index: Dev/branches/rest-dojo-ui/client/config/db.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/config/db.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/config/db.js	(revision 410)
@@ -1,134 +1,139 @@
-require([
-         'dojo/_base/json',
-         'dojo/_base/lang',
-         'dojo/_base/xhr',
-         'dojo/parser',
-         'dojo/query',
-         'dijit/registry',
-         'dojo/domReady!'
-],function(json,lang,xhr,parser,query,registry){
-    var logNode;
-    var usernameInput, passwordInput, resetInput, configButton;
-    var defaultUsername = "", defaultPassword = "";
-    var dbUrl = "../data/";
+define([
+     'dojo/_base/json',
+     'dojo/_base/lang',
+     'dojo/Deferred',
+     'dojo/request',
+     'dojox/lang/functional',
+     './docs'
+],function(json,lang,Deferred,request,func,docs){
+    var dbUrl = "http://localhost:5984/";
 
-    parser.parse()
-    .then(function(){
-        query("#log").forEach(function(n){logNode = n;});
-        usernameInput = registry.byId('username');
-        passwordInput = registry.byId('password');
-        resetInput = registry.byId('reset');
-        configButton = registry.byId('configure');
-
-        usernameInput.set('value',defaultUsername);
-        passwordInput.set('value',defaultPassword);
-        configButton.on('click',configure);
-
-        log("Give CouchDB admin username & password and click 'Configure' to start.\nIf the database already exists, rft_admin password will suffice.",true);
-    });
-
-    function configure(){
-        var docs;
-
-        log("Configuring CouchDB for RFT:",true);
-
-        var username = usernameInput.get('value');
-        var password = passwordInput.get('value');
-        var reset = resetInput.get('value');
-
-        log("Downloading most recent configuration.");
-        xhr('GET',{
-            url: 'docs.json',
-            handleAs: 'json',
-            sync: true
-        },true)
-        .then(function(result){
-            docs = result;
-        });
-
-        function req(method,url,body) {
-            var args = {
-                url: dbUrl+url,
-                contentType: 'application/json',
-                handleAs: 'json',
-                sync: true
-            };
-            if ( !body || lang.isObject(body) ) {
-                body = json.toJson(body || {});
-            }
-            args.rawBody = body;
-            if ( username ) {
-                args.user = username;
-                args.password = password;
-            }
-            return xhr(method,args,true);
+    function serializeFunctions(value) {
+        if ( value === null ) {
+            return null;
+        } else if ( lang.isArray(value) ) {
+            return value;
+        } else if ( lang.isFunction(value) ) {
+            return value.toString();
+        } else if ( lang.isObject(value) ) {
+            return func.mapIn(value,serializeFunctions);
+        } else {
+            return value;
         }
-
-        log("Checking CouchDB version");
-        req('GET','/')
-        .then(function(res){
-            if (res.version !== "1.2.0" ) {
-                log("Found "+res.version+", only tested with CouchDB 1.2.0")
-            } else {
-                log("CouchDB 1.2.0 found")
-            }
-        });
-
-        var exists = false;
-        log("Checking database 'rft'");
-        req('GET','/rft')
-        .then(function(db){
-            log("Database 'rft' found.")
-            exists = true;
-        });
-        if ( exists && reset ) {
-            req('DELETE','/rft');
-            exists = false;
-        }
-        if ( !exists ) {
-            log("Creating database 'rft'")
-            req('PUT','/rft')
-        };
-
-        function processDoc(docUrl,doc){
-            var configAction = doc.__configAction;
-            delete doc.__configAction;
-            switch (configAction) {
-                case "ignore":
-                    log(docUrl+" ignored.");
-                    return false;
-                case "update":
-                    log(docUrl+" updating.");
-                    return req('GET',docUrl)
-                    .then(function(oldDoc){
-                        lang.mixin(oldDoc,doc);
-                        return req('PUT',docUrl,oldDoc);
-                    },function(){
-                        return req('PUT',docUrl,doc);
-                    });
-                case "replace":
-                default:
-                    log(docUrl+" replacing.");
-                    return req('GET',docUrl)
-                    .then(function(oldDoc){
-                        doc['_rev'] = oldDoc['_rev'];
-                        return req('PUT',docUrl,doc);
-                    },function(){
-                        return req('PUT',docUrl,doc);
-                    });
-            }
-        }
-
-        for (var docUrl in docs) {
-            processDoc(docUrl,docs[docUrl]);
-        }
-
-        log("Done!");
     }
 
-    function log(text,overwrite) {
-        if ( overwrite ) logNode.innerHTML = text
-        else logNode.innerHTML = logNode.innerHTML + '\n' + text;
+    function req(method,path,body) {
+        var args = {
+            contentType: 'application/json',
+            handleAs: 'json',
+            data: json.toJson(body || {})
+        };
+        return request[method](dbUrl+path,args);
     }
 
+    function asyncSeq(functions) {
+        var d = new Deferred();
+        (function stepper(fs,arg) {
+            if ( fs.length > 0 ) {
+                try {
+                    var f = fs.shift();
+                    var ret = f(arg);
+                    if ( ret && ret.then ) {
+                        ret.then(function(ret){
+                            stepper(fs,ret);
+                        });
+                    } else {
+                        stepper(fs,ret);
+                    }
+                } catch(err) {
+                    d.reject(err);
+                }
+            } else {
+                d.resolve();
+            }
+        })(functions);
+        return d.promise;
+    }
+
+    function asyncFor(list,callback,ctx) {
+        var d = new Deferred();
+        (function stepper(list,idx){
+            if ( list.length > 0 ) {
+                var el = list.shift();
+                var ret = callback.call(ctx,el,idx);
+                if ( ret && ret.then ) {
+                    ret.then(function(){
+                        stepper(list,idx+1);
+                    });
+                } else {
+                    stepper(list,idx+1);
+                }
+            } else {
+                d.resolve();
+            }
+        })(list,0);
+        return d.promise;
+    }
+
+    asyncSeq([
+        function(){
+            console.log("Configuring CouchDB for RFT:");
+        },
+        function(){
+            console.log("Checking CouchDB version");
+            return req('get','')
+            .then(function(res){
+                if (res.version !== "1.2.0" ) {
+                    console.log("Found "+res.version+", only tested with CouchDB 1.2.0")
+                } else {
+                    console.log("CouchDB 1.2.0 found");
+                }
+            });
+        },function(){
+            console.log("Checking database 'rft'");
+            return req('get','rft')
+            .then(function(){
+                console.log("Database 'rft' found.");
+            },function(){
+                console.log("Creating database 'rft'");
+                return req('put','/rft');
+            });
+        },function(){
+            return serializeFunctions(docs);
+        },function(docs){
+            console.log("Putting documents in database.");
+            return asyncFor(func.keys(docs),function(docUrl){
+                var doc = docs[docUrl];
+                var configAction = doc.__configAction;
+                delete doc.__configAction;
+                switch (configAction) {
+                    case "ignore":
+                        console.log(docUrl+" ignored.");
+                    case "update":
+                        console.log(docUrl+" updating.");
+                        return req('get',docUrl)
+                        .then(function(oldDoc){
+                            lang.mixin(oldDoc,doc);
+                            return req('put',docUrl,oldDoc);
+                        },function(){
+                            return req('put',docUrl,doc);
+                        });
+                    case "replace":
+                    default:
+                        console.log(docUrl+" replacing.");
+                        return req('get',docUrl)
+                        .then(function(oldDoc){
+                            doc['_rev'] = oldDoc['_rev'];
+                            return req('put',docUrl,doc);
+                        },function(){
+                            return req('put',docUrl,doc);
+                        });
+                }
+            });
+        },function(){
+            console.log("Done!");
+        }
+    ]);
+
+
 });
Index: Dev/branches/rest-dojo-ui/client/config/docs.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/config/docs.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/config/docs.js	(revision 410)
@@ -0,0 +1,107 @@
+define([],function(){
+    return {
+        "_users/rft_admin": {
+            __configAction: "ignore",
+            _id: "org.couchdb.user:rft_admin",
+            name: "rft_admin",
+            password: "Welkom01",
+            roles: [ "rft_admin" ],
+            type: "user"
+        },
+        "rft/_security": {
+            __configAction: "ignore",
+            admins: {
+                names: [],
+                roles: ["rft_admin"]
+            },
+            readers: {
+                names: [],
+                roles: ["rft_user"]
+            }
+        },
+        "rft/_design/default": {
+            __configAction: "replace",
+            _id: "_design/default",
+            language: "javascript",
+            validate_doc_update: function(newDoc, oldDoc, userCtx, secObj) {
+                if ( oldDoc && oldDoc.publicationDate ) { throw({forbidden:'Published documents cannot be modified.'}); }
+                if ( !newDoc._deleted && !newDoc.type ) { throw({forbidden:'Documents must have a type field.'}); }
+            },
+            views: {
+                by_type: {
+                    map: function(doc){
+                        emit(doc.type, doc);
+                    }
+                }
+            }
+        },
+        "rft/_design/questions": {
+            __configAction: "replace",
+            _id: "_design/questions",
+            language: "javascript",
+            validate_doc_update: function(newDoc, oldDoc, userCtx, secObj) {
+                if( newDoc._deleted || newDoc.type!=='Question' ){ return; }
+                if( !newDoc.code ){ throw({forbidden:'Question must have a code field.'});}
+            },
+            views: {
+                all: {
+                    map: function(doc){
+                        if ( doc.type !== 'Question' ) { return; }
+                        if ( doc.categories && doc.categories.length > 0 ) {
+                            for ( var i = 0; i < doc.categories.length; i++ ) { 
+                                emit([doc.categories[i],doc.topic||null],1);
+                            } 
+                        } else { 
+                            emit([null,null],1); 
+                        } 
+                    },
+                    reduce: function(keys,values){ return sum(values); }
+                },
+                published: {
+                    map: function(doc){
+                        if ( doc.type!=='Question' || !doc.publicationDate ) { return; }
+                        if ( doc.categories && doc.categories.length > 0 ) {
+                            for ( var i = 0; i < doc.categories.length; i++ ) {
+                                emit([doc.categories[i],doc.topic||null],1);
+                            }
+                        } else {
+                            emit([null,null],1);
+                        }
+                    },
+                    reduce: function(keys,values){ return sum(values); }
+                },
+                all_topics: {
+                    map: function(doc){
+                        if( doc.type !== 'Question' ){ return; }
+                        emit(doc.topic);
+                    }
+                },
+                published_topics: {
+                    map: function(doc){
+                        if ( doc.type !== 'Question' || !doc.publicationDate ) { return; }
+                        emit(doc.topic);
+                    }
+                }
+            }
+        },
+        "rft/_design/surveys": {
+            __configAction: "replace",
+            _id: "_design/surveys",
+            language: "javascript",
+            views: {
+                drafts: {
+                    map: function(doc){
+                        if ( doc.type !== 'Survey' || doc.publicationDate ) { return; }
+                        emit(doc._id,doc);
+                    }
+                },
+                published: {
+                    map: function(doc){
+                        if ( doc.type !== 'Survey' || !doc.publicationDate ) { return; }
+                        emit(doc._id,doc);
+                    }
+                }
+            }
+        }
+    };
+});
Index: Dev/branches/rest-dojo-ui/client/config/docs.json
===================================================================
--- Dev/branches/rest-dojo-ui/client/config/docs.json	(revision 409)
+++ 	(revision )
@@ -1,43 +1,0 @@
-{
-    "_users/rft_admin": {
-        "__configAction": "ignore",
-        "_id": "org.couchdb.user:rft_admin",
-        "name": "rft_admin",
-        "password": "Welkom01",
-        "roles": [ "rft_admin" ],
-        "type": "user"
-    },
-    "rft/_security": {
-        "__configAction": "ignore",
-        "admins" : {
-            "names" : [],
-            "roles" : ["rft_admin"]
-        },
-        "readers" : {
-            "names" : [],
-            "roles" : ["rft_user"]
-        }
-    },
-    "rft/_design/default": {
-        "__configAction": "replace",
-        "_id": "_design/default",
-        "language": "javascript",
-        "validate_doc_update": "function(newDoc, oldDoc, userCtx, secObj) { if(oldDoc&&oldDoc.published){throw({forbidden:'Published documents cannot be modified.'});} if (!newDoc.type){throw({forbidden:'Documents must have a type field.'});} }",
-        "views": {
-            "by_type": {
-                "map": "function(doc){ emit(doc.type, doc); }"
-            },
-            "unpublished": {
-                "map": "function(doc){ if ( doc.type === 'Survey' && !doc.published ) { emit(doc._id, doc); } }"
-            },
-            "questions": {
-                "map": "function(doc){ if ( doc.type === 'Question' ) { if ( doc.categories && doc.categories.length > 0 ) { for (var i = 0; i < doc.categories.length; i++) { emit([doc.categories[i],doc.topic || null],1); } } else { emit([null,null],1); } } }",
-                "reduce": "function(keys,values) { return sum(values); }"
-            },
-            "topics": {
-                "map": "function(doc){ if ( doc.type === 'Question' ) { emit(doc.topic, 1); } }",
-                "reduce":   "function(keys, values) { return null; }"
-            }
-        }
-    }
-}
Index: Dev/branches/rest-dojo-ui/client/config/index.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/config/index.html	(revision 409)
+++ 	(revision )
@@ -1,26 +1,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Research Facilitator Tool - CouchDB Configure</title>
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-        <link rel="stylesheet" type="text/css" href="../dijit/themes/claro/claro.css" />
-        <style>
-            label {
-                width: 200px;
-                display: inline-block;
-            }
-        </style>
-    </head>
-    <body class="claro">
-        <script type="text/javascript" src="../dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
-        <script type="text/javascript" src="db.js"></script>
-        <h1>Configure CouchDB</h1>
-        <div>
-            <label for="username">Admin username</label><input type="text" id="username" data-dojo-type="dijit/form/TextBox"/><br/>
-            <label for="password">Admin password</label><input type="password" id="password" data-dojo-type="dijit/form/TextBox"/><br/>
-            <label for="reset">Full reset</label><input type="checkbox" id="reset" data-dojo-type="dijit/form/CheckBox"/><br/>
-            <button id="configure" data-dojo-type="dijit/form/Button">Configure</button><br/>
-        </div>
-        <pre id="log">Waiting for configurator to initialize...</pre>
-    </body>
-</html>
Index: Dev/branches/rest-dojo-ui/client/data/.htaccess
===================================================================
--- Dev/branches/rest-dojo-ui/client/data/.htaccess	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/data/.htaccess	(revision 410)
@@ -1,2 +1,3 @@
 RewriteEngine on
-RewriteRule (.*) http://localhost:5984/$1 [P,QSA]
+RewriteRule couch/(.*) http://localhost:5984/rft/$1 [P,QSA]
+RewriteRule elastic http://localhost:9200/rft/_search [P,QSA]
Index: Dev/branches/rest-dojo-ui/client/index.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/index.html	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/index.html	(revision 410)
@@ -15,6 +15,5 @@
             </div>
         </div>
-        <div id="toaster" data-dojo-type="rft/ui/Notifications">
-        </div>
+        <div id="toaster" data-dojo-type="rft/ui/Notifications"></div>
     </body>
 </html>
Index: Dev/branches/rest-dojo-ui/client/rft/api.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/api.js	(revision 409)
+++ 	(revision )
@@ -1,35 +1,0 @@
-define(['dojo/_base/xhr','dojo/_base/json','dojo/_base/connect'],
-    function(xhr,json,connect){
-        return new (function(){
-            var self = this;
-            this.post = function(path,args) {
-                var dfd = xhr.post({
-                    url: "../server/api/"+path,
-                    handleAs: "json",
-                    headers: {
-                        "Content-Type": "application/json"
-                    },
-                    rawBody: json.toJson(args)
-                });
-                dfd.then(
-                    function(){},
-                    self.defaultErrorHandler);
-                return dfd;
-            };
-            this.defaultErrorHandler = function(error) {
-                var msg = "An error occured!";
-                try {
-                    msg = json.fromJson(error.responseText).errorMsg;
-                } catch (ex) {
-                    msg = error.message;
-                }
-                self.notify(msg,'error');
-            };
-            this.notify =  function(text,type) {
-                connect.publish('/rft/notify',{
-                    text: text,
-                    type: type
-                });
-            };
-        })();
-    });
Index: Dev/branches/rest-dojo-ui/client/rft/app/Content.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/app/Content.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/app/Content.js	(revision 410)
@@ -0,0 +1,46 @@
+define([
+    'dojo/_base/declare',
+    'dijit/registry'
+],function(declare,registry){
+
+    var Content = declare(null,{
+        _started: false,
+        _container: null,
+        _previousContent: null,
+        startup: function() {
+            if ( this._started ) { return; }
+
+            this._container = registry.byId('content');
+            if ( !this._container || !this._container.addChild ) {
+                throw new Error("Cannot find container widget with id 'content'.");
+            }
+
+            this._toaster = registry.byId('toaster');
+            if ( !this._toaster || !this._toaster.setContent ) {
+                throw new Error("Cannot find toaster widget with id 'toaster'.");
+            }
+
+            this._started = true;
+        },
+        set: function(widget) {
+            if ( !this._started ) {
+                throw new Error('Call startup() before setting content.');
+            }
+            if ( this._previousContent ) {
+                this._previousContent.destroyRecursive();
+                this._previousContent = null;
+            }
+            widget.region = 'center';
+            this._container.addChild(widget);
+            this._previousContent = widget;
+        },
+        notify: function(text,type) {
+            if ( !this._started ) {
+                throw new Error('Call startup() before setting content.');
+            }
+            this._toaster.setContent(text,type || 'message');
+        }
+    });
+
+    return new Content();
+});
Index: Dev/branches/rest-dojo-ui/client/rft/app/Controller.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/app/Controller.js	(revision 409)
+++ 	(revision )
@@ -1,145 +1,0 @@
-define([
-    'dojo/_base/declare',
-    'dojo/hash',
-    'dojo/io-query',
-    'dojo/topic',
-    './Page',
-    'dijit/registry'
-],function(declare,hash,ioQuery,topic,Page,registry){
-
-
-    var Controller = declare(null,{
-        _started: false,
-        _routes: null,
-        _container: null,
-        _previousHash: null,
-        _previousContent: null,
-
-        _paramMatch: /:(\w[\w\d]*)/g,
-        _paramReplace: "([^\\/]+)",
-
-        constructor: function() {
-            this._routes = [];
-        },
-        startup: function() {
-            if ( this._started ) { return; }
-
-            var self = this;
-
-            this._container = registry.byId('content');
-            if ( !this._container || !this._container.addChild ) {
-                throw new Error("Cannot find container widget with id 'content'.");
-            }
-            this._started = true;
-
-            if ( hash() === "" ) {
-                hash("#!/");
-            }
-            this._handlePathChange(hash());
-			topic.subscribe("/dojo/hashchange", function(){
-                self._handlePathChange.apply(self,arguments);
-            });
-        },
-        register: function(route) {
-            var self = this;
-            var callback;
-            if ( route.callback ) {
-                callback = function(params){
-                    self._setContent(route.callback(params));
-                };
-            } else if ( route.redirect ) {
-                callback = function(params){
-                    self.go(route.redirect);
-                };
-            } else if ( route.constructor ) {
-                callback = function(params){
-                    self._setContent( new route.constructor(params) );
-                };
-            }
-            if ( callback ) {
-                if ( route.path ) {
-                    this._routes.push(this._createRoute(route.path,callback));
-                } else {
-                    this._defaultCallback = callback;
-                }
-            } else {
-                console.warn("Route "+(route.path||"default")+" has no action.");
-            }
-        },
-        _createRoute: function(path,callback) {
-            var match, route = {};
-            route.callback = callback;
-            route.paramNames = [];
-			while((match = this._paramMatch.exec(path)) !== null){
-				route.paramNames.push(match[1]);
-			}
-            path = path.replace(this._paramMatch, this._paramReplace);
-            route.regexp = new RegExp('^!'+path+'(!.*)?$');
-            return route;
-        },
-        _handlePathChange: function(newHash) {
-            if ( this._previousHash === newHash ) { return; }
-            this._previousHash = newHash;
-
-            for (var i = this._routes.length-1; i >= 0; i--) {
-                var result;
-                var route = this._routes[i];
-
-                if ((result = route.regexp.exec(newHash)) !== null) {
-                    var numParams = route.paramNames.length;
-
-                    var params = {};
-                    for (var j = 0; j < numParams; j++) {
-                        params[route.paramNames[j]] = result[j+1];
-                    }
-
-                    if ( result.length > numParams+1 ) {
-                        params.options = ioQuery.queryToObject(result[numParams]);
-                    }
-
-                    try {
-                        route.callback(params);
-                    } catch(err) {
-                        console.warn("Page change failed.",err);
-                    }
-                    return;
-                }
-            }
-
-            try {
-                this._defaultCallback();
-            } catch(err) {
-                console.warn("Default page failed.",err);
-            }
-        },
-        go: function(path,args) {
-            if ( !this._started ) { return; }
-            var newHash = this._pathToHash(path,args);
-            this._handlePathChange(newHash);
-            hash(newHash);
-        },
-        _pathToHash: function(path,args) {
-            var hash = '!'+path;
-            if ( args ) {
-                hash += '!'+ioQuery.objectToQuery(args);
-            }
-            return hash;
-        },
-        _setContent: function(widget) {
-            if ( this._previousContent ) {
-                this._previousContent.destroyRecursive();
-                this._previousContent = null;
-            }
-            widget.region = 'center';
-            this._container.addChild(widget);
-            this._previousContent = widget;
-        },
-        _defaultCallback: function() {
-            this._setContent(new Page({
-                templateString: "<div>Requested page not found. Go <a href=\"#!/\">home</a>.</div>"
-            }));
-        }
-    });
-
-    return new Controller();
-});
Index: Dev/branches/rest-dojo-ui/client/rft/app/Router.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/app/Router.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/app/Router.js	(revision 410)
@@ -0,0 +1,134 @@
+define([
+    'dojo/_base/declare',
+    'dojo/hash',
+    'dojo/io-query',
+    'dojo/topic',
+    './Content',
+    './Page'
+],function(declare,hash,ioQuery,topic,Content,Page){
+
+    var Router = declare(null,{
+        _started: false,
+        _routes: null,
+        _previousHash: null,
+
+        _paramMatch: /:(\w[\w\d]*)/g,
+        _paramReplace: "([^\\/!]+)",
+
+        constructor: function() {
+            this._routes = [];
+        },
+        startup: function() {
+            if ( this._started ) { return; }
+            if ( !Content._started ) {
+                Content.startup();
+            }
+            this._started = true;
+
+            var self = this;
+
+            if ( hash() === "" ) {
+                hash("#!/");
+            }
+            this._handlePathChange(hash());
+			topic.subscribe("/dojo/hashchange", function(){
+                self._handlePathChange.apply(self,arguments);
+            });
+        },
+        register: function(route) {
+            if ( this._started ) {
+                console.warn('Registering routes after startup() is called is discouraged.');
+            }
+            var self = this;
+            var callback;
+            if ( route.callback ) {
+                callback = function(params){
+                    Content.set(route.callback(params));
+                };
+            } else if ( route.redirect ) {
+                callback = function(params){
+                    self.go(route.redirect);
+                };
+            } else if ( route.constructor ) {
+                callback = function(params){
+                    Content.set( new route.constructor(params) );
+                };
+            }
+            if ( callback ) {
+                if ( route.path ) {
+                    this._routes.push(this._createRoute(route.path,callback));
+                } else {
+                    this._defaultCallback = callback;
+                }
+            } else {
+                console.warn("Route "+(route.path||"default")+" has no action.");
+            }
+        },
+        _createRoute: function(path,callback) {
+            var match, route = {};
+            route.callback = callback;
+            route.paramNames = [];
+			while((match = this._paramMatch.exec(path)) !== null){
+				route.paramNames.push(match[1]);
+			}
+            path = path.replace(this._paramMatch, this._paramReplace);
+            route.regexp = new RegExp('^!'+path+'(!(.*))?$');
+            return route;
+        },
+        _handlePathChange: function(newHash) {
+            if ( this._previousHash === newHash ) { return; }
+            this._previousHash = newHash;
+
+            for (var i = this._routes.length-1; i >= 0; i--) {
+                var result;
+                var route = this._routes[i];
+
+                if ((result = route.regexp.exec(newHash)) !== null) {
+                    var numParams = route.paramNames.length;
+
+                    var params = {};
+                    for (var j = 0; j < numParams; j++) {
+                        params[route.paramNames[j]] = result[j+1];
+                    }
+
+                    if ( result.length > numParams+1 && result[numParams+2] ) {
+                        params.options = ioQuery.queryToObject(result[numParams+2]);
+                    }
+
+                    try {
+                        route.callback(params);
+                    } catch(err) {
+                        console.warn("Page change failed.",err);
+                    }
+                    return;
+                }
+            }
+
+            try {
+                this._defaultCallback();
+            } catch(err) {
+                console.warn("Default page failed.",err);
+            }
+        },
+        go: function(path,args) {
+            if ( !this._started ) { return; }
+            var newHash = this._pathToHash(path,args);
+            this._handlePathChange(newHash);
+            hash(newHash);
+        },
+        _pathToHash: function(path,args) {
+            var hash = '!'+path;
+            if ( args ) {
+                hash += '!'+ioQuery.objectToQuery(args);
+            }
+            return hash;
+        },
+        _defaultCallback: function() {
+            Content.set(new Page({
+                templateString: "<div>Requested page not found. Go <a href=\"#!/\">home</a>.</div>"
+            }));
+        }
+    });
+
+    return new Router();
+});
Index: Dev/branches/rest-dojo-ui/client/rft/auth.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/auth.js	(revision 409)
+++ 	(revision )
@@ -1,70 +1,0 @@
-define(['dojo/_base/Deferred','dojo/_base/xhr','dojo/_base/json'],
-    function(Deferred,xhr,json){
-        return new (function() {
-            var self = this;
-            var currentUser = null;
-
-            function post(path,args) {
-                return xhr.post({
-                    url: "../server/api"+path,
-                    handleAs: "json",
-                    headers: {
-                        "Content-Type": "application/json"
-                    },
-                    rawBody: json.toJson(args)
-                });
-            }
-
-            self.login = function(username,password) {
-                var d = new Deferred();
-                /*post("/login",
-                {
-                    email:username,
-                    password:password
-                })
-                .then(function(data) {
-                    currentUser = data;
-                    d.resolve(data);
-                },function(){
-                    d.reject(); 
-                });*/
-                d.resolve();
-                return d.promise;
-            };
-
-            self.restore = function() {
-                var d = new Deferred();
-                /*post("/login",{})
-                .then(function(data) {
-                    currentUser = data;
-                    d.resolve(data);
-                },function(){
-                    d.reject(); 
-                });*/
-                d.resolve();
-                return d.promise;
-            
-            };
-
-            self.register = function(username,password) {
-                var d = new Deferred();
-                /*post("/register",
-                {
-                    email:username,
-                    password:password
-                })
-                .then(function(data) {
-                    currentUser = data;
-                    d.resolve(data);
-                },function(){
-                    d.reject(); 
-                });*/
-                d.resolve();
-                return d.promise;
-            };
-            
-            self.getUser = function(){
-                return currentUser;
-            };
-        })();
-    });
Index: Dev/branches/rest-dojo-ui/client/rft/pages/index.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/index.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/index.js	(revision 410)
@@ -1,8 +1,8 @@
 define([
     'dojo/_base/declare',
-    '../app/Controller',
+    '../app/Router',
     '../app/Page',
     'dojo/text!./index.html'
-],function(declare,Controller,Page,template){
+],function(declare,Router,Page,template){
     return declare([Page],{
         templateString: template,
@@ -11,11 +11,11 @@
             if ( this._started ) { return; }
             this.inherited(arguments);
-            this.btnContentCreate.on("click",function(){ Controller.go("/sessions"); });
-            this.btnContentFacilitate.on("click",function(){ Controller.go("/run"); });
-            this.btnSurveys.on("click",function(){ Controller.go("/surveys"); });
-            this.btnQuestions.on("click",function(){ Controller.go("/questions"); });
-            this.btnApplications.on("click",function(){ Controller.go("/applications"); });
-            this.btnDashboards.on("click",function(){ Controller.go("/dashboards"); });
-            this.btnResults.on("click",function(){ Controller.go("/results"); });
+            this.btnContentCreate.on("click",function(){ Router.go("/sessions"); });
+            this.btnContentFacilitate.on("click",function(){ Router.go("/run"); });
+            this.btnSurveys.on("click",function(){ Router.go("/surveys"); });
+            this.btnQuestions.on("click",function(){ Router.go("/questions"); });
+            this.btnApplications.on("click",function(){ Router.go("/applications"); });
+            this.btnDashboards.on("click",function(){ Router.go("/dashboards"); });
+            this.btnResults.on("click",function(){ Router.go("/results"); });
         }
     });
Index: Dev/branches/rest-dojo-ui/client/rft/pages/question.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/question.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/question.js	(revision 410)
@@ -5,10 +5,11 @@
     'dojo/_base/lang',
     '../store',
-    '../app/Controller',
+    '../app/Content',
+    '../app/Router',
     '../app/Page',
     '../ui/QuestionEditorPreview',
     '../ui/QuestionEditorToolkit',
     'dojo/text!./question.html'
-],function(declare, Deferred, event, lang, store, Controller, Page, QuestionEditorPreview, QuestionEditorToolkit, template){
+],function(declare, Deferred, event, lang, store, Content, Router, Page, QuestionEditorPreview, QuestionEditorToolkit, template){
     return declare([Page], {
         templateString: template,
@@ -43,5 +44,7 @@
             store.put(this.question)
             .then(function() {
-                Controller.go('/questions');
+                Router.go('/questions');
+            },function(err){
+                Content.notify(err.reason,'error');
             });
             evt && event.stop( evt );
@@ -49,5 +52,5 @@
         },
         _onDiscard: function() {
-            Controller.go('/questions');
+            Router.go('/questions');
             return true;
         },
Index: Dev/branches/rest-dojo-ui/client/rft/pages/questions.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/questions.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/questions.js	(revision 410)
@@ -4,10 +4,11 @@
     'dojo/_base/event',
     'dojo/_base/lang',
-    '../app/Controller',
     '../store',
+    '../app/Content',
+    '../app/Router',
     '../app/Page',
     '../ui/TabbedQuestionBrowser',
     'dojo/text!./questions.html'
-],function(declare,Deferred,event,lang,Controller,store,Page,TabbedQuestionBrowser,template) {
+],function(declare,Deferred,event,lang,store,Content,Router,Page,TabbedQuestionBrowser,template) {
     return declare([Page],{
         templateString: template,
@@ -19,18 +20,17 @@
                 'class': 'blue',
                 itemActions: {
-                    'Edit': {
+                    Edit: {
                         callback: lang.hitch(this,"onEditQuestion"),
-                           icon: 'Edit',
-                           description: 'Edit question'
+                        icon: 'Edit',
+                        description: 'Edit question'
+                    },
+                    Publish: {
+                        callback: lang.hitch(this,"onPublishQuestion"),
+                        icon: 'Publish',
+                        description: 'Publish question'
                     }
                 }
             },this.questionBrowser);
             this.questionBrowser.startup();
-        },
-        _refresh: function() {
-            Deferred.when(store.query('_design/default/_view/by_type',{key: 'Question'}))
-            .then(lang.hitch(this,function(items){
-                this._list.setItems(items);
-            }));
         },
         onNewQuestion: function() {
@@ -41,5 +41,14 @@
         },
         onEditQuestion: function(question) {
-            Controller.go("/question/"+question._id);
+            Router.go("/question/"+question._id);
+        },
+        onPublishQuestion: function(question) {
+            question.publicationDate = store.timestamp();
+            store.put(question)
+            .then(function(){
+                Content.notify("Question puplished.");
+            },function(err){
+                Content.notify(err.reason,'error');
+            });
         }
     });
Index: Dev/branches/rest-dojo-ui/client/rft/pages/session.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/session.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/session.js	(revision 410)
@@ -2,15 +2,15 @@
     'dojo/_base/array',
     'dojo/_base/declare',
+    'dojo/_base/Deferred',
+    'dojo/_base/event',
     'dojo/_base/lang',
-    'dojo/_base/event',
-    'dojo/_base/Deferred',
-    '../app/Controller',
+    '../search',
     '../store',
-    '../elastic/ElasticSearchFilteringSelect',
-    '../elastic/ElasticReadStore',
     '../app/Page',
+    '../app/Router',
+    '../ui/ThresholdFilteringSelect',
     '../ui/lists/AccountListView',
     'dojo/text!./session.html'
-],function(array,declare,lang,event,Deferred,ElasticSearchFilteringSelect,ElasticReadStore,store,Page,Controller,AccountListView,template){
+],function(array,declare,Deferred,event,lang,search,store,Page,Router,ThresholdFilteringSelect,AccountListView,template){
     return declare([Page],{
         templateString: template,
@@ -48,5 +48,5 @@
             store.put(this.session)
             .then(function(){
-                Controller.go('/sessions');
+                Router.go('/sessions');
             });
             event.stop(evt);
@@ -56,5 +56,5 @@
             this.propertiesForm.reset();
             event.stop(evt);
-            Controller.go('/sessions');
+            Router.go('/sessions');
             return false;
         },
@@ -70,10 +70,6 @@
         },
         _setupAutoComplete: function() {
-            var accountStore = new ElasticReadStore({
-                url: "http://localhost:9200/rft/_search",
-                requestMethod: "POST"
-            });
-            this._select = new ElasticSearchFilteringSelect({
-                store: accountStore,
+            this._select = new ThresholdFilteringSelect({
+                store: search,
                 autoComplete: false,
                 required: false,
Index: Dev/branches/rest-dojo-ui/client/rft/pages/sessions.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/sessions.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/sessions.js	(revision 410)
@@ -4,9 +4,9 @@
     'dojo/date/stamp',
     '../store',
-    '../app/Controller',
+    '../app/Router',
     '../app/Page',
     '../ui/ObjectBox',
     'dojo/text!./sessions.html'
-],function(declare,lang,dateStamp,store,Controller,Page,ObjectBox,template){
+],function(declare,lang,dateStamp,store,Router,Page,ObjectBox,template){
     return declare([Page],{
         templateString: template,
@@ -18,5 +18,5 @@
             this.templateActions = {
                 "Edit": function(obj){
-                    Controller.go('/session/'+store.getIdentity(obj));
+                    Router.go('/session/'+store.getIdentity(obj));
                 },
                 "Delete": lang.hitch(this,function(obj){
@@ -30,5 +30,5 @@
             this.sessionActions = {
                 "Facilitate": function(obj){
-                    Controller.go('run',{uid:store.getIdentity(obj)});
+                    Router.go('run',{uid:store.getIdentity(obj)});
                 },
                 "Delete": lang.hitch(this,function(obj){
@@ -62,5 +62,5 @@
             })
             .then(lang.hitch(this,function(obj){
-                Controller.go('/session/'+store.getIdentity(obj));
+                Router.go('/session/'+store.getIdentity(obj));
             }));
         },
@@ -70,5 +70,5 @@
             delete session[store.revProperty];
             session.type = "SessionInstance";
-            session.publishedDate = dateStamp.toISOString(new Date(),{zulu: true});
+            session.publicationDate = store.timestamp();
             session.creator = "Igor Mayer";
             store.add(session)
Index: Dev/branches/rest-dojo-ui/client/rft/pages/survey.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/survey.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/survey.js	(revision 410)
@@ -5,5 +5,5 @@
     'dojo/_base/event',
     'dojo/_base/lang',
-    '../app/Controller',
+    '../app/Router',
     '../store',
     '../app/Page',
@@ -11,5 +11,5 @@
     '../ui/TabbedQuestionBrowser',
     'dojo/text!./survey.html'
-],function(array,declare,Deferred,event,lang,Controller,store,Page,
+],function(array,declare,Deferred,event,lang,Router,store,Page,
          QuestionListView,TabbedQuestionBrowser,template){
     return declare([Page],{
@@ -27,11 +27,5 @@
                 this._setupQuestionBrowser();
                 this._setupListView();
-                Deferred.when(store.get(this.surveyId))
-                .then(lang.hitch(this,function(obj){
-                    this.survey = obj;
-                    store.query(null,{keys:this.survey.questions,include_docs:true})
-                    .forEach(lang.hitch(this.questionList,'appendItem'));
-                    this.refresh();
-                }));
+                this._setupSurvey();
             } else {
                 throw "No valid uid or survey passed!";
@@ -42,4 +36,5 @@
                 region: 'center',
                 'class': 'blue',
+                include: 'published',
                 selectedActions: {
                     "Include": {
@@ -59,7 +54,4 @@
             this.questionBrowser.startup();
         },
-        _includeQuestion: function(question) {
-            this.questionList.insertItem(question);
-        },
         _setupListView: function() {
             this.questionList = new QuestionListView({
@@ -67,4 +59,16 @@
             },this.surveyListViewNode);
             this.questionList.startup();
+        },
+        _setupSurvey: function() {
+            Deferred.when(store.get(this.surveyId))
+            .then(lang.hitch(this,function(survey){
+                this.survey = survey;
+                store.query(null,{keys:this.survey.questions || [], include_docs: true})
+                .forEach(lang.hitch(this.questionList,'appendItem'));
+                this.refresh();
+            }));
+        },
+        _includeQuestion: function(question) {
+            this.questionList.insertItem(question);
         },
         refresh: function() {
@@ -94,5 +98,5 @@
             store.put(this.survey)
             .then(function() { 
-                Controller.go('/surveys');
+                Router.go('/surveys');
             });
             event.stop(evt);
@@ -100,7 +104,10 @@
         },
         _onDiscard: function(evt) {
+            Router.go('/surveys');
         },
         _onShowPreview: function() {
-            Controller.go('/viewSurvey/'+store.getIdentity(this.survey));
+            Router.go('/viewSurvey/'+store.getIdentity(this.survey),{
+                preview: true
+            });
         }
     });
Index: Dev/branches/rest-dojo-ui/client/rft/pages/surveys.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/surveys.html	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/surveys.html	(revision 410)
@@ -1,7 +1,35 @@
 <div>
-    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">
-        <button data-dojo-type="dijit/form/Button" class="blue" data-dojo-props="disabled: true, baseClass: 'rftBlockButton', iconClass: 'rftIcon rftIconEdit'" data-dojo-attach-point="btnEdit">Edit</button>
-        <button data-dojo-type="dijit/form/Button" class="blue" data-dojo-props="baseClass: 'rftBlockButton', iconClass: 'rftIcon rftIconPlus'" data-dojo-attach-point="btnNew">New</button>
-        <div data-dojo-type="dojox/grid/DataGrid" data-dojo-props="autoWidth:true,autoHeight:true,structure:[{name:'Title',field:'title'}]" data-dojo-attach-point="grid"></div>
+
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
+        <h2>
+            <span class="rftIcon rftIconSurvey"></span>
+            <span class="headerText">Surveys</span>
+        </h2>
     </div>
+
+    <div data-dojo-attach-point="tabContainer" data-dojo-type="dijit/layout/TabContainer" class="blue" data-dojo-props="tabPosition:'left-h',region:'center'">
+
+        <div data-dojo-type="dijit/layout/BorderContainer" title="Drafts" data-dojo-attach-point="draftsTab">
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'" data-dojo-attach-point="draftsContainer">
+            </div>
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'" style="height: 40px;">
+                <button data-dojo-type="dijit/form/Button" class="blue" data-dojo-props="baseClass: 'rftBlockButton', iconClass: 'rftIcon rftIconNew'" data-dojo-attach-event="onClick:_onNewSurvey">New survey</button>
+            </div>
+        </div>
+
+        <div data-dojo-type="dijit/layout/BorderContainer" title="Published" data-dojo-attach-point="publishedTab">
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'" data-dojo-attach-point="publishedContainer">
+            </div>
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'" style="height: 40px;">
+            </div>
+        </div>
+
+        <div data-dojo-type="dijit/layout/BorderContainer" title="Runs" data-dojo-attach-point="runsTab">
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'" data-dojo-attach-point="runsContainer">
+            </div>
+            <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'" style="height: 40px;">
+            </div>
+        </div>
+    </div>
+
 </div>
Index: Dev/branches/rest-dojo-ui/client/rft/pages/surveys.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/surveys.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/surveys.js	(revision 410)
@@ -1,45 +1,149 @@
 define([
+    'dojo/_base/array',
     'dojo/_base/declare',
     'dojo/_base/lang',
-    'dojo/_base/Deferred',
-    'dojo/data/ObjectStore',
-    '../auth',
+    'dojo/when',
     '../store',
-    '../app/Controller',
+    '../app/Content',
     '../app/Page',
+    '../app/Router',
+    '../ui/LineWithActionsWidget',
     'dojo/text!./surveys.html'
-],function(declare,lang,Deferred,ObjectStore,auth,store,Controller,Page,template){
+],function(array,declare,lang,when,store,Content,Page,Router,LineWithActionsWidget,template){
     return declare([Page],{
         templateString: template,
-        selectedObject: null,
         startup: function() {
             if ( this._started ) { return; }
             this.inherited(arguments);
-            this.grid.setStore(
-                ObjectStore({objectStore: store}),
-                "_design/default/_view/by_type",{key:'Survey'});
-            
-            this.grid.on('rowclick',lang.hitch(this,function(evt){
-                this.selectedObject = evt.grid.getItem(evt.rowIndex);
-                this.btnEdit.set('disabled',!this.selectedObject);
+            this.refresh();
+        },
+        _onNewSurvey: function(){
+            when( store.add({type:'Survey'}) )
+            .then(function(survey) {
+                Router.go('/survey/'+store.getIdentity(survey));
+            });
+        },
+        _onPublishSurvey:function(survey){
+            var self = this;
+            survey.publicationDate = store.timestamp();
+            store.put(survey).then(function(){
+                self.refreshDrafts();
+                self.refreshPublished();
+            },function(err){
+                Content.notify(err.reason,'error');
+            });
+        },
+        _onDeleteSurvey:function(survey){
+            var self = this;
+            store.remove(store.getIdentity(survey),store.getRevision(survey))
+            .then(function(){
+                self.refreshDrafts();
+            },function(err){
+                Content.notify(err.reason,'error');
+            });
+        },
+        _onEditSurvey:function(survey){
+            Router.go('/survey/'+store.getIdentity(survey));
+        },
+        _onPreviewSurvey:function(survey){
+            Router.go('/viewSurvey/'+store.getIdentity(survey),{preview:true});
+        },
+        _onRunSurvey:function(survey){
+
+        },
+        refresh: function() {
+            this.refreshDrafts();
+            this.refreshPublished();
+            this.refreshRuns();
+        },
+        refreshDrafts: function() {
+            this.draftsContainer.set('content','');
+            when(store.query("_design/surveys/_view/drafts")
+                    ,lang.hitch(this,function(surveys){
+                this.draftsTab.set('title','Drafts ('+surveys.length+')');
+                array.forEach(surveys,function(survey){
+                    var w = new LineWithActionsWidget({
+                        title: survey.title || '(unnamed)',
+                        actions: [{
+                            callback: lang.hitch(this,'_onPublishSurvey',survey),
+                            properties: {
+                                label: 'Publish',
+                                tooltip: 'Publish survey',
+                                icon: 'Publish'
+                            }
+                        },{
+                            callback: lang.hitch(this,'_onPreviewSurvey',survey),
+                            properties: {
+                                label: 'Preview',
+                                tooltip: 'Preview survey',
+                                icon: 'Preview'
+                            }
+                        },{
+                            callback: lang.hitch(this,'_onDeleteSurvey',survey),
+                            properties: {
+                                label: 'Delete',
+                                tooltip: 'Delete survey',
+                                icon: 'Delete'
+                            }
+                        },{
+                            callback: lang.hitch(this,'_onEditSurvey',survey),
+                            properties: {
+                                label: 'Edit',
+                                tooltip: 'Edit survey',
+                                icon: 'Edit'
+                            }
+                        }]
+                    });
+                    this.draftsContainer.addChild(w);
+                },this);
             }));
-
-            this.grid.on('rowdblclick',lang.hitch(this,function(evt){
-                var obj = evt.grid.getItem(evt.rowIndex);
-                Controller.go('/survey/'+store.getIdentity(obj));
+        },
+        refreshPublished: function() {
+            this.publishedContainer.set('content','');
+            when(store.query("_design/surveys/_view/published")
+                    ,lang.hitch(this,function(surveys){
+                this.publishedTab.set('title','Published ('+surveys.length+')');
+                array.forEach(surveys,function(survey){
+                    var w = new LineWithActionsWidget({
+                        title: survey.title,
+                        actions:[{
+                            callback: lang.hitch(this,'_onPreviewSurvey',survey),
+                            properties: {
+                                label: 'Preview',
+                                tooltip: 'Preview survey',
+                                icon: 'Preview'
+                            }
+                        },{
+                            callback: lang.hitch(this,'_onRunSurvey',survey),
+                            properties: {
+                                label: 'Run',
+                                tooltip: 'Run survey',
+                                icon: 'Run'
+                            }
+                        }]
+                    });
+                    this.publishedContainer.addChild(w);
+                },this);
             }));
-            
-            this.btnNew.on('click',lang.hitch(this,function(){
-                Deferred.when( store.add({type:'Survey',creator:auth.getUser()}) )
-                .then(function(obj) {
-                    Controller.go('/survey/'+store.getIdentity(obj));
-                });
-            }));
-
-            this.btnEdit.on('click',lang.hitch(this,function(){
-                if ( this.selectedObject ) {
-                    Controller.go('/survey/'+store.getIdentity(this.selectedObject));
-                }
-                
+        },
+        refreshRuns: function() {
+            this.runsContainer.set('content','');
+            when(store.query("_design/default/_view/by_type",{key:'SurveyRun'})
+                    ,lang.hitch(this,function(surveyRuns){
+                this.runsTab.set('title','Runs ('+surveyRuns.length+')');
+                array.forEach(surveyRuns,function(surveyRun){
+                    var w = new LineWithActionsWidget({
+                        title: survey.title,
+                        actions:[{
+                            callback: lang.hitch(this,'_onCloseRun',surveyRun),
+                            properties: {
+                                label: 'Close',
+                                tooltip: 'Close survey',
+                                icon: 'Close'
+                            }
+                        }]
+                    });
+                    this.runsContainer.addChild(w);
+                },this);
             }));
         }
Index: Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.html	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.html	(revision 410)
@@ -16,5 +16,5 @@
     </div>
     
-    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'bottom'">
+    <div data-dojo-attach-point="buttonsPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'bottom'">
         <button data-dojo-type="dijit/form/Button"
                 type="submit"
Index: Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/pages/viewSurvey.js	(revision 410)
@@ -13,15 +13,28 @@
         templateString: template,
         survey: null,
+        surveyId: "",
+        options: null,
         constructor: function(){
             this._dataMap = {};
+            this.options = this.options || {};
         },
         startup: function() {
             if ( this._started ) { return; }
             this.inherited(arguments);
+
+
             if ( this.surveyId ) {
                 Deferred.when(store.get(this.surveyId))
-                .then(lang.hitch(this,function(obj){
+                .then(lang.hitch(this,function(survey){
+                    if ( !survey.published ) {
+                        this.options.preview = true;
+                    }
+                    if ( this.options.preview ) {
+                        this.buttonsPane.destroyRecursive();
+                    }
+                    this.titleNode.innerHTML = survey.title +
+                            (this.options.preview?' [preview]':'');
                     var f = new ContentWidgetFactory();
-                    this.survey = obj;
+                    this.survey = survey;
                     store.query(null,{keys:this.survey.questions,include_docs:true})
                     .forEach(function(question){
@@ -37,8 +50,9 @@
                 }));
             } else {
-                throw "No valid uid or survey passed!";
+                throw new Error("No valid uid or survey passed!");
             }
         },
         _onSubmit: function(evt) {
+            if ( this.options.preview ) { return; }
             var value = this.questionsForm.get('value');
             this.questionsPane.set('content','<pre>'+JSON.stringify(value)+'</pre>');
@@ -47,4 +61,5 @@
         },
         _onCancel: function(evt) {
+            if ( this.options.preview ) { return; }
             event.stop(evt);
             return false;
Index: Dev/branches/rest-dojo-ui/client/rft/run.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/run.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/run.js	(revision 410)
@@ -2,13 +2,13 @@
     'dojo/_base/array',
     'dojo/parser',
-    'rft/app/Controller',
+    'rft/app/Router',
     'rft/routes',
     'rft/stddeps',
     'dojo/domReady!'
-],function(array,parser,controller,routes) {
+],function(array,parser,router,routes) {
     parser.parse();
     array.forEach(routes,function(route){
-        controller.register(route);
+        router.register(route);
     });
-    controller.startup();
+    router.startup();
 });
Index: Dev/branches/rest-dojo-ui/client/rft/search.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/search.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/search.js	(revision 410)
@@ -0,0 +1,9 @@
+define([
+    './store/ElasticReadStore'
+],function(ElasticReadStore){
+    return new ElasticReadStore({
+        url: "data/elastic",
+        requestMethod: "POST"
+    });
+
+});
Index: Dev/branches/rest-dojo-ui/client/rft/store.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/store.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/store.js	(revision 410)
@@ -1,256 +1,18 @@
 define([
-    'dojo/_base/array',
-    'dojo/_base/declare',
-    'dojo/_base/Deferred',
-    'dojo/_base/json',
-    'dojo/_base/lang',
-    'dojo/_base/xhr',
-    'dojo/store/util/QueryResults'
-],function(array,declare,Deferred,json,lang,xhr,QueryResults){
+    'dojo/date/stamp',
+    './store/CouchStore'
+],function(stamp,CouchStore){
     
-    var CouchStore = declare(null, {
-        /** dojo Store implementation for CouchDB
-         *
-         * See for details on the REST API, the wiki
-         * at http://wiki.apache.org/couchdb/HTTP_Document_API.
-         */
-        target: "",
-        accepts: "application/json", 
-        idProperty: "_id",
-        revProperty: "_rev",
-        _responseIdProperty: "id",
-        _responseRevProperty: "rev",
-        constructor: function(options){
-            declare.safeMixin(this, options);
-        },
-        getIdentity: function(object){
-            return object[this.idProperty];
-        },
-        getRevision: function(object){
-            return object[this.revProperty];
-        },
-        get: function(id){
-            var dfd = new Deferred();
-            xhr("GET", {
-                url: this.target + id,
-                handleAs: "json",
-                headers: {
-                    Accept: this.accepts
-                }
-            }).then(function(result){
-                if ( result.error ) {
-                    dfd.reject(result.reason);
-                } else {
-                    dfd.resolve(result);
-                }
-            }, function(err){
-                dfd.reject(err);
-            });
-            return dfd.promise;
-        },
-        put: function(object, options){
-             // summary:
-             //     put an object in CouchDB
-             // object: Object
-             //     The object to put
-             // options: Object
-             //     Options object as
-             //         id: String
-             //
-            options = options || {};
-
-            var dfd = new Deferred();
-            var id = options.id ? options.id : this.getIdentity(object);
-            var hasId = typeof id != "undefined";
-            xhr(hasId ? "PUT" : "POST", {
-                url: hasId ? this.target + id : this.target,
-                postData: json.toJson(object),
-                handleAs: "json",
-                headers:{
-                    "Content-Type": "application/json",
-                    Accept: this.accepts
-                }
-            }).then(lang.hitch(this,function(result){
-                if ( result.error ) {
-                    dfd.reject(result.reason);
-                } else {
-                    object[this.idProperty] = result[this._responseIdProperty];
-                    object[this.revProperty] = result[this._responseRevProperty];
-                    dfd.resolve(object);
-                }
-            }), function(err){
-                dfd.reject(err);
-            });
-            return dfd.promise;
-        },
-        add: function(object, options){
-            return this.put(object,options);
-        },
-        remove: function(id,rev){
-            var dfd = new Deferred();
-            xhr("DELETE",{
-                url: this.target + id,
-                headers: {
-                    'If-Match': rev
-                }
-            }).then(function(result){
-                if ( result.error ) {
-                    dfd.reject(result.reason);
-                } else {
-                    dfd.resolve();
-                }
-            },function(err){
-                dfd.reject(err);
-            });
-            return dfd.promise;
-        },
-        query: function(query, options){
-            // summary:
-            //    query a couchdb view
-            // query: String
-            //    name of a couchdb view you want to query, relative to the current database
-            // options: Object
-            //     options object as
-            //        start: Number
-            //            Start results at this item
-            //        count: Number
-            //            Number of items to return
-            //        sort: [{attribute:'key',descending:true|false}]
-            //            CouchDB only support sorting by key, so only 'key'
-            //            is allowed as attribute value. Multiple sort items
-            //            are ignored.
-            //        key: String|Array|Object
-            //            Return only values with this key.
-            //            Excludes start/endkey usage.
-            //        startkey: String|Array|Object
-            //            Return values starting from this key.
-            //        endkey: String|Array|Object
-            //            Return values with key lower than this key.
-            //        include_docs: true|false
-            //            Return the full documents instead of the view
-            //            values.
-            //        reduce: true|false
-            //            Execute reduce on the view or not. Default depends
-            //            on if a reduce function is defined on the view.
-            //        group: true|false
-            //            Should values be grouped per key or not? Default
-            //            is false.
-            //        group_level: Number
-            //            When group = true and the key is an array,
-            //            determines which elements starting from the first
-            //            are used for grouping. Default is 0.
-            //        get_keys: true|false
-            //            Instead of returning the values or documents,
-            //            return the array of keys as the result.
-            //            This does not affect the forPairs function.
-            options = options || {};
-
-            var dfd = new Deferred();
-            var queryOpts = {};
-            if ( !query ) {
-                query = '_all_docs';
-            }
-
-            if (!lang.isString(query)) {
-                console.warn("Query must be a view name");
-            }
-
-            // Standard options
-            if (options.start >= 0) {
-                queryOpts.skip = options.start;
-            }
-            if (options.count >= 0) {
-                queryOpts.limit = options.count;
-            }
-            if (options.sort) {
-                if (options.sort[0]) {
-                    if (options.sort[0].attribute && options.sort[0].attribute !== "key") {
-                        console.warn("Can only sort on key");
-                    }
-                    if (options.sort[0].descending) {
-                        queryOpts.descending = true;
-                    }
-                }
-                if (options.sort.length > 1) {
-                    console.warn("multiple sort fields not supported");
-                }
-            }
-
-            // Custom options
-            if (options.key !== undefined) {
-                queryOpts.key = options.key;
-            } else if (options.keys !== undefined) {
-                queryOpts.keys = options.keys;
-            } else if (options.startkey !== undefined || options.endkey !== undefined) {
-                queryOpts.startkey = options.startkey;
-                queryOpts.endkey = options.endkey;
-            }
-            if (options.include_docs !== undefined) {
-                queryOpts.include_docs = options.include_docs;
-            }
-            if (options.reduce !== undefined) {
-                queryOpts.reduce = options.reduce;
-            }
-            if (options.group !== undefined) {
-                queryOpts.group = options.group;
-                if (options.group_level !== undefined) {
-                    queryOpts.group_level = options.group_level;
-                }
-            }
-
-            for (var qp in queryOpts) {
-                queryOpts[qp] = json.toJson(queryOpts[qp]);
-            }
-            query += '?' + xhr.objectToQuery(queryOpts);
-
-            xhr("GET", {
-                url: this.target + query,
-                handleAs: "json",
-                headers: {
-                    Accept: this.accepts
-                }
-            }).then(function(result){
-                if (result.error) {
-                    dfd.reject(result.reason);
-                } else  {
-                    var results;
-                    var values = array.map(result.rows,function(result){
-                        return options.include_docs === true ? result.doc : result.value;
-                    });
-                    var keys = array.map(result.rows,function(result){
-                        return result.key;
-                    });
-                    if (options.get_keys === true) {
-                        results = keys;
-                        results.values = values;
-                    } else {
-                        results = values;
-                        results.keys = keys;
-                    }
-                    dfd.resolve(results);
-                }
-            },function(err){
-                dfd.reject(err);
-            });
-            return CouchResults(dfd.promise);
-        }
-    });
-
-    function CouchResults(results) {
-        results = QueryResults(results);
-        results.forPairs = function(callback,thisObject) {
-            callback = lang.hitch(thisObject,callback);
-            return Deferred.when(results,function(results) {
-                var values = results.values || results;
-                var keys = results.keys || results;
-                return array.forEach(values, function(value,index) {
-                    callback(value,keys[index],index);
-                });
-            });
-        };
-        return results;
-    }
-
-    return new CouchStore({target: 'data/rft/'});
+    var store = new CouchStore({target: 'data/couch/'});
+    store.formatDate = function(date){
+        return stamp.toISOString(date,{zulu:true});
+    };
+    store.parseDate = function(dateString){
+        return stamp.fromISOString(dateString);
+    };
+    store.timestamp = function() {
+        return this.formatDate(new Date());
+    };
+    return store;
 
 });
Index: Dev/branches/rest-dojo-ui/client/rft/store/CouchStore.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/store/CouchStore.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/store/CouchStore.js	(revision 410)
@@ -0,0 +1,262 @@
+define([
+    'dojo/_base/array',
+    'dojo/_base/declare',
+    'dojo/_base/Deferred',
+    'dojo/_base/json',
+    'dojo/_base/lang',
+    'dojo/_base/xhr',
+    'dojo/store/util/QueryResults'
+],function(array,declare,Deferred,json,lang,xhr,QueryResults){
+    
+    function getCouchError(err){
+        return (err.response && err.response.data)
+                ? json.fromJson(err.response.data)
+                : "Unknown error.";
+    }
+
+    var CouchStore = declare(null, {
+        /** dojo Store implementation for CouchDB
+         *
+         * See for details on the REST API, the wiki
+         * at http://wiki.apache.org/couchdb/HTTP_Document_API.
+         */
+        target: "",
+        accepts: "application/json", 
+        idProperty: "_id",
+        revProperty: "_rev",
+        _responseIdProperty: "id",
+        _responseRevProperty: "rev",
+        constructor: function(options){
+            declare.safeMixin(this, options);
+        },
+        getIdentity: function(object){
+            return object[this.idProperty];
+        },
+        getRevision: function(object){
+            return object[this.revProperty];
+        },
+        get: function(id){
+            var dfd = new Deferred();
+            xhr("GET", {
+                url: this.target + id,
+                handleAs: "json",
+                headers: {
+                    Accept: this.accepts
+                }
+            }).then(function(result){
+                if ( result.error ) {
+                    dfd.reject(result.reason);
+                } else {
+                    dfd.resolve(result);
+                }
+            }, function(err){
+                dfd.reject(getCouchError(err));
+            });
+            return dfd.promise;
+        },
+        put: function(object, options){
+             // summary:
+             //     put an object in CouchDB
+             // object: Object
+             //     The object to put
+             // options: Object
+             //     Options object as
+             //         id: String
+             //
+            options = options || {};
+
+            var dfd = new Deferred();
+            var id = options.id ? options.id : this.getIdentity(object);
+            var hasId = typeof id != "undefined";
+            xhr(hasId ? "PUT" : "POST", {
+                url: hasId ? this.target + id : this.target,
+                postData: json.toJson(object),
+                handleAs: "json",
+                headers:{
+                    "Content-Type": "application/json",
+                    Accept: this.accepts
+                }
+            }).then(lang.hitch(this,function(result){
+                if ( result.error ) {
+                    dfd.reject(result.reason);
+                } else {
+                    object[this.idProperty] = result[this._responseIdProperty];
+                    object[this.revProperty] = result[this._responseRevProperty];
+                    dfd.resolve(object);
+                }
+            }), function(err){
+                dfd.reject(getCouchError(err));
+            });
+            return dfd.promise;
+        },
+        add: function(object, options){
+            return this.put(object,options);
+        },
+        remove: function(id,rev){
+            var dfd = new Deferred();
+            xhr("DELETE",{
+                url: this.target + id,
+                headers: {
+                    'If-Match': rev
+                }
+            }).then(function(result){
+                if ( result.error ) {
+                    dfd.reject(result.reason);
+                } else {
+                    dfd.resolve();
+                }
+            },function(err){
+                dfd.reject(getCouchError(err));
+            });
+            return dfd.promise;
+        },
+        query: function(query, options){
+            // summary:
+            //    query a couchdb view
+            // query: String
+            //    name of a couchdb view you want to query, relative to the current database
+            // options: Object
+            //     options object as
+            //        start: Number
+            //            Start results at this item
+            //        count: Number
+            //            Number of items to return
+            //        sort: [{attribute:'key',descending:true|false}]
+            //            CouchDB only support sorting by key, so only 'key'
+            //            is allowed as attribute value. Multiple sort items
+            //            are ignored.
+            //        key: String|Array|Object
+            //            Return only values with this key.
+            //            Excludes start/endkey usage.
+            //        startkey: String|Array|Object
+            //            Return values starting from this key.
+            //        endkey: String|Array|Object
+            //            Return values with key lower than this key.
+            //        include_docs: true|false
+            //            Return the full documents instead of the view
+            //            values.
+            //        reduce: true|false
+            //            Execute reduce on the view or not. Default depends
+            //            on if a reduce function is defined on the view.
+            //        group: true|false
+            //            Should values be grouped per key or not? Default
+            //            is false.
+            //        group_level: Number
+            //            When group = true and the key is an array,
+            //            determines which elements starting from the first
+            //            are used for grouping. Default is 0.
+            //        get_keys: true|false
+            //            Instead of returning the values or documents,
+            //            return the array of keys as the result.
+            //            This does not affect the forPairs function.
+            options = options || {};
+
+            var dfd = new Deferred();
+            var queryOpts = {};
+            if ( !query ) {
+                query = '_all_docs';
+            }
+
+            if (!lang.isString(query)) {
+                console.warn("Query must be a view name");
+            }
+
+            // Standard options
+            if (options.start >= 0) {
+                queryOpts.skip = options.start;
+            }
+            if (options.count >= 0) {
+                queryOpts.limit = options.count;
+            }
+            if (options.sort) {
+                if (options.sort[0]) {
+                    if (options.sort[0].attribute && options.sort[0].attribute !== "key") {
+                        console.warn("Can only sort on key");
+                    }
+                    if (options.sort[0].descending) {
+                        queryOpts.descending = true;
+                    }
+                }
+                if (options.sort.length > 1) {
+                    console.warn("multiple sort fields not supported");
+                }
+            }
+
+            // Custom options
+            if (options.key !== undefined) {
+                queryOpts.key = options.key;
+            } else if (options.keys !== undefined) {
+                queryOpts.keys = options.keys;
+            } else if (options.startkey !== undefined || options.endkey !== undefined) {
+                queryOpts.startkey = options.startkey;
+                queryOpts.endkey = options.endkey;
+            }
+            if (options.include_docs !== undefined) {
+                queryOpts.include_docs = options.include_docs;
+            }
+            if (options.reduce !== undefined) {
+                queryOpts.reduce = options.reduce;
+            }
+            if (options.group !== undefined) {
+                queryOpts.group = options.group;
+                if (options.group_level !== undefined) {
+                    queryOpts.group_level = options.group_level;
+                }
+            }
+
+            for (var qp in queryOpts) {
+                queryOpts[qp] = json.toJson(queryOpts[qp]);
+            }
+            query += '?' + xhr.objectToQuery(queryOpts);
+
+            xhr("GET", {
+                url: this.target + query,
+                handleAs: "json",
+                headers: {
+                    Accept: this.accepts
+                }
+            }).then(function(result){
+                if (result.error) {
+                    dfd.reject(result.reason);
+                } else  {
+                    var results;
+                    var values = array.map(result.rows,function(result){
+                        return options.include_docs === true ? result.doc : result.value;
+                    });
+                    var keys = array.map(result.rows,function(result){
+                        return result.key;
+                    });
+                    if (options.get_keys === true) {
+                        results = keys;
+                        results.values = values;
+                    } else {
+                        results = values;
+                        results.keys = keys;
+                    }
+                    dfd.resolve(results);
+                }
+            },function(err){
+                dfd.reject(getCouchError(err));
+            });
+            return CouchResults(dfd.promise);
+        }
+    });
+
+    function CouchResults(results) {
+        results = QueryResults(results);
+        results.forPairs = function(callback,thisObject) {
+            callback = lang.hitch(thisObject,callback);
+            return Deferred.when(results,function(results) {
+                var values = results.values || results;
+                var keys = results.keys || results;
+                return array.forEach(values, function(value,index) {
+                    callback(value,keys[index],index);
+                });
+            });
+        };
+        return results;
+    }
+
+    return CouchStore;
+
+});
Index: Dev/branches/rest-dojo-ui/client/rft/store/ElasticReadStore.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/store/ElasticReadStore.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/store/ElasticReadStore.js	(revision 410)
@@ -0,0 +1,52 @@
+define([
+    'dojo/_base/declare',
+    "dojo/_base/json",
+    'dojo/_base/lang',
+    'dojo/_base/xhr',
+    'dojox/data/QueryReadStore'
+],function(declare, json, lang, xhr, QueryReadStore) {
+    return declare(QueryReadStore, {
+        fetch:function(request){
+            var attr = Object.keys(request.query)[0];
+            if (request.query[attr].length === 0) {
+                return 0;
+            }
+            var q = request.query[attr];
+
+            request.serverQuery = json.toJson({ 
+                query: {
+                    query_string: {
+                        default_field: attr, 
+                        query: q
+                    }
+                }
+            });
+            return this.inherited(arguments);
+          },
+        _fetchItems: function(request, fetchHandler, errorHandler){
+            var serverQuery = request.serverQuery;
+            var xhrHandler = xhr.post({
+                url: this.url,
+                handleAs: "json",
+                postData: serverQuery
+            }).then(lang.hitch(this, function(data){
+                this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
+            }),function(error){
+                errorHandler(error, request);
+            });
+            request.abort = function(){
+                xhrHandler.cancel();
+            };
+        },
+        _xhrFetchHandler: function(data, request, fetchHandler, errorHandler) {
+            data = this._filterResponse(data);
+            this._items = [];
+            var numHits = data.hits.total || -1;
+            if(numHits > 0) {
+                this._items.push({i:data.hits.hits[0]._source, r:this, n:0});
+            }
+            fetchHandler(this._items, request, data.hits.total);
+            this._numRows = data.hits.total;
+        }
+    });
+});
Index: Dev/branches/rest-dojo-ui/client/rft/ui/Breadcrumbs.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/Breadcrumbs.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/Breadcrumbs.js	(revision 410)
@@ -6,6 +6,6 @@
     'dojo/topic',
     'dijit/_WidgetBase',
-    '../app/Controller'
-], function(declare, baseArray, Button, domClass, topic, _WidgetBase,Controller){
+    '../app/Router'
+], function(declare, baseArray, Button, domClass, topic, _WidgetBase,Router){
     return declare([_WidgetBase], {
         _crumbs: [],
@@ -61,5 +61,5 @@
                 iconClass: "dijitNoIcon",
                 onClick: lang.hitch(this, function(){
-                    Controller.go(path);  // TODO: fix this call!
+                    Router.go(path);  // TODO: fix this call!
                 })
             });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/LoginDialog.js	(revision 410)
@@ -1,5 +1,5 @@
 define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/event','dijit/Dialog',
     'dijit/_WidgetsInTemplateMixin','../auth', 'dojo/text!./templates/LoginDialog.html',
-    '../app/Controller','dijit/form/Form','dijit/form/Button','dijit/form/TextBox'],
+    '../app/Router','dijit/form/Form','dijit/form/Button','dijit/form/TextBox'],
     function (declare, lang, event, Dialog, _WidgetsInTemplateMixin, auth, template, content) {
         return declare([Dialog,_WidgetsInTemplateMixin], {
Index: Dev/branches/rest-dojo-ui/client/rft/ui/MenuBarLink.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/MenuBarLink.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/MenuBarLink.js	(revision 410)
@@ -1,4 +1,4 @@
-define(['dojo/_base/declare','dijit/MenuBarItem','../app/Controller'],
-function(declare,MenuBarItem,Controller){
+define(['dojo/_base/declare','dijit/MenuBarItem','../app/Router'],
+function(declare,MenuBarItem,Router){
     return declare([MenuBarItem],{
         options:{
@@ -6,5 +6,5 @@
         },
         onClick: function(){
-            this.path && Controller.go(this.path);
+            this.path && Router.go(this.path);
         }
     });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/MenuLink.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/MenuLink.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/MenuLink.js	(revision 410)
@@ -1,4 +1,4 @@
-define(['dojo/_base/declare','dijit/MenuItem','../app/Controller'],
-function(declare,MenuItem,Controller){
+define(['dojo/_base/declare','dijit/MenuItem','../app/Router'],
+function(declare,MenuItem,Router){
     return declare([MenuItem],{
         options:{
@@ -6,5 +6,5 @@
         },
         onClick: function(){
-            this.path && Controller.go(this.path);
+            this.path && Router.go(this.path);
         }
     });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/Notifications.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/Notifications.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/Notifications.js	(revision 410)
@@ -1,17 +1,7 @@
-define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/connect','dojox/widget/Toaster'],
-    function(declare,lang,connect,Toaster){
-        return declare([Toaster],{
-            positionDirection: "br-up",
-            duration: 1000,
-            postCreate: function() {
-                this.inherited(arguments);
-                this.handle = connect.subscribe('/rft/notify',lang.hitch(this,function(notification){
-                    this.setContent(notification.text,notification.type || 'message');
-                }));
-            },
-            destroy: function() {
-                connect.unsubscribe(this.handle);
-                this.inherited(arguments);
-            }
-        });
+define(['dojo/_base/declare','dojo/_base/lang','dojox/widget/Toaster'],
+function(declare,lang,Toaster){
+    return declare([Toaster],{
+        positionDirection: "br-up",
+        duration: 1000
     });
+});
Index: Dev/branches/rest-dojo-ui/client/rft/ui/QuestionEditorToolkit.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/QuestionEditorToolkit.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/QuestionEditorToolkit.js	(revision 410)
@@ -129,5 +129,5 @@
             _setupCategories: function() {
                 this._categoryStore = new Memory({data: [] });
-                store.query("_design/default/_view/questions", {reduce:true, group:false, group_level:1})
+                store.query("_design/questions/_view/all", {reduce:true, group:false, group_level:1})
                 .forPairs(lang.hitch(this, function(value, key) {
                     this._categoryStore.put({ id: key[0] });
@@ -143,5 +143,5 @@
             _setupTopic: function(topic) {
                 this._topicStore = new Memory( {data: [] });
-                store.query("_design/default/_view/topics", {reduce:true, group:true})
+                store.query("_design/questions/_view/all_topics", {reduce:true, group:true})
                 .forPairs(lang.hitch(this, function(value, key) {
                     this._topicStore.put({ id: key });
Index: Dev/branches/rest-dojo-ui/client/rft/ui/Selector.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/Selector.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/Selector.js	(revision 410)
@@ -149,5 +149,5 @@
         },
 
-        addItem: function(item) {
+        addItem: function(item,displayTitle) {
             var actions = {};
             var action;
@@ -156,7 +156,5 @@
                     action = this.itemActions[actionName];
                     actions[actionName] = {
-                        callback: function(){
-                            action.callback && action.callback(item);
-                        },
+                        callback: action.callback && lang.partial(action.callback,item),
                         properties: {
                             blockButton: false,
@@ -165,9 +163,9 @@
                             tooltip: action.description
                         }
-                    }
+                    };
                 }
             }
             var w = new LineWithActionsWidget({
-                title: item.title,
+                title: displayTitle || item.title,
                 actions: actions
             }).placeAt(this.optionsNode);
Index: Dev/branches/rest-dojo-ui/client/rft/ui/TabbedQuestionBrowser.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/TabbedQuestionBrowser.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/TabbedQuestionBrowser.js	(revision 410)
@@ -9,111 +9,119 @@
     'rft/ui/Selector',
     'dojo/domReady!'
-    ],
-    function(declare,lang,win,ContentPane,TabContainer,Standby,store,Selector){
-        return declare([TabContainer],{
-            tabPosition: 'left-h',
+],function(declare,lang,win,ContentPane,TabContainer,Standby,store,Selector){
+    return declare([TabContainer],{
+        tabPosition: 'left-h',
+        include: 'all',
 
-            selectedActions: null,
-            itemActions: null,
+        selectedActions: null,
+        itemActions: null,
 
-            _dataMap: null,
-            _busyCount: 0,
-            constructor: function(){
-                this.inherited(arguments);
-                this._dataMap = {};
-            },
-            startup: function() {
-                if ( this._started ){ return; }
-                this.inherited(arguments);
-                this._busyWidget = new Standby({
-                    target: this.domNode,
-                    duration: 200
-                }).placeAt(win.body());
-                this._busyWidget.startup();
-                this.watch("selectedChildWidget",lang.hitch(this,function(name,oldTab,newTab){
-                    this._fillCategoryTab(newTab.__category);
+        _dataMap: null,
+        _busyCount: 0,
+        constructor: function(){
+            this.inherited(arguments);
+            this._dataMap = {};
+        },
+        postCreate: function() {
+            this.inherited(arguments);
+            this._query = '_design/questions/_view/'+this.include;
+        },
+        startup: function() {
+            if ( this._started ){ return; }
+            this.inherited(arguments);
+            this._busyWidget = new Standby({
+                target: this.domNode,
+                duration: 200
+            }).placeAt(win.body());
+            this._busyWidget.startup();
+            this.watch("selectedChildWidget",lang.hitch(this,function(name,oldTab,newTab){
+                this._fillCategoryTab(newTab.__category);
+            }));
+            store.query(this._query, {reduce:true,group:true,group_level:1})
+            .forPairs(lang.hitch(this,function(value,key){
+                this._createCategoryTab(key[0],value);
+            }));
+        },
+        _createCategoryTab: function(category,count) {
+            if (this._dataMap[category] === undefined) {
+                var categoryTab = new ContentPane({
+                    __category: category,
+                    title: (category || '[No category]')+" ("+count+")"
+                });
+                categoryTab.startup();
+                this._dataMap[category] = {
+                    _widget: categoryTab
+                };
+                this.addChild(categoryTab);
+            }
+        },
+        _fillCategoryTab: function(category) {
+            var categoryMap = this._dataMap[category];
+            if (!categoryMap._filled) {
+                this._busy();
+                categoryMap._filled = true;
+                store.query(this._query, {reduce:true,group:true,group_level:2,startkey:[category],endkey:[category,{}]})
+                .forPairs(lang.hitch(this,function(value,key){
+                    this._createTopicSelector(key[1],category,value);
+                })).then(lang.hitch(this,function(){
+                    this._done();
                 }));
-                store.query('_design/default/_view/questions', {reduce:true,group:true,group_level:1})
-                .forPairs(lang.hitch(this,function(value,key){
-                    this._createCategoryTab(key[0],value);
+            }
+        },
+        _createTopicSelector: function(topic,category,count){
+            var categoryMap = this._dataMap[category];
+            if (categoryMap[topic] === undefined) {
+                var w = new Selector({
+                    __category: category,
+                    __topic: topic,
+                    title: (topic || '[No topic]')+" ("+count+")",
+                    selectedActions: this.selectedActions,
+                    itemActions: this.itemActions
+                }).placeAt(categoryMap._widget.containerNode);
+                w.startup();
+                categoryMap[topic] = {
+                    _widget: w
+                };
+                this._fillTopicSelector(topic,category);
+            }
+        },
+        _fillTopicSelector: function(topic,category) {
+            var categoryMap = this._dataMap[category];
+            var topicMap = categoryMap[topic];
+            if (!topicMap._filled) {
+                topicMap._filled = true;
+                this._busy();
+                store.query(this._query, {reduce:false,include_docs:true,key:[category,topic]})
+                .forEach(lang.hitch(this,function(value){
+                    var title;
+                    if ( this.include === 'all' ) {
+                        title = value.title+(value.publicationDate?' (published on '+value.publicationDate+')':' (unpublished)');
+                    }
+                    topicMap._widget.addItem(value,title);
+                })).then(lang.hitch(this,function(){
+                    this._done();
                 }));
-            },
-            _createCategoryTab: function(category,count) {
-                if (this._dataMap[category] === undefined) {
-                    var categoryTab = new ContentPane({
-                        __category: category,
-                        title: (category || '[No category]')+" ("+count+")"
-                    });
-                    categoryTab.startup();
-                    this._dataMap[category] = {
-                        _widget: categoryTab
-                    };
-                    this.addChild(categoryTab);
+            }
+        },
+        _busy: function() {
+            if ( this._busyCount === 0 ) {
+                this._busyWidget.show();
+            }
+            this._busyCount++;
+        },
+        _done: function() {
+            if ( this._busyCount > 0 ) {
+                this._busyCount--;
+                if ( this._busyCount === 0 ) {
+                    this._busyWidget.hide();
                 }
-            },
-            _fillCategoryTab: function(category) {
-                var categoryMap = this._dataMap[category];
-                if (!categoryMap._filled) {
-                    this._busy();
-                    categoryMap._filled = true;
-                    store.query('_design/default/_view/questions', {reduce:true,group:true,group_level:2,startkey:[category],endkey:[category,{}]})
-                    .forPairs(lang.hitch(this,function(value,key){
-                        this._createTopicSelector(key[1],category,value);
-                    })).then(lang.hitch(this,function(){
-                        this._done();
-                    }));
-                }
-            },
-            _createTopicSelector: function(topic,category,count){
-                var categoryMap = this._dataMap[category];
-                if (categoryMap[topic] === undefined) {
-                    var w = new Selector({
-                        __category: category,
-                        __topic: topic,
-                        title: (topic || '[No topic]')+" ("+count+")",
-                        selectedActions: this.selectedActions,
-                        itemActions: this.itemActions
-                    }).placeAt(categoryMap._widget.containerNode);
-                    w.startup();
-                    categoryMap[topic] = {
-                        _widget: w
-                    };
-                    this._fillTopicSelector(topic,category);
-                }
-            },
-            _fillTopicSelector: function(topic,category) {
-                var categoryMap = this._dataMap[category];
-                var topicMap = categoryMap[topic];
-                if (!topicMap._filled) {
-                    topicMap._filled = true;
-                    this._busy();
-                    store.query('_design/default/_view/questions', {reduce:false,include_docs:true,key:[category,topic]})
-                    .forEach(lang.hitch(this,function(value){
-                        topicMap._widget.addItem(value);
-                    })).then(lang.hitch(this,function(){
-                        this._done();
-                    }));
-                }
-            },
-            _busy: function() {
-                if ( this._busyCount === 0 ) {
-                    this._busyWidget.show();
-                }
-                this._busyCount++;
-            },
-            _done: function() {
-                if ( this._busyCount > 0 ) {
-                    this._busyCount--;
-                    if ( this._busyCount === 0 ) {
-                        this._busyWidget.hide();
-                    }
-                } else {
-                    console.warn('_done() was called more times than _busy().');
-                }
-            },
-            destroy: function() {
-                this._busyWidget.destroyRecursive();
-                this.inherited(arguments);
+            } else {
+                console.warn('_done() was called more times than _busy().');
             }
-        });
+        },
+        destroy: function() {
+            this._busyWidget.destroyRecursive();
+            this.inherited(arguments);
+        }
     });
+});
Index: Dev/branches/rest-dojo-ui/client/rft/ui/ThresholdFilteringSelect.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/ui/ThresholdFilteringSelect.js	(revision 410)
+++ Dev/branches/rest-dojo-ui/client/rft/ui/ThresholdFilteringSelect.js	(revision 410)
@@ -0,0 +1,18 @@
+define([
+    'dojo/_base/declare',
+    'dijit/form/FilteringSelect'
+], function(declare, FilteringSelect) {
+    return declare(FilteringSelect, {
+         // custom min input character count to trigger search
+         minKeyCount: 3,
+
+         // override search method, count the input length
+         _startSearch: function (/*String*/key) {
+             if (!key || key.length < this.minKeyCount) {
+                 this.closeDropDown();
+                 return;
+             }
+             this.inherited(arguments);
+         }
+    });
+});
Index: Dev/branches/rest-dojo-ui/client/rft/util.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/util.js	(revision 409)
+++ 	(revision )
@@ -1,37 +1,0 @@
-define(['dojo/_base/lang'],
-    function(lang){
-        return {
-            isEmpty: function(arg) {
-                if ( arg == undefined || arg == null )
-                    return true;
-                if ( lang.isString(arg) )
-                    return this.isEmptyString(arg);
-                if ( lang.isArrayLike(arg) )
-                    return this.isEmptyArray(arg);
-                if ( lang.isObject() )
-                    return this.isEmptyObject(arg);
-                if ( lang.isAlien(arg) )
-                    return false;
-                return false;
-            },
-            isEmptyObject: function(obj) {
-                if ( !lang.isObject(obj) )
-                    return false;
-                for(var prop in obj) {
-                    if(obj.hasOwnProperty(prop)) 
-                        return false;
-                } 
-                return true;
-            },
-            isEmptyString: function(str) {
-                if ( !lang.isString(str) )
-                    return false;
-                return str === '';
-            },
-            isEmptyArray: function(array) {
-                if ( !lang.isArrayLike(array) )
-                    return false;
-                return array.length > 0;
-            }
-        };
-    });
Index: Dev/branches/rest-dojo-ui/client/rft/view.js
===================================================================
--- Dev/branches/rest-dojo-ui/client/rft/view.js	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/rft/view.js	(revision 410)
@@ -1,11 +1,29 @@
 require([
+    'dojo/hash',
     'dojo/parser',
-    'rft/stddeps',
-    'dojo/domReady!'
-],function(parser) {
+    'rft/app/Content',
+    'rft/app/Page',
+    'rft/pages/viewSurvey',
+    'dojo/domReady!',
+    'rft/stddeps'
+],function(hash,parser,Content,Page,viewSurvey) {
     parser.parse();
+    Content.startup();
+
+    var match = /^!\/(\w+)$/g.exec(hash());
+    if ( !match ) {
+        Content.set(new Page({
+            templateString: "<div>Something is wrong with the URL, don't know what survey to show you. Sorry.</div>"
+        }));
+        return;
+    }
+    var surveyId = match[1];
+
     // read options from hash/url
+    // 
     // authenticate
-    // set content
-    // go for it
+
+    Content.set(new viewSurvey({
+        surveyId: surveyId
+    }));
 });
Index: Dev/branches/rest-dojo-ui/client/view.html
===================================================================
--- Dev/branches/rest-dojo-ui/client/view.html	(revision 409)
+++ Dev/branches/rest-dojo-ui/client/view.html	(revision 410)
@@ -9,9 +9,7 @@
         <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
         <script type="text/javascript" src="rft/view.js"></script>
-        <div class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
+        <div id="content" class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
             <div class="topbar" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
                 <h1>Survey</h1>
-            </div>
-            <div id="content" class="content" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'">
             </div>
         </div>
