source: Dev/branches/Demo/js/sequencerScripts.js @ 322

Last change on this file since 322 was 191, checked in by fpvanagthoven, 13 years ago
  • Clean up van bestanden die niet meer gebruikt worden/niet nodig zijn/zijn gemerged met bestaande files.
  • Daarnaast question/survey editor nu grotendeels werkend (min save functie...)
  • Inloggen werkt nu op userUid ipv naam, werkt beter met het aanmaken van creators.
  • Bug in returnObjectDisplay gefixt, er stond nog een var_dump tussen de echoes. JSON houdt niet van HTML tags.
File size: 36.9 KB
Line 
1/*
2 * You can change the type of sequencer by including a hidden input field with id "contentTypeField" in your document.
3 * The initEditor() method will then adjust internal variables in the sequencer object to match that type of content.
4 *
5 * Note: code makes heavy use of shorthand functions:
6 * ce(var) = document.createElement(var);
7 * ge(var) = document.getElementById(var);
8 * Defined in generalscripts.js
9 */
10
11var sequencer = createVarArray();
12
13// Basic functions, intialization, updating
14
15function createVarArray(){
16    // Function that returns creates the global variable object, the sequencer's variable storage.
17   
18    if (sequencer) delete window.sequencer;
19    return {   // GLOBAL VAR TO STORE SEQUENCER SETTINGS!
20        uid: "",                // The unique id of this sequencer (not DB related!). This will help to avoid global var conflicts. Assign this randomly! (STRING)
21        session: {              // Properties of the currently loaded session
22            title: "",              // Title or name (STRING)
23            uid: "",                // Database UID of the current session (STRING)
24            pipeline: {             // Pipeline
25                uids: [],               // Uids of objects in pipeline (STRING)
26                types: [],              // Types of objects in pipeline (STRING)
27                upToDate: []            // Whether or not object displays are up to date (BOOL)
28            }
29        },
30        survey: {               // Properties of the loaded survey, if applicable (either this or the session tree above is filled in!) [THIS IS NOT CURRENTLY USED AND I SHOULD DELIBERATE ON WHAT VARIABLES TO PUT IN THIS TREE TO MAKE A FUNCTIONAL EDITOR THAT ALSO MATCHES THE DB FORMAT!]
31            title: "",              // Title or the name of the survey (STRING)
32            uid: "",                // Uid of the survey (STRING)
33            description: "",        // Description of the survey (STRING)
34            questions: {            // Properties of the questions contained within this survey
35                uids: [],               // An array of uids of the questions, in the order that they appear in the survey (STRING)
36                upToDate: []            // Whether or not a certain question needs an update (BOOL)
37            }
38        },
39        state: {                // Operating state of the sequencer
40            editing: false,         // Whether or not one of the contained child objects is currently being edited or in edit mode. Which one can be determined from the selectedObject property. (BOOL)
41            updating: false,        // Whether or not new steps are currently being queried (to disable any further actions) (BOOL)
42            numSteps: 0,            // Number of objects currently drawn in the editor (not necessarily same as number of objects in pipeline/survey!) (INTEGER)
43            loaded: false,          // Whether or not the sequencer content has been updated for the first time (BOOL)
44            selectedObject: {       // Properties of the currently selected step
45                uid: "",                // UID of this step (STRING)
46                index: null             // The 'index' of this step in the current sequencer view (NOT the pipeline!) (INTEGER)
47            },
48            pages: {                // State regarding page division of content objects
49                total: null,            // The number of pages the content is divided across
50                currentPage: null       // The page that is currently displayed in the sequencer           
51            }
52        },
53        settings: {             // Various settings to determine the workings of the sequencer
54            content: {              // Properties related to the content view of the sequencer
55                contentType: null,      // Type of the loaded parent object (STRING)
56                width: null,            // Width of the viewing area (INTEGER)
57                height: null,           // Height of the viewing area (INTEGER)
58                maxObjects: null,       // The maximum number of content elements to be displayed at once time (INTEGER)
59                orientation: null,      // Whether the editor should be a vertical or horizontal editor (STRING)
60                pages: false            // Whether or not to divide content across pages
61            },
62            efficientUpdating: true // Whether or not to use selective querying of the database for new step objects. True will only refresh out-of-date steps, False will refresh the whole pipeline (BOOL)
63        }
64    };
65
66}
67
68function loadSequencer() {
69    // Reads hidden fields created by PHP, copies the values into the global var then deletes them.
70   
71    // Load hidden fields and set required properties in global object var.
72    try {
73        // settings fields first
74        initSequencer();
75        debugger;
76        switch (sequencer.settings.content.contentType.toLowerCase()) {
77            case "session":
78                // Content-related fields next
79                var fPipelineString = ge("pipelineStringField");
80                var fPipelineTypes = ge("pipelineTypeField");
81                var fSessionUid = ge("sessionField");
82                var fSessionTitle = ge("sessionTitleField");
83                sequencer.session.title = fSessionTitle.value;
84                sequencer.session.uid = fSessionUid.value;
85                sequencer.session.pipeline.uids = stringToArray(fPipelineString.value, ",");
86                sequencer.session.pipeline.types = stringToArray(fPipelineTypes.value, ",");
87                sequencer.session.pipeline.upToDate = new Array();
88       
89                for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
90                    sequencer.session.pipeline.upToDate.push(true);
91                }
92                break;
93            case "survey":
94                var fSurveyUid = ge("surveyUidField");
95                var fQuestionUids = ge("questionUidsField");
96                var fSurveyTitle = ge("surveyTitleField");
97                var fnumQuestions = ge("numQuestionsField");
98                var fSurveyDescription = ge("surveyDescriptionField");
99                sequencer.survey.title = fSurveyTitle.value;
100                sequencer.survey.uid = fSurveyUid.value;
101                sequencer.survey.description = fSurveyDescription.value;
102                sequencer.survey.questions.uids = stringToArray(fQuestionUids.value, ",");
103                break;
104            default:
105                break;
106        }
107       
108       
109        sequencer.state.numSteps = 0;
110        sequencer.state.loaded = false;
111        sequencer.settings.efficientUpdating = true;
112    }
113    catch (e) {
114        // Alert developer of any errors while setting these variables
115        for (error in e) alert(error.message);
116    }
117    // Then remove the hidden fields from the HTML document
118    var hiddenInputs = ge("hiddenInputs");
119    hiddenInputs.parentNode.removeChild(hiddenInputs);
120   
121    // finally, run updateSequencer to refresh the visual display of the pipeline
122    updateSequencer();
123}
124
125function initSequencer() {
126    // Called from loadSequencer(). Sets sequencer.settings properties depending on content type and values passed by PHP in hidden fields (currently only one), then removes these fields.
127    // Example: stuff like editor orientation, width/height, content type, maxObjects contained, etc...
128   
129    // load settings fields first
130    var fContentType = ge("sContentTypeField");
131    var content = ge("seqContent");
132    //sequencer.settings.content.contentType = fContentType.value.toLowerCase();
133    sequencer.settings.content.contentType = fContentType.value;
134    //Then select settings from a few presets
135   
136    switch (sequencer.settings.content.contentType.toLowerCase()) {
137        case "session":
138            sequencer.settings.content.orientation = "horizontal";
139            sequencer.settings.content.width = 800;
140            sequencer.settings.content.height = 125;
141            content.style.width = sequencer.settings.content.width+"px";
142            content.style.height = sequencer.settings.content.height+"px";
143            addClass(content, "horizontal");
144            break;
145        case "survey":
146            sequencer.settings.content.orientation = "vertical";
147            sequencer.settings.content.width = 650;     //guesstimated
148            var roomH = screen.availHeight;
149            sequencer.settings.content.height = roomH - 200;
150            content.style.width = sequencer.settings.content.width+"px";
151            content.style.height = sequencer.settings.content.height+"px";
152            addClass(content, "vertical");
153            break;
154        default:
155            break;
156    }
157    fContentType.parentNode.parentNode.removeChild(fContentType.parentNode);
158}
159
160// Updating, drawing, formatting
161
162function updateSequencer() {
163    // Code that manages drawing and adding of new visual representations of objects in the sequencer.
164
165    /*
166         * Description:
167         * This function updates the visual elements in the sequencer content view to match the current state of the sequencer.session.pipeline property.
168         * It queries the database for object properties via AJAX (returnStep/Display/.php), then inserts divider div's in between where needed.
169         */
170       
171    switch (sequencer.settings.content.contentType.toLowerCase()) {
172        case "session":
173            updateSequencer_Session();
174            break;
175        case "survey":
176            updateSequencer_Survey();
177            break;
178        default:
179            // Why would this even be called?
180            break;
181    }
182}
183
184function updateSequencer_Session() {
185    var content = ge("seqContentWrapper");
186    var requestString, needsUpdating;
187    var args;
188   
189    if (sequencer.state.loaded == false || sequencer.settings.efficientUpdating == false) {  // This is the first update of the sequencer since page load, therefore it contains no previous steps
190        // Stop running this function if the pipeline does not contain any elements to draw
191        if (sequencer.session.pipeline.uids.length == 0 || !sequencer.session.pipeline.uids.length) return;
192        // First clear the entire content wrapper, just for safety and in case efficientUpdating is off
193        while (content.firstChild) {
194            content.removeChild(content.firstChild);
195        }
196        args = [];
197        needsUpdating = [];
198        for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
199            args.push({
200                uid: sequencer.session.pipeline.uids[i],
201                type: sequencer.session.pipeline.types[i]
202            });
203            needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i]));
204        }
205       
206        requestString = "args="+JSON.stringify(args);
207        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
208            content.removeChild(loadingGif);
209            insertNewObjects(result.responseText, needsUpdating);
210        }, true);
211        sequencer.state.loaded = true;
212        var loadingGif = ce("div");
213        loadingGif.innerHTML = "<img src='images/ui/ajax-loader-round.gif' style='float: left; margin:auto auto;' />";
214        content.appendChild(loadingGif);
215    }
216    else {
217        // This means that one or more steps are being added, not an entire pipeline's worth of them
218        needsUpdating = new Array();
219        args = [];
220        // Add steps that need updating to the needsUpdating array (index, uid, type).
221        for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
222            if (sequencer.session.pipeline.upToDate[i] == true) continue;
223            needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i]));
224            args.push({
225                uid: sequencer.session.pipeline.uids[i],
226                type: sequencer.session.pipeline.types[i]
227            });       
228        }
229       
230        requestString = "args="+JSON.stringify(args);
231        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
232            console.log(result.responseText);
233            insertNewObjects(result.responseText, needsUpdating);
234        }, true);
235       
236        // Optional bit with the loading GIF
237        for (var i = 0; i < needsUpdating.length; i++) {
238            var loadingDiv = ce("div");
239            loadingDiv.className = "displayStep loading";
240            loadingDiv.innerHTML = "<img src='images/ui/ajax-loader-round.gif' />";
241            if (needsUpdating[i][0] > sequencer.state.numSteps-1) {
242                content.appendChild(loadingDiv);
243                sequencer.state.numSteps++;
244            }
245            else {
246                content.replaceChild(loadingDiv, content.childNodes[i][0]*2);
247            }
248        }
249        updateDividers();
250    // End optional
251    }
252}
253
254// THIS NEEDS SOME WORK! A LOT OF IT ACTUALLY!
255function updateSequencer_Survey() {
256   
257    var content = ge("seqContentWrapper");
258    var requestString, needsUpdating, args;
259    // Create a reference to the correct field, this might not be needed if we do not generalize the updateSequencer function and instead keep two separate update functions for separate content types.
260    var variables = sequencer.survey;
261   
262    if (sequencer.state.loaded == false || sequencer.settings.efficientUpdating == false) {  // This is the first update of the sequencer since page load, therefore it contains no previous steps
263        // Stop running this function if the questions array does not contain any elements to draw
264        if (sequencer.survey.questions.uids.length == 0 || !sequencer.survey.questions.uids.length) return;
265        // First clear the entire content wrapper, just for safety and in case efficientUpdating is off
266        while (content.firstChild) {
267            content.removeChild(content.firstChild);
268        }
269        args = [];
270        needsUpdating = [];
271        for (var i = 0; i < sequencer.survey.questions.uids.length; i++) {
272            args.push({
273                uid: sequencer.survey.questions.uids[i],
274                type: "Question"
275            });
276            needsUpdating.push(new Array(i, sequencer.survey.questions.uids[i], "Question"));
277        }
278       
279        requestString = "args="+JSON.stringify(args);
280        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
281            content.removeChild(loadingGif);
282            insertNewObjects(result.responseText, needsUpdating);
283            sequencer.state.loaded = true;
284        }, true);
285        sequencer.state.loaded = false;
286        var loadingGif = ce("div");
287        loadingGif.innerHTML = "<img src='images/ui/ajax-loader-round.gif' style='float: left; margin:auto auto;' />";
288        content.appendChild(loadingGif);
289    }
290    else {
291
292        // This means that one or more steps are being added, not an entire pipeline's worth of them
293        needsUpdating = new Array();
294        args = [];
295        // Add steps that need updating to the needsUpdating array (index, uid, type).
296        for (var i = 0; i < sequencer.survey.questions.uids.length; i++) {
297            if (sequencer.survey.questions.upToDate[i] == true) continue;
298            needsUpdating.push(new Array(i, sequencer.survey.questions.uids[i], "Question"));
299            args.push({
300                uid: sequencer.survey.questions.uids[i],
301                type: "Question"
302            });       
303        }
304       
305        requestString = "args="+JSON.stringify(args);
306        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
307            //console.log(result.responseText);
308            insertNewObjects(result.responseText, needsUpdating);
309        }, true);
310       
311        // Optional bit with the loading GIF
312        for (var i = 0; i < needsUpdating.length; i++) {
313            var loadingDiv = ce("div");
314            loadingDiv.className = "displayStep loading";
315            loadingDiv.innerHTML = "<img src='images/ui/ajax-loader-round.gif' />";
316            if (needsUpdating[i][0] > sequencer.state.numSteps-1) {
317                content.appendChild(loadingDiv);
318                sequencer.state.numSteps++;
319            }
320            else {
321                content.replaceChild(loadingDiv, content.childNodes[i][0]*2);
322            }
323        }
324        updateDividers();
325    // End optional
326       
327    }
328       
329}
330
331function updateDividers() {
332    // Function that checks for correct number of dividers in the pipeline, adds or deletes as needed.
333   
334    var content = ge("seqContentWrapper");
335    // Loop through all elements in seqContentWrapper
336    for (var i = 0; i < content.childNodes.length; i++) {
337        var element = content.childNodes[i];
338        // If the element is not a displayStep, continue with the next childNode.
339        if (!hasClass(element, "displayStep")) {
340            continue;
341        }
342        // Get the two elements next to the current element, if they don't exist or are not displaySteps, store false.
343        var lastElement = (element.previousSibling && element.previousSibling.nodeName == "DIV") ? element.previousSibling : false;
344        var nextElement = (element.nextSibling && element.nextSibling.nodeName == "DIV") ? element.nextSibling : false;
345        // If these elements exist, and they are not dividers, add an element in between.
346        if (lastElement != false) {
347            if (!hasClass(lastElement, "divider")){
348                var newDivider = ce("div");
349                addClass(newDivider, "divider");
350                addClass(newDivider, sequencer.settings.content.orientation);
351                content.insertBefore(newDivider, element);
352                delete newDivider;
353            }
354        }
355        if (nextElement != false) {
356            if (!hasClass(nextElement, "divider")){
357                var newDivider = ce("div");
358                addClass(newDivider, "divider");
359                addClass(newDivider, sequencer.settings.content.orientation);
360                content.insertBefore(newDivider, nextElement);
361                delete newDivider;
362            }
363        }
364    }
365   
366    // Because updateDividers is always called last, we set updating to false here, so the user can again issue commands to the pipeline
367    sequencer.state.updating = false;   // Re-enable new actions
368}
369
370// Adding new objects
371
372function submitToolbox(type) {
373    // Handles new object creation code when user clicks on a toolbox button
374   
375    // Do not accept new creation requests if the sequencer is still updating a previous object.
376    if (sequencer.state.updating == true) return;
377    sequencer.state.updating = true;
378    deselectStep();   
379    var c = "objectToCreate="+type;
380    var u = "createObject.php";
381       
382    newAjaxRequest(c, u, function(result) {
383        sequencer.session.pipeline.uids.push(removeNL(result.responseText));
384        sequencer.session.pipeline.types.push(type);
385        sequencer.session.pipeline.upToDate.push(false);
386        updateSequencer();
387    }, true);
388}
389
390function insertNewObjects(responseText, needsUpdating) {
391    //console.log(responseText);
392    // Container function that calls different insertNewX() functions depending on content type. Called from updateSequencer().
393    var response = JSON.parse(responseText);
394    // For now I assume that only one type of element can be displayed in the editor at one time. Therefore, the type of response[0] is the type of all elements of response.
395    switch (sequencer.settings.content.contentType.toLowerCase()) {
396        case "session":
397            insertNewSteps(response, needsUpdating);
398            break;
399        case "survey":
400            insertNewQuestions(response, needsUpdating);
401            break;
402        default:
403            break;
404               
405    }
406}
407
408function addQuestion_Click() {
409    if (sequencer.state.updating == true) return;
410    sequencer.state.updating = true;
411    deselectStep();   
412    var c = "objectToCreate=question";
413    var u = "createObject.php";
414       
415    newAjaxRequest(c, u, function(result) {
416        sequencer.survey.questions.uids.push(removeNL(result.responseText));
417        sequencer.survey.questions.upToDate.push(false);
418        updateSequencer();
419    }, true);
420}
421
422//> Session specific
423
424function insertNewSteps(response, needsUpdating) {
425    /*
426         * This is a function displaying how to handle the visual object representation in solely javascript.
427         * Communication of relevant variables between PHP and JS happens in JSON format.
428         * PHP returns a JSON array of objects to be created by JS
429         * JS then loops through this array and creates DIVS to be inserted into the sequencer.
430         * These are inserted at the position needsUpdating gives us.
431         */
432    var content = ge("seqContentWrapper");
433    // Remove optional loading images
434    for (var  i = 0; i < content.childNodes.length; i++) {
435        if (hasClass(content.childNodes[i], "loading")) {
436            content.removeChild(content.childNodes[i]);
437        }
438    }
439    // End optional
440   
441    for (var i = 0; i < response.length; i++) {
442        var tempDiv = ce("div");
443        tempDiv.id = response[i].uid;
444        tempDiv.className = "displayStep";
445        var divImageContainer = ce("div");
446        divImageContainer.className = "displayStepIcon";
447        divImageContainer.addEventListener("click", function(){
448            clickStep(this.parentNode.id);
449        }, false);
450        var divImage = ce("img");
451        divImage.src = "images/icons/"+response[i].type.toLowerCase()+".png";
452        divImageContainer.appendChild(divImage);
453        tempDiv.appendChild(divImageContainer);
454        var divLabel = ce("p");
455        divLabel.innerHTML = response[i].title;
456        tempDiv.appendChild(divLabel);
457       
458        // This for needs to loop backwards so that the steps at the end of the pipeline are added or changed first. This keeps the childNodes index for all further steps intact.
459        for (var j = needsUpdating.length-1; j >= 0; j--) {
460            if (needsUpdating[j][1] != response[i].uid) continue;
461            if (needsUpdating[j][0] > sequencer.state.numSteps-1) {
462                content.appendChild(tempDiv);
463            }
464            else {
465                content.replaceChild(tempDiv, content.childNodes[j][0]*2);
466            }
467            sequencer.session.pipeline.upToDate[needsUpdating[j][0]] = true;
468        }
469    }
470   
471    updateDividers();
472}
473
474function deleteStep() {
475    // Delete a step from both the pipeline.uids variable and its visual representation from the sequencer. Does not actually delete object from database!
476   
477    // check if a step is selected
478    if (sequencer.state.selectedObject.uid == null) {
479        return;
480    }
481    var uid = sequencer.state.selectedObject.uid;
482    // deselect the step to reset the info panel and selection code
483    deselectStep();
484    // splice the step's data from the pipeline
485    var index = sequencer.session.pipeline.uids.indexOf(uid);
486    if (index >= 0 && index < sequencer.session.pipeline.uids.length) {
487        sequencer.session.pipeline.uids.splice(index, 1);
488        sequencer.session.pipeline.types.splice(index, 1);
489        sequencer.session.pipeline.upToDate.splice(index, 1);
490        // Then delete the step visually
491        var element = ge(uid);
492        var divider;
493        if (!element.nextSibling) {
494            // the element is at the end of the pipeline
495            // therefore we remove the previous divider.
496            // Note: it could also be the only element left in the pipeline!
497            divider = (element.previousSibling) ? element.previousSibling : false;
498            if (divider != false) {
499                divider.parentNode.removeChild(divider);
500            }
501        }
502        else {
503            // the element is at any position except the last, therefore we remove the next divider
504            divider = (element.nextSibling) ? element.nextSibling : false;
505            if (divider != false) {
506                divider.parentNode.removeChild(divider);
507            }
508        }
509       
510        // Finally, remove the element itself.
511        element.parentNode.removeChild(element);
512        sequencer.state.numSteps--;
513       
514    }
515}
516
517//> Survey specific
518
519function insertNewQuestions(response, needsUpdating) {
520    //Code that inserts or replaces new object displays in the sequencer. Question version.
521    var content = ge("seqContentWrapper");
522    // Loop through returned question objects
523    for (var i = 0; i < response.length; i++) {
524       
525        /*
526         * The following block of code defines the layout and composition of the question display
527         */
528       
529        // Define the outer frame
530        var frameDiv = ce("div");
531        frameDiv.className = "smallFrame question";
532        frameDiv.id = response[i].uid;
533        var titleDiv = ce("div");
534        titleDiv.className = "smallTitle";
535        var numberDiv = ce("div");
536        numberDiv.className = "listNumber";
537        numberDiv.innerHTML = i.toString();
538        titleDiv.appendChild(numberDiv);
539        titleDiv.innerHTML += response[i].uid;
540        frameDiv.appendChild(titleDiv);
541       
542        // The frame now has a header bar
543        // On to the content frame
544        // Will use new "ce();" function, shorthand for ce
545       
546        var contentDiv = ce("div");
547        contentDiv.className = "content";
548        var questionBody = ce("p");
549        questionBody.innerHTML = response[i].description;
550        var questionParamsDiv = ce("div");
551        questionParamsDiv.className = "questionParamsView";
552        questionParamsDiv.innerHTML = "Object type: "+response[i].type;
553        contentDiv.appendChild(questionBody);
554        contentDiv.appendChild(questionParamsDiv);
555        frameDiv.appendChild(contentDiv);
556       
557        // And finally the controls div
558        var controlsDiv = ce("div");
559        controlsDiv.className = "controls";
560        var editButton = ce("input");
561        var removeButton = ce("input");
562        editButton.value = "Edit";
563        removeButton.value = "Remove";
564        editButton.className = "smallButton";
565        removeButton.className = "smallButton";
566        editButton.addEventListener("click", function(e){
567            alert('Editing not yet supported!');
568        }, false);
569        removeButton.addEventListener("click", function(e){
570            alert('Removing not yet supported!');
571        }, false);
572        controlsDiv.appendChild(editButton);
573        controlsDiv.appendChild(removeButton);
574        frameDiv.addEventListener("click", function(){
575            clickStep(this.id);
576        }, false);
577        frameDiv.appendChild(controlsDiv);
578       
579        /*
580         * This is where the layout code ends
581         * We proceed to insertion of the created DIV into the document
582         */
583       
584        // We now have a full question display DIV contained in the frameDiv variable. We should now add this to the sequencer content.
585        for (var j = needsUpdating.length - 1; j >= 0; j--) {
586            if (needsUpdating[j][1] != response[i].uid) continue;
587            if (needsUpdating[j][0] > sequencer.state.numSteps-1) {
588                content.appendChild(frameDiv);
589                sequencer.state.numSteps++;
590            }
591            else {
592                content.replaceChild(frameDiv, content.childNodes[needsUpdating[j][0]*2]);
593            }
594            sequencer.survey.questions.upToDate[needsUpdating[j][0]] = true;
595           
596        }
597    }
598   
599    sequencer.state.updating = false;   //re-enable user commands
600}
601
602function saveSurvey(confirmSave) {
603    // Sends an AJAX request to the PHP server that saves the current object.
604    var answer = (confirmSave == true) ? confirm("Save changes?") : true;
605    if (answer == false) return;
606   
607    // Check for object type being edited, adjust requestString and target URL to this information.
608    var request = new Object(), url, requestString;
609    switch (sequencer.settings.content.contentType.toLowerCase()) {
610        case "survey":
611            url = "saveSurvey.php";
612            request.title = sequencer.survey.title;
613            request.uid = sequencer.survey.uid;
614            request.description = sequencer.survey.description;
615            request.questions = new Object();
616            request.questions.uids = sequencer.survey.questions.uids;
617            requestString = "args="+JSON.stringify(request);
618            console.log(request);
619            newAjaxRequest(requestString, url, function(result){
620                console.log(result.responseText);
621            }, true);
622            break;
623        case "blaaaat":
624            url = "savesession.php";
625            request.pipeline.uids = sequencer.session.pipeline.uids;
626            request.pipeline.types = sequencer.session.pipeline.types;
627            break;
628    }
629}
630
631// general functions and user actions
632
633function clickStep(uid) {
634    // Handles selection of steps
635   
636    if (uid == sequencer.state.selectedObject.uid) {  // user deselected a currently selected step.
637        deselectStep();
638    }
639    else {
640        if (sequencer.state.selectedObject.uid != undefined && sequencer.state.selectedObject.uid != "") {
641            // Change selection if something is already selected
642            deselectStep();
643            selectStep(uid);
644        }
645        else {
646            // Make new selection if nothing was selected
647            selectStep(uid);
648        }       
649    }
650}
651
652function selectStep(uid) {
653    // Called from clickStep(), manages CSS class assignment and updating of state variables. Also calls for info panel update.
654   
655    var element = ge(uid);
656    if (element) {
657        addClass(element, "selected");
658        var type;
659        switch (sequencer.settings.content.contentType.toLowerCase()) {
660            case "session":
661                type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(uid)];
662                break;
663            case "survey":
664                type = "Question";
665                break;
666            default:
667                //Dunno
668                break;
669        }
670        (type != "survey") ? ajaxInfoRequest(uid, ge("infoPanelContent"), type) : type=type /*This does nothing*/;
671        sequencer.state.selectedObject.uid = uid;
672        sequencer.state.selectedObject.index = null;  //Don't know how to do this yet.
673    }
674}
675
676function deselectStep() {
677    // Called from clickStep(). Handles unassignment and updating of state variables. Clears info panel.
678   
679    if (!sequencer.state.selectedObject.uid) return;
680    var element = ge(sequencer.state.selectedObject.uid);
681    removeClass(element, "selected");
682    sequencer.state.selectedObject.uid = null;
683    sequencer.state.selectedObject.index = null;
684    var infoPanel = ge("infoPanelContent");
685    if (infoPanel) {
686        while (infoPanel.firstChild) infoPanel.removeChild(infoPanel.firstChild);
687    }
688}
689
690
691
692function savePipeline (confirmSave) {
693    // Sends an AJAX request to the PHP server that saves the current pipeline. Does not yet support surveys.
694   
695    // First check if user should confirm save action or not.
696    var answer;
697    if (confirmSave == true) {
698        answer = confirm("Save changes to pipeline?");
699    }
700    else {
701        answer = true;
702    }
703    if (answer == false) return;
704    // Then compose requestString for savesession.php, containing pipeline uids, types and the session uid.
705    // TODO: should eventually include stuff like session name as well!
706    var requestString = "uids=";
707    requestString += arrayToString(sequencer.session.pipeline.uids, ",");
708    requestString = requestString.slice(0, requestString.length - 1);   // remove trailing commas
709    requestString += "&types=";
710    requestString += arrayToString(sequencer.session.pipeline.types, ",");
711    requestString = requestString.slice(0, requestString.length - 1);   // remove trailing commas
712    requestString += "&sessionUid=";
713    requestString += sequencer.session.uid;
714    console.log(requestString);
715    newAjaxRequest(requestString, "savesession.php", function(result){
716        console.log(result.responseText);
717    }, true);
718}
719
720function editStep() {
721    // Redirects the browser to the appropriate editor for the selected step type.
722   
723    // first save
724    savePipeline(false);
725    // Then post relevant information so the next editor page knows what object it is supposed to be editing.
726    var postForm = ce("form");
727    var type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedObject.uid)];
728    postForm.action = type.toLowerCase()+"Editor.php";
729    postForm.method = "POST";
730    var objectUid = ce("input");
731    objectUid.type = "hidden";
732    objectUid.name = "objectUid";
733    objectUid.value = sequencer.state.selectedObject.uid;
734    postForm.appendChild(objectUid);
735    postForm.submit();
736}
737
738function moveStep (direction) {
739    // Moves the selected step up/down (left/right) in the pipeline.
740   
741    // Check if a step is selected
742    if (sequencer.state.selectedObject.uid == null || direction == null) return;
743    // Check if the step is not at either end of the pipeline
744    var index = sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedObject.uid);
745    if (index == -1) return;
746    if ((index < 0) || (index >= sequencer.session.pipeline.uids.length) || (index == 0 && direction == -1) || (index == sequencer.session.pipeline.uids.length - 1 && direction == 1)) {
747        alert("Cannot move out of bounds!");
748        return;
749    }
750   
751    // Find the two elements in the editor content display
752    var content = ge("seqContentWrapper");
753    var element = ge(sequencer.session.pipeline.uids[index]);
754    var otherElement = ge(sequencer.session.pipeline.uids[index+direction]);
755    // First buffer the two elements
756    var tempElement = element.cloneNode(true);
757    var tempOtherElement = otherElement.cloneNode(true);
758    var placeHolderElement = ce("div");
759    placeHolderElement.id = "placeholder_element";
760    content.replaceChild(placeHolderElement, otherElement);
761    content.replaceChild(tempOtherElement, element);
762    content.replaceChild(tempElement, placeHolderElement);
763    //This should work.
764    // A-B     Start positions, backup to tA and tB
765    // A-X     Replace B with placeholder X
766    // B-X     Replace A with tB
767    // B-A     Replace placeholder X with tA.
768    // The two elements are now swapped.
769     
770    // Now swap the array entries.
771    sequencer.session.pipeline.uids[index] = sequencer.session.pipeline.uids.splice(index+direction, 1, sequencer.session.pipeline.uids[index])[0];
772    sequencer.session.pipeline.types[index] = sequencer.session.pipeline.types.splice(index+direction, 1, sequencer.session.pipeline.types[index])[0];
773   
774    // Finally, rebind the onCLick events to work properly again. I don't know if this 'clones' the event as well (therefore re-adding them casuses memory bloat...) or not.
775    for (var i = 0; i < tempOtherElement.childNodes.length; i++){
776        var childNode = tempOtherElement.childNodes[i];
777        if (hasClass(childNode, "displayStepIcon")) {
778            childNode.addEventListener("click", function() {
779                clickStep(this.parentNode.id);
780            }, false);
781        }
782    }
783    for (var i = 0; i < tempElement.childNodes.length; i++){
784        var childNode = tempElement.childNodes[i];
785        if (hasClass(childNode, "displayStepIcon")) {
786            childNode.addEventListener("click", function() {
787                clickStep(this.parentNode.id);
788            }, false);
789        }
790    }
791   
792   
793   
794     
795// The alternative is to use event bubbling to capture the event on a higher level.
796// Basically, we bind a super-structure onclick event that uses e.target|| event.srcElement to determine which element to move and select.
797// http://stackoverflow.com/questions/29624/how-to-maintain-correct-javascript-event-after-using-clonenodetrue
798// Pro: clean implementation, less events binded.
799// Con: Difficult, this already works fine, probably tougher to use in conjunction with multifunctionality (sessions, surveys, questionsets, etc in one kind of editor)
800     
801}
802
803// WORK IN PROGRESS
804
805
806
807
808
809
810
811
812/******************/
813/* TEMP FUNCTIONS */
814/******************/
815
816// Temp function that creates a dummy question to test the insertNewQuestions function.
817// NOTE: CAN BE REMOVED!
818function debug_addQuestion() {
819    // Derp, natuurlijk werkt de addQuestion call niet twee keer. Hij creeert twee keer exact hetzelfde object. Bottom line: het werkt. Nu de editing code voor deze questions gaan schrijven!
820    var response = [{
821        uid: "1234567890testuid",
822        title: "dummyQuestion",
823        description: "This is a dummy question, not a real one!",
824        type: "question"
825    }];
826   
827    var needsUpdating = [[
828    0,
829    "1234567890testuid",
830    "question"
831    ]];
832   
833    insertNewQuestions(response, needsUpdating);
834}
835
836// Temp function that articially switches content type when the page is already loaded, to easily test layout changes and content flow.
837// NOTE: CAN BE REMOVED!
838function debug_switchType() {
839    var content = ge("seqContent");
840    if (sequencer.settings.content.contentType == "session") {
841        // Set to survey
842        sequencer.settings.content.contentType = "survey";
843        sequencer.settings.content.orientation = "vertical";
844        removeClass(content, "horizontal");
845        addClass(content, "vertical");
846    }
847    else {
848        // Set to session
849        sequencer.settings.content.contentType = "session";
850        sequencer.settings.content.orientation = "horizontal";
851        removeClass(content, "vertical");
852        addClass(content, "horizontal");
853    }
854}
Note: See TracBrowser for help on using the repository browser.