source: Dev/branches/jos-branch/js/questionEditorScripts.js @ 236

Last change on this file since 236 was 236, checked in by tschipper, 13 years ago
File size: 16.5 KB
Line 
1var questionEditor = new QuestionEditor();
2
3function createNewElement(tag, type, id, cl, value) {
4    var newElement = document.createElement(tag);
5    if (type != undefined) newElement.type = type;
6    if (id != undefined) newElement.id = id;
7    if (cl != undefined) newElement.className = cl;
8    if (value != undefined) {
9        newElement.value = value;
10        newElement.text = value;
11    }
12    return newElement;
13}
14
15function createNewInputLabel(text, target, side) {
16    var newLabel = document.createElement("label");
17    if (target) newLabel.setAttribute("for",target);
18    newLabel.innerHTML = text;
19    if (side) newLabel.className = side;
20    return newLabel;
21}
22
23function updateIdentifier() {
24    var identField = document.getElementById("questionIdentifierField");
25    if (identField.value == undefined && identField.value == "") {
26        return;
27    }
28    var headerField = document.getElementById("header_identifier");
29    headerField.innerHTML = identField.value;
30}
31
32
33//////////////////////////////////////////
34/* QUESTION EDITOR CLASS BASED APPROACH */
35//////////////////////////////////////////
36
37function QuestionEditor() {
38    // Properties   
39    this.uid = null;    // The uid of the question contained in this editor
40    var me = this;      // Retarded self-reference because: Javascript
41    this.element = null;    // The parent div element containing the questionEditor
42    this.paramsElement = null;  // The parent parameters element where all the input sets will be located
43    this.paramSets = null;  // The currently enabled input sets to be displayed in the paramsElement.
44    //Currently only supports a single param set (based on answer type), but functionality will scale to multiple sets as needed.
45   
46    // Methods
47    // Basic functionality
48    this.setValues = function(arguments) {
49        var question = JSON.parse(arguments);       
50    }
51    this.init = function() {
52        // Outer div
53        me.element = ce("div");
54        me.element.className = "smallFrame questionEditor";
55        me.element.id = sequencer.state.selectedObject.uid;
56        me.uid = sequencer.state.selectedObject.uid;
57        // Header
58        var titleDiv = ce("div");
59        titleDiv.className = "smallTitle";
60        var numberDiv = ce("div");
61        numberDiv.className = "listNumber";
62        numberDiv.innerHTML = "4"; //TODO
63        var nameSpan = ce("span");
64        nameSpan.id = "qeTitleField";
65        nameSpan.innerHTML = "New question";
66        titleDiv.appendChild(numberDiv);
67        titleDiv.innerHTML += "Editing: ";
68        titleDiv.appendChild(nameSpan);
69        me.element.appendChild(titleDiv);
70   
71        //Content area
72        var contentDiv = ce("div");
73        contentDiv.className = "content";
74        var bodyText = createNewElement("textarea", null, "qeBodyTextField", "qeBodyTextField", null);
75        bodyText.value = "Question body text goes here";
76        contentDiv.appendChild(bodyText);
77       
78        // The dynamic questionParams div, where all the control elements and inputs will be located
79        var questionParams = ce("div");
80        me.paramsElement = questionParams;
81        questionParams.className = "questionParams";
82        questionParams.id = "qeQuestionParamsDiv";
83       
84        var basicContainer = ce("div");
85        basicContainer.id = "basicInputs";
86       
87        var qeCodeField = createNewElement("input", "text", "qeCodeField", "qeParamField", null);
88        var qeCodeField_lbl = createNewInputLabel("Question code:","qeCodeField", "l");
89        basicContainer.appendChild(qeCodeField_lbl);
90        basicContainer.appendChild(qeCodeField);
91   
92        var qeTypeField = createNewElement("select", null, "qeTypeField", "qeParamField", null);
93        var qeTypeField_lbl = createNewInputLabel("Answer type:","qeTypeField", "l");
94        basicContainer.appendChild(qeTypeField_lbl);
95        basicContainer.appendChild(qeTypeField);
96        questionParams.appendChild(basicContainer);
97        qeTypeField.addEventListener("change", function(){
98            //debugger;
99            me.selectAnswerType();
100        }, false);
101        // Add the select options. Do this in a block scope to prevent the o1 var from messing things up.
102        // Also helps in structuring code.
103        {
104            var o1 = ce("option");
105            o1.value = null;
106            o1.text = "";
107            qeTypeField.appendChild(o1);
108       
109            o1 = ce("option");
110            o1.value = "int";
111            o1.text = "Integer";
112            qeTypeField.appendChild(o1);
113       
114            o1 = ce("option");
115            o1.value = "scale";
116            o1.text = "Scale";
117            qeTypeField.appendChild(o1);
118       
119            o1 = ce("option");
120            o1.value = "choice";
121            o1.text = "Multiple choice";
122            qeTypeField.appendChild(o1);
123       
124            o1 = ce("option");
125            o1.value = "text";
126            o1.text = "Text";
127            qeTypeField.appendChild(o1);
128        }
129   
130        contentDiv.appendChild(questionParams);
131        me.element.appendChild(contentDiv);
132   
133        // Controls bar
134        var controlsDiv = ce("div");
135        controlsDiv.className = "controls";
136        var btnDiscard = createNewElement("input", "button", "btnDiscard", null, "Discard");
137        var btnSave = createNewElement("input", "button", "btnSave", null, "Save");
138        controlsDiv.appendChild(btnDiscard);
139        controlsDiv.appendChild(btnSave);
140        btnSave.addEventListener("click", function(){
141            me.save();
142        }, false);
143        btnDiscard.addEventListener("click", function(){
144            me.discard();
145        }, false);
146        me.element.appendChild(controlsDiv);
147        me.paramSets = new Array();
148        me.paramSets.push("basic");
149    }
150    this.save = function() {
151        var request = {
152            "title": ge("qeTitleField").value,
153            "type": ge("qeTypeField").value
154        }
155       
156        switch (ge("qeTypeField").value) {
157            case "int":
158                request.answerType = "int";
159                request.minValue = parseInt(ge("qeMinValueField").value);
160                request.maxValue = parseInt(ge("qeMaxValueField").value);
161                break;
162            case "scale":
163                request.answerType = "scale";
164                request.numChoices = parseInt(ge("qeNumChoicesField").value);
165                request.legendsEnabled = ge("qeLegendsEnabledField").checked;
166                request.lowerLegend = ge("qeLowerLegendField").value;
167                request.upperLegend = ge("qeUpperLegendField").value;
168                break;
169            case "choice":
170                request.answerType = "choice";
171                break;
172            case "text":
173                request.answerType = "text";
174                break;
175        }
176       
177        newAjaxRequest(requestString, "createObject.php", function(result){
178            // Display a success message, or throw an error.
179            }, true);
180    }
181    this.reset = function() {
182        me.init();
183    }
184   
185    // Updating input fields
186    this.selectAnswerType = function () {
187        // Switch statement, call this.type_x where x is the relevant answer type.
188        // For all this.type_X funtions:
189        // Use the createNewElement and createNewInputLabel methods to add controls or input fields.
190        // Input field convention:  class = questionParamField, id = qTypeField / qScaleSize, etc...
191        // Input label class: "l" or "r" depending on alignment with respect to parent ("for") input element.
192        // Important: use the this.paramsElement to access the questionParams div!
193        // To fully make use of this class-based approach, the editor should be added to a global variable. This global variable should be removed on page unload!
194   
195        var type = ge("qeTypeField").value;
196        switch (type) {
197            case "int":
198                me.type_Integer();
199                break;
200            case "scale":
201                me.type_Scale();
202                break;
203            case "choice":
204                me.type_Choice();
205                break;
206            case "text":
207                me.type_Text();
208                break;
209            default:
210                //Do nothing
211                break;
212               
213        }
214   
215    }
216    this.checkInputSets = function () {
217        // Loop through all input containers in the paramsField
218        for (var n = 0; n < me.paramsElement.childNodes.length; n++) {
219            if (me.paramsElement.childNodes[n].id == "basicInputs") continue;
220            // Check if the id (inputSet) is currently in paramSets
221            if (me.paramSets.indexOf(me.paramsElement.childNodes[n].id) < 0) {
222                me.paramsElement.childNodes[n].parentNode.removeChild(me.paramsElement.childNodes[n]);
223                n--;
224            }
225           
226        }
227    }
228   
229    this.type_Integer = function () {
230        if (me.paramSets.indexOf("int_basic") < 0) {
231            me.paramSets = new Array("int_basic");
232        }
233        else return;
234       
235        me.checkInputSets();
236       
237        var container = ce("div");
238        container.id = "int_basic";
239
240        var qeMinValueField = createNewElement("input", "text", "qeMinValueField", "qeParamField", null);
241        var qeMinValueField_lbl = createNewInputLabel("Minimum value: ", "qeMinValueField", "l");
242        var qeMaxValueField = createNewElement("input", "text", "qeMaxValueField", "qeParamField", null);
243        var qeMaxValueField_lbl = createNewInputLabel("Maximum value: ", "qeMaxValueField", "l");
244       
245        container.appendChild(qeMinValueField_lbl);
246        container.appendChild(qeMinValueField);
247        container.appendChild(qeMaxValueField_lbl);
248        container.appendChild(qeMaxValueField);
249        me.paramsElement.appendChild(container);
250       
251    }
252    this.type_Scale = function () {
253        if (me.paramSets.indexOf("scale_basic") < 0) {
254            me.paramSets = new Array("scale_basic");
255        }
256        else return;
257        // Clear any input sets that should not be there
258        me.checkInputSets();
259       
260        var container = ce("div");
261        container.id = "scale_basic";
262       
263        // Number of choices SELECT
264        var numChoicesField = createNewElement("select", null, "qeNumChoicesField", "qeParamField", null);
265        var numChoicesField_lbl = createNewInputLabel("Number of choices", "qeNumChoicesField", "l");
266        // SELECT options
267        for (var n = 2; n < 11; n++) {
268            var o = ce("option");
269            o.value = n;
270            o.text = n;
271            numChoicesField.appendChild(o);
272        }
273        container.appendChild(numChoicesField_lbl);
274        container.appendChild(numChoicesField);
275       
276        // Scale labels CHECKBOX and TEXTs
277        var legendsEnabledField = createNewElement("input", "checkbox", "qeLegendsEnabledField", "qeParamField", null);
278        var legendsEnabledField_lbl = createNewInputLabel("Enable legends", "qeLegendsEnabledField", "l");
279        container.appendChild(legendsEnabledField_lbl);
280        container.appendChild(legendsEnabledField);
281       
282        var upperLegendText = createNewElement("input", "text", "qeUpperLegendField", "qeParamField", null);
283        var lowerLegendText = createNewElement("input", "text", "qeLowerLegendField", "qeParamField", null);
284        var upperLegendText_lbl = createNewInputLabel("Upper legend", "qeUpperLegendField", "l");
285        var lowerLegendText_lbl = createNewInputLabel("Lower legend", "qeLowerLegendField", "l");
286        container.appendChild(lowerLegendText_lbl);
287        container.appendChild(lowerLegendText);
288        container.appendChild(upperLegendText_lbl);
289        container.appendChild(upperLegendText);
290       
291        me.paramsElement.appendChild(container);
292    }
293    this.type_Text = function () {
294        if (me.paramSets.indexOf("text_basic") < 0) {
295            me.paramSets = new Array("text_basic");
296        }
297        else return;
298       
299        me.checkInputSets();
300       
301        var container = ce("div");
302        container.id="text_basic";
303    }
304    this.type_Choice = function() {
305        //debugger;
306        if (me.paramSets.indexOf("choice_basic") < 0) {
307            me.paramSets = new Array("choice_basic");
308        }
309        else return;
310       
311        me.checkInputSets();
312       
313        var container = ce("div");
314        container.id = "choice_basic";
315        // num options SELECT
316        var numOptionsSelect = createNewElement("select", null, "qeNumOptionsField", "qeParamField", null);
317        var numOptionsSelect_lbl = createNewInputLabel("Number of options", "qeNumOptionsField", "l");
318        for (var n = 2; n < 11; n++) {
319            var o = ce("option");
320            o.value = n;
321            o.text = n;
322            numOptionsSelect.appendChild(o);
323        }
324        container.appendChild(numOptionsSelect_lbl);
325        container.appendChild(numOptionsSelect);
326        numOptionsSelect.addEventListener("change", me.type_Choice_CheckAnswerFields, true);
327       
328        var allowMultiple = createNewElement("input", "checkbox", "qeMultipleAnswersField", "qeParamField", null);
329        var allowMultiple_lbl = createNewInputLabel("Allow multiple answers", "qeMultipleAnswersField", "l");
330        container.appendChild(allowMultiple_lbl);
331        container.appendChild(allowMultiple);
332       
333        var answersField = ce("div");
334        answersField.className = "qeParamFieldset";
335        answersField.id = "qeParamsAnswerFieldset"
336        container.appendChild(answersField);
337                       
338        me.paramsElement.appendChild(container);
339        me.type_Choice_CheckAnswerFields();
340    }
341    this.type_Choice_CheckAnswerFields = function() {
342        debugger;
343        var container = ge("qeParamsAnswerFieldset");
344        var numAnswers = parseInt(document.getElementById("qeNumOptionsField").value, 10);
345        var numAnswerFields = 0;
346        {
347            for (var i = container.childNodes.length-1; i >= 0; i--) {
348                if (container.childNodes[i].className = "qeChoiceAnswerGroup") {
349                    numAnswerFields++;
350                }
351            }
352           
353        }
354       
355        // If there already are the correct number of answer fields, exit the function
356        if (numAnswers == numAnswerFields) return;
357        else if (numAnswers > numAnswerFields) {
358            // Extra inputs need to be added
359            var n = numAnswers - numAnswerFields;
360            for (var x = 0; x < n; x++) {
361                var group = ce("div");
362                group.className = "qeChoiceAnswerGroup";
363                var field = createNewElement("input", "text", "qeAnswerField"+(numAnswerFields+x), "qeParamField", null);
364                var field_lbl = createNewInputLabel((numAnswerFields+x), "qeAnswerField"+(numAnswerFields+x), "l");
365               
366                group.appendChild(field_lbl);
367                group.appendChild(field);
368                container.appendChild(group);
369            }
370        }
371        else if (numAnswers < numAnswerFields) {
372            // There are too many inputs and some need to be removed. Start at the end!
373            // TODO: This is SO inefficient. There has to be a better way, perhaps adding elements to an array?
374            // TODO: Another approach would be to use the previousSibling property as a way to prevent having to loop through the entire container tree every time an object needs to be removed.
375            var n = numAnswerFields-numAnswers;
376            for (var x = 0; x < n; x++) {
377                for (var y = container.childNodes.length-1; y >= 0; y--) {
378                    if (container.childNodes[y].className == "qeChoiceAnswerGroup") {
379                        container.removeChild(container.childNodes[y]);
380                        break;
381                    }
382                }
383            }
384        }
385    }
386     
387    // Editing
388    this.editQuestion = function(uid) {
389        if (sequencer.state.editing == true) return;
390        if (sequencer.state.loaded == false) return;
391        sequencer.state.editing = true;
392       
393        var request = {
394            "type": "Question",
395            "uid": uid
396        }
397       
398        var requestString = "args="+JSON.stringify(request);
399        sequencer.state.loaded = false;
400        newAjaxRequest(requestString, getObject.php, function(result){
401            // Once results are in
402            questionEditor.setValues(result.responseText);
403            sequencer.state.loaded = true;
404        }, true);
405    }
406    this.createNewQuestion = function() {
407        if (sequencer.state.editing == true) return;
408        if (sequencer.state.loading == true) return;
409        sequencer.state.editing = true;
410   
411        me.reset();
412        var container = ge("seqContentWrapper");
413        container.appendChild(me.element);
414    }
415}
Note: See TracBrowser for help on using the repository browser.