source: Dev/trunk/js/sequencerScripts.js @ 185

Last change on this file since 185 was 185, checked in by fpvanagthoven, 13 years ago
File size: 34.7 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       
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 = 800;
148            sequencer.settings.content.height = "auto";
149            content.style.width = sequencer.settings.content.width+"px";
150            content.style.height = sequencer.settings.content.height+"px";
151            addClass(content, "vertical");
152            break;
153        default:
154            break;
155    }
156    fContentType.parentNode.parentNode.removeChild(fContentType.parentNode);
157}
158
159// Updating, drawing, formatting
160
161function updateSequencer() {
162    // Code that manages drawing and adding of new visual representations of objects in the sequencer.
163
164    /*
165         * Description:
166         * This function updates the visual elements in the sequencer content view to match the current state of the sequencer.session.pipeline property.
167         * It queries the database for object properties via AJAX (returnStep/Display/.php), then inserts divider div's in between where needed.
168         */
169       
170    switch (sequencer.settings.content.contentType.toLowerCase()) {
171        case "session":
172            updateSequencer_Session();
173            break;
174        case "survey":
175            updateSequencer_Survey();
176            break;
177        default:
178            // Why would this even be called?
179            break;
180    }
181   
182   
183   
184   
185    //console.log(sequencer);
186    sequencer.state.updating = false;   // Re-enable new actions
187}
188
189function updateSequencer_Session() {
190    var content = ge("seqContentWrapper");
191    var requestString, needsUpdating;
192    var args;
193   
194    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
195        // Stop running this function if the pipeline does not contain any elements to draw
196        if (sequencer.session.pipeline.uids.length == 0 || !sequencer.session.pipeline.uids.length) return;
197        // First clear the entire content wrapper, just for safety and in case efficientUpdating is off
198        while (content.firstChild) {
199            content.removeChild(content.firstChild);
200        }
201        args = [];
202        needsUpdating = [];
203        for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
204            args.push({
205                uid: sequencer.session.pipeline.uids[i],
206                type: sequencer.session.pipeline.types[i]
207            });
208            needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i]));
209        }
210       
211        requestString = "args="+JSON.stringify(args);
212        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
213            content.removeChild(loadingGif);
214            insertNewObjects(result.responseText, needsUpdating);
215        }, true);
216        sequencer.state.loaded = true;
217        var loadingGif = ce("div");
218        loadingGif.innerHTML = "<img src='images/ui/ajax-loader-round.gif' style='float: left; margin:auto auto;' />";
219        content.appendChild(loadingGif);
220    }
221    else {
222        // This means that one or more steps are being added, not an entire pipeline's worth of them
223        needsUpdating = new Array();
224        args = [];
225        // Add steps that need updating to the needsUpdating array (index, uid, type).
226        for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
227            if (sequencer.session.pipeline.upToDate[i] == true) continue;
228            needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i]));
229            args.push({
230                uid: sequencer.session.pipeline.uids[i],
231                type: sequencer.session.pipeline.types[i]
232            });       
233        }
234       
235        requestString = "args="+JSON.stringify(args);
236        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
237            insertNewObjects(result.responseText, needsUpdating);
238        }, true);
239       
240        // Optional bit with the loading GIF
241        for (var i = 0; i < needsUpdating.length; i++) {
242            var loadingDiv = ce("div");
243            loadingDiv.className = "displayStep loading";
244            loadingDiv.innerHTML = "<img src='images/ui/ajax-loader-round.gif' />";
245            if (needsUpdating[i][0] > sequencer.state.numSteps-1) {
246                content.appendChild(loadingDiv);
247                sequencer.state.numSteps++;
248            }
249            else {
250                content.replaceChild(loadingDiv, content.childNodes[i][0]*2);
251            }
252        }
253        updateDividers();
254    // End optional
255    }
256}
257
258// THIS NEEDS SOME WORK! A LOT OF IT ACTUALLY!
259function updateSequencer_Survey() {
260    //debugger;
261    var content = ge("seqContentWrapper");
262    var requestString, needsUpdating, args;
263    // 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.
264    var variables = sequencer.survey;
265   
266    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
267        // Stop running this function if the questions array does not contain any elements to draw
268        if (sequencer.survey.questions.uids.length == 0 || !sequencer.survey.questions.uids.length) return;
269        // First clear the entire content wrapper, just for safety and in case efficientUpdating is off
270        while (content.firstChild) {
271            content.removeChild(content.firstChild);
272        }
273        args = [];
274        needsUpdating = [];
275        for (var i = 0; i < sequencer.survey.questions.uids.length; i++) {
276            args.push({
277                uid: sequencer.survey.questions.uids[i],
278                type: "Question"
279            });
280            needsUpdating.push(new Array(i, sequencer.survey.questions.uids[i], "Question"));
281        }
282       
283        requestString = "args="+JSON.stringify(args);
284        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
285            //debugger;
286            content.removeChild(loadingGif);
287            insertNewObjects(result.responseText, needsUpdating);
288        }, true);
289        sequencer.state.loaded = false;
290        var loadingGif = ce("div");
291        loadingGif.innerHTML = "<img src='images/ui/ajax-loader-round.gif' style='float: left; margin:auto auto;' />";
292        content.appendChild(loadingGif);
293    }
294    else {
295        // This means that one or more steps are being added, not an entire pipeline's worth of them
296        needsUpdating = new Array();
297        args = [];
298        // Add steps that need updating to the needsUpdating array (index, uid, type).
299        for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) {
300            if (sequencer.session.pipeline.upToDate[i] == true) continue;
301            needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i]));
302            args.push({
303                uid: sequencer.session.pipeline.uids[i],
304                type: sequencer.session.pipeline.types[i]
305            });       
306        }
307       
308        requestString = "args="+JSON.stringify(args);
309        newAjaxRequest(requestString, "returnObjectDisplay.php", function(result){
310            insertNewObjects(result.responseText, needsUpdating);
311        }, true);
312       
313        // Optional bit with the loading GIF
314        for (var i = 0; i < needsUpdating.length; i++) {
315            var loadingDiv = ce("div");
316            loadingDiv.className = "displayStep loading";
317            loadingDiv.innerHTML = "<img src='images/ui/ajax-loader-round.gif' />";
318            if (needsUpdating[i][0] > sequencer.state.numSteps-1) {
319                content.appendChild(loadingDiv);
320                sequencer.state.numSteps++;
321            }
322            else {
323                content.replaceChild(loadingDiv, content.childNodes[i][0]*2);
324            }
325        }
326        updateDividers();
327    // End optional
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
367// Adding new objects
368
369function submitToolbox(type) {
370    // Handles new object creation code when user clicks on a toolbox button
371   
372    // Do not accept new creation requests if the sequencer is still updating a previous object.
373    if (sequencer.state.updating == true) return;
374    sequencer.state.updating = true;
375    deselectStep();   
376    var c = "objectToCreate="+type;
377    var u = "createObject.php";
378       
379    newAjaxRequest(c, u, function(result) {
380        sequencer.session.pipeline.uids.push(removeNL(result.responseText));
381        sequencer.session.pipeline.types.push(type);
382        sequencer.session.pipeline.upToDate.push(false);
383        updateSequencer();
384    }, true);
385}
386
387function insertNewObjects(responseText, needsUpdating) {
388    // Container function that calls different insertNewX() functions depending on content type. Called from updateSequencer().
389    debugger;
390    var response = JSON.parse(responseText);
391    // 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.
392    switch (sequencer.settings.content.contentType.toLowerCase()) {
393        case "session":
394            insertNewSteps(response, needsUpdating);
395            break;
396        case "question":
397            insertNewQuestions(response, needsUpdating);
398            break;
399        default:
400            break;
401               
402    }
403}
404
405function addQuestion_Click() {
406    if (sequencer.state.updating == true) return;
407    sequencer.state.updating = true;
408    deselectStep();   
409    var c = "objectToCreate=question";
410    var u = "createObject.php";
411       
412    newAjaxRequest(c, u, function(result) {
413        debugger;
414        sequencer.survey.questions.uids.push(removeNL(result.responseText));
415        sequencer.survey.questions.upToDate.push(false);
416        updateSequencer();
417    }, true);
418}
419
420//> Session specific
421
422function insertNewSteps(response, needsUpdating) {
423    /*
424         * This is a function displaying how to handle the visual object representation in solely javascript.
425         * Communication of relevant variables between PHP and JS happens in JSON format.
426         * PHP returns a JSON array of objects to be created by JS
427         * JS then loops through this array and creates DIVS to be inserted into the sequencer.
428         * These are inserted at the position needsUpdating gives us.
429         */
430    var content = ge("seqContentWrapper");
431    // Remove optional loading images
432    for (var  i = 0; i < content.childNodes.length; i++) {
433        if (hasClass(content.childNodes[i], "loading")) {
434            content.removeChild(content.childNodes[i]);
435        }
436    }
437    // End optional
438   
439    for (var i = 0; i < response.length; i++) {
440        var tempDiv = ce("div");
441        tempDiv.id = response[i].uid;
442        tempDiv.className = "displayStep";
443        var divImageContainer = ce("div");
444        divImageContainer.className = "displayStepIcon";
445        divImageContainer.addEventListener("click", function(){
446            clickStep(this.parentNode.id);
447        }, false);
448        var divImage = ce("img");
449        divImage.src = "images/icons/"+response[i].type.toLowerCase()+".png";
450        divImageContainer.appendChild(divImage);
451        tempDiv.appendChild(divImageContainer);
452        var divLabel = ce("p");
453        divLabel.innerHTML = response[i].title;
454        tempDiv.appendChild(divLabel);
455       
456        // 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.
457        for (var j = needsUpdating.length-1; j >= 0; j--) {
458            if (needsUpdating[j][1] != response[i].uid) continue;
459            if (needsUpdating[j][0] > sequencer.state.numSteps-1) {
460                content.appendChild(tempDiv);
461            }
462            else {
463                content.replaceChild(tempDiv, content.childNodes[j][0]*2);
464            }
465            sequencer.session.pipeline.upToDate[needsUpdating[j][0]] = true;
466        }
467    }
468   
469    updateDividers();
470}
471
472function deleteStep() {
473    // Delete a step from both the pipeline.uids variable and its visual representation from the sequencer. Does not actually delete object from database!
474   
475    // check if a step is selected
476    if (sequencer.state.selectedObject.uid == null) {
477        return;
478    }
479    var uid = sequencer.state.selectedObject.uid;
480    // deselect the step to reset the info panel and selection code
481    deselectStep();
482    // splice the step's data from the pipeline
483    var index = sequencer.session.pipeline.uids.indexOf(uid);
484    if (index >= 0 && index < sequencer.session.pipeline.uids.length) {
485        sequencer.session.pipeline.uids.splice(index, 1);
486        sequencer.session.pipeline.types.splice(index, 1);
487        sequencer.session.pipeline.upToDate.splice(index, 1);
488        // Then delete the step visually
489        var element = ge(uid);
490        var divider;
491        if (!element.nextSibling) {
492            // the element is at the end of the pipeline
493            // therefore we remove the previous divider.
494            // Note: it could also be the only element left in the pipeline!
495            divider = (element.previousSibling) ? element.previousSibling : false;
496            if (divider != false) {
497                divider.parentNode.removeChild(divider);
498            }
499        }
500        else {
501            // the element is at any position except the last, therefore we remove the next divider
502            divider = (element.nextSibling) ? element.nextSibling : false;
503            if (divider != false) {
504                divider.parentNode.removeChild(divider);
505            }
506        }
507       
508        // Finally, remove the element itself.
509        element.parentNode.removeChild(element);
510        sequencer.state.numSteps--;
511       
512    }
513}
514
515//> Survey specific
516
517function insertNewQuestions(response, needsUpdating) {
518    //Code that inserts or replaces new object displays in the sequencer. Question version.
519   
520    var content = ge("seqContentWrapper");
521    // Loop through returned question objects
522    for (var i = 0; i < response.length; i++) {
523        // Define the outer frame
524        var frameDiv = ce("div");
525        frameDiv.className = "smallFrame question";
526        frameDiv.id = response[i].uid;
527        var titleDiv = ce("div");
528        titleDiv.className = "smallTitle";
529        var numberDiv = ce("div");
530        numberDiv.className = "listNumber";
531        numberDiv.innerHTML = i.toString();
532        titleDiv.appendChild(numberDiv);
533        titleDiv.innerHTML += response[i].uid;
534        frameDiv.appendChild(titleDiv);
535       
536        // The frame now has a header bar
537        // On to the content frame
538        // Will use new "ce();" function, shorthand for ce
539       
540        var contentDiv = ce("div");
541        contentDiv.className = "content";
542        var questionBody = ce("p");
543        questionBody.innerHTML = response[i].description;
544        var questionParamsDiv = ce("div");
545        questionParamsDiv.className = "questionParamsView";
546        questionParamsDiv.innerHTML = "Object type: "+response[i].type;
547        contentDiv.appendChild(questionBody);
548        contentDiv.appendChild(questionParamsDiv);
549        frameDiv.appendChild(contentDiv);
550       
551        // And finally the controls div
552        var controlsDiv = ce("div");
553        controlsDiv.className = "controls";
554        var editButton = ce("input");
555        var removeButton = ce("input");
556        editButton.value = "Edit";
557        removeButton.value = "Remove";
558        editButton.className = "smallButton";
559        removeButton.className = "smallButton";
560        editButton.addEventListener("click", function(e){
561            alert('Editing not yet supported!');
562        }, false);
563        removeButton.addEventListener("click", function(e){
564            alert('Removing not yet supported!');
565        }, false);
566        controlsDiv.appendChild(editButton);
567        controlsDiv.appendChild(removeButton);
568        frameDiv.appendChild(controlsDiv);
569       
570        // We now have a full question display DIV contained in the frameDiv variable. We should now add this to the sequencer content.
571        for (var j = needsUpdating.length - 1; j >= 0; j--) {
572            if (needsUpdating[j][1] != response[i].uid) continue;
573            if (needsUpdating[j][0] > sequencer.state.numSteps-1) {
574                content.appendChild(frameDiv);
575            }
576            else {
577                content.replaceChild(frameDiv, content.childNodes[needsUpdating[j][0]*2]);
578            }
579            sequencer.state.numSteps++;
580        }
581    }
582}
583
584// general functions and user actions
585
586function clickStep(uid) {
587    // Handles selection of steps
588   
589    if (uid == sequencer.state.selectedObject.uid) {  // user deselected a currently selected step.
590        deselectStep();
591    }
592    else {
593        if (sequencer.state.selectedObject.uid != undefined && sequencer.state.selectedObject.uid != "") {
594            // Change selection if something is already selected
595            deselectStep();
596            selectStep(uid);
597        }
598        else {
599            // Make new selection if nothing was selected
600            selectStep(uid);
601        }       
602    }
603}
604
605function selectStep(uid) {
606    // Called from clickStep(), manages CSS class assignment and updating of state variables. Also calls for info panel update.
607   
608    var element = ge(uid);
609    if (element) {
610        addClass(element, "selected");
611        var type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(uid)];
612        ajaxInfoRequest(uid, ge("infoPanelContent"), type);
613        sequencer.state.selectedObject.uid = uid;
614        sequencer.state.selectedObject.index = null;  //Don't know how to do this yet.
615    }
616}
617
618function deselectStep() {
619    // Called from clickStep(). Handles unassignment and updating of state variables. Clears info panel.
620   
621    if (!sequencer.state.selectedObject.uid) return;
622    var element = ge(sequencer.state.selectedObject.uid);
623    removeClass(element, "selected");
624    sequencer.state.selectedObject.uid = null;
625    sequencer.state.selectedObject.index = null;
626    var infoPanel = ge("infoPanelContent");
627    if (infoPanel) {
628        while (infoPanel.firstChild) infoPanel.removeChild(infoPanel.firstChild);
629    }
630}
631
632function ajaxInfoRequest(uid, el, type) {
633    // Info panel update.
634   
635    var c = "uid="+uid;
636    c += "&type="+type;
637    var u = "getInfo.php";
638    newAjaxRequest(c, u, function(result) {
639        el.innerHTML = result.responseText;
640    }, true);
641}
642
643function savePipeline (confirmSave) {
644    // Sends an AJAX request to the PHP server that saves the current pipeline. Does not yet support surveys.
645   
646    // First check if user should confirm save action or not.
647    var answer;
648    if (confirmSave == true) {
649        answer = confirm("Save changes to pipeline?");
650    }
651    else {
652        answer = true;
653    }
654    if (answer == false) return;
655    // Then compose requestString for savesession.php, containing pipeline uids, types and the session uid.
656    // TODO: should eventually include stuff like session name as well!
657    var requestString = "uids=";
658    requestString += arrayToString(sequencer.session.pipeline.uids, ",");
659    requestString = requestString.slice(0, requestString.length - 1);   // remove trailing commas
660    requestString += "&types=";
661    requestString += arrayToString(sequencer.session.pipeline.types, ",");
662    requestString = requestString.slice(0, requestString.length - 1);   // remove trailing commas
663    requestString += "&sessionUid=";
664    requestString += sequencer.session.uid;
665    console.log(requestString);
666    newAjaxRequest(requestString, "savesession.php", function(result){
667        console.log(result.responseText);
668    }, true);
669}
670
671function editStep() {
672    // Redirects the browser to the appropriate editor for the selected step type.
673   
674    // first save
675    savePipeline(false);
676    // Then post relevant information so the next editor page knows what object it is supposed to be editing.
677    var postForm = ce("form");
678    var type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedObject.uid)];
679    postForm.action = type.toLowerCase()+"Editor.php";
680    postForm.method = "POST";
681    var objectUid = ce("input");
682    objectUid.type = "hidden";
683    objectUid.name = "objectUid";
684    objectUid.value = sequencer.state.selectedObject.uid;
685    postForm.appendChild(objectUid);
686    postForm.submit();
687}
688
689function moveStep (direction) {
690    // Moves the selected step up/down (left/right) in the pipeline.
691   
692    // Check if a step is selected
693    if (sequencer.state.selectedObject.uid == null || direction == null) return;
694    // Check if the step is not at either end of the pipeline
695    var index = sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedObject.uid);
696    if (index == -1) return;
697    if ((index < 0) || (index >= sequencer.session.pipeline.uids.length) || (index == 0 && direction == -1) || (index == sequencer.session.pipeline.uids.length - 1 && direction == 1)) {
698        alert("Cannot move out of bounds!");
699        return;
700    }
701   
702    // Find the two elements in the editor content display
703    var content = ge("seqContentWrapper");
704    var element = ge(sequencer.session.pipeline.uids[index]);
705    var otherElement = ge(sequencer.session.pipeline.uids[index+direction]);
706    // First buffer the two elements
707    var tempElement = element.cloneNode(true);
708    var tempOtherElement = otherElement.cloneNode(true);
709    var placeHolderElement = ce("div");
710    placeHolderElement.id = "placeholder_element";
711    content.replaceChild(placeHolderElement, otherElement);
712    content.replaceChild(tempOtherElement, element);
713    content.replaceChild(tempElement, placeHolderElement);
714    //This should work.
715    // A-B     Start positions, backup to tA and tB
716    // A-X     Replace B with placeholder X
717    // B-X     Replace A with tB
718    // B-A     Replace placeholder X with tA.
719    // The two elements are now swapped.
720     
721    // Now swap the array entries.
722    sequencer.session.pipeline.uids[index] = sequencer.session.pipeline.uids.splice(index+direction, 1, sequencer.session.pipeline.uids[index])[0];
723    sequencer.session.pipeline.types[index] = sequencer.session.pipeline.types.splice(index+direction, 1, sequencer.session.pipeline.types[index])[0];
724   
725    // 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.
726    for (var i = 0; i < tempOtherElement.childNodes.length; i++){
727        var childNode = tempOtherElement.childNodes[i];
728        if (hasClass(childNode, "displayStepIcon")) {
729            childNode.addEventListener("click", function() {
730                clickStep(this.parentNode.id);
731            }, false);
732        }
733    }
734    for (var i = 0; i < tempElement.childNodes.length; i++){
735        var childNode = tempElement.childNodes[i];
736        if (hasClass(childNode, "displayStepIcon")) {
737            childNode.addEventListener("click", function() {
738                clickStep(this.parentNode.id);
739            }, false);
740        }
741    }
742   
743   
744   
745     
746// The alternative is to use event bubbling to capture the event on a higher level.
747// Basically, we bind a super-structure onclick event that uses e.target|| event.srcElement to determine which element to move and select.
748// http://stackoverflow.com/questions/29624/how-to-maintain-correct-javascript-event-after-using-clonenodetrue
749// Pro: clean implementation, less events binded.
750// Con: Difficult, this already works fine, probably tougher to use in conjunction with multifunctionality (sessions, surveys, questionsets, etc in one kind of editor)
751     
752}
753
754// WORK IN PROGRESS
755
756
757
758
759
760
761
762
763/******************/
764/* TEMP FUNCTIONS */
765/******************/
766
767// Temp function that creates a dummy question to test the insertNewQuestions function.
768function debug_addQuestion() {
769    // 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!
770    var response = [{
771        uid: "1234567890testuid",
772        title: "dummyQuestion",
773        description: "This is a dummy question, not a real one!",
774        type: "question"
775    }];
776   
777    var needsUpdating = [[
778    0,
779    "1234567890testuid",
780    "question"
781    ]];
782   
783    insertNewQuestions(response, needsUpdating);
784}
785
786// Temp function that articially switches content type when the page is already loaded, to easily test layout changes and content flow.
787function debug_switchType() {
788    var content = ge("seqContent");
789    if (sequencer.settings.content.contentType == "session") {
790        // Set to survey
791        sequencer.settings.content.contentType = "survey";
792        sequencer.settings.content.orientation = "vertical";
793        removeClass(content, "horizontal");
794        addClass(content, "vertical");
795    }
796    else {
797        // Set to session
798        sequencer.settings.content.contentType = "session";
799        sequencer.settings.content.orientation = "horizontal";
800        removeClass(content, "vertical");
801        addClass(content, "horizontal");
802    }
803}
Note: See TracBrowser for help on using the repository browser.