Changeset 420


Ignore:
Timestamp:
12/16/12 20:07:35 (12 years ago)
Author:
hendrikvanantwerpen
Message:

We can store answers for surveys now!

Introduces SurveyRun?, which can be edited. Workflow not quite clear yet. A
running survey can be accessed to leave a response. When the response
has an ID, it is loaded (used for closed surveys and continuations). A
researcher cannot create responses yet. He should also be able to add
comments to responses (that he creates).

Introduced caching of store requests.

Factored out path matching and formatting.

Put object creation in separate classes, to localize model/storage
dependency. Not consistent at the moment.

Location:
Dev/branches/rest-dojo-ui
Files:
20 added
1 deleted
18 edited
2 copied
6 moved

Legend:

Unmodified
Added
Removed
  • Dev/branches/rest-dojo-ui/client/admin.html

    r419 r420  
    88    <body class="dijitReset claro" id="rft">
    99        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
    10         <script type="text/javascript" src="qed/run.js"></script>
     10        <script type="text/javascript" src="qed/main-admin.js"></script>
    1111        <div id="content" class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
    1212            <div class="topbar" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
  • Dev/branches/rest-dojo-ui/client/index.html

    r419 r420  
    77    </head>
    88    <body class="dijitReset claro" id="rft">
    9         <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
    10         <script type="text/javascript" src="qed/run.js"></script>
    11         <div id="content" class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
    12             <div class="topbar" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
    13                 <a href="#!/"><h1>QED</h1></a>
    14                 <div id="menu"></div>
    15             </div>
     9        <div class="topbar">
     10            <h1>QED</h1><br>
     11            <p>Nothing is shown here.</p>
    1612        </div>
    17         <div id="toaster" data-dojo-type="qed/app/Notifications"></div>
    1813    </body>
    1914</html>
  • Dev/branches/rest-dojo-ui/client/qed/app/Notifications.js

    r418 r420  
    33    return declare([Toaster],{
    44        positionDirection: "br-up",
    5         duration: 1000
     5        duration: 3000
    66    });
    77});
  • Dev/branches/rest-dojo-ui/client/qed/app/Router.js

    r410 r420  
    22    'dojo/_base/declare',
    33    'dojo/hash',
    4     'dojo/io-query',
    54    'dojo/topic',
    65    './Content',
    7     './Page'
    8 ],function(declare,hash,ioQuery,topic,Content,Page){
     6    './Page',
     7    './Path'
     8],function(declare,hash,topic,Content,Page,Path){
    99
    1010    var Router = declare(null,{
     
    1212        _routes: null,
    1313        _previousHash: null,
    14 
    15         _paramMatch: /:(\w[\w\d]*)/g,
    16         _paramReplace: "([^\\/!]+)",
    1714
    1815        constructor: function() {
     
    2926
    3027            if ( hash() === "" ) {
    31                 hash("#!/");
     28                hash(Path.getDefault());
    3229            }
    3330            this._handlePathChange(hash());
     
    4037                console.warn('Registering routes after startup() is called is discouraged.');
    4138            }
     39            var callback = this._normalizeCallback(route);
     40            if ( callback ) {
     41                if ( route.path ) {
     42                    route.callback = callback;
     43                    route.path = new Path(route.path);
     44                    this._routes.push(route);
     45                } else {
     46                    this._defaultCallback = callback;
     47                }
     48            } else {
     49                console.warn("Route "+(route.path||"default")+" has no action.");
     50            }
     51        },
     52        _normalizeCallback: function(route) {
    4253            var self = this;
    43             var callback;
     54            var callback = null;
    4455            if ( route.callback ) {
    4556                callback = function(params){
     
    5566                };
    5667            }
    57             if ( callback ) {
    58                 if ( route.path ) {
    59                     this._routes.push(this._createRoute(route.path,callback));
    60                 } else {
    61                     this._defaultCallback = callback;
    62                 }
    63             } else {
    64                 console.warn("Route "+(route.path||"default")+" has no action.");
    65             }
    66         },
    67         _createRoute: function(path,callback) {
    68             var match, route = {};
    69             route.callback = callback;
    70             route.paramNames = [];
    71                         while((match = this._paramMatch.exec(path)) !== null){
    72                                 route.paramNames.push(match[1]);
    73                         }
    74             path = path.replace(this._paramMatch, this._paramReplace);
    75             route.regexp = new RegExp('^!'+path+'(!(.*))?$');
    76             return route;
     68            return callback;
    7769        },
    7870        _handlePathChange: function(newHash) {
    7971            if ( this._previousHash === newHash ) { return; }
    8072            this._previousHash = newHash;
    81 
    8273            for (var i = this._routes.length-1; i >= 0; i--) {
    83                 var result;
    8474                var route = this._routes[i];
    85 
    86                 if ((result = route.regexp.exec(newHash)) !== null) {
    87                     var numParams = route.paramNames.length;
    88 
    89                     var params = {};
    90                     for (var j = 0; j < numParams; j++) {
    91                         params[route.paramNames[j]] = result[j+1];
    92                     }
    93 
    94                     if ( result.length > numParams+1 && result[numParams+2] ) {
    95                         params.options = ioQuery.queryToObject(result[numParams+2]);
    96                     }
    97 
     75                var params = null;
     76                if ((params = route.path.match(newHash)) !== null) {
    9877                    try {
    9978                        route.callback(params);
    10079                    } catch(err) {
    101                         console.warn("Page change failed.",err);
     80                        console.error("Page change failed.",err);
    10281                    }
    10382                    return;
    10483                }
    10584            }
    106 
    10785            try {
    10886                this._defaultCallback();
    10987            } catch(err) {
    110                 console.warn("Default page failed.",err);
     88                console.error("Default page failed.",err);
    11189            }
    11290        },
    11391        go: function(path,args) {
    11492            if ( !this._started ) { return; }
    115             var newHash = this._pathToHash(path,args);
     93            var newHash = Path.format(path,args);
    11694            this._handlePathChange(newHash);
    11795            hash(newHash);
    11896        },
    119         _pathToHash: function(path,args) {
    120             var hash = '!'+path;
    121             if ( args ) {
    122                 hash += '!'+ioQuery.objectToQuery(args);
    123             }
    124             return hash;
    125         },
    12697        _defaultCallback: function() {
    12798            Content.set(new Page({
    128                 templateString: "<div>Requested page not found. Go <a href=\"#!/\">home</a>.</div>"
     99                templateString: "<div>Requested page not found. Go <a href=\"#"+Path.getDefault()+"\">home</a>.</div>"
    129100            }));
    130101        }
  • Dev/branches/rest-dojo-ui/client/qed/css/main.css

    r412 r420  
    11/* Menu.css */
    22@import "external.css";
     3@import "hva-mods.css";
    34DESCRIPTION {
    45  /*
  • Dev/branches/rest-dojo-ui/client/qed/lib/async.js

    r415 r420  
    11define([
    2      'dojo/Deferred',
    3 ],function(Deferred){
     2     'dojo/_base/array',
     3     'dojo/_base/lang',
     4     'dojo/when',
     5     'dojo/Deferred'
     6],function(array,lang,when,Deferred){
    47
    5     function seq(functions) {
     8    /** Execute possible async functions in sequence
     9     * Execute functions in order. When a function returns a Promise,
     10     * the next function is only executed when the promise resolves.
     11     * If the function returns a value, the next function is executed
     12     * synchronously. The return value of every function is passed as argument
     13     * to the next.
     14     * Returns the return value of the last function in the chain.
     15     */
     16    function seq(functions,ctx) {
    617        var d = new Deferred();
    7         (function stepper(fs,arg) {
     18
     19        var cancelled = false;
     20        var running = null;
     21        function cancel(err) {
     22            if ( cancelled ) return;
     23            cancelled = true;
     24            running && running.cancel && !running.isFulfilled() && running.cancel();
     25            d.reject(err);
     26        }
     27
     28        function update(fs,arg) {
     29            if ( cancelled ) return;
    830            if ( fs.length > 0 ) {
    931                try {
    1032                    var f = fs.shift();
    11                     var ret = f(arg);
    12                     if ( ret && ret.then ) {
    13                         ret.then(function(ret){
    14                             stepper(fs,ret);
    15                         });
    16                     } else {
    17                         stepper(fs,ret);
    18                     }
     33                    running = when(f.call(ctx,arg))
     34                    .then(function(res){
     35                        update(fs,res);
     36                    },function(err){
     37                        cancel(err);
     38                    });
    1939                } catch(err) {
    20                     d.reject(err);
     40                    cancel(err);
    2141                }
    2242            } else {
    23                 d.resolve();
     43                d.resolve(arg);
    2444            }
    25         })(functions);
     45        }
     46
     47        update(functions);
     48
    2649        return d.promise;
    2750    }
    2851
     52    /** Execute possible async functions in parallel
     53     * Execute all functions in parallel, resolve only when all have resolved.
     54     * NB. When a function is not async, it will fully complete before the next
     55     * async function is started. If you mix async and non async functions, put
     56     * the sync functions last to fully exploit the async ones.
     57     * Return an array with all the return values of the functions.
     58     */
     59    function par(functions,ctx) {
     60        var d = new Deferred(cancel);
     61
     62        var cancelled = false;
     63        var running = [];
     64        function cancel(err) {
     65            if (cancelled) return;
     66            cancelled = true;
     67            array.forEach(running,function(running){
     68                running && running.cancel && !running.isFulfilled() && running.cancel();
     69            });
     70            d.reject(err);
     71        }
     72
     73        var results = [];
     74        var left = functions.length;
     75        function update(res,idx) {
     76            if (cancelled) return;
     77            results[idx] = res;
     78            left -= 1; // Works because/as long as AJAX/JS is single-threaded.
     79            if ( left === 0 ) {
     80                d.resolve(results);
     81            }
     82        }
     83
     84        array.forEach(functions,function(f,idx){
     85            if (cancelled) return;
     86            try {
     87                running.push(when(f.call(ctx)).then(function(res){
     88                    update(res,idx);
     89                },function(err){
     90                    cancel(err);
     91                }));
     92            } catch (err) {
     93                cancel();
     94            }
     95        });
     96
     97        return d.promise;
     98    }
     99
     100    /** Iterate over an array with async callback
     101     * Execute the callback and if it's async, wait until it's finished before
     102     * executing the next one.
     103     */
    29104    function forEach(list,callback,ctx) {
    30         var d = new Deferred();
    31         (function stepper(list,idx){
    32             if ( list.length > 0 ) {
    33                 var el = list.shift();
    34                 var ret = callback.call(ctx,el,idx);
    35                 if ( ret && ret.then ) {
    36                     ret.then(function(){
    37                         stepper(list,idx+1);
    38                     });
    39                 } else {
    40                     stepper(list,idx+1);
    41                 }
    42             } else {
    43                 d.resolve();
    44             }
    45         })(list,0);
    46         return d.promise;
     105        var fs = array.map(list,function(item,index,items){
     106            return lang.hitch(ctx,callback,item,index,items);
     107        });
     108        return seq(fs);
     109    }
     110
     111    /** Map a list, possible async
     112     * Map every element of a list, returning a promise to the mapped list.
     113     */
     114    function map(list,callback,ctx) {
     115        var fs = array.map(list,function(item,index,items){
     116            return lang.hitch(ctx,callback,item,index,items);
     117        });
     118        return par(fs);
    47119    }
    48120
    49121    return {
    50122        seq: seq,
    51         forEach: forEach
     123        par: par,
     124        forEach: forEach,
     125        map: map
    52126    };
    53127
  • Dev/branches/rest-dojo-ui/client/qed/main-response.js

    r419 r420  
    11require([
     2    'dojo/date',
     3    'dojo/date/locale',
    24    'dojo/hash',
    35    'dojo/parser',
     6    'qed/store',
    47    'qed/app/Content',
    58    'qed/app/Page',
    6     'qed/pages/viewSurvey',
     9    'qed/app/Path',
     10    'qed/lib/async',
     11    'qed/model/classes/Response',
     12    'qed/model/classes/SurveyRun',
     13    'qed/pages/response',
    714    'dojo/domReady!',
    815    'qed/stddeps'
    9 ],function(hash,parser,Content,Page,viewSurvey) {
     16],function(date,locale,hash,parser,store,Content,Page,Path,async,Response,SurveyRun,response) {
    1017    parser.parse();
    1118    Content.startup();
    1219
    13     var match = /^!\/(\w+)$/g.exec(hash());
    14     if ( !match ) {
     20    function error(msg) {
    1521        Content.set(new Page({
    16             templateString: "<div>Something is wrong with the URL, don't know what survey to show you. Sorry.</div>"
     22            templateString: "<div>"+msg+"</div>"
    1723        }));
     24    }
     25
     26    var path = new Path('/:surveyRunId');
     27    var params = path.match(hash());
     28    params.options = params.options || {};
     29
     30    if ( !params || !params.surveyRunId ) {
     31        error("Something is wrong with the URL, don't know what survey to show you. Sorry.");
    1832        return;
    1933    }
    20     var surveyId = match[1];
    2134
    22     // read options from hash/url
    23     //
    24     // authenticate
     35    var surveyRunId = params.surveyRunId;
    2536
    26     Content.set(new viewSurvey({
    27         surveyId: surveyId
    28     }));
     37    function checkDates(surveyRun) {
     38        var now = new Date();
     39        var startDate = SurveyRun.StartDate.get(surveyRun);
     40        var endDate = SurveyRun.EndDate.get(surveyRun);
     41        if ( startDate && date.compare(startDate,now) > 0 ) {
     42            error("This survey will start on "+locale.format(startDate,'date'));
     43            throw false;
     44        }
     45        if ( endDate && date.compare(now,endDate) > 0 ) {
     46            error("This survey ended on "+locale.format(endDate,'date'));
     47            throw false;
     48        }
     49    }
     50
     51    store.get(surveyRunId)
     52    .then(function(surveyRun){
     53        checkDates(surveyRun);
     54        if ( params.options.id ) {
     55            return params.options.id;
     56        } else {
     57            if ( surveyRun.mode === "open") {
     58                var response = Response.create();
     59                response.surveyRunId = surveyRunId;
     60                return store.put(response).then(function(res){
     61                    return store.getIdentity(res);
     62                });
     63            } else {
     64                error("Cannot respond to closed survey without response id. Sorry.");
     65                throw false;
     66            }
     67        }
     68        return surveyRun;
     69    },function(){
     70        error("No running survey found for the given id. Sorry.");
     71        throw false;
     72    })
     73    .then(function(responseId){
     74        hash(Path.format("/"+surveyRunId,{ id: responseId }));
     75        Content.set(new response({
     76            surveyRunId: surveyRunId,
     77            options: {
     78                id: responseId
     79            }
     80        }));
     81    });
    2982});
  • Dev/branches/rest-dojo-ui/client/qed/model/schema.js

    r417 r420  
    7474    //
    7575
     76    var Respondent = dbobject('Respondent',{
     77        secret: nestring()
     78    });
     79
     80    var Question = dbobject('Question',{
     81        code:nestring(),
     82        title: nestring(),
     83        description: string(optional()),
     84        topic: string(optional()),
     85        categories: array(nestring(),optional()),
     86        content: array([
     87            typedobject('Header',{
     88                content:string(optional())
     89            }),
     90            typedobject('Text',{
     91                content:string(optional())
     92            }),
     93            typedobject('Divider'),
     94            typedobject('StringInput'),
     95            typedobject('TextInput',{
     96                maxLength:integer()
     97            }),
     98            typedobject('IntegerInput',{
     99                min:integer(),
     100                max:integer(),
     101                step:integer()
     102            }),
     103            typedobject('MultipleChoiceInput',{
     104                multiple:boolean(),
     105                items:array(nestring())
     106            })
     107        ],optional()),
     108        publicationDate:datetime(optional())
     109    });
     110
     111    var Survey = dbobject('Survey',{
     112        title: nestring(),
     113        description: string(optional()),
     114        questions: array(Question,optional()),
     115        publicationDate:datetime(optional())
     116    });
     117
     118    var SurveyRun = dbobject('SurveyRun',{
     119        description: string(optional()),
     120        survey: Survey,
     121        publicationDate:datetime(optional()),
     122        startDate:datetime(optional()),
     123        endDate:datetime(optional()),
     124        mode:{type:'string',enum:['open','closed'],optional:true},
     125        metadata:string(optional())
     126    });
     127
     128    var Response = dbobject('Response',{
     129        surveyRunId: nestring(),
     130        answers:{type:'object',optional:true},
     131        publicationDate:datetime(optional())
     132    });
     133
     134    var Session = dbobject('Session',{
     135        title: nestring(),
     136        description: string(optional()),
     137        publicationDate: datetime(optional())
     138    });
     139
     140    var SessionTemplate = dbobject('SessionTemplate',{
     141        title: nestring(),
     142        description: string(optional()),
     143        publicationDate: datetime(optional()),
     144        plannedDate: datetime(optional())
     145    });
     146
    76147    var schema = {type:[
    77         dbobject('Question',{
    78             code:nestring(),
    79             title: nestring(),
    80             description: string(optional()),
    81             topic: string(optional()),
    82             categories: array(nestring(),optional()),
    83             content: array([
    84                 typedobject('Header',{
    85                     content:string(optional())
    86                 }),
    87                 typedobject('Text',{
    88                     content:string(optional())
    89                 }),
    90                 typedobject('Divider'),
    91                 typedobject('StringInput'),
    92                 typedobject('TextInput',{
    93                     maxLength:integer()
    94                 }),
    95                 typedobject('IntegerInput',{
    96                     min:integer(),
    97                     max:integer(),
    98                     step:integer()
    99                 }),
    100                 typedobject('MultipleChoiceInput',{
    101                     multiple:boolean(),
    102                     items:array(nestring())
    103                 })
    104             ],optional()),
    105             publicationDate:datetime(optional())
    106         }),
    107         dbobject('Survey',{
    108             title: nestring(),
    109             description: string(optional()),
    110             questions: array(nestring(),optional()),
    111             publicationDate:datetime(optional())
    112         }),
    113         dbobject('SurveyRun',{
    114             description: string(optional()),
    115             surveyId: nestring(),
    116             publicationDate:datetime(optional()),
    117             startDate:datetime(),
    118             endDate:datetime(),
    119             mode:{type:'string',enum:['open','closed']},
    120             respondents: array(nestring(),optional()),
    121             metadata:{type:'object',optional:true}
    122         }),
    123         dbobject('Session',{
    124             title: nestring(),
    125             description: string(optional()),
    126             publicationDate: datetime(optional())
    127         }),
    128         dbobject('SessionTemplate',{
    129             title: nestring(),
    130             description: string(optional()),
    131             publicationDate: datetime(optional()),
    132             plannedDate: datetime(optional())
    133         })
     148        Question,
     149        Survey,
     150        SurveyRun,
     151        Response,
     152        Session,
     153        SessionTemplate
    134154    ]};
    135155
  • Dev/branches/rest-dojo-ui/client/qed/model/widgets/templates/SurveyRunFieldset.html

    r419 r420  
    1 <fieldset class="${baseClass}">
     1<fieldset class="${baseClass} qedFieldset">
    22
    3     <label for="mode" class="loginLabel">Description</label>
    4     <textarea name="description" data-dojo-type="dijit/form/Textarea"></textarea>
    5     <br>
     3    <div>
     4    <label for="mode" class="qedLabel">Description</label>
     5    <textarea name="description" class="qedField" data-dojo-type="dijit/form/Textarea"></textarea>
     6    </div>
    67
    7     <label for="startDate" class="loginLabel">Start date</label>
    8     <input type="text" name="startDatetime" data-dojo-type="qed/widgets/DateTimeTextBox" />
    9     <br>
     8    <div>
     9    <label for="startDate" class="qedLabel">Start date</label>
     10    <input type="text" name="startDate" class="qedField" data-dojo-type="dijit/form/DateTextBox" data-dojo-attach-point="startDateBox" />
     11    </div>
    1012
    11     <label for="endDate" class="loginLabel">End date</label>
    12     <input type="text" name="endDatetime" data-dojo-type="qed/widgets/DateTimeTextBox" />
    13     <br>
     13    <div>
     14    <label for="endDate" class="qedLabel">End date</label>
     15    <input type="text" name="endDate" class="qedField" data-dojo-type="dijit/form/DateTextBox" data-dojo-attach-point="endDateBox" />
     16    </div>
    1417
    15     <label for="mode" class="loginLabel">Mode</label>
    16     <select name="mode" data-dojo-type="dijit/form/Select">
     18    <div>
     19    <label for="mode" class="qedLabel">Mode</label>
     20    <select name="mode" class="qedField" data-dojo-type="dijit/form/Select">
    1721        <option value="open" selected="selected">Open</option>
    1822        <option value="closed">Closed</option>
    1923    </select>
    20     <br>
     24    </div>
    2125
    2226</fieldset>
  • Dev/branches/rest-dojo-ui/client/qed/pages/previewSurvey.js

    r418 r420  
    22    'dojo/_base/array',
    33    'dojo/_base/declare',
    4     'dojo/_base/Deferred',
    5     'dojo/_base/event',
    64    'dojo/_base/lang',
     5    'dojo/when',
    76    '../store',
    87    '../app/Page',
    9     '../model/widgets/QuestionWidgetFactory',
    10     'dojo/text!./templates/viewSurvey.html'
    11 ],function(array,declare,Deferred,event,lang,store,Page,QuestionWidgetFactory,template){
     8    'dojo/text!./templates/previewSurvey.html'
     9],function(array,declare,lang,when,store,Page,template){
    1210    return declare([Page],{
    1311        templateString: template,
    14         survey: null,
    15         surveyId: "",
    16         options: null,
    17         constructor: function(){
    18             this._dataMap = {};
    19             this.options = this.options || {};
    20         },
    2112        startup: function() {
    2213            if ( this._started ) { return; }
    2314            this.inherited(arguments);
    24 
    25 
    2615            if ( this.surveyId ) {
    27                 Deferred.when(store.get(this.surveyId))
     16                when(store.get(this.surveyId))
    2817                .then(lang.hitch(this,function(survey){
    29                     if ( !survey.published ) {
    30                         this.options.preview = true;
    31                     }
    32                     if ( this.options.preview ) {
    33                         this.buttonsPane.destroyRecursive();
    34                     }
    35                     this.titleNode.innerHTML = survey.title +
    36                             (this.options.preview?' [preview]':'');
    37                     var f = new QuestionWidgetFactory();
    38                     this.survey = survey;
    39                     store.query(null,{keys:this.survey.questions,include_docs:true})
    40                     .forEach(function(question){
    41                         array.forEach(question.content || [],function(item){
    42                             var w = f.createViewWidget(item,{
    43                                 name: question.code+'.'+item.code
    44                             });
    45                             if ( w !== null ) {
    46                                 w.placeAt(this.questionsAnchor,'before');
    47                             }
    48                         },this);
    49                     },this);
     18                    this.titleNode.innerHTML = survey.title;
     19                    this.surveyWidget.set('survey',survey);
    5020                }));
    5121            } else {
    5222                throw new Error("No valid uid or survey passed!");
    5323            }
    54         },
    55         _onSubmit: function(evt) {
    56             if ( this.options.preview ) { return; }
    57             var value = this.questionsForm.get('value');
    58             this.questionsPane.set('content','<pre>'+JSON.stringify(value)+'</pre>');
    59             event.stop(evt);
    60             return false;
    61         },
    62         _onCancel: function(evt) {
    63             if ( this.options.preview ) { return; }
    64             event.stop(evt);
    65             return false;
    6624        }
    6725    });
  • Dev/branches/rest-dojo-ui/client/qed/pages/question.js

    r418 r420  
    11define([
    22    'dojo/_base/declare',
    3     'dojo/_base/Deferred',
    43    'dojo/_base/event',
    54    'dojo/_base/lang',
     5    'dojo/when',
    66    '../store',
    77    '../app/Content',
    88    '../app/Router',
    99    '../app/Page',
     10    '../model/classes/Question',
    1011    '../model/widgets/QuestionEditorPreview',
    1112    '../model/widgets/QuestionEditorToolkit',
    1213    'dojo/text!./templates/question.html'
    13 ],function(declare, Deferred, event, lang, store, Content, Router, Page, QuestionEditorPreview, QuestionEditorToolkit, template){
     14],function(declare, event, lang, when, store, Content, Router, Page, Question, QuestionEditorPreview, QuestionEditorToolkit, template){
    1415    return declare([Page], {
    1516        templateString: template,
     
    2627            this._setupEditor();
    2728            if (this.questionId === "new") {
    28                 this.question = { type: 'Question' };
     29                this.question = Question.create();
    2930                this._refresh();
    3031            } else {
    31                 Deferred.when(store.get(this.questionId))
     32                when(store.get(this.questionId))
    3233                .then(lang.hitch(this, function(obj) {
    3334                    this.question = obj;
     
    4041        },
    4142        _refresh: function () {
    42             this.titleNode.innerHTML = this.question.title || "";
     43            this.titleNode.innerHTML = Question.DisplayTitle.get(this.question);
    4344            this._toolkit.set('value',this.question);
    44             this._preview.appendItems(this.question.content || []);
     45            this._preview.appendItems(Question.Content.get(this.question));
    4546        },
    4647        _onSave: function(evt) {
    4748            lang.mixin(this.question, this._toolkit.get('value'));
    48             this.question.content = this._preview.getItems();
     49            Question.Content.set(this.question, this._preview.getItems());
    4950            store.put(this.question)
    5051            .then(function() {
  • Dev/branches/rest-dojo-ui/client/qed/pages/session.js

    r418 r420  
    22    'dojo/_base/array',
    33    'dojo/_base/declare',
    4     'dojo/_base/Deferred',
    54    'dojo/_base/event',
    65    'dojo/_base/lang',
     6    'dojo/when',
    77    '../search',
    88    '../store',
     
    1010    '../app/Router',
    1111    '../widgets/ThresholdFilteringSelect',
     12    '../model/classes/SessionTemplate',
    1213    '../model/widgets/AccountListView',
    1314    'dojo/text!./templates/session.html'
    14 ],function(array,declare,Deferred,event,lang,search,store,Page,Router,ThresholdFilteringSelect,AccountListView,template){
     15],function(array,declare,event,lang,when,search,store,Page,Router,ThresholdFilteringSelect,SessionTemplate,AccountListView,template){
    1516    return declare([Page],{
    1617        templateString: template,
     
    3031        _loadSession: function() {
    3132            if ( this.sessionId === "new" ) {
    32                 this.session = {
    33                     type: 'SessionTemplate'
    34                 };
     33                this.session = SessionTemplate.create();
    3534            } else {
    36                 Deferred.when(store.get(this.sessionId))
     35                when(store.get(this.sessionId))
    3736                .then(lang.hitch(this,function(obj){
    3837                    this.session = obj;
     
    4342        },
    4443        _refresh: function() {
    45             this.titleNode.innerHTML = this.session.title || '';
     44            this.titleNode.innerHTML = SessionTemplate.DisplayTitle.get(this.session);
    4645            this.propertiesForm.set('value',this.session);
    4746        },
  • Dev/branches/rest-dojo-ui/client/qed/pages/survey.js

    r418 r420  
    22    'dojo/_base/array',
    33    'dojo/_base/declare',
    4     'dojo/_base/Deferred',
    54    'dojo/_base/event',
    65    'dojo/_base/lang',
     6    'dojo/when',
    77    '../app/Router',
    88    '../store',
    99    '../app/Page',
     10    '../model/classes/Survey',
    1011    '../model/widgets/QuestionListView',
    1112    '../model/widgets/TabbedQuestionBrowser',
    1213    'dojo/text!./templates/survey.html'
    13 ],function(array,declare,Deferred,event,lang,Router,store,Page,
     14],function(array,declare,event,lang,when,Router,store,Page,Survey,
    1415         QuestionListView,TabbedQuestionBrowser,template){
    1516    return declare([Page],{
     
    1718        survey: null,
    1819        questionList: null,
    19         _dataMap: null,
    20         constructor: function(){
    21             this._dataMap = {};
    22         },
    2320        startup: function() {
    2421            if ( this._started ) { return; }
     
    6259        _loadSurvey: function() {
    6360            if ( this.surveyId === "new" ) {
    64                 this.survey = {
    65                     type: 'Survey'
    66                 };
     61                this.survey = Survey.create();
    6762                this.refresh();
    6863            } else {
    69                 Deferred.when(store.get(this.surveyId))
     64                when(store.get(this.surveyId))
    7065                .then(lang.hitch(this,function(survey){
    7166                    this.survey = survey;
    72                     store.query(null,{keys:this.survey.questions || [], include_docs: true})
    73                     .forEach(lang.hitch(this.questionList,'appendItem'));
     67                    array.forEach(Survey.Questions.get(this.survey),
     68                        lang.hitch(this.questionList,'appendItem'));
    7469                    this.refresh();
    7570                }));
     
    8075        },
    8176        refresh: function() {
    82             this.titleNode.innerHTML = this.survey.title || "(set title in properties)";
     77            this.titleNode.innerHTML = Survey.DisplayTitle.get(this.survey) || "(set title in properties)";
    8378            this.propertiesDialog.set('value',this.survey);
    8479        },
     
    10095        },
    10196        _onSave: function(evt) {
    102             this.survey.questions = array.map(this.questionList.getItems(),function(item){
    103                 return store.getIdentity(item);
    104             });
     97            this.survey.questions = this.questionList.getItems();
    10598            store.put(this.survey)
    10699            .then(function() {
     
    114107        },
    115108        _onShowPreview: function() {
    116             Router.go('/viewSurvey/'+store.getIdentity(this.survey),{
     109            Router.go('/previewSurvey/'+store.getIdentity(this.survey),{
    117110                preview: true
    118111            });
  • Dev/branches/rest-dojo-ui/client/qed/pages/surveyRun.js

    r418 r420  
    11define([
    2     'dojo/_base/array',
    32    'dojo/_base/declare',
    4     'dojo/_base/Deferred',
    53    'dojo/_base/event',
    64    'dojo/_base/lang',
     5    'dojo/when',
     6    '../app/Content',
    77    '../app/Router',
     8    '../lib/func',
    89    '../store',
    910    '../app/Page',
    10     '../model/widgets/QuestionListView',
    11     '../model/widgets/TabbedQuestionBrowser',
    12     'dojo/text!./templates/survey.html'
    13 ],function(array,declare,Deferred,event,lang,Router,store,Page,
    14          QuestionListView,TabbedQuestionBrowser,template){
     11    '../model/classes/SurveyRun',
     12    'dojo/text!./templates/surveyRun.html'
     13],function(declare,event,lang,when,Content,Router,func,store,Page,SurveyRun,template){
    1514    return declare([Page],{
    1615        templateString: template,
    17         survey: null,
    18         questionList: null,
    19         _dataMap: null,
    20         constructor: function(){
    21             this._dataMap = {};
    22         },
     16        surveyRun: null,
    2317        startup: function() {
    2418            if ( this._started ) { return; }
    2519            this.inherited(arguments);
    26             if ( this.surveyId ) {
    27                 this._setupQuestionBrowser();
    28                 this._setupListView();
    29                 this._loadSurvey();
     20            this.propertiesForm.on("blur",lang.hitch(this,'_onPropChange'));
     21            if ( this.surveyRunId ) {
     22                this._loadSurveyRun();
    3023            } else {
    3124                throw "No valid uid or survey passed!";
    3225            }
    3326        },
    34         _setupQuestionBrowser: function() {
    35             this.questionBrowser = new TabbedQuestionBrowser({
    36                 region: 'center',
    37                 'class': 'blue',
    38                 include: 'published',
    39                 selectedActions: {
    40                     "Include": {
    41                         callback: lang.hitch(this,this._includeQuestion),
    42                         icon: "Accept",
    43                         description: "Include in survey"
    44                     }
    45                 },
    46                 itemActions: {
    47                     "Info": {
    48                         callback: function(item){ item.description && alert(item.description); },
    49                         icon: "Inspect",
    50                         description: "Show item description"
    51                     }
    52                 }
    53             },this.questionBrowser);
    54             this.questionBrowser.startup();
     27        _loadSurveyRun: function() {
     28            when(store.get(this.surveyRunId))
     29            .then(lang.hitch(this,function(surveyRun){
     30                this.surveyRun = surveyRun;
     31                this.refresh();
     32            }));
    5533        },
    56         _setupListView: function() {
    57             this.questionList = new QuestionListView({
    58                 region: 'center'
    59             },this.surveyListViewNode);
    60             this.questionList.startup();
     34        refresh: function() {
     35            this.titleNode.innerHTML = SurveyRun.DisplayTitle.get(this.surveyRun);
     36            this.surveyNode.set('value',SurveyRun.Survey.get(this.surveyRun));
     37            this.propertiesForm.set('value',this.surveyRun);
     38            this._onPropChange();
    6139        },
    62         _loadSurvey: function() {
    63             if ( this.surveyId === "new" ) {
    64                 this.survey = {
    65                     type: 'Survey'
    66                 };
    67                 this.refresh();
     40        _onPropChange: function(e) {
     41            var surveyRun = this.propertiesForm.get('value');
     42            if ( surveyRun.mode === "open" ) {
     43                var url = 'response.html#!/'+store.getIdentity(this.surveyRun);
     44                this.runURLNode.innerHTML = '<a target="_black" href="'+url+'">'+url+'</a>';
    6845            } else {
    69                 Deferred.when(store.get(this.surveyId))
    70                 .then(lang.hitch(this,function(survey){
    71                     this.survey = survey;
    72                     store.query(null,{keys:this.survey.questions || [], include_docs: true})
    73                     .forEach(lang.hitch(this.questionList,'appendItem'));
    74                     this.refresh();
    75                 }));
     46                this.runURLNode.innerHTML = "No general URL. Add individual respondents below.";
    7647            }
    7748        },
    78         _includeQuestion: function(question) {
    79             this.questionList.insertItem(question);
    80         },
    81         refresh: function() {
    82             this.titleNode.innerHTML = this.survey.title || "(set title in properties)";
    83             this.propertiesDialog.set('value',this.survey);
    84         },
    85         _onShowProperties: function(evt) {
    86             this.propertiesDialog.show();
    87         },
    88         _onPropertiesOk: function(evt) {
    89             this.propertiesDialog.hide();
    90             lang.mixin(this.survey, this.propertiesDialog.get('value'));
    91             this.refresh();
    92             event.stop(evt);
    93             return false;
    94         },
    95         _onPropertiesCancel: function(evt) {
    96             this.propertiesDialog.hide();
    97             this.propertiesDialog.reset('value',this.survey);
    98             event.stop(evt);
    99             return false;
    100         },
    10149        _onSave: function(evt) {
    102             this.survey.questions = array.map(this.questionList.getItems(),function(item){
    103                 return store.getIdentity(item);
    104             });
    105             store.put(this.survey)
     50            lang.mixin(this.surveyRun,this.propertiesForm.get('value'));
     51            var not = function(p){ return !p; };
     52            func.modPropIf(this.surveyRun,"startDate",not.compose(lang.isString),store.formatDate);
     53            func.modPropIf(this.surveyRun,"endDate",not.compose(lang.isString),store.formatDate);
     54            store.put(this.surveyRun)
    10655            .then(function() {
    10756                Router.go('/surveys');
     57            },function(err){
     58                Content.notify(err);
    10859            });
    10960            event.stop(evt);
     
    11263        _onDiscard: function(evt) {
    11364            Router.go('/surveys');
    114         },
    115         _onShowPreview: function() {
    116             Router.go('/viewSurvey/'+store.getIdentity(this.survey),{
    117                 preview: true
    118             });
    11965        }
    12066    });
  • Dev/branches/rest-dojo-ui/client/qed/pages/surveys.js

    r418 r420  
    99    '../app/Page',
    1010    '../app/Router',
     11    '../model/classes/Survey',
     12    '../model/classes/SurveyRun',
    1113    '../widgets/LineWithActionsWidget',
    1214    'dojo/text!./templates/surveys.html'
    13 ],function(array,declare,lang,when,Rx,store,Content,Page,Router,LineWithActionsWidget,template){
     15],function(array,declare,lang,when,Rx,store,Content,Page,Router,Survey,SurveyRun,LineWithActionsWidget,template){
    1416    return declare([Page],{
    1517        templateString: template,
     
    4547        },
    4648        _onPreviewSurvey:function(survey){
    47             Router.go('/viewSurvey/'+store.getIdentity(survey),{preview:true});
     49            Router.go('/previewSurvey/'+store.getIdentity(survey));
    4850        },
    4951        _onRunSurvey:function(survey){
    50             this.surveyRun = {
    51                 type: 'SurveyRun',
    52                 surveyId: store.getIdentity(survey),
    53                 publicationDate: store.timestamp()
    54             };
    55             this.surveyRunDialog.set('value',this.surveyRun);
    56             this.surveyRunDialog.show();
    57         },
    58         _onSurveyRunOk: function() {
    59             var surveyRun = lang.mixin(lang.clone(this.surveyRun),this.surveyRunDialog.get('value'));
     52            var surveyRun = SurveyRun.create();
     53            SurveyRun.Survey.set(surveyRun,survey);
    6054            store.put(surveyRun)
    61             .then(lang.hitch(this,function(){
    62                 this.surveyRunDialog.hide();
    63                 this.refreshRuns();
     55            .then(lang.hitch(this,function(surveyRun){
     56                this._onRunDetails(surveyRun);
    6457            }),function(err){
    6558                Content.notify(err);
    6659            });
    6760        },
    68         _onSurveyRunCancel: function() {
    69             this.surveyRunDialog.hide();
     61        _onRunDetails: function(surveyRun) {
     62            Router.go('/surveyRun/'+store.getIdentity(surveyRun));
    7063        },
    7164        refresh: function() {
     
    8174                array.forEach(surveys,function(survey){
    8275                    var w = new LineWithActionsWidget({
    83                         title: survey.title || '(unnamed)',
     76                        title: Survey.DisplayTitle.get(survey) || '(unnamed)',
    8477                        actions: [{
    8578                            callback: lang.hitch(this,'_onPublishSurvey',survey),
     
    123116                array.forEach(surveys,function(survey){
    124117                    var w = new LineWithActionsWidget({
    125                         title: survey.title,
     118                        title: Survey.DisplayTitle.get(survey),
    126119                        actions:[{
    127120                            callback: lang.hitch(this,'_onPreviewSurvey',survey),
     
    151144                array.forEach(surveyRuns,function(surveyRun){
    152145                    var w = new LineWithActionsWidget({
    153                         title: surveyRun.title+" (from "+surveyRun.startDate+" to "+surveyRun.endDate+")",
     146                        title: SurveyRun.DisplayTitle.get(surveyRun),
    154147                        actions:[{
    155                             callback: lang.hitch(this,'_onCloseRun',surveyRun),
     148                            callback: lang.hitch(this,'_onRunDetails',surveyRun),
    156149                            properties: {
    157                                 label: 'Close',
    158                                 tooltip: 'Close survey',
    159                                 icon: 'Close'
     150                                label: 'Details',
     151                                tooltip: 'Show details for this run',
     152                                icon: 'Details'
    160153                            }
    161154                        }]
  • Dev/branches/rest-dojo-ui/client/qed/pages/templates/previewSurvey.html

    r418 r420  
    99   
    1010    <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'" data-dojo-attach-point="questionsPane">
    11         <form data-dojo-type="dijit/form/Form" data-dojo-attach-point="questionsForm"
    12               data-dojo-attach-event="onSubmit:_onSubmit"
    13               style="overflow: auto">
    14             <div data-dojo-attach-point="questionsAnchor"></div>
     11        <form data-dojo-type="dijit/form/Form" data-dojo-attach-point="questionsForm" style="overflow: auto">
     12            <div data-dojo-type="qed/model/widgets/SurveyWidget" data-dojo-attach-point="surveyWidget"></div>
    1513        </form>
    1614    </div>
    1715   
    18     <div data-dojo-attach-point="buttonsPane" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'bottom'">
    19         <button data-dojo-type="dijit/form/Button"
    20                 type="submit"
    21                 data-dojo-attach-event="onClick:_onSubmit">
    22             Submit</button>
    23         <button data-dojo-type="dijit/form/Button"
    24                 type="button"
    25                 data-dojo-attach-event="onClick:_onCancel">
    26             Cancel</button>
    27     </div>
    28 
    2916</div>
  • Dev/branches/rest-dojo-ui/client/qed/pages/templates/surveys.html

    r419 r420  
    3636    </div>
    3737
    38     <div data-dojo-type="dijit/Dialog"
    39          title="SurveyRun properties"
    40          data-dojo-attach-point="surveyRunDialog">
    41         <fieldset data-dojo-type="qed/model/widgets/SurveyRunFieldset"></fieldset>
    42         <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_onSurveyRunOk">OK</button>
    43         <button data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:_onSurveyRunCancel">Cancel</button>
    44     </div>
    45 
    4638</div>
  • Dev/branches/rest-dojo-ui/client/qed/routes.js

    r407 r420  
    55    './pages/surveys',
    66    './pages/survey',
     7    './pages/surveyRun',
    78    './pages/sessions',
    89    './pages/session',
    9     './pages/viewSurvey'
    10 ],function(index,questions,question,surveys,survey,sessions,session,viewSurvey){
     10    './pages/previewSurvey'
     11],function(index,questions,question,surveys,survey,surveyRun,sessions,session,previewSurvey){
    1112
    1213    return [
     
    1718        { path: "/surveys", constructor: surveys },
    1819        { path: "/survey/:surveyId", constructor: survey },
    19         { path: "/sessions", constructor: sessions },
    20         { path: "/session/:sessionId", constructor: session },
    21         { path: "/viewSurvey/:surveyId", constructor: viewSurvey }
     20        { path: "/surveyRun/:surveyRunId", constructor: surveyRun },
     21        //{ path: "/sessions", constructor: sessions },
     22        //{ path: "/session/:sessionId", constructor: session },
     23        { path: "/previewSurvey/:surveyId", constructor: previewSurvey }
    2224    ];
    2325
  • Dev/branches/rest-dojo-ui/client/qed/stddeps.js

    r419 r420  
    2727    'qed/app/Notifications',
    2828
     29    'qed/model/widgets/AccountListView',
     30    'qed/model/widgets/QuestionWidget',
    2931    'qed/model/widgets/SurveyFieldset',
    3032    'qed/model/widgets/SurveyRunFieldset',
    31     'qed/model/widgets/QuestionWidget',
    32     'qed/model/widgets/AccountListView',
     33    'qed/model/widgets/SurveySummary',
     34    'qed/model/widgets/SurveyWidget',
    3335
    3436    'qed/ui/MainMenu',
     
    4042    'qed/widgets/Selector',
    4143    'qed/widgets/TitleGroup',
    42     'qed/widgets/DateTimeTextBox',
    4344    'qed/widgets/list/List',
    4445    'qed/widgets/list/OrderedList'
  • Dev/branches/rest-dojo-ui/client/qed/store.js

    r418 r420  
    2020        }
    2121    });
    22     var memoryStore = new Memory();
    23     var cacheStore = new Cache(couchStore,memoryStore,{});
     22    var memoryStore = new Memory({
     23        idProperty: couchStore.idProperty
     24    });
     25    var cacheStore = new Cache(couchStore,memoryStore,{
     26        isLoaded: function(object) {
     27            // Unfortunately we cannot determine of the full documents were
     28            // loaded or not. Therefore never cache query results.
     29            return false;
     30        }
     31    });
    2432
    2533    var store = cacheStore;
  • Dev/branches/rest-dojo-ui/client/qed/widgets/list/List.js

    r407 r420  
    6363
    6464        getItems: function() {
    65             return this.source.getAllNodes()
    66             .map(function(node){
    67                 return this.source.getItem(node.id).data;
     65            var items = [];
     66            this.source.getAllNodes()
     67            .forEach(function(node){
     68                items.push(this.source.getItem(node.id).data);
    6869            },this);
     70            return items;
    6971        },
    7072
  • Dev/branches/rest-dojo-ui/client/response.html

    r419 r420  
    66        <link rel="stylesheet" type="text/css" href="qed/css/main.css" />
    77    </head>
    8     <body class="dijitReset claro">
     8    <body id="rft" class="dijitReset claro">
    99        <script type="text/javascript" src="dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: false, isDebug: true"></script>
    10         <script type="text/javascript" src="qed/view.js"></script>
     10        <script type="text/javascript" src="qed/main-response.js"></script>
    1111        <div id="content" class="page" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%;">
    12             <div class="topbar" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
    13                 <h1>Survey</h1>
    14             </div>
    1512        </div>
    1613        <div id="toaster" data-dojo-type="qed/app/Notifications">
  • Dev/branches/rest-dojo-ui/server/couchdb-admin/bootstrap.js

    r415 r420  
    55            'dijit',
    66            'dojox',
    7             'rft'
     7            'qed'
    88        ],
    99        '.':[
  • Dev/branches/rest-dojo-ui/server/couchdb-admin/config/check.js

    r415 r420  
    11define([
    22     'dojo/_base/array',
     3     'dojo/_base/lang',
    34     'dojox/json/schema',
    4      './util/async',
     5     'qed/lib/async',
    56     './util/db',
    6      'rft/schema'
    7 ],function(array,jsonSchema,async,db,schema){
     7     'qed/model/schema'
     8],function(array,lang,jsonSchema,async,db,schema){
    89
    910    async.seq([
     
    2324        function(docs){
    2425            console.log("Validating documents.");
    25             return async.forEach(docs,function(doc){
     26            return async.map(docs,function(doc){
    2627                var id = doc.id;
    2728                return db.req('get','qed/'+id)
     
    3132                });
    3233            });
    33         },function(){
    34             console.log("Done!");
    3534        }
    36     ]);
     35    ]).then(function() {
     36        console.log("Done!");
     37    },function(err){
     38        console.log("Fail!",err.stack);
     39    });
    3740
    3841
  • Dev/branches/rest-dojo-ui/server/couchdb-admin/config/config.js

    r415 r420  
    33     'dojo/_base/lang',
    44     'dojox/lang/functional',
    5      './util/async',
     5     'qed/lib/async',
    66     './util/db',
    77     './data/design-docs'
Note: See TracChangeset for help on using the changeset viewer.