Index: /Dev/branches/rest-dojo-ui/client/admin.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/admin.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/admin.html	(revision 420)
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>QED</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" type="text/css" href="qed/css/main.css" />
+    </head>
+    <body class="dijitReset claro" id="rft">
+        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
+        <script type="text/javascript" src="qed/main-admin.js"></script>
+        <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'">
+                <a href="#!/"><h1>QED</h1></a>
+                <div id="menu"></div>
+            </div>
+        </div>
+        <div id="toaster" data-dojo-type="qed/app/Notifications"></div>
+    </body>
+</html>
Index: /Dev/branches/rest-dojo-ui/client/index.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/index.html	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/index.html	(revision 420)
@@ -7,13 +7,8 @@
     </head>
     <body class="dijitReset claro" id="rft">
-        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
-        <script type="text/javascript" src="qed/run.js"></script>
-        <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'">
-                <a href="#!/"><h1>QED</h1></a>
-                <div id="menu"></div>
-            </div>
+        <div class="topbar">
+            <h1>QED</h1><br>
+            <p>Nothing is shown here.</p>
         </div>
-        <div id="toaster" data-dojo-type="qed/app/Notifications"></div>
     </body>
 </html>
Index: /Dev/branches/rest-dojo-ui/client/qed/app/Notifications.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/app/Notifications.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/app/Notifications.js	(revision 420)
@@ -3,5 +3,5 @@
     return declare([Toaster],{
         positionDirection: "br-up",
-        duration: 1000
+        duration: 3000
     });
 });
Index: /Dev/branches/rest-dojo-ui/client/qed/app/Path.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/app/Path.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/app/Path.js	(revision 420)
@@ -0,0 +1,51 @@
+define([
+    'dojo/_base/declare',
+    'dojo/io-query'
+],function(declare,ioQuery){
+
+    var _paramMatch = /:(\w[\w\d]*)/g;
+    var _paramReplace = "([^\\/!]+)";
+
+    var Path = declare(null,{
+        _paramNames: null,
+        _matcher: null,
+        constructor: function(pattern) {
+            var match = null;
+            this._paramNames = [];
+			while((match = _paramMatch.exec(pattern)) !== null){
+				this._paramNames.push(match[1]);
+			}
+            pattern = pattern.replace(_paramMatch, _paramReplace);
+            this._matcher = new RegExp('^!'+pattern+'(!(.*))?$');
+        },
+        match: function(path) {
+            var params = null;
+            var result = null;
+            if ((result = this._matcher.exec(path)) !== null) {
+                var numParams = this._paramNames.length;
+                params = {};
+                for (var j = 0; j < numParams; j++) {
+                    params[this._paramNames[j]] = result[j+1];
+                }
+                if ( result.length > numParams+1 && result[numParams+2] ) {
+                    params.options = ioQuery.queryToObject(result[numParams+2]);
+                }
+            }
+            return params;
+        }
+    });
+
+    Path.getDefault = function() {
+        return "!/";
+    };
+
+    Path.format = function(path,options) {
+        var hash = '!'+path;
+        if ( options ) {
+            hash += '!'+ioQuery.objectToQuery(options);
+        }
+        return hash;
+    };
+
+    return Path;
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/app/Router.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/app/Router.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/app/Router.js	(revision 420)
@@ -2,9 +2,9 @@
     'dojo/_base/declare',
     'dojo/hash',
-    'dojo/io-query',
     'dojo/topic',
     './Content',
-    './Page'
-],function(declare,hash,ioQuery,topic,Content,Page){
+    './Page',
+    './Path'
+],function(declare,hash,topic,Content,Page,Path){
 
     var Router = declare(null,{
@@ -12,7 +12,4 @@
         _routes: null,
         _previousHash: null,
-
-        _paramMatch: /:(\w[\w\d]*)/g,
-        _paramReplace: "([^\\/!]+)",
 
         constructor: function() {
@@ -29,5 +26,5 @@
 
             if ( hash() === "" ) {
-                hash("#!/");
+                hash(Path.getDefault());
             }
             this._handlePathChange(hash());
@@ -40,6 +37,20 @@
                 console.warn('Registering routes after startup() is called is discouraged.');
             }
+            var callback = this._normalizeCallback(route);
+            if ( callback ) {
+                if ( route.path ) {
+                    route.callback = callback;
+                    route.path = new Path(route.path);
+                    this._routes.push(route);
+                } else {
+                    this._defaultCallback = callback;
+                }
+            } else {
+                console.warn("Route "+(route.path||"default")+" has no action.");
+            }
+        },
+        _normalizeCallback: function(route) {
             var self = this;
-            var callback;
+            var callback = null;
             if ( route.callback ) {
                 callback = function(params){
@@ -55,76 +66,36 @@
                 };
             }
-            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;
+            return callback;
         },
         _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]);
-                    }
-
+                var params = null;
+                if ((params = route.path.match(newHash)) !== null) {
                     try {
                         route.callback(params);
                     } catch(err) {
-                        console.warn("Page change failed.",err);
+                        console.error("Page change failed.",err);
                     }
                     return;
                 }
             }
-
             try {
                 this._defaultCallback();
             } catch(err) {
-                console.warn("Default page failed.",err);
+                console.error("Default page failed.",err);
             }
         },
         go: function(path,args) {
             if ( !this._started ) { return; }
-            var newHash = this._pathToHash(path,args);
+            var newHash = Path.format(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>"
+                templateString: "<div>Requested page not found. Go <a href=\"#"+Path.getDefault()+"\">home</a>.</div>"
             }));
         }
Index: /Dev/branches/rest-dojo-ui/client/qed/css/hva-mods.css
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/css/hva-mods.css	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/css/hva-mods.css	(revision 420)
@@ -0,0 +1,23 @@
+a {
+    color: inherit;
+}
+
+#rft.claro .qedInfo {
+    max-width: 500px;
+}
+
+#rft.claro .qedLabel {
+    display: inline-block;
+    float: left;
+    width: 150px;
+}
+
+#rft.claro .qedField {
+    max-width: 350px;
+    display: inline-block;
+}
+
+#rft.claro .qedFieldset {
+    margin: 1em 0px;
+    border: 1px solid grey;
+}
Index: /Dev/branches/rest-dojo-ui/client/qed/css/main.css
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/css/main.css	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/css/main.css	(revision 420)
@@ -1,4 +1,5 @@
 /* Menu.css */
 @import "external.css";
+@import "hva-mods.css";
 DESCRIPTION {
   /*
Index: /Dev/branches/rest-dojo-ui/client/qed/lib/async.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/lib/async.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/lib/async.js	(revision 420)
@@ -0,0 +1,128 @@
+define([
+     'dojo/_base/array',
+     'dojo/_base/lang',
+     'dojo/when',
+     'dojo/Deferred'
+],function(array,lang,when,Deferred){
+
+    /** Execute possible async functions in sequence
+     * Execute functions in order. When a function returns a Promise,
+     * the next function is only executed when the promise resolves.
+     * If the function returns a value, the next function is executed
+     * synchronously. The return value of every function is passed as argument
+     * to the next.
+     * Returns the return value of the last function in the chain.
+     */
+    function seq(functions,ctx) {
+        var d = new Deferred();
+
+        var cancelled = false;
+        var running = null;
+        function cancel(err) {
+            if ( cancelled ) return;
+            cancelled = true;
+            running && running.cancel && !running.isFulfilled() && running.cancel();
+            d.reject(err);
+        }
+
+        function update(fs,arg) {
+            if ( cancelled ) return;
+            if ( fs.length > 0 ) {
+                try {
+                    var f = fs.shift();
+                    running = when(f.call(ctx,arg))
+                    .then(function(res){
+                        update(fs,res);
+                    },function(err){
+                        cancel(err);
+                    });
+                } catch(err) {
+                    cancel(err);
+                }
+            } else {
+                d.resolve(arg);
+            }
+        }
+
+        update(functions);
+
+        return d.promise;
+    }
+
+    /** Execute possible async functions in parallel
+     * Execute all functions in parallel, resolve only when all have resolved.
+     * NB. When a function is not async, it will fully complete before the next
+     * async function is started. If you mix async and non async functions, put
+     * the sync functions last to fully exploit the async ones.
+     * Return an array with all the return values of the functions.
+     */
+    function par(functions,ctx) {
+        var d = new Deferred(cancel);
+
+        var cancelled = false;
+        var running = [];
+        function cancel(err) {
+            if (cancelled) return;
+            cancelled = true;
+            array.forEach(running,function(running){
+                running && running.cancel && !running.isFulfilled() && running.cancel();
+            });
+            d.reject(err);
+        }
+
+        var results = [];
+        var left = functions.length;
+        function update(res,idx) {
+            if (cancelled) return;
+            results[idx] = res;
+            left -= 1; // Works because/as long as AJAX/JS is single-threaded.
+            if ( left === 0 ) {
+                d.resolve(results);
+            }
+        }
+
+        array.forEach(functions,function(f,idx){
+            if (cancelled) return;
+            try {
+                running.push(when(f.call(ctx)).then(function(res){
+                    update(res,idx);
+                },function(err){
+                    cancel(err);
+                }));
+            } catch (err) {
+                cancel();
+            }
+        });
+
+        return d.promise;
+    }
+
+    /** Iterate over an array with async callback
+     * Execute the callback and if it's async, wait until it's finished before
+     * executing the next one.
+     */
+    function forEach(list,callback,ctx) {
+        var fs = array.map(list,function(item,index,items){
+            return lang.hitch(ctx,callback,item,index,items);
+        });
+        return seq(fs);
+    }
+
+    /** Map a list, possible async
+     * Map every element of a list, returning a promise to the mapped list.
+     */
+    function map(list,callback,ctx) {
+        var fs = array.map(list,function(item,index,items){
+            return lang.hitch(ctx,callback,item,index,items);
+        });
+        return par(fs);
+    }
+
+    return {
+        seq: seq,
+        par: par,
+        forEach: forEach,
+        map: map
+    };
+
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/lib/func.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/lib/func.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/lib/func.js	(revision 420)
@@ -0,0 +1,21 @@
+define(['dojo/_base/lang'],function(lang){
+
+    Function.prototype.compose = function(g) {
+        var f = this;
+        return function(){
+            return f(g.apply(arguments));
+        };
+    };
+
+    function modPropIf(object,prop,pred,map) {
+        var value = object[prop];
+        if ( pred(value) ) {
+            object[prop] = map(value);
+        }
+        return object;
+    }
+
+    return {
+        modPropIf: modPropIf
+    };
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/main-admin.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/main-admin.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/main-admin.js	(revision 420)
@@ -0,0 +1,28 @@
+require([
+    'dojo/_base/array',
+    'dojo/parser',
+    'qed/app/Content',
+    'qed/app/Page',
+    'qed/app/Router',
+    'qed/ui/MainMenu',
+    'qed/routes',
+    'qed/store',
+    'qed/stddeps',
+    'dojo/domReady!'
+],function(array,parser,Content,Page,Router,MainMenu,routes,store) {
+    parser.parse();
+
+    store.info()
+    .then(function(){
+        new MainMenu({},"menu").startup();
+        array.forEach(routes,function(route){
+            Router.register(route);
+        });
+        Router.startup();
+    },function(){
+        Content.startup();
+        Content.set(new Page({
+            templateString: "<div>Application database is not properly configured. Contact your system administrator.</div>"
+        }));
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/main-response.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/main-response.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/main-response.js	(revision 420)
@@ -0,0 +1,82 @@
+require([
+    'dojo/date',
+    'dojo/date/locale',
+    'dojo/hash',
+    'dojo/parser',
+    'qed/store',
+    'qed/app/Content',
+    'qed/app/Page',
+    'qed/app/Path',
+    'qed/lib/async',
+    'qed/model/classes/Response',
+    'qed/model/classes/SurveyRun',
+    'qed/pages/response',
+    'dojo/domReady!',
+    'qed/stddeps'
+],function(date,locale,hash,parser,store,Content,Page,Path,async,Response,SurveyRun,response) {
+    parser.parse();
+    Content.startup();
+
+    function error(msg) {
+        Content.set(new Page({
+            templateString: "<div>"+msg+"</div>"
+        }));
+    }
+
+    var path = new Path('/:surveyRunId');
+    var params = path.match(hash());
+    params.options = params.options || {};
+
+    if ( !params || !params.surveyRunId ) {
+        error("Something is wrong with the URL, don't know what survey to show you. Sorry.");
+        return;
+    }
+
+    var surveyRunId = params.surveyRunId;
+
+    function checkDates(surveyRun) {
+        var now = new Date();
+        var startDate = SurveyRun.StartDate.get(surveyRun);
+        var endDate = SurveyRun.EndDate.get(surveyRun);
+        if ( startDate && date.compare(startDate,now) > 0 ) {
+            error("This survey will start on "+locale.format(startDate,'date'));
+            throw false;
+        }
+        if ( endDate && date.compare(now,endDate) > 0 ) {
+            error("This survey ended on "+locale.format(endDate,'date'));
+            throw false;
+        }
+    }
+
+    store.get(surveyRunId)
+    .then(function(surveyRun){
+        checkDates(surveyRun);
+        if ( params.options.id ) {
+            return params.options.id;
+        } else {
+            if ( surveyRun.mode === "open") {
+                var response = Response.create();
+                response.surveyRunId = surveyRunId;
+                return store.put(response).then(function(res){
+                    return store.getIdentity(res);
+                });
+            } else {
+                error("Cannot respond to closed survey without response id. Sorry.");
+                throw false;
+            }
+        }
+        return surveyRun;
+    },function(){
+        error("No running survey found for the given id. Sorry.");
+        throw false;
+    })
+    .then(function(responseId){
+        hash(Path.format("/"+surveyRunId,{ id: responseId }));
+        Content.set(new response({
+            surveyRunId: surveyRunId,
+            options: {
+                id: responseId
+            }
+        }));
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/classes/Question.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/classes/Question.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/classes/Question.js	(revision 420)
@@ -0,0 +1,20 @@
+define(function(){
+    return {
+        create: function(){
+            return { type:'Question' };
+        },
+        DisplayTitle: {
+            get: function(q) {
+                return q.title || '';
+            }
+        },
+        Content: {
+            get: function(q) {
+                return q.content || [];
+            },
+            set: function(q,content) {
+                q.content = content;
+            }
+        }
+    };
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/classes/Response.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/classes/Response.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/classes/Response.js	(revision 420)
@@ -0,0 +1,17 @@
+define([],function(){
+    var SurveyRun = {
+        create: function(){
+            return { type:'Response' };
+        },
+        SurveyRun: {
+            get: function(r) {
+                return r.surveyRunId || null;
+            },
+            set: function(r,sr) {
+                r.surveyRunId = sr;
+                return r;
+            }
+        }
+    };
+    return SurveyRun;
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/classes/SessionTemplate.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/classes/SessionTemplate.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/classes/SessionTemplate.js	(revision 420)
@@ -0,0 +1,12 @@
+define(function(){
+    return {
+        create: function(){
+            return { type:'SessionTemplate' };
+        },
+        DisplayTitle: {
+            get: function(st) {
+                return st.title || '';
+            }
+        }
+    };
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/classes/Survey.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/classes/Survey.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/classes/Survey.js	(revision 420)
@@ -0,0 +1,20 @@
+define(function(){
+    return {
+        create: function(){
+            return { type:'Survey' };
+        },
+        DisplayTitle: {
+            get: function(s) {
+                return s.title || '';
+            }
+        },
+        Questions: {
+            get: function(s) {
+                return s.questions || [];
+            },
+            set: function(s,questions) {
+                s.questions = questions;
+            }
+        }
+    };
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/classes/SurveyRun.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/classes/SurveyRun.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/classes/SurveyRun.js	(revision 420)
@@ -0,0 +1,47 @@
+define(['dojo/_base/lang','dojo/date/locale','dojo/date/stamp'],function(lang,locale,stamp){
+    var SurveyRun = {
+        create: function(){
+            return { type:'SurveyRun' };
+        },
+        StartDate: {
+            get: function(sr) {
+                var d;
+                if ( sr.startDate ) {
+                    d = lang.isString(sr.startDate) ? stamp.fromISOString(sr.startDate) : sr.startDate;
+                }
+                return d;
+            }
+        },
+        EndDate: {
+            get: function(sr) {
+                var d;
+                if ( sr.endDate ) {
+                    d = lang.isString(sr.endDate) ? stamp.fromISOString(sr.endDate) : sr.endDate;
+                }
+                return d;
+            }
+        },
+        DisplayTitle: {
+            get: function(sr) {
+                var t = "Run of '"+sr.survey.title+"'";
+                if ( sr.startDate ) {
+                    t += " from "+locale.format(SurveyRun.StartDate.get(sr));
+                }
+                if ( sr.endDate ) {
+                    t += " until "+locale.format(SurveyRun.EndDate.get(sr));
+                }
+                return t;
+            }
+        },
+        Survey: {
+            get: function(sr) {
+                return sr.survey || null;
+            },
+            set: function(sr,s) {
+                sr.survey = s;
+                return sr;
+            }
+        }
+    };
+    return SurveyRun;
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/schema.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/schema.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/schema.js	(revision 420)
@@ -74,62 +74,82 @@
     //
 
+    var Respondent = dbobject('Respondent',{
+        secret: nestring()
+    });
+
+    var Question = dbobject('Question',{
+        code:nestring(),
+        title: nestring(),
+        description: string(optional()),
+        topic: string(optional()),
+        categories: array(nestring(),optional()),
+        content: array([
+            typedobject('Header',{
+                content:string(optional())
+            }),
+            typedobject('Text',{
+                content:string(optional())
+            }),
+            typedobject('Divider'),
+            typedobject('StringInput'),
+            typedobject('TextInput',{
+                maxLength:integer()
+            }),
+            typedobject('IntegerInput',{
+                min:integer(),
+                max:integer(),
+                step:integer()
+            }),
+            typedobject('MultipleChoiceInput',{
+                multiple:boolean(),
+                items:array(nestring())
+            })
+        ],optional()),
+        publicationDate:datetime(optional())
+    });
+
+    var Survey = dbobject('Survey',{
+        title: nestring(),
+        description: string(optional()),
+        questions: array(Question,optional()),
+        publicationDate:datetime(optional())
+    });
+
+    var SurveyRun = dbobject('SurveyRun',{
+        description: string(optional()),
+        survey: Survey,
+        publicationDate:datetime(optional()),
+        startDate:datetime(optional()),
+        endDate:datetime(optional()),
+        mode:{type:'string',enum:['open','closed'],optional:true},
+        metadata:string(optional())
+    });
+
+    var Response = dbobject('Response',{
+        surveyRunId: nestring(),
+        answers:{type:'object',optional:true},
+        publicationDate:datetime(optional())
+    });
+
+    var Session = dbobject('Session',{
+        title: nestring(),
+        description: string(optional()),
+        publicationDate: datetime(optional())
+    });
+
+    var SessionTemplate = dbobject('SessionTemplate',{
+        title: nestring(),
+        description: string(optional()),
+        publicationDate: datetime(optional()),
+        plannedDate: datetime(optional())
+    });
+
     var schema = {type:[
-        dbobject('Question',{
-            code:nestring(),
-            title: nestring(),
-            description: string(optional()),
-            topic: string(optional()),
-            categories: array(nestring(),optional()),
-            content: array([
-                typedobject('Header',{
-                    content:string(optional())
-                }),
-                typedobject('Text',{
-                    content:string(optional())
-                }),
-                typedobject('Divider'),
-                typedobject('StringInput'),
-                typedobject('TextInput',{
-                    maxLength:integer()
-                }),
-                typedobject('IntegerInput',{
-                    min:integer(),
-                    max:integer(),
-                    step:integer()
-                }),
-                typedobject('MultipleChoiceInput',{
-                    multiple:boolean(),
-                    items:array(nestring())
-                })
-            ],optional()),
-            publicationDate:datetime(optional())
-        }),
-        dbobject('Survey',{
-            title: nestring(),
-            description: string(optional()),
-            questions: array(nestring(),optional()),
-            publicationDate:datetime(optional())
-        }),
-        dbobject('SurveyRun',{
-            description: string(optional()),
-            surveyId: nestring(),
-            publicationDate:datetime(optional()),
-            startDate:datetime(),
-            endDate:datetime(),
-            mode:{type:'string',enum:['open','closed']},
-            respondents: array(nestring(),optional()),
-            metadata:{type:'object',optional:true}
-        }),
-        dbobject('Session',{
-            title: nestring(),
-            description: string(optional()),
-            publicationDate: datetime(optional())
-        }),
-        dbobject('SessionTemplate',{
-            title: nestring(),
-            description: string(optional()),
-            publicationDate: datetime(optional()),
-            plannedDate: datetime(optional())
-        })
+        Question,
+        Survey,
+        SurveyRun,
+        Response,
+        Session,
+        SessionTemplate
     ]};
 
Index: /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveySummary.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveySummary.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveySummary.js	(revision 420)
@@ -0,0 +1,22 @@
+define([
+    'dojo/_base/declare',
+    'dijit/_WidgetBase',
+    'dijit/_TemplatedMixin',
+    'dojo/text!./templates/SurveySummary'
+],function(declare,_WidgetBase,_TemplatedMixin,template){
+    return declare([_WidgetBase,_TemplatedMixin],{
+        templateString: template,
+        survey: null,
+        startup: function() {
+            this.inherited(arguments);
+            if ( this.survey ) {
+                this._setValueAttr(this.survey);
+            }
+        },
+        _setValueAttr: function(survey) {
+            this.titleNode.innerHTML = survey.title;
+            this.descriptionNode.innerHTML = survey.description;
+            this.questionsNode.innerHTML = (survey.questions || []).length;
+        }
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveyWidget.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveyWidget.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/widgets/SurveyWidget.js	(revision 420)
@@ -0,0 +1,38 @@
+define([
+    'dojo/_base/array',
+    'dojo/_base/declare',
+    'dijit/_Container',
+    'dijit/_WidgetBase',
+    'dijit/_TemplatedMixin',
+    'dijit/_WidgetsInTemplateMixin',
+    '../classes/Survey',
+    '../widgets/QuestionWidgetFactory',
+    'dojo/text!./templates/SurveyWidget.html'
+],function(array,declare,_Container,_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,Survey,QuestionWidgetFactory,template){
+    return declare([_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,_Container],{
+        templateString: template,
+        survey: null,
+        startup: function() {
+            if ( this._started ) { return; }
+            this.inherited(arguments);
+            if ( this.survey ) {
+                this._setSurveyAttr(this.survey);
+            }
+        },
+        _setSurveyAttr: function(survey) {
+            this.domNode.innerHTML = "";
+            this.survey = survey;
+            var f = new QuestionWidgetFactory();
+            array.forEach(Survey.Questions.get(this.survey),function(question){
+                array.forEach(question.content || [],function(item,index){
+                    // The dot causes values to be grouped in an object!
+                    item.code = question.code.toString()+'.'+index.toString();
+                    var w = f.createViewWidget(item);
+                    if ( w !== null ) {
+                        w.placeAt(this.domNode);
+                    }
+                },this);
+            },this);
+        }
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyRunFieldset.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyRunFieldset.html	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyRunFieldset.html	(revision 420)
@@ -1,22 +1,26 @@
-<fieldset class="${baseClass}">
+<fieldset class="${baseClass} qedFieldset">
 
-    <label for="mode" class="loginLabel">Description</label>
-    <textarea name="description" data-dojo-type="dijit/form/Textarea"></textarea>
-    <br>
+    <div>
+    <label for="mode" class="qedLabel">Description</label>
+    <textarea name="description" class="qedField" data-dojo-type="dijit/form/Textarea"></textarea>
+    </div>
 
-    <label for="startDate" class="loginLabel">Start date</label>
-    <input type="text" name="startDatetime" data-dojo-type="qed/widgets/DateTimeTextBox" />
-    <br>
+    <div>
+    <label for="startDate" class="qedLabel">Start date</label>
+    <input type="text" name="startDate" class="qedField" data-dojo-type="dijit/form/DateTextBox" data-dojo-attach-point="startDateBox" />
+    </div>
 
-    <label for="endDate" class="loginLabel">End date</label>
-    <input type="text" name="endDatetime" data-dojo-type="qed/widgets/DateTimeTextBox" />
-    <br>
+    <div>
+    <label for="endDate" class="qedLabel">End date</label>
+    <input type="text" name="endDate" class="qedField" data-dojo-type="dijit/form/DateTextBox" data-dojo-attach-point="endDateBox" />
+    </div>
 
-    <label for="mode" class="loginLabel">Mode</label>
-    <select name="mode" data-dojo-type="dijit/form/Select">
+    <div>
+    <label for="mode" class="qedLabel">Mode</label>
+    <select name="mode" class="qedField" data-dojo-type="dijit/form/Select">
         <option value="open" selected="selected">Open</option>
         <option value="closed">Closed</option>
     </select>
-    <br>
+    </div>
 
 </fieldset>
Index: /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveySummary.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveySummary.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveySummary.html	(revision 420)
@@ -0,0 +1,6 @@
+<fieldset class="qedFieldset">
+    <legend>Survey</legend>
+    <div><div class="qedLabel">Title</div><div class="qedField" data-dojo-attach-point="titleNode"></div></div>
+    <div><div class="qedLabel">Description</div><div class="qedField" data-dojo-attach-point="descriptionNode"></div></div>
+    <div><div class="qedLabel">Questions</div><div class="qedField" data-dojo-attach-point="questionsNode"></div></div>
+</fieldset>
Index: /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyWidget.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyWidget.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyWidget.html	(revision 420)
@@ -0,0 +1,2 @@
+<div class="${baseClass}">
+</div>
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/previewSurvey.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/previewSurvey.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/previewSurvey.js	(revision 420)
@@ -0,0 +1,26 @@
+define([
+    'dojo/_base/array',
+    'dojo/_base/declare',
+    'dojo/_base/lang',
+    'dojo/when',
+    '../store',
+    '../app/Page',
+    'dojo/text!./templates/previewSurvey.html'
+],function(array,declare,lang,when,store,Page,template){
+    return declare([Page],{
+        templateString: template,
+        startup: function() {
+            if ( this._started ) { return; }
+            this.inherited(arguments);
+            if ( this.surveyId ) {
+                when(store.get(this.surveyId))
+                .then(lang.hitch(this,function(survey){
+                    this.titleNode.innerHTML = survey.title;
+                    this.surveyWidget.set('survey',survey);
+                }));
+            } else {
+                throw new Error("No valid uid or survey passed!");
+            }
+        }
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/question.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/question.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/question.js	(revision 420)
@@ -1,15 +1,16 @@
 define([
     'dojo/_base/declare',
-    'dojo/_base/Deferred',
     'dojo/_base/event',
     'dojo/_base/lang',
+    'dojo/when',
     '../store',
     '../app/Content',
     '../app/Router',
     '../app/Page',
+    '../model/classes/Question',
     '../model/widgets/QuestionEditorPreview',
     '../model/widgets/QuestionEditorToolkit',
     'dojo/text!./templates/question.html'
-],function(declare, Deferred, event, lang, store, Content, Router, Page, QuestionEditorPreview, QuestionEditorToolkit, template){
+],function(declare, event, lang, when, store, Content, Router, Page, Question, QuestionEditorPreview, QuestionEditorToolkit, template){
     return declare([Page], {
         templateString: template,
@@ -26,8 +27,8 @@
             this._setupEditor();
             if (this.questionId === "new") {
-                this.question = { type: 'Question' };
+                this.question = Question.create();
                 this._refresh();
             } else {
-                Deferred.when(store.get(this.questionId))
+                when(store.get(this.questionId))
                 .then(lang.hitch(this, function(obj) {
                     this.question = obj;
@@ -40,11 +41,11 @@
         },
         _refresh: function () {
-            this.titleNode.innerHTML = this.question.title || "";
+            this.titleNode.innerHTML = Question.DisplayTitle.get(this.question);
             this._toolkit.set('value',this.question);
-            this._preview.appendItems(this.question.content || []);
+            this._preview.appendItems(Question.Content.get(this.question));
         },
         _onSave: function(evt) {
             lang.mixin(this.question, this._toolkit.get('value'));
-            this.question.content = this._preview.getItems();
+            Question.Content.set(this.question, this._preview.getItems());
             store.put(this.question)
             .then(function() {
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/response.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/response.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/response.js	(revision 420)
@@ -0,0 +1,78 @@
+define([
+    'dojo/_base/declare',
+    'dojo/_base/event',
+    'dojo/_base/lang',
+    'dojo/promise/all',
+    'dojo/when',
+    '../store',
+    '../app/Page',
+    '../lib/async',
+    '../model/classes/Response',
+    '../model/classes/Survey',
+    '../model/classes/SurveyRun',
+    'dojo/text!./templates/response.html'
+],function(declare,event,lang,all,when,store,Page,async,Response,Survey,SurveyRun,template){
+    return declare([Page],{
+        templateString: template,
+        response: null,
+        postCreate: function(){
+            this.options = this.options || {};
+        },
+        startup: function() {
+            if ( this._started ) { return; }
+            this.inherited(arguments);
+            this._disableButtons();
+            var surveyRunId = this.surveyRunId;
+            var responseId = this.options && this.options.id;
+            if ( surveyRunId && responseId ) {
+                this._loadSurveyAndResponse(surveyRunId,responseId)
+                .then(lang.hitch(this,"_enableButtons"));
+            } else {
+                throw new Error("No valid uid or survey passed!");
+            }
+        },
+        _loadSurveyAndResponse: function(surveyRunId,responseId){
+            return all([store.get(surveyRunId),store.get(responseId)])
+            .then(lang.hitch(this,function(surveyAndResponse){
+                var surveyRun = surveyAndResponse[0];
+                this.response = surveyAndResponse[1];
+                if ( this.response.surveyRunId !== surveyRunId ) {
+                    throw "Survey does not match the response...";
+                }
+                this.titleNode.innerHTML = Survey.DisplayTitle.get(surveyRun.survey);
+                this.surveyWidget.set('survey',surveyRun.survey);
+                this.responseForm.set('value',this.response.answers || {});
+            }));
+        },
+        _enableButtons: function() {
+            this.submitButton.set('disabled',false);
+            this.continueButton.set('disabled',false);
+        },
+        _disableButtons: function() {
+            this.submitButton.set('disabled',true);
+            this.continueButton.set('disabled',true);
+        },
+        _getAnswersAndSaveResponse: function() {
+            this.response.answers = this.responseForm.get('value');
+            console.log(this.response);
+            return store.put(this.response);
+        },
+        _onSubmit: function(e) {
+            this.response.publicationDate = store.timestamp();
+            this._getAnswersAndSaveResponse();
+            this.content.domNode.innerHTML = "<div>Thanks for filling in the survey.</div>";
+            e && event.stop(e);
+            return false;
+        },
+        _onContinueLater: function(e) {
+            this._getAnswersAndSaveResponse();
+            this.content.domNode.innerHTML = "<div>To continue with this survey later, just save the URL in the location bar and revisit it later. Your answers will still be there.</div>";
+            e && event.stop(e);
+            return false;
+        },
+        _ignoreEvent: function(e) {
+            e && event.stop(e);
+            return false;
+        }
+    });
+});
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/session.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/session.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/session.js	(revision 420)
@@ -2,7 +2,7 @@
     'dojo/_base/array',
     'dojo/_base/declare',
-    'dojo/_base/Deferred',
     'dojo/_base/event',
     'dojo/_base/lang',
+    'dojo/when',
     '../search',
     '../store',
@@ -10,7 +10,8 @@
     '../app/Router',
     '../widgets/ThresholdFilteringSelect',
+    '../model/classes/SessionTemplate',
     '../model/widgets/AccountListView',
     'dojo/text!./templates/session.html'
-],function(array,declare,Deferred,event,lang,search,store,Page,Router,ThresholdFilteringSelect,AccountListView,template){
+],function(array,declare,event,lang,when,search,store,Page,Router,ThresholdFilteringSelect,SessionTemplate,AccountListView,template){
     return declare([Page],{
         templateString: template,
@@ -30,9 +31,7 @@
         _loadSession: function() {
             if ( this.sessionId === "new" ) {
-                this.session = {
-                    type: 'SessionTemplate'
-                };
+                this.session = SessionTemplate.create();
             } else {
-                Deferred.when(store.get(this.sessionId))
+                when(store.get(this.sessionId))
                 .then(lang.hitch(this,function(obj){
                     this.session = obj;
@@ -43,5 +42,5 @@
         },
         _refresh: function() {
-            this.titleNode.innerHTML = this.session.title || '';
+            this.titleNode.innerHTML = SessionTemplate.DisplayTitle.get(this.session);
             this.propertiesForm.set('value',this.session);
         },
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/survey.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/survey.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/survey.js	(revision 420)
@@ -2,14 +2,15 @@
     'dojo/_base/array',
     'dojo/_base/declare',
-    'dojo/_base/Deferred',
     'dojo/_base/event',
     'dojo/_base/lang',
+    'dojo/when',
     '../app/Router',
     '../store',
     '../app/Page',
+    '../model/classes/Survey',
     '../model/widgets/QuestionListView',
     '../model/widgets/TabbedQuestionBrowser',
     'dojo/text!./templates/survey.html'
-],function(array,declare,Deferred,event,lang,Router,store,Page,
+],function(array,declare,event,lang,when,Router,store,Page,Survey,
          QuestionListView,TabbedQuestionBrowser,template){
     return declare([Page],{
@@ -17,8 +18,4 @@
         survey: null,
         questionList: null,
-        _dataMap: null,
-        constructor: function(){
-            this._dataMap = {};
-        },
         startup: function() {
             if ( this._started ) { return; }
@@ -62,14 +59,12 @@
         _loadSurvey: function() {
             if ( this.surveyId === "new" ) {
-                this.survey = {
-                    type: 'Survey'
-                };
+                this.survey = Survey.create();
                 this.refresh();
             } else {
-                Deferred.when(store.get(this.surveyId))
+                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'));
+                    array.forEach(Survey.Questions.get(this.survey),
+                        lang.hitch(this.questionList,'appendItem'));
                     this.refresh();
                 }));
@@ -80,5 +75,5 @@
         },
         refresh: function() {
-            this.titleNode.innerHTML = this.survey.title || "(set title in properties)";
+            this.titleNode.innerHTML = Survey.DisplayTitle.get(this.survey) || "(set title in properties)";
             this.propertiesDialog.set('value',this.survey);
         },
@@ -100,7 +95,5 @@
         },
         _onSave: function(evt) {
-            this.survey.questions = array.map(this.questionList.getItems(),function(item){
-                return store.getIdentity(item);
-            });
+            this.survey.questions = this.questionList.getItems();
             store.put(this.survey)
             .then(function() { 
@@ -114,5 +107,5 @@
         },
         _onShowPreview: function() {
-            Router.go('/viewSurvey/'+store.getIdentity(this.survey),{
+            Router.go('/previewSurvey/'+store.getIdentity(this.survey),{
                 preview: true
             });
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/surveyRun.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/surveyRun.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/surveyRun.js	(revision 420)
@@ -0,0 +1,68 @@
+define([
+    'dojo/_base/declare',
+    'dojo/_base/event',
+    'dojo/_base/lang',
+    'dojo/when',
+    '../app/Content',
+    '../app/Router',
+    '../lib/func',
+    '../store',
+    '../app/Page',
+    '../model/classes/SurveyRun',
+    'dojo/text!./templates/surveyRun.html'
+],function(declare,event,lang,when,Content,Router,func,store,Page,SurveyRun,template){
+    return declare([Page],{
+        templateString: template,
+        surveyRun: null,
+        startup: function() {
+            if ( this._started ) { return; }
+            this.inherited(arguments);
+            this.propertiesForm.on("blur",lang.hitch(this,'_onPropChange'));
+            if ( this.surveyRunId ) {
+                this._loadSurveyRun();
+            } else {
+                throw "No valid uid or survey passed!";
+            }
+        },
+        _loadSurveyRun: function() {
+            when(store.get(this.surveyRunId))
+            .then(lang.hitch(this,function(surveyRun){
+                this.surveyRun = surveyRun;
+                this.refresh();
+            }));
+        },
+        refresh: function() {
+            this.titleNode.innerHTML = SurveyRun.DisplayTitle.get(this.surveyRun);
+            this.surveyNode.set('value',SurveyRun.Survey.get(this.surveyRun));
+            this.propertiesForm.set('value',this.surveyRun);
+            this._onPropChange();
+        },
+        _onPropChange: function(e) {
+            var surveyRun = this.propertiesForm.get('value');
+            if ( surveyRun.mode === "open" ) {
+                var url = 'response.html#!/'+store.getIdentity(this.surveyRun);
+                this.runURLNode.innerHTML = '<a target="_black" href="'+url+'">'+url+'</a>';
+            } else {
+                this.runURLNode.innerHTML = "No general URL. Add individual respondents below.";
+            }
+        },
+        _onSave: function(evt) {
+            lang.mixin(this.surveyRun,this.propertiesForm.get('value'));
+            var not = function(p){ return !p; };
+            func.modPropIf(this.surveyRun,"startDate",not.compose(lang.isString),store.formatDate);
+            func.modPropIf(this.surveyRun,"endDate",not.compose(lang.isString),store.formatDate);
+            store.put(this.surveyRun)
+            .then(function() { 
+                Router.go('/surveys');
+            },function(err){
+                Content.notify(err);
+            });
+            event.stop(evt);
+            return false;
+        },
+        _onDiscard: function(evt) {
+            Router.go('/surveys');
+        }
+    });
+});
+
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/surveys.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/surveys.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/surveys.js	(revision 420)
@@ -9,7 +9,9 @@
     '../app/Page',
     '../app/Router',
+    '../model/classes/Survey',
+    '../model/classes/SurveyRun',
     '../widgets/LineWithActionsWidget',
     'dojo/text!./templates/surveys.html'
-],function(array,declare,lang,when,Rx,store,Content,Page,Router,LineWithActionsWidget,template){
+],function(array,declare,lang,when,Rx,store,Content,Page,Router,Survey,SurveyRun,LineWithActionsWidget,template){
     return declare([Page],{
         templateString: template,
@@ -45,27 +47,18 @@
         },
         _onPreviewSurvey:function(survey){
-            Router.go('/viewSurvey/'+store.getIdentity(survey),{preview:true});
+            Router.go('/previewSurvey/'+store.getIdentity(survey));
         },
         _onRunSurvey:function(survey){
-            this.surveyRun = {
-                type: 'SurveyRun',
-                surveyId: store.getIdentity(survey),
-                publicationDate: store.timestamp()
-            };
-            this.surveyRunDialog.set('value',this.surveyRun);
-            this.surveyRunDialog.show();
-        },
-        _onSurveyRunOk: function() {
-            var surveyRun = lang.mixin(lang.clone(this.surveyRun),this.surveyRunDialog.get('value'));
+            var surveyRun = SurveyRun.create();
+            SurveyRun.Survey.set(surveyRun,survey);
             store.put(surveyRun)
-            .then(lang.hitch(this,function(){
-                this.surveyRunDialog.hide();
-                this.refreshRuns();
+            .then(lang.hitch(this,function(surveyRun){
+                this._onRunDetails(surveyRun);
             }),function(err){
                 Content.notify(err);
             });
         },
-        _onSurveyRunCancel: function() {
-            this.surveyRunDialog.hide();
+        _onRunDetails: function(surveyRun) {
+            Router.go('/surveyRun/'+store.getIdentity(surveyRun));
         },
         refresh: function() {
@@ -81,5 +74,5 @@
                 array.forEach(surveys,function(survey){
                     var w = new LineWithActionsWidget({
-                        title: survey.title || '(unnamed)',
+                        title: Survey.DisplayTitle.get(survey) || '(unnamed)',
                         actions: [{
                             callback: lang.hitch(this,'_onPublishSurvey',survey),
@@ -123,5 +116,5 @@
                 array.forEach(surveys,function(survey){
                     var w = new LineWithActionsWidget({
-                        title: survey.title,
+                        title: Survey.DisplayTitle.get(survey),
                         actions:[{
                             callback: lang.hitch(this,'_onPreviewSurvey',survey),
@@ -151,11 +144,11 @@
                 array.forEach(surveyRuns,function(surveyRun){
                     var w = new LineWithActionsWidget({
-                        title: surveyRun.title+" (from "+surveyRun.startDate+" to "+surveyRun.endDate+")",
+                        title: SurveyRun.DisplayTitle.get(surveyRun),
                         actions:[{
-                            callback: lang.hitch(this,'_onCloseRun',surveyRun),
+                            callback: lang.hitch(this,'_onRunDetails',surveyRun),
                             properties: {
-                                label: 'Close',
-                                tooltip: 'Close survey',
-                                icon: 'Close'
+                                label: 'Details',
+                                tooltip: 'Show details for this run',
+                                icon: 'Details'
                             }
                         }]
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/templates/previewSurvey.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/templates/previewSurvey.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/templates/previewSurvey.html	(revision 420)
@@ -0,0 +1,16 @@
+<div class="blue">
+
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
+        <h2>
+            <span class="rftIcon rftIconSurvey"></span>
+            <span class="headerText" data-dojo-attach-point="titleNode">Survey</span>
+        </h2>
+    </div>
+    
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'" data-dojo-attach-point="questionsPane">
+        <form data-dojo-type="dijit/form/Form" data-dojo-attach-point="questionsForm" style="overflow: auto">
+            <div data-dojo-type="qed/model/widgets/SurveyWidget" data-dojo-attach-point="surveyWidget"></div>
+        </form>
+    </div>
+    
+</div>
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/templates/response.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/templates/response.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/templates/response.html	(revision 420)
@@ -0,0 +1,31 @@
+<div class="blue">
+
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
+        <h2>
+            <span class="rftIcon rftIconSurvey"></span>
+            <span class="headerText" data-dojo-attach-point="titleNode">Survey</span>
+        </h2>
+    </div>
+    
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'" data-dojo-attach-point="content">
+        <form data-dojo-type="dijit/form/Form" data-dojo-attach-point="responseForm"
+              data-dojo-attach-event="onSubmit:_ignoreEvent" style="overflow: auto">
+            <div data-dojo-type="qed/model/widgets/SurveyWidget" data-dojo-attach-point="surveyWidget"></div>
+        </form>
+    </div>
+    
+
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'bottom'">
+        <button data-dojo-type="dijit/form/Button"
+                type="submit"
+                data-dojo-attach-point="submitButton"
+                data-dojo-attach-event="onClick:_onSubmit">
+            Submit</button>
+        <button data-dojo-type="dijit/form/Button"
+                type="button"
+                data-dojo-attach-point="continueButton"
+                data-dojo-attach-event="onClick:_onContinueLater">
+            Continue later</button>
+    </div>
+
+</div>
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveyRun.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveyRun.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveyRun.html	(revision 420)
@@ -0,0 +1,31 @@
+<div class="blue">
+
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
+        <h2>
+            <span class="rftIcon rftIconSurvey"></span>
+            <span class="headerText" data-dojo-attach-point="titleNode">SurveyRun Editor</span>
+        </h2>
+    </div>
+    
+    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'">
+        <div data-dojo-type="qed/model/widgets/SurveySummary" data-dojo-attach-point="surveyNode"></div>
+        <div data-dojo-type="dijit/form/Form"
+             data-dojo-attach-point="propertiesForm">
+            <fieldset data-dojo-type="qed/model/widgets/SurveyRunFieldset"></fieldset>
+        </div>
+        <fieldset class="qedFieldset">
+            <div><div class="qedLabel">General URL</div><div class="qedField" data-dojo-attach-point="runURLNode" style="overflow: ellipsis"></div></div>
+        </fieldset>
+    </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 rftIconSave'"
+                data-dojo-attach-event="onClick:_onSave">Save &amp; Close</button>
+        <button data-dojo-type="dijit/form/Button"
+                class="blue"
+                data-dojo-props="baseClass: 'rftBlockButton', iconClass: 'rftIcon rftIconClose'"
+                data-dojo-attach-event="onClick:_onDiscard">Discard &amp; Close</button>
+    </div>
+</div>
Index: /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveys.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveys.html	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveys.html	(revision 420)
@@ -36,11 +36,3 @@
     </div>
 
-    <div data-dojo-type="dijit/Dialog"
-         title="SurveyRun properties"
-         data-dojo-attach-point="surveyRunDialog">
-        <fieldset data-dojo-type="qed/model/widgets/SurveyRunFieldset"></fieldset>
-        <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_onSurveyRunOk">OK</button>
-        <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_onSurveyRunCancel">Cancel</button>
-    </div>
-
 </div>
Index: v/branches/rest-dojo-ui/client/qed/pages/templates/viewSurvey.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/templates/viewSurvey.html	(revision 419)
+++ 	(revision )
@@ -1,29 +1,0 @@
-<div class="blue">
-
-    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
-        <h2>
-            <span class="rftIcon rftIconSurvey"></span>
-            <span class="headerText" data-dojo-attach-point="titleNode">Survey</span>
-        </h2>
-    </div>
-    
-    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'" data-dojo-attach-point="questionsPane">
-        <form data-dojo-type="dijit/form/Form" data-dojo-attach-point="questionsForm"
-              data-dojo-attach-event="onSubmit:_onSubmit"
-              style="overflow: auto">
-            <div data-dojo-attach-point="questionsAnchor"></div>
-        </form>
-    </div>
-    
-    <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"
-                data-dojo-attach-event="onClick:_onSubmit">
-            Submit</button>
-        <button data-dojo-type="dijit/form/Button"
-                type="button"
-                data-dojo-attach-event="onClick:_onCancel">
-            Cancel</button>
-    </div>
-
-</div>
Index: v/branches/rest-dojo-ui/client/qed/pages/viewSurvey.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/pages/viewSurvey.js	(revision 419)
+++ 	(revision )
@@ -1,68 +1,0 @@
-define([
-    'dojo/_base/array',
-    'dojo/_base/declare',
-    'dojo/_base/Deferred',
-    'dojo/_base/event',
-    'dojo/_base/lang',
-    '../store',
-    '../app/Page',
-    '../model/widgets/QuestionWidgetFactory',
-    'dojo/text!./templates/viewSurvey.html'
-],function(array,declare,Deferred,event,lang,store,Page,QuestionWidgetFactory,template){
-    return declare([Page],{
-        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(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 QuestionWidgetFactory();
-                    this.survey = survey;
-                    store.query(null,{keys:this.survey.questions,include_docs:true})
-                    .forEach(function(question){
-                        array.forEach(question.content || [],function(item){
-                            var w = f.createViewWidget(item,{
-                                name: question.code+'.'+item.code
-                            });
-                            if ( w !== null ) {
-                                w.placeAt(this.questionsAnchor,'before');
-                            }
-                        },this);
-                    },this);
-                }));
-            } else {
-                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>');
-            event.stop(evt);
-            return false;
-        },
-        _onCancel: function(evt) {
-            if ( this.options.preview ) { return; }
-            event.stop(evt);
-            return false;
-        }
-    });
-});
Index: /Dev/branches/rest-dojo-ui/client/qed/routes.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/routes.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/routes.js	(revision 420)
@@ -5,8 +5,9 @@
     './pages/surveys',
     './pages/survey',
+    './pages/surveyRun',
     './pages/sessions',
     './pages/session',
-    './pages/viewSurvey'
-],function(index,questions,question,surveys,survey,sessions,session,viewSurvey){
+    './pages/previewSurvey'
+],function(index,questions,question,surveys,survey,surveyRun,sessions,session,previewSurvey){
 
     return [
@@ -17,7 +18,8 @@
         { path: "/surveys", constructor: surveys },
         { path: "/survey/:surveyId", constructor: survey },
-        { path: "/sessions", constructor: sessions },
-        { path: "/session/:sessionId", constructor: session },
-        { path: "/viewSurvey/:surveyId", constructor: viewSurvey }
+        { path: "/surveyRun/:surveyRunId", constructor: surveyRun },
+        //{ path: "/sessions", constructor: sessions },
+        //{ path: "/session/:sessionId", constructor: session },
+        { path: "/previewSurvey/:surveyId", constructor: previewSurvey }
     ];
 
Index: v/branches/rest-dojo-ui/client/qed/run.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/run.js	(revision 419)
+++ 	(revision )
@@ -1,28 +1,0 @@
-require([
-    'dojo/_base/array',
-    'dojo/parser',
-    'qed/app/Content',
-    'qed/app/Page',
-    'qed/app/Router',
-    'qed/ui/MainMenu',
-    'qed/routes',
-    'qed/store',
-    'qed/stddeps',
-    'dojo/domReady!'
-],function(array,parser,Content,Page,Router,MainMenu,routes,store) {
-    parser.parse();
-
-    store.info()
-    .then(function(){
-        new MainMenu({},"menu").startup();
-        array.forEach(routes,function(route){
-            Router.register(route);
-        });
-        Router.startup();
-    },function(){
-        Content.startup();
-        Content.set(new Page({
-            templateString: "<div>Application database is not properly configured. Contact your system administrator.</div>"
-        }));
-    });
-});
Index: /Dev/branches/rest-dojo-ui/client/qed/stddeps.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/stddeps.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/stddeps.js	(revision 420)
@@ -27,8 +27,10 @@
     'qed/app/Notifications',
 
+    'qed/model/widgets/AccountListView',
+    'qed/model/widgets/QuestionWidget',
     'qed/model/widgets/SurveyFieldset',
     'qed/model/widgets/SurveyRunFieldset',
-    'qed/model/widgets/QuestionWidget',
-    'qed/model/widgets/AccountListView',
+    'qed/model/widgets/SurveySummary',
+    'qed/model/widgets/SurveyWidget',
 
     'qed/ui/MainMenu',
@@ -40,5 +42,4 @@
     'qed/widgets/Selector',
     'qed/widgets/TitleGroup',
-    'qed/widgets/DateTimeTextBox',
     'qed/widgets/list/List',
     'qed/widgets/list/OrderedList'
Index: /Dev/branches/rest-dojo-ui/client/qed/store.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/store.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/store.js	(revision 420)
@@ -20,6 +20,14 @@
         }
     });
-    var memoryStore = new Memory();
-    var cacheStore = new Cache(couchStore,memoryStore,{});
+    var memoryStore = new Memory({
+        idProperty: couchStore.idProperty
+    });
+    var cacheStore = new Cache(couchStore,memoryStore,{
+        isLoaded: function(object) {
+            // Unfortunately we cannot determine of the full documents were
+            // loaded or not. Therefore never cache query results.
+            return false;
+        }
+    });
 
     var store = cacheStore;
Index: v/branches/rest-dojo-ui/client/qed/view.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/view.js	(revision 419)
+++ 	(revision )
@@ -1,29 +1,0 @@
-require([
-    'dojo/hash',
-    'dojo/parser',
-    'qed/app/Content',
-    'qed/app/Page',
-    'qed/pages/viewSurvey',
-    'dojo/domReady!',
-    'qed/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
-
-    Content.set(new viewSurvey({
-        surveyId: surveyId
-    }));
-});
Index: v/branches/rest-dojo-ui/client/qed/widgets/DateTimeTextBox.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/widgets/DateTimeTextBox.js	(revision 419)
+++ 	(revision )
@@ -1,22 +1,0 @@
-define([
-    'dojo/_base/declare',
-    'dojo/on',
-    'dijit/_WidgetBase',
-    'dijit/_TemplatedMixin',
-    'dijit/_WidgetsInTemplateMixin',
-    'dojo/text!./templates/DateTimeTextBox.html'
-],function(declare,on,_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,template){
-    return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin],{
-        templateString: template,
-        baseClass: "rftDateTimeTextBox",
-        _getValueAttr: function() {
-            var date = this.dateBox.get('value').getTime();
-            var time = this.timeBox.get('value').getTime();
-            return new Date(date+time);
-        },
-        _setValueAttr: function(value) {
-            this.dateBox.set('value',value);
-            this.timeBox.set('value',value);
-        }
-    });
-});
Index: /Dev/branches/rest-dojo-ui/client/qed/widgets/list/List.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/qed/widgets/list/List.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/client/qed/widgets/list/List.js	(revision 420)
@@ -63,8 +63,10 @@
 
         getItems: function() {
-            return this.source.getAllNodes()
-            .map(function(node){
-                return this.source.getItem(node.id).data;
+            var items = [];
+            this.source.getAllNodes()
+            .forEach(function(node){
+                items.push(this.source.getItem(node.id).data);
             },this);
+            return items;
         },
 
Index: /Dev/branches/rest-dojo-ui/client/response.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/response.html	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/response.html	(revision 420)
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Survey</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" type="text/css" href="qed/css/main.css" />
+    </head>
+    <body id="rft" class="dijitReset claro">
+        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
+        <script type="text/javascript" src="qed/main-response.js"></script>
+        <div id="content" class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
+        </div>
+        <div id="toaster" data-dojo-type="qed/app/Notifications">
+        </div>
+    </body>
+</html>
Index: /Dev/branches/rest-dojo-ui/client/run.sh
===================================================================
--- /Dev/branches/rest-dojo-ui/client/run.sh	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/run.sh	(revision 420)
@@ -0,0 +1,2 @@
+#!/bin/sh
+nodejs dojo/dojo load=$1
Index: /Dev/branches/rest-dojo-ui/client/test/async.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/test/async.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/test/async.js	(revision 420)
@@ -0,0 +1,38 @@
+define([
+    'dojo/_base/lang',
+    'dojo/Deferred',
+    'qed/lib/async'
+],function(lang,Deferred,async){
+
+    function delayed(i) {
+        var t = setTimeout(function(){
+            console.log("Delay fired after "+i+"s");
+            d.resolve(i);
+        },i*1000);
+        var d = new Deferred(function(){
+            t && clearTimeout(t);
+            d.reject(i);
+        });
+        return d.promise;
+    };
+
+    var is = [5,1,4,2,3];
+
+    async.seq([
+        function() {
+            console.log("foreach - items should appear in original order");
+            return async.forEach(is,delayed).then(console.log);
+        },
+        function () {
+            console.log("map - items should appear sorted");
+            return async.map(is,delayed).then(console.log);
+        }
+    ]).then(function(){
+        console.log("Done!");
+    },function(err){
+        console.log("Fail!",err);
+    });
+
+
+
+});
Index: /Dev/branches/rest-dojo-ui/client/test/promise.js
===================================================================
--- /Dev/branches/rest-dojo-ui/client/test/promise.js	(revision 420)
+++ /Dev/branches/rest-dojo-ui/client/test/promise.js	(revision 420)
@@ -0,0 +1,22 @@
+define([
+    'dojo/_base/lang',
+    'dojo/Deferred'
+],function(lang,Deferred){
+
+    var d = new Deferred();
+    d.then(function(){
+        console.log("Then 1 was called.");
+        throw "Fail";
+    })
+    .then(function(){
+        console.log("Then 2 was called.");
+    })
+    .then(function(){
+        console.log("Then 3 was called.");
+    },function(){
+        console.log("Otherwise 3 was called.");
+    });
+
+    d.resolve(true);
+
+});
Index: v/branches/rest-dojo-ui/client/view.html
===================================================================
--- /Dev/branches/rest-dojo-ui/client/view.html	(revision 419)
+++ 	(revision )
@@ -1,19 +1,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <title>Survey</title>
-        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-        <link rel="stylesheet" type="text/css" href="qed/css/main.css" />
-    </head>
-    <body class="dijitReset claro">
-        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
-        <script type="text/javascript" src="qed/view.js"></script>
-        <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>
-        <div id="toaster" data-dojo-type="qed/app/Notifications">
-        </div>
-    </body>
-</html>
Index: /Dev/branches/rest-dojo-ui/server/couchdb-admin/bootstrap.js
===================================================================
--- /Dev/branches/rest-dojo-ui/server/couchdb-admin/bootstrap.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/server/couchdb-admin/bootstrap.js	(revision 420)
@@ -5,5 +5,5 @@
             'dijit',
             'dojox',
-            'rft'
+            'qed'
         ],
         '.':[
Index: /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/check.js
===================================================================
--- /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/check.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/check.js	(revision 420)
@@ -1,9 +1,10 @@
 define([
      'dojo/_base/array',
+     'dojo/_base/lang',
      'dojox/json/schema',
-     './util/async',
+     'qed/lib/async',
      './util/db',
-     'rft/schema'
-],function(array,jsonSchema,async,db,schema){
+     'qed/model/schema'
+],function(array,lang,jsonSchema,async,db,schema){
 
     async.seq([
@@ -23,5 +24,5 @@
         function(docs){
             console.log("Validating documents.");
-            return async.forEach(docs,function(doc){
+            return async.map(docs,function(doc){
                 var id = doc.id;
                 return db.req('get','qed/'+id)
@@ -31,8 +32,10 @@
                 });
             });
-        },function(){
-            console.log("Done!");
         }
-    ]);
+    ]).then(function() {
+        console.log("Done!");
+    },function(err){
+        console.log("Fail!",err.stack);
+    });
 
 
Index: /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/config.js
===================================================================
--- /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/config.js	(revision 419)
+++ /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/config.js	(revision 420)
@@ -3,5 +3,5 @@
      'dojo/_base/lang',
      'dojox/lang/functional',
-     './util/async',
+     'qed/lib/async',
      './util/db',
      './data/design-docs'
Index: v/branches/rest-dojo-ui/server/couchdb-admin/config/util/async.js
===================================================================
--- /Dev/branches/rest-dojo-ui/server/couchdb-admin/config/util/async.js	(revision 419)
+++ 	(revision )
@@ -1,54 +1,0 @@
-define([
-     'dojo/Deferred',
-],function(Deferred){
-
-    function seq(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 forEach(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;
-    }
-
-    return {
-        seq: seq,
-        forEach: forEach
-    };
-
-});
