source: Dev/branches/jQueryUI/js/old/questionEditorScripts.js @ 244

Last change on this file since 244 was 244, checked in by hendrikvanantwerpen, 13 years ago

Inlogscherm werkt met jQuery UI! Woot!

File size: 21.1 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 QuestionEditor() {
24    // Properties   
25    this.uid = null;    // The uid of the question contained in this editor
26    var me = this;      // Retarded self-reference because: Javascript
27    this.element = null;    // The parent div element containing the questionEditor
28    this.paramsElement = null;  // The parent parameters element where all the input sets will be located
29    this.paramSets = null;  // The currently enabled input sets to be displayed in the paramsElement.
30    //Currently only supports a single param set (based on answer type), but functionality will scale to multiple sets as needed.
31   
32    // Methods
33    // Basic functionality
34    this.setValues = function(arguments) {
35        debugger;
36        var question = JSON.parse(arguments)[0];
37        var qeTypeField = ge("qeTypeField");
38        ge("qeCodeField").value = question.code;
39        ge("qeBodyTextField").value = question.description;
40        switch (question.type.toLowerCase()) {
41            case "int":
42                // First make sure all the necessary fields are present
43                me.type_Integer();
44                // Then fill them in using the available data
45                qeTypeField.value = "int";
46                ge("qeMinValueField").value = question.minValue;
47                ge("qeMaxValueField").value = question.maxValue;
48                break;
49            case "scale":
50                me.type_Scale();
51                qeTypeField.value = "scale";
52                ge("qeNumChoicesField").value = question.numChoices;
53                ge("qeLegendsEnabledField").checked = question.legendsEnabled;
54                ge("qeLowerLegendField").value = question.lowerLegend;
55                ge("qeUpperLegendField").value = question.upperLegend;
56                break;
57            case "choice":
58                me.type_Choice();
59                qeTypeField.value = "choice";
60                ge("qeNumOptionsFIeld").value = question.numOptions;
61                ge("qeMultipleAnswersField").value = question.multipleAnswers;
62                // then some more to add textboxes (qeParamAnswerGroup) for all the possble choices.
63                // Maybe a central version that appends these groups/textinputs? Maybe not, though. Don't want stuff too abstracted...
64                break;
65            case "text":
66                me.type_Text();
67                qeTypeField.value = "text";
68                ge("qeTextMaxLengthField").value = question.maxTextLength;
69                break;
70               
71        }
72    }
73    this.init = function(uid) {
74        // Outer div
75        this.saved = false;
76        me.element = ce("div");
77        me.element.className = "smallFrame questionEditor";
78        me.element.id = sequencer.state.selectedObject.uid;
79        me.uid = uid;
80        // Header
81        var titleDiv = ce("div");
82        titleDiv.className = "smallTitle";
83        var numberDiv = ce("div");
84        numberDiv.className = "listNumber";
85        numberDiv.innerHTML = "4"; //TODO
86        var nameSpan = ce("span");
87        nameSpan.id = "qeTitleField";
88        nameSpan.innerHTML = "New question";
89        titleDiv.appendChild(numberDiv);
90        titleDiv.innerHTML += "Editing: ";
91        titleDiv.appendChild(nameSpan);
92        me.element.appendChild(titleDiv);
93   
94        //Content area
95        var contentDiv = ce("div");
96        contentDiv.className = "content";
97        var bodyText = createNewElement("textarea", null, "qeBodyTextField", "qeBodyTextField", null);
98        bodyText.value = "Question body text goes here";
99        contentDiv.appendChild(bodyText);
100       
101        // The dynamic questionParams div, where all the control elements and inputs will be located
102        var questionParams = ce("div");
103        me.paramsElement = questionParams;
104        questionParams.className = "questionParams";
105        questionParams.id = "qeQuestionParamsDiv";
106       
107        var basicContainer = ce("div");
108        basicContainer.id = "basicInputs";
109       
110        var qeCodeField = createNewElement("input", "text", "qeCodeField", "qeParamField", null);
111        var qeCodeField_lbl = createNewInputLabel("Question code:","qeCodeField", "l");
112        basicContainer.appendChild(qeCodeField_lbl);
113        basicContainer.appendChild(qeCodeField);
114   
115        var qeTypeField = createNewElement("select", null, "qeTypeField", "qeParamField", null);
116        var qeTypeField_lbl = createNewInputLabel("Answer type:","qeTypeField", "l");
117        basicContainer.appendChild(qeTypeField_lbl);
118        basicContainer.appendChild(qeTypeField);
119        questionParams.appendChild(basicContainer);
120        qeTypeField.addEventListener("change", function(){
121            //debugger;
122            me.selectAnswerType();
123        }, false);
124        // Add the select options. Do this in a block scope to prevent the o1 var from messing things up.
125        // Also helps in structuring code.
126        {
127            var o1 = ce("option");
128            o1.value = null;
129            o1.text = "";
130            qeTypeField.appendChild(o1);
131       
132            o1 = ce("option");
133            o1.value = "int";
134            o1.text = "Integer";
135            qeTypeField.appendChild(o1);
136       
137            o1 = ce("option");
138            o1.value = "scale";
139            o1.text = "Scale";
140            qeTypeField.appendChild(o1);
141       
142            o1 = ce("option");
143            o1.value = "choice";
144            o1.text = "Multiple choice";
145            qeTypeField.appendChild(o1);
146       
147            o1 = ce("option");
148            o1.value = "text";
149            o1.text = "Text";
150            qeTypeField.appendChild(o1);
151        }
152   
153        contentDiv.appendChild(questionParams);
154        me.element.appendChild(contentDiv);
155   
156        // Controls bar
157        var controlsDiv = ce("div");
158        controlsDiv.className = "controls";
159        var btnDiscard = createNewElement("input", "button", "btnDiscard", null, "Discard");
160        var btnSave = createNewElement("input", "button", "btnSave", null, "Save");
161        controlsDiv.appendChild(btnDiscard);
162        controlsDiv.appendChild(btnSave);
163        btnSave.addEventListener("click", function(){
164            me.save();
165        }, false);
166        btnDiscard.addEventListener("click", function(){
167            me.discard();
168        }, false);
169        me.element.appendChild(controlsDiv);
170        me.paramSets = new Array();
171        me.paramSets.push("basic");
172    }
173    this.save = function() {
174        var request = {
175            "title": ge("qeTitleField").innerHTML,
176            "code": ge("qeCodeField").value,
177            "description": ge("qeBodyTextField").value,
178            "uid": me.uid
179        }
180       
181        switch (ge("qeTypeField").value) {
182            case "int":
183                request.answerType = "int";
184                request.minValue = parseInt(ge("qeMinValueField").value);
185                if (request.minValue == "NaN") request.minValue = -1;   // Is this the correct way to do this?
186                request.maxValue = parseInt(ge("qeMaxValueField").value);
187                if (request.maxValue == "NaN") request.maxValue = -1;
188                // Include error checking and form validation!!
189                break;
190            case "scale":
191                request.answerType = "scale";
192                request.numChoices = parseInt(ge("qeNumChoicesField").value);
193                request.legendsEnabled = ge("qeLegendsEnabledField").checked;
194                request.lowerLegend = ge("qeLowerLegendField").value;
195                request.upperLegend = ge("qeUpperLegendField").value;
196                // Include error checking and form validation!!
197                break;
198            case "choice":
199                request.answerType = "choice";
200                request.multipleAnswers = ge("qeMultipleAnswersField").checked;
201                request.possibleAnswers = array();
202                var answerFieldset = ge("qeParamsAnswerFieldset");
203                var count = ge("qeNumOptionsField").value;
204               
205                for (var i = 0; i < count; i++) {
206                    var el = ge("qeAnswerField"+i);
207                    request.possibleAnswers.push(el.value);
208                }
209                // Include error checking and form validation!!
210                break;
211            case "text":
212                request.answerType = "text";
213                request.maxTextLength = ge("qeTextMaxLength").value;
214                // Include error checking and form validation!!
215                break;
216        }
217       
218        requestString = "args="+JSON.stringify(request);
219       
220        newAjaxRequest(requestString, "setQuestion.php", function(result){
221            // Then add the returned uid, if existing, to the sequencer.survey.questions array and set it for update
222            debugger;
223            console.log(result.responseText);
224            var response = JSON.parse(result.responseText);
225            console.log(response);
226            if (response.created == true) {
227                if (response.uid) {
228                    // Created a new question, add it to the sequencer content array and set it for an update
229                    sequencer.survey.questions.uids.push(response.uid);
230                    sequencer.survey.questions.upToDate.push(false);
231                }
232                else {
233                    alert("ERROR!");
234                }
235            } else {
236                if (response.uid){
237                    // Edited an existing question that was already in the sequencer content. Set it for an update
238                    sequencer.survey.questions.upToDate[sequencer.survey.questions.uids.indexOf(response.uid)] = false;
239                }
240                else {
241                    alert("ERROR!");
242                }
243            }
244           
245           
246            // Then remove the editor from the sequencer content, so that it can be replaced with the correct question view.
247            me.element.parentNode.removeChild(me.element);
248            sequencer.state.editing = false;
249            updateSequencer();
250        }, true);
251    }
252    this.discard = function() {
253        debugger;
254        me.element.parentNode.removeChild(me.element);
255        sequencer.state.loaded = true;
256        sequencer.state.editing = false;
257        sequencer.survey.questions.upToDate[sequencer.survey.questions.uids.indexOf(me.uid)] = false;
258        updateSequencer();
259       
260    }
261    this.reset = function() {
262        me.init();
263    }
264   
265    // Updating input fields
266    this.selectAnswerType = function () {
267        // Switch statement, call this.type_x where x is the relevant answer type.
268        // For all this.type_X funtions:
269        // Use the createNewElement and createNewInputLabel methods to add controls or input fields.
270        // Input field convention:  class = questionParamField, id = qTypeField / qScaleSize, etc...
271        // Input label class: "l" or "r" depending on alignment with respect to parent ("for") input element.
272        // Important: use the this.paramsElement to access the questionParams div!
273        // 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!
274   
275        var type = ge("qeTypeField").value;
276        switch (type) {
277            case "int":
278                me.type_Integer();
279                break;
280            case "scale":
281                me.type_Scale();
282                break;
283            case "choice":
284                me.type_Choice();
285                break;
286            case "text":
287                me.type_Text();
288                break;
289            default:
290                //Do nothing
291                break;
292               
293        }
294   
295    }
296    this.checkInputSets = function () {
297        // Loop through all input containers in the paramsField
298        for (var n = 0; n < me.paramsElement.childNodes.length; n++) {
299            if (me.paramsElement.childNodes[n].id == "basicInputs") continue;
300            // Check if the id (inputSet) is currently in paramSets
301            if (me.paramSets.indexOf(me.paramsElement.childNodes[n].id) < 0) {
302                me.paramsElement.childNodes[n].parentNode.removeChild(me.paramsElement.childNodes[n]);
303                n--;
304            }
305           
306        }
307    }
308   
309    this.type_Integer = function () {
310        if (me.paramSets.indexOf("int_basic") < 0) {
311            me.paramSets = new Array("int_basic");
312        }
313        else return;
314       
315        me.checkInputSets();
316       
317        var container = ce("div");
318        container.id = "int_basic";
319
320        var qeMinValueField = createNewElement("input", "text", "qeMinValueField", "qeParamField", null);
321        var qeMinValueField_lbl = createNewInputLabel("Minimum value: ", "qeMinValueField", "l");
322        var qeMaxValueField = createNewElement("input", "text", "qeMaxValueField", "qeParamField", null);
323        var qeMaxValueField_lbl = createNewInputLabel("Maximum value: ", "qeMaxValueField", "l");
324       
325        container.appendChild(qeMinValueField_lbl);
326        container.appendChild(qeMinValueField);
327        container.appendChild(qeMaxValueField_lbl);
328        container.appendChild(qeMaxValueField);
329        me.paramsElement.appendChild(container);
330       
331    }
332    this.type_Scale = function () {
333        if (me.paramSets.indexOf("scale_basic") < 0) {
334            me.paramSets = new Array("scale_basic");
335        }
336        else return;
337        // Clear any input sets that should not be there
338        me.checkInputSets();
339       
340        var container = ce("div");
341        container.id = "scale_basic";
342       
343        // Number of choices SELECT
344        var numChoicesField = createNewElement("select", null, "qeNumChoicesField", "qeParamField", null);
345        var numChoicesField_lbl = createNewInputLabel("Number of choices", "qeNumChoicesField", "l");
346        // SELECT options
347        for (var n = 2; n < 11; n++) {
348            var o = ce("option");
349            o.value = n;
350            o.text = n;
351            numChoicesField.appendChild(o);
352        }
353        container.appendChild(numChoicesField_lbl);
354        container.appendChild(numChoicesField);
355       
356        // Scale labels CHECKBOX and TEXTs
357        var legendsEnabledField = createNewElement("input", "checkbox", "qeLegendsEnabledField", "qeParamField", null);
358        var legendsEnabledField_lbl = createNewInputLabel("Enable legends", "qeLegendsEnabledField", "l");
359        container.appendChild(legendsEnabledField_lbl);
360        container.appendChild(legendsEnabledField);
361       
362        var upperLegendText = createNewElement("input", "text", "qeUpperLegendField", "qeParamField", null);
363        var lowerLegendText = createNewElement("input", "text", "qeLowerLegendField", "qeParamField", null);
364        var upperLegendText_lbl = createNewInputLabel("Upper legend", "qeUpperLegendField", "l");
365        var lowerLegendText_lbl = createNewInputLabel("Lower legend", "qeLowerLegendField", "l");
366        container.appendChild(lowerLegendText_lbl);
367        container.appendChild(lowerLegendText);
368        container.appendChild(upperLegendText_lbl);
369        container.appendChild(upperLegendText);
370       
371        me.paramsElement.appendChild(container);
372    }
373    this.type_Text = function () {
374        if (me.paramSets.indexOf("text_basic") < 0) {
375            me.paramSets = new Array("text_basic");
376        }
377        else return;
378       
379        me.checkInputSets();
380       
381        var container = ce("div");
382        container.id="text_basic";
383    }
384    this.type_Choice = function() {
385        //debugger;
386        if (me.paramSets.indexOf("choice_basic") < 0) {
387            me.paramSets = new Array("choice_basic");
388        }
389        else return;
390       
391        me.checkInputSets();
392       
393        var container = ce("div");
394        container.id = "choice_basic";
395        // num options SELECT
396        var numOptionsSelect = createNewElement("select", null, "qeNumOptionsField", "qeParamField", null);
397        var numOptionsSelect_lbl = createNewInputLabel("Number of options", "qeNumOptionsField", "l");
398        for (var n = 2; n < 11; n++) {
399            var o = ce("option");
400            o.value = n;
401            o.text = n;
402            numOptionsSelect.appendChild(o);
403        }
404        container.appendChild(numOptionsSelect_lbl);
405        container.appendChild(numOptionsSelect);
406        numOptionsSelect.addEventListener("change", me.type_Choice_CheckAnswerFields, true);
407       
408        var allowMultiple = createNewElement("input", "checkbox", "qeMultipleAnswersField", "qeParamField", null);
409        var allowMultiple_lbl = createNewInputLabel("Allow multiple answers", "qeMultipleAnswersField", "l");
410        container.appendChild(allowMultiple_lbl);
411        container.appendChild(allowMultiple);
412       
413        var answersField = ce("div");
414        answersField.className = "qeParamFieldset";
415        answersField.id = "qeParamsAnswerFieldset"
416        container.appendChild(answersField);
417                       
418        me.paramsElement.appendChild(container);
419        me.type_Choice_CheckAnswerFields();
420    }
421    this.type_Choice_CheckAnswerFields = function() {
422        var container = ge("qeParamsAnswerFieldset");
423        var numAnswers = parseInt(document.getElementById("qeNumOptionsField").value, 10);
424        var numAnswerFields = 0;
425        {
426            for (var i = container.childNodes.length-1; i >= 0; i--) {
427                if (container.childNodes[i].className = "qeChoiceAnswerGroup") {
428                    numAnswerFields++;
429                }
430            }
431           
432        }
433       
434        // If there already are the correct number of answer fields, exit the function
435        if (numAnswers == numAnswerFields) return;
436        else if (numAnswers > numAnswerFields) {
437            // Extra inputs need to be added
438            var n = numAnswers - numAnswerFields;
439            for (var x = 1; x < n+1; x++) {
440                var group = ce("div");
441                group.className = "qeChoiceAnswerGroup";
442                var field = createNewElement("input", "text", "qeAnswerField"+(numAnswerFields+x), "qeParamField", null);
443                var field_lbl = createNewInputLabel((numAnswerFields+x), "qeAnswerField"+(numAnswerFields+x), "l");
444               
445                group.appendChild(field_lbl);
446                group.appendChild(field);
447                container.appendChild(group);
448            }
449        }
450        else if (numAnswers < numAnswerFields) {
451            // There are too many inputs and some need to be removed. Start at the end!
452            // TODO: This is SO inefficient. There has to be a better way, perhaps adding elements to an array?
453            // 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.
454            var n = numAnswerFields-numAnswers;
455            for (var x = 0; x < n; x++) {
456                for (var y = container.childNodes.length-1; y >= 0; y--) {
457                    if (container.childNodes[y].className == "qeChoiceAnswerGroup") {
458                        container.removeChild(container.childNodes[y]);
459                        break;
460                    }
461                }
462            }
463        }
464    }
465     
466    // Editing
467    this.editQuestion = function(uid) {
468        debugger;
469        if (sequencer.state.editing == true) return;
470        if (sequencer.state.loaded == false) return;
471        sequencer.state.editing = true;
472        sequencer.state.loaded = false;
473       
474        var request = new Array({
475            type: "Question",
476            uid: uid
477        });
478        me.init(uid);
479        var oldElement = ge(uid);
480        if (oldElement) {
481            // There really should be.... I don't know why I am doing this check...
482            oldElement.parentNode.replaceChild(me.element, oldElement);
483        }
484        var requestString = "args="+JSON.stringify(request);
485        newAjaxRequest(requestString, "getObject.php", function(result){
486            // Once results are in
487            me.setValues(result.responseText);
488            sequencer.state.loaded = true;
489        }, true);
490    }
491    this.createNewQuestion = function() {
492        if (sequencer.state.editing == true) return;
493        if (sequencer.state.loading == false) return;
494        sequencer.state.editing = true;
495   
496        me.init(null);
497        var container = ge("seqContentWrapper");
498        container.appendChild(me.element);
499    }
500}
501
502// IT LIIIIIIVESSSSS
503// TODO: Add database fields for all the necessary question parameters. Maybe only one question parameter property that holds all the settings, then read it out in javascript?
504// Why have a question Description? Is this even necessary?
505// Needed properties:
506// Also not exactly sure what "question->category" is for. Is this one of those questionSet things that Julia was talking about?
507//
Note: See TracBrowser for help on using the repository browser.