Ignore:
Timestamp:
04/29/13 19:35:10 (12 years ago)
Author:
hendrikvanantwerpen
Message:

Big cleanup of the question content.

  • Replaced old list implementations with a new one that behaves like a form widget.
  • All question content is now in separate widgets, not in the factory itself.
  • Added form and widget validation for question editing.
Location:
Dev/trunk/client/qed/model/widgets/questions
Files:
27 added
6 moved

Legend:

Unmodified
Added
Removed
  • Dev/trunk/client/qed/model/widgets/questions/Factory.js

    r435 r441  
    11define([
    2     "../../widgets/list/OrderedList",
    3     "../../widgets/list/_EditableListMixin",
     2    "./HeaderConfigWidget",
     3    "./HeaderWidget",
     4    "./MultipleChoiceInputConfigWidget",
     5    "./MultipleChoiceInputWidget",
     6    "./NumberInputConfigWidget",
     7    "./NumberInputWidget",
    48    "./ScaleInputConfigWidget",
    59    "./ScaleInputWidget",
    6     "dijit/_Container",
    7     "dijit/_TemplatedMixin",
     10    "./StringInputConfigWidget",
     11    "./StringInputWidget",
     12    "./TextConfigWidget",
     13    "./TextInputConfigWidget",
     14    "./TextInputWidget",
     15    "./TextWidget",
    816    "dijit/_WidgetBase",
    9     "dijit/form/Button",
    10     "dijit/form/CheckBox",
    11     "dijit/form/Form",
    12     "dijit/form/NumberSpinner",
    13     "dijit/form/RadioButton",
    14     "dijit/form/TextBox",
    15     "dijit/form/Textarea",
    16     "dojo/_base/array",
    17     "dojo/_base/declare",
    18     "dojo/_base/lang",
    19     "dojo/dom-construct",
    20     "dojox/layout/TableContainer"
    21 ], function(OrderedList, _EditableListMixin, ScaleInputConfigWidget, ScaleInputWidget, _Container, _TemplatedMixin, _WidgetBase, Button, CheckBox, Form, NumberSpinner, RadioButton, TextBox, Textarea, array, declare, lang, domConstruct, TableContainer) {
     17    "dojo/_base/declare"
     18], function(HeaderConfigWidget, HeaderWidget, MultipleChoiceInputConfigWidget, MultipleChoiceInputWidget, NumberInputConfigWidget, NumberInputWidget, ScaleInputConfigWidget, ScaleInputWidget, StringInputConfigWidget, StringInputWidget, TextConfigWidget, TextInputConfigWidget, TextInputWidget, TextWidget, _WidgetBase, declare) {
    2219    var factory = declare(null, {
    2320        createViewWidget: function(/*Object*/options) {
     
    3128        createEditWidget: function(/*Object*/options) {
    3229            var fun = this['create'+options.type+'EditWidget'];
    33             var view = fun !== undefined ? fun() : null;
    34             if (view) {
    35                 view.set('value', options);
    36             }
     30            var view = fun !== undefined ? fun(options) : null;
    3731            return view;
    3832        },
    3933
    40         createHeaderViewWidget: function(options) {
    41             return new HeaderView({
    42                 options: options
     34        createHeaderViewWidget: function(config) {
     35            return new HeaderWidget(config);
     36        },
     37        createHeaderEditWidget: function(config) {
     38            return new HeaderConfigWidget({
     39                value: config
    4340            });
    4441        },
    45         createHeaderEditWidget: function() {
    46             return new HeaderEdit();
     42
     43        createTextViewWidget: function(config) {
     44            return new TextWidget(config);
    4745        },
    48 
    49         createTextViewWidget: function(options) {
    50             return new TextView({
    51                 options: options
     46        createTextEditWidget: function(config) {
     47            return new TextConfigWidget({
     48                value: config
    5249            });
    53         },
    54         createTextEditWidget: function() {
    55             return new TextEdit();
    5650        },
    5751
     
    6256        },
    6357
    64         createStringInputViewWidget: function(options) {
    65             return new StringInputView({
    66                 options: options
     58        createStringInputViewWidget: function(config) {
     59            return new StringInputWidget(config);
     60        },
     61        createStringInputEditWidget: function(config) {
     62            return new StringInputConfigWidget({
     63                value: config
    6764            });
    6865        },
    69         createStringInputEditWidget: function() {
    70             return new StringInputEdit();
     66
     67        createTextInputViewWidget: function(config) {
     68            return new TextInputWidget(config);
     69        },
     70        createTextInputEditWidget: function(config) {
     71            return new TextInputConfigWidget({
     72                value: config
     73            });
    7174        },
    7275
    73         createTextInputViewWidget: function(options) {
    74             return new TextInputView({
    75                 options: options
     76        createNumberInputViewWidget: function(config) {
     77            return new NumberInputWidget(config);
     78        },
     79        createNumberInputEditWidget: function(config) {
     80            return new NumberInputConfigWidget({
     81                value: config
    7682            });
    7783        },
    78         createTextInputEditWidget: function() {
    79             return new TextInputEdit();
     84
     85        createMultipleChoiceInputViewWidget: function(config) {
     86            return new MultipleChoiceInputWidget(config);
    8087        },
    81 
    82         createIntegerInputViewWidget: function(options) {
    83             return new IntegerInputView({
    84                 options: options
     88        createMultipleChoiceInputEditWidget: function(config) {
     89            return new MultipleChoiceInputConfigWidget({
     90                value: config
    8591            });
    86         },
    87         createIntegerInputEditWidget: function() {
    88             return new IntegerInputEdit();
    89         },
    90 
    91         createMultipleChoiceInputViewWidget: function(options) {
    92             return new MultipleChoiceInputView({
    93                 options: options
    94             });
    95         },
    96         createMultipleChoiceInputEditWidget: function() {
    97             return new MultipleChoiceInputEdit();
    9892        },
    9993
     
    108102    });
    109103
    110     var DefaultEdit = declare([Form,_Container],{
    111         type: null,
    112         addChild: function(widget) {
    113             domConstruct.create("label",{
    114                 innerHTML: widget.title || ''
    115             },this.containerNode,'last');
    116             this.inherited(arguments,[widget]);
    117         },
    118         _getValueAttr: function() {
    119             var val = this.inherited(arguments);
    120             val.type = this.type;
    121             return val;
    122         }
    123     });
    124 
    125     var HeaderView = declare([_WidgetBase], {
    126         postCreate: function() {
    127             this.domNode.innerHTML = "<h2>"+this.options.content+"</h2>";
    128         }
    129     });
    130 
    131     var HeaderEdit = declare([DefaultEdit], {
    132         type: 'Header',
    133         postCreate: function() {
    134             this.inherited(arguments);
    135             this.addChild(new TextBox({
    136                 title: 'Content',
    137                 name: 'content'
    138             }));
    139         }
    140     });
    141 
    142     var TextView = declare([_WidgetBase], {
    143         postCreate: function() {
    144             this.domNode.innerHTML = "<p>"+this.options.content+"</p>";
    145         }
    146     });
    147 
    148     var TextEdit = declare([DefaultEdit], {
    149         type: 'Text',
    150         postCreate: function() {
    151             this.inherited(arguments);
    152             this.addChild(new Textarea({
    153                 title: 'Content',
    154                 name: 'content'
    155             }));
    156         }
    157     });
    158 
    159104    var DividerView = declare([_WidgetBase], {
    160105        postCreate: function() {
     
    163108    });
    164109
    165     var DefaultInputEdit = declare([DefaultEdit],{
    166         postCreate: function() {
    167             this.inherited(arguments);
    168             this.addChild(new TextBox({
    169                 title: 'Text',
    170                 name: 'text'
    171             }));
    172         }
    173     });
    174 
    175     var StringInputView = declare([_WidgetBase, _Container],{
    176         _textBox: null,
    177         postCreate: function() {
    178             this._textBox = new TextBox({
    179                 name: this.options.code || ''
    180             });
    181             this.addChild(this._textBox);
    182             this._textBox.startup();
    183         },
    184         _setReadOnlyAttr: function(value) {
    185             this._textBox.set('readOnly', value);
    186         },
    187         _getCodeAttr: function() {
    188             return this.options.code;
    189         },
    190         _getValueAttr: function() {
    191             return this._textBox.get('value') || "";
    192         },
    193         _setValueAttr: function(value) {
    194             this._textBox.set('value',value || "");
    195         }
    196     });
    197 
    198     var StringInputEdit = declare([DefaultInputEdit],{
    199         type: 'StringInput'
    200     });
    201 
    202     var TextInputView = declare([_WidgetBase, _Container], {
    203         _textArea: null,
    204         postCreate: function() {
    205             this._textArea = new Textarea({
    206                 name: this.options.code
    207             });
    208             this._textArea.set('maxLength', this.options.maxLength || 1000);
    209             this._textArea.set('value', this.options.defaultValue || "");
    210             this.addChild(this._textArea);
    211             this._textArea.startup();
    212         },
    213         _getCodeAttr: function() {
    214             return this.options.code;
    215         },
    216         _getValueAttr: function() {
    217             return this._textArea.get('value');
    218         },
    219         _setReadOnlyAttr: function(value) {
    220             this._textArea.set('readOnly', value);
    221         }
    222     });
    223 
    224     var TextInputEdit = declare([DefaultInputEdit], {
    225         type: 'TextInput',
    226         postCreate: function() {
    227             this.inherited(arguments);
    228             this.addChild(new NumberSpinner({
    229                 name: 'maxLength',
    230                 title: "Maximum length",
    231                 constraints: {
    232                     min: 0
    233                 }
    234             }));
    235         }
    236     });
    237 
    238     var IntegerInputView = declare([_WidgetBase, _Container], {
    239         _numberInput: null,
    240         postCreate: function() {
    241             this._numberInput = new NumberSpinner({
    242                 name: this.options.code || '',
    243                 constraints: {
    244                     min: this.options.min,
    245                     max: this.options.max,
    246                     smallDelta: this.options.step
    247                 }
    248             });
    249             this.addChild(this._numberInput);
    250             this._numberInput.startup();
    251         },
    252         _getCodeAttr: function() {
    253             return this.options.code;
    254         },
    255         _getValueAttr: function() {
    256             return this._numberInput.get('value');
    257         },
    258         _setReadOnlyAttr: function(value) {
    259             this._numberInput.set('readOnly', value);
    260         }
    261     });
    262 
    263     var IntegerInputEdit = declare([DefaultInputEdit], {
    264         type: 'IntegerInput',
    265         postCreate: function() {
    266             this.inherited(arguments);
    267             this.addChild(new NumberSpinner({
    268                 title: "Minimum",
    269                 name: 'min'
    270             }));
    271             this.addChild(new NumberSpinner({
    272                 title: "Maximum",
    273                 name: 'max'
    274             }));
    275             this.addChild(new NumberSpinner({
    276                 title: "Step",
    277                 name: 'step'
    278             }));
    279         }
    280     });
    281 
    282     var MultipleChoiceInputView = declare([_WidgetBase, _Container], {
    283         postCreate: function() {
    284             var table = new TableContainer({ cols: 1, customClass: "labelsAndValues"} );
    285 
    286             var Ctor = this.options.multiple === true ? CheckBox : RadioButton;
    287             array.forEach(this.options.items || [], function(item){
    288                 table.addChild(new Ctor({
    289                     name: this.options.code || '',
    290                     title: item
    291                 }));
    292             },this);
    293 
    294             this.addChild(table);
    295             table.startup();
    296         },
    297         _getCodeAttr: function() {
    298             return this.options.code;
    299         },
    300         _getValueAttr: function() {
    301             var checked = array.filter(this.getChildren(),function(child){
    302                 return child.get('checked');
    303             });
    304             if ( this.options.multiple ) {
    305                 return array.map(checked,function(child){
    306                     return child.get('value');
    307                 });
    308             } else {
    309                 return checked.length > 0 ? checked[0].get('value') : null;
    310             }
    311         },
    312         _setReadOnlyAttr: function(value) {
    313             array.forEach(this.getChildren(),function(child){
    314                 child.set('readOnly', value);
    315             },this);
    316         }
    317     });
    318 
    319     var MCOptionItem = declare([_WidgetBase,_TemplatedMixin,_Container],{
    320         templateString: '<div><span class="dojoDndHandle">=</span></div>',
    321         _textBox: null,
    322         postCreate: function() {
    323             this._textBox = new TextBox({});
    324             this.addChild(this._textBox);
    325             this._textBox.startup();
    326 
    327             var del = new Button({
    328                 label: "X",
    329                 onClick: lang.hitch(this,'onDelete')
    330             });
    331             this.addChild(del);
    332             del.startup();
    333         },
    334         _getValueAttr: function() {
    335             return this._textBox.set('value');
    336         },
    337         _setValueAttr: function(value) {
    338             this._textBox.set('value',value);
    339         },
    340         onDelete: function(){}
    341     });
    342 
    343     var MCOptionList = declare([OrderedList,_EditableListMixin],{
    344         type: 'multipleChoiceOption',
    345         withHandles: true,
    346         _createAvatarNode: function(item){
    347             return domConstruct.create("div",{
    348                 'class': 'dragAvatar',
    349                 innerHTML: item
    350             });
    351         },
    352         _createListNode: function(item) {
    353             var w = new MCOptionItem();
    354             w.startup();
    355             w.set('value',item);
    356             w.on('delete',lang.hitch(this,'_onRemove',w,item));
    357             return w.domNode;
    358         },
    359         _onRemove: function(w,item) {
    360             w.destroyRecursive();
    361             if ( this.removeCallback ) { this.removeCallback(item); }
    362             this.source.sync();
    363         }
    364     });
    365 
    366     var MultipleChoiceInputEdit = declare([_WidgetBase, _Container], {
    367         _multipleInput: null,
    368         postCreate: function() {
    369             var table = new TableContainer({ cols: 1, customClass: "labelsAndValues"} );
    370 
    371             this._multipleInput = new CheckBox({
    372                 title: "Allow multiple"
    373             });
    374             table.addChild(this._multipleInput);
    375             this._multipleInput.startup();
    376 
    377             var add = new Button({
    378                 label: "Add",
    379                 onClick: lang.hitch(this,'_addOption',"")
    380             });
    381             table.addChild(add);
    382             add.startup();
    383 
    384             this._optionsList = new MCOptionList();
    385             table.addChild(this._optionsList);
    386             this._optionsList.startup();
    387 
    388             this.addChild(table);
    389             table.startup();
    390         },
    391 
    392         _addOption: function() {
    393             this._optionsList.appendItems([""]);
    394         },
    395 
    396         _setValueAttr: function(value) {
    397             this._multipleInput.set('checked', value.multiple);
    398             this._optionsList.deleteItems();
    399             this._optionsList.appendItems(value.items || []);
    400         },
    401         _getValueAttr: function() {
    402             return {
    403                 type: "MultipleChoiceInput",
    404                 multiple: this._multipleInput.get('checked'),
    405                 items: array.map(this._optionsList.getItems(),function(item){
    406                     return item.get('value');
    407                 },this)
    408             };
    409         }
    410     });
    411 
    412110    return factory;
    413111});
  • Dev/trunk/client/qed/model/widgets/questions/ScaleInputConfigWidget.js

    r435 r441  
    11define([
    2     "dijit/_Container",
    3     "dijit/_TemplatedMixin",
    4     "dijit/_WidgetBase",
    5     "dijit/_WidgetsInTemplateMixin",
     2    "../../../widgets/ListWidget",
     3    "../../../widgets/_ComplexValueWidget",
    64    "dijit/form/Button",
    75    "dijit/form/RadioButton",
    86    "dijit/form/TextBox",
    97    "dijit/form/ValidationTextBox",
    10     "dijit/form/_FormMixin",
    118    "dojo/_base/array",
    129    "dojo/_base/declare",
     
    2017    "dijit/form/NumberTextBox",
    2118    "dijit/form/TextBox"
    22 ], function(_Container, _TemplatedMixin, _WidgetBase, _WidgetsInTemplateMixin, Button, RadioButton, TextBox, ValidationTextBox, _FormMixin, array, declare, event, lang, domAttr, domConstruct, domStyle, rowTemplate, template) {
     19], function(ListWidget, _ComplexValueWidget, Button, RadioButton, TextBox, ValidationTextBox, array, declare, event, lang, domAttr, domConstruct, domStyle, rowTemplate, template) {
    2320
    24     var Row = declare([_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,_Container,_FormMixin],{
     21    var Row = declare([_ComplexValueWidget],{
    2522        templateString: rowTemplate,
    2623        hasNA: true,
     
    2926            this.inherited(arguments);
    3027        },
    31         onDestroy: function(e) {
    32             this.destroyRecursive();
    33             event.stop(e);
    34             return false;
    35         },
     28        onDestroy: function(evt) {},
    3629        _setHasNAAttr: function(value) {
    3730            if ( value === true ) {
     
    4336    });
    4437   
    45     return declare([_WidgetBase,_TemplatedMixin,_WidgetsInTemplateMixin,_Container,_FormMixin],{
     38    return declare([_ComplexValueWidget],{
    4639        templateString: template,
    4740        baseClass: "qedScaleWidget",
    48         value: null,
    4941        _hasNA: false,
    5042        constuctor: function() {
     
    5244            this.value = {};
    5345        },
    54         onAddItem: function(e) {
    55             this._addItem();
     46        buildRendering: function() {
     47            this.inherited(arguments);
     48            this.itemsWidget = new ListWidget({
     49                name: "items",
     50                type: "ScaleItem",
     51                delay: 5,
     52                skipForm: true,
     53                createListElement: lang.hitch(this, "_createRowWidget"),
     54                createAvatar: lang.hitch(this, "_createAvatar")
     55            }, this.itemsNode);
     56        },
     57        _createRowWidget: function(id, item) {
     58            var widget = new Row({
     59                value: item,
     60                hasNA: this._hasNA,
     61                id: id,
     62                onDestroy: lang.hitch(this,function(evt){
     63                    this.itemsWidget.removeItem(id);
     64                    event.stop(evt);
     65                    return false;
     66                })
     67            });
     68            widget.startup();
     69            return widget;
     70        },
     71        _createAvatar: function(id, item) {
     72            return domConstruct.create("div", {
     73                innerHTML: item.text || "(empty item)"
     74            });
     75        },
     76        onAddNewItem: function(e) {
     77            this.itemsWidget.appendItem({});
    5678            event.stop(e);
    5779            return false;
    5880        },
    59         _clearItems: function() {
    60             domConstruct.empty(this.itemsNode);
    61         },
    62         _addItem: function(value) {
    63             var item = new Row({
    64                 name: 'items',
    65                 value: value,
    66                 hasNA: this._hasNA
    67             });
    68             item.placeAt(this.itemsNode);
    69             return item;
    70         },
    71         _setValueAttr: function(value) {
    72             this.inherited(arguments);
    73             this._clearItems();
    74             array.forEach(value.items, function(value){
    75                 this._addItem(value);
    76             }, this);
    77         },
    7881        _getValueAttr: function(){
    7982            var value = this.inherited(arguments);
    80             if ( value.items && !lang.isArray(value.items) ) {
    81                 value.items = [value.items];
    82             }
    8383            value.type = 'ScaleInput';
    8484            return value;
     
    8989        },
    9090        _updateNA: function(value) {
    91             array.forEach(this.getChildren(), function(child) {
     91            array.forEach(this.itemsWidget.getChildren(), function(child) {
    9292                child.set('hasNA', this._hasNA);
    9393            }, this);
  • Dev/trunk/client/qed/model/widgets/questions/ScaleInputWidget.js

    r435 r441  
    11define([
    2     "dijit/_Container",
    3     "dijit/_TemplatedMixin",
    4     "dijit/_WidgetBase",
     2    "../../../widgets/_ComplexValueWidget",
    53    "dijit/form/RadioButton",
    6     "dijit/form/_FormMixin",
    74    "dojo/_base/array",
    85    "dojo/_base/declare",
     
    118    "dojo/dom-construct",
    129    "dojo/text!./templates/ScaleInputWidget.html"
    13 ], function(_Container, _TemplatedMixin, _WidgetBase, RadioButton, _FormMixin, array, declare, lang, domAttr, domConstruct, template) {
    14     return declare([_WidgetBase,_TemplatedMixin,_Container,_FormMixin],{
     10], function(_ComplexValueWidget, RadioButton, array, declare, lang, domAttr, domConstruct, template) {
     11    return declare([_ComplexValueWidget],{
    1512        templateString: template,
    1613        baseClass: "qedScaleWidget",
     
    4138                domConstruct.create("th", {
    4239                    innerHTML: i.toString()
    43                 }, this.minNode, "after");
     40                }, this.maxNode, "before");
    4441            }
    4542        },
     
    6966                    className: 'max'
    7067                }, tr);
    71                 if ( this.naLabel !== null ) {
     68                if ( this.naLabel !== null && this.naLabel !== "" ) {
    7269                    td = domConstruct.create("td", {}, tr);
    7370                    radio = new RadioButton({
  • Dev/trunk/client/qed/model/widgets/questions/templates/ScaleInputConfigWidget.html

    r435 r441  
    4141        <td class="item">
    4242          <button data-dojo-type="dijit/form/Button"
    43                   data-dojo-attach-event="onClick:onAddItem">Click to add item</button>
     43                  data-dojo-attach-event="onClick:onAddNewItem">Click to add item</button>
    4444        </td>
    4545      </tr>
Note: See TracChangeset for help on using the changeset viewer.