1 | /* |
---|
2 | * To change this template, choose Tools | Templates |
---|
3 | * and open the template in the editor. |
---|
4 | */ |
---|
5 | |
---|
6 | var sequencer = { // GLOBAL VAR TO STORE SEQUENCER SETTINGS! |
---|
7 | uid: "", // The unique id of this sequencer (not DB related!). This will help to avoid global var conflicts. Assign this randomly! (STRING) |
---|
8 | session: { // Properties of the currently loaded session |
---|
9 | title: "", // Title or name (STRING) |
---|
10 | uid: "", // Database UID of the current session (STRING) |
---|
11 | pipeline: { // Pipeline |
---|
12 | uids: [], // Uids of objects in pipeline (STRING) |
---|
13 | types: [], // Types of objects in pipeline (STRING) |
---|
14 | upToDate: [] // Whether or not object displays are up to date (BOOL) |
---|
15 | } |
---|
16 | }, |
---|
17 | state: { // Operating state of the sequencer |
---|
18 | 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 selectedStep property. |
---|
19 | numSteps: 0, // Number of steps currently drawn in the editor (not necessarily same as number of steps in pipeline!) (INTEGER) |
---|
20 | loaded: false, // Whether or not the sequencer content has been updated for the first time (BOOL) |
---|
21 | selectedStep: { // Properties of the currently selected step |
---|
22 | uid: "", // UID of this step (STRING) |
---|
23 | index: null // The 'index' of this step in the current sequencer view (NOT the pipeline!) (INTEGER) |
---|
24 | } |
---|
25 | }, |
---|
26 | settings: { // Various settings to determine the workings of the sequencer |
---|
27 | content: { // Properties related to the content view of the sequencer |
---|
28 | contentType: "session", // Type of the loaded parent object |
---|
29 | width: null, // Width of the viewing area (INTEGER) |
---|
30 | height: null, // Height of the viewing area (INTEGER) |
---|
31 | maxObjects: null, // The maximum number of content elements to be displayed at once time (INTEGER) |
---|
32 | orientation: "horizontal" // Whether the editor should be a vertical or horizontal editor (STRING) |
---|
33 | }, |
---|
34 | 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 |
---|
35 | } |
---|
36 | } |
---|
37 | |
---|
38 | |
---|
39 | function SubmitToolbox(type) { |
---|
40 | deselectStep(); |
---|
41 | var c = "objectToCreate="+type; |
---|
42 | var u = "createObject.php"; |
---|
43 | |
---|
44 | newAjaxRequest(c, u, function(result) { |
---|
45 | sequencer.session.pipeline.uids.push(removeNL(result.responseText)); |
---|
46 | sequencer.session.pipeline.types.push(type); |
---|
47 | sequencer.session.pipeline.upToDate.push(false); |
---|
48 | updateSequencer(); |
---|
49 | }, true); |
---|
50 | } |
---|
51 | |
---|
52 | function clickStep(uid) { |
---|
53 | if (uid == sequencer.state.selectedStep.uid) { // user deselected a currently selected step. |
---|
54 | deselectStep(); |
---|
55 | } |
---|
56 | else { |
---|
57 | if (sequencer.state.selectedStep.uid != undefined && sequencer.state.selectedStep.uid != "") { |
---|
58 | deselectStep(); |
---|
59 | selectStep(uid); |
---|
60 | } |
---|
61 | else { |
---|
62 | selectStep(uid); |
---|
63 | } |
---|
64 | } |
---|
65 | } |
---|
66 | |
---|
67 | function selectStep(uid) { |
---|
68 | var element = document.getElementById(uid); |
---|
69 | if (element) { |
---|
70 | addClass(element, "selected"); |
---|
71 | var type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(uid)]; |
---|
72 | ajaxInfoRequest(uid, document.getElementById("infoPanelContent"), type); |
---|
73 | sequencer.state.selectedStep.uid = uid; |
---|
74 | sequencer.state.selectedStep.index = null; //Don't know how to do this yet. |
---|
75 | } |
---|
76 | } |
---|
77 | |
---|
78 | function deleteStep() { |
---|
79 | // check if a step is selected |
---|
80 | if (sequencer.state.selectedStep.uid == null) { |
---|
81 | return; |
---|
82 | } |
---|
83 | var uid = sequencer.state.selectedStep.uid; |
---|
84 | // deselect the step to reset the info panel and selection code |
---|
85 | deselectStep(); |
---|
86 | // splice the step's data from the pipeline |
---|
87 | var index = sequencer.session.pipeline.uids.indexOf(uid); |
---|
88 | if (index >= 0 && index < sequencer.session.pipeline.uids.length) { |
---|
89 | sequencer.session.pipeline.uids.splice(index, 1); |
---|
90 | sequencer.session.pipeline.types.splice(index, 1); |
---|
91 | sequencer.session.pipeline.upToDate.splice(index, 1); |
---|
92 | // Then delete the step visually |
---|
93 | var element = document.getElementById(uid); |
---|
94 | var divider; |
---|
95 | if (!element.nextSibling) { |
---|
96 | // the element is at the end of the pipeline |
---|
97 | // therefore we remove the previous divider. |
---|
98 | // Note: it could also be the only element left in the pipeline! |
---|
99 | divider = (element.previousSibling) ? element.previousSibling : false; |
---|
100 | if (divider != false) { |
---|
101 | divider.parentNode.removeChild(divider); |
---|
102 | } |
---|
103 | } |
---|
104 | else { |
---|
105 | // the element is at any position except the last, therefore we remove the next divider |
---|
106 | divider = (element.nextSibling) ? element.nextSibling : false; |
---|
107 | if (divider != false) { |
---|
108 | divider.parentNode.removeChild(divider); |
---|
109 | } |
---|
110 | } |
---|
111 | |
---|
112 | // Finally, remove the element itself. |
---|
113 | element.parentNode.removeChild(element); |
---|
114 | sequencer.state.numSteps--; |
---|
115 | |
---|
116 | } |
---|
117 | } |
---|
118 | |
---|
119 | function deselectStep() { |
---|
120 | if (!sequencer.state.selectedStep.uid) return; |
---|
121 | var element = document.getElementById(sequencer.state.selectedStep.uid); |
---|
122 | removeClass(element, "selected"); |
---|
123 | sequencer.state.selectedStep.uid = null; |
---|
124 | sequencer.state.selectedStep.index = null; |
---|
125 | var infoPanel = document.getElementById("infoPanelContent"); |
---|
126 | while (infoPanel.firstChild) infoPanel.removeChild(infoPanel.firstChild); |
---|
127 | } |
---|
128 | |
---|
129 | function ajaxInfoRequest(uid, el, type) { |
---|
130 | var c = "uid="+uid; |
---|
131 | c += "&type="+type; |
---|
132 | var u = "getInfo.php"; |
---|
133 | newAjaxRequest(c, u, function(result) { |
---|
134 | el.innerHTML = result.responseText; |
---|
135 | }, true); |
---|
136 | } |
---|
137 | |
---|
138 | function updateSequencer() { |
---|
139 | |
---|
140 | /* |
---|
141 | * Description: |
---|
142 | * This function updates the visual elements in the sequencer content view to match the current state of the sequencer.session.pipeline property. |
---|
143 | * It queries the database for object properties via AJAX (returnStep/Display/.php), then inserts divider div's in between where needed. |
---|
144 | */ |
---|
145 | var content = document.getElementById("seqContentWrapper"); |
---|
146 | 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 |
---|
147 | // First clear the entire content wrapper, just for safety and in case efficientUpdating is off |
---|
148 | while (content.firstChild) { |
---|
149 | content.removeChild(content.firstChild); |
---|
150 | } |
---|
151 | // Compose a request string - "uids=1,2,3&types=a,b,c" |
---|
152 | var requestString = "uids="; |
---|
153 | for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) { |
---|
154 | requestString += sequencer.session.pipeline.uids[i]+","; |
---|
155 | sequencer.state.numSteps++; |
---|
156 | } |
---|
157 | |
---|
158 | requestString = requestString.slice(0, requestString.length - 1); // remove trailing commas |
---|
159 | requestString += "&types="; |
---|
160 | for (var j = 0; j < sequencer.session.pipeline.types.length; j++) { |
---|
161 | requestString += sequencer.session.pipeline.types[j]+","; |
---|
162 | } |
---|
163 | requestString = requestString.slice(0, requestString.length - 1); // remove trailing commas |
---|
164 | newAjaxRequest(requestString, "returnStep.php", function(result) { |
---|
165 | content.innerHTML = result.responseText; |
---|
166 | updateDividers(); |
---|
167 | sequencer.state.loaded = true; |
---|
168 | for (var i = 0; i < sequencer.session.pipeline.upToDate.length; i++) { |
---|
169 | sequencer.session.pipeline.upToDate.length[i] = true; |
---|
170 | } |
---|
171 | if (content.childNodes[0].nodeType == 3) content.removeChild(content.childNodes[0]); |
---|
172 | }, true); |
---|
173 | } |
---|
174 | else { |
---|
175 | // Pack together all needed updates in one request string, insert responseText.firstChild whenever sequencer.session.pipeline.upToDate[i] == false |
---|
176 | debugger; |
---|
177 | var needsUpdating = new Array(); |
---|
178 | // Add steps that need updating to the needsUpdating array (index, uid, type). |
---|
179 | for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) { |
---|
180 | if (sequencer.session.pipeline.upToDate[i] == true) continue; |
---|
181 | needsUpdating.push(new Array(i, sequencer.session.pipeline.uids[i], sequencer.session.pipeline.types[i])); |
---|
182 | } |
---|
183 | // Now that we have an array of the steps that need to be refreshed, we compose a requestString. |
---|
184 | var requestString = "uids="; |
---|
185 | for (var i = 0; i < needsUpdating.length; i++) { |
---|
186 | requestString += needsUpdating[i][1]+","; |
---|
187 | } |
---|
188 | if (requestString.slice(-1) == ",") requestString = requestString.slice(0, requestString.length - 1); // remove trailing commas |
---|
189 | requestString += "&types="; |
---|
190 | for (var i = 0; i < needsUpdating.length; i++) { |
---|
191 | requestString += needsUpdating[i][2]; |
---|
192 | } |
---|
193 | if (requestString.slice(-1) == ",") requestString = requestString.slice(0, requestString.length - 1); // remove trailing commas |
---|
194 | |
---|
195 | // perform the AJAX request |
---|
196 | newAjaxRequest(requestString, "returnStep.php", function(result) { |
---|
197 | insertNewSteps(result.responseText, needsUpdating); |
---|
198 | }, true); |
---|
199 | } |
---|
200 | |
---|
201 | } |
---|
202 | |
---|
203 | function loadSequencer() { |
---|
204 | /* |
---|
205 | * Description: |
---|
206 | * Load data from hidden fields (put there by PHP), store them in the global var "sequencer" (as well as several initialization properties), |
---|
207 | * then remove the hidden fields from the HTML document tree. |
---|
208 | */ |
---|
209 | |
---|
210 | // Load hidden fields and set required properties in global object var. |
---|
211 | try { |
---|
212 | |
---|
213 | var fPipelineString = document.getElementById("pipelineStringField"); |
---|
214 | var fPipelineTypes = document.getElementById("pipelineTypeField"); |
---|
215 | var fSessionUid = document.getElementById("sessionField"); |
---|
216 | var fSessionTitle = document.getElementById("sessionTitleField"); |
---|
217 | |
---|
218 | sequencer.session.title = fSessionTitle.value; |
---|
219 | sequencer.session.uid = fSessionUid.value; |
---|
220 | sequencer.session.pipeline.uids = stringToArray(fPipelineString.value, ","); |
---|
221 | sequencer.session.pipeline.types = stringToArray(fPipelineTypes.value, ","); |
---|
222 | sequencer.session.pipeline.upToDate = new Array(); |
---|
223 | |
---|
224 | for (var i = 0; i < sequencer.session.pipeline.uids.length; i++) { |
---|
225 | sequencer.session.pipeline.upToDate.push(true); |
---|
226 | } |
---|
227 | sequencer.state.numSteps = 0; |
---|
228 | sequencer.state.loaded = false; |
---|
229 | sequencer.settings.content.orientation = "h"; |
---|
230 | sequencer.settings.efficientUpdating = true; |
---|
231 | } |
---|
232 | catch (e) { |
---|
233 | // Alert developer of any errors while setting these variables |
---|
234 | for (error in e) alert(error.message); |
---|
235 | } |
---|
236 | |
---|
237 | // Then remove the hidden fields from the HTML document |
---|
238 | var hiddenInputs = document.getElementById("hiddenInputs"); |
---|
239 | hiddenInputs.parentNode.removeChild(hiddenInputs); |
---|
240 | |
---|
241 | console.log(sequencer); |
---|
242 | // finally, run updateSequencer to refresh the visual display of the pipeline |
---|
243 | |
---|
244 | updateSequencer(); |
---|
245 | } |
---|
246 | |
---|
247 | function insertNewSteps(responseText, needsUpdating) { |
---|
248 | debugger; |
---|
249 | var content = document.getElementById("seqContentWrapper"); |
---|
250 | // Create a holder Div that converts the returned HTML to HTMLDivElements implicitly, so that they can be manipulated using DOM methods |
---|
251 | var holderDiv = document.createElement("div"); |
---|
252 | holderDiv.innerHTML = responseText; |
---|
253 | // tempDiv will serve to |
---|
254 | var tempDiv; |
---|
255 | if (holderDiv.firstChild.nodeType == "3") holderDiv.removeChild(holderDiv.firstChild); |
---|
256 | // Loop through |
---|
257 | for (var i = 0; i < needsUpdating.length; i++) { |
---|
258 | tempDiv = holderDiv.firstChild; |
---|
259 | holderDiv.removeChild(holderDiv.firstChild); |
---|
260 | if (needsUpdating[i][0] > sequencer.state.numSteps-1) { |
---|
261 | // this step comes at the end of the pipeline |
---|
262 | content.appendChild(tempDiv); |
---|
263 | sequencer.state.numSteps++; |
---|
264 | } |
---|
265 | else { |
---|
266 | content.replaceChild(tempDiv, content.childNodes[needsUpdating[i][0]*2]); |
---|
267 | } |
---|
268 | // The element is now updated, therefore set this flag in the global var |
---|
269 | sequencer.session.pipeline.upToDate[needsUpdating[i][0]] = true; |
---|
270 | } |
---|
271 | |
---|
272 | // Then add or adjust dividers as needed! |
---|
273 | updateDividers(); |
---|
274 | } |
---|
275 | |
---|
276 | function insertNewQuestion(responseText, needsUpdating) { |
---|
277 | /* |
---|
278 | * This is a test function displaying how to handle the visual object representation in solely javascript. |
---|
279 | * Communication of relevant variables between PHP and JS happens in JSON format. |
---|
280 | * PHP returns a JSON array of objects to be created by JS |
---|
281 | * JS then loops through this array and creates DIVS to be inserted into the sequencer. |
---|
282 | * These are inserted at the position needsUpdating gives us. |
---|
283 | */ |
---|
284 | |
---|
285 | var content = document.getElementById("seqContent"); |
---|
286 | var response = eval(responseText); |
---|
287 | |
---|
288 | for (var i = 0; i < response.length; i++) { |
---|
289 | var tempDiv = document.createElement("div"); |
---|
290 | tempDiv.id = response.id; |
---|
291 | tempDiv.className = "displayStep"; |
---|
292 | var divImage = document.createElement("img"); |
---|
293 | divImage.src = "./images/displayStep.png"; |
---|
294 | divImage.addEventListener("Click", function(e){ |
---|
295 | selectStep(this.id); |
---|
296 | }, false); |
---|
297 | tempDiv.appendChild(divImage); |
---|
298 | var divLabel = document.createElement("p"); |
---|
299 | divLabel.innerHTML = response.title; |
---|
300 | tempDiv.appendChild(divLabel); |
---|
301 | |
---|
302 | // 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. |
---|
303 | for (var j = needsUpdating.length; j >= 0; j--) { |
---|
304 | if (needsUpdating[j][1] != response.id) continue; |
---|
305 | if (needsUpdating[j][0] > sequencer.state.numSteps-1) { |
---|
306 | content.appendChild(tempDiv); |
---|
307 | } |
---|
308 | else { |
---|
309 | content.replaceChild(tempDiv, content.childNodes[j][0]*2); |
---|
310 | } |
---|
311 | } |
---|
312 | } |
---|
313 | } |
---|
314 | |
---|
315 | function updateDividers() { |
---|
316 | var content = document.getElementById("seqContentWrapper"); |
---|
317 | for (var i = 0; i < content.childNodes.length; i++) { |
---|
318 | var element = content.childNodes[i]; |
---|
319 | if (!hasClass(element, "displayStep")) { |
---|
320 | continue; |
---|
321 | } |
---|
322 | var lastElement = (element.previousSibling && element.previousSibling.nodeName == "DIV") ? element.previousSibling : false; |
---|
323 | var nextElement = (element.nextSibling && element.nextSibling.nodeName == "DIV") ? element.nextSibling : false; |
---|
324 | if (lastElement != false) { |
---|
325 | if (!hasClass(lastElement, "divider")){ |
---|
326 | var newDivider = document.createElement("div"); |
---|
327 | addClass(newDivider, "divider"); |
---|
328 | addClass(newDivider, sequencer.settings.content.orientation); |
---|
329 | content.insertBefore(newDivider, element); |
---|
330 | delete newDivider; |
---|
331 | } |
---|
332 | } |
---|
333 | if (nextElement != false) { |
---|
334 | if (!hasClass(nextElement, "divider")){ |
---|
335 | var newDivider = document.createElement("div"); |
---|
336 | addClass(newDivider, "divider"); |
---|
337 | addClass(newDivider, sequencer.settings.content.orientation); |
---|
338 | content.insertBefore(newDivider, nextElement); |
---|
339 | delete newDivider; |
---|
340 | } |
---|
341 | } |
---|
342 | } |
---|
343 | } |
---|
344 | |
---|
345 | function savePipeline (confirmSave) { |
---|
346 | var answer; |
---|
347 | if (confirmSave == true) { |
---|
348 | answer = confirm("Save changes to pipeline?"); |
---|
349 | } |
---|
350 | else { |
---|
351 | answer = true; |
---|
352 | } |
---|
353 | if (answer == false) return; |
---|
354 | |
---|
355 | var requestString = "uids="; |
---|
356 | requestString += arrayToString(sequencer.session.pipeline.uids, ","); |
---|
357 | requestString += "&types="; |
---|
358 | requestString += arrayToString(sequencer.session.pipeline.types, ","); |
---|
359 | requestString += "&sessionUid="; |
---|
360 | requestString += sequencer.session.uid; |
---|
361 | newAjaxRequest(requestString, "savePipeline.php", function(result){ |
---|
362 | console.log(result.responseText); |
---|
363 | }, true); |
---|
364 | } |
---|
365 | |
---|
366 | function editStep() { |
---|
367 | // first save |
---|
368 | |
---|
369 | //savePipeline(false); |
---|
370 | |
---|
371 | var postForm = document.createElement("form"); |
---|
372 | var type = sequencer.session.pipeline.types[sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedStep.uid)]; |
---|
373 | postForm.action = type.toLowerCase()+"Editor.php"; |
---|
374 | postForm.method = "POST"; |
---|
375 | var objectUid = document.createElement("input"); |
---|
376 | objectUid.type = "hidden"; |
---|
377 | objectUid.name = "objectUid"; |
---|
378 | objectUid.value = sequencer.state.selectedStep.uid; |
---|
379 | postForm.appendChild(objectUid); |
---|
380 | postForm.submit(); |
---|
381 | } |
---|
382 | |
---|
383 | function moveStep (direction) { |
---|
384 | // Check if a step is selected |
---|
385 | |
---|
386 | if (sequencer.state.selectedStep.uid == null || direction == null) return; |
---|
387 | // Check if the step is not at either end of the pipeline |
---|
388 | var index = sequencer.session.pipeline.uids.indexOf(sequencer.state.selectedStep.uid); |
---|
389 | if ((index < 0) || (index >= sequencer.session.pipeline.uids.length) || (index == 0 && direction == -1) || (index == sequencer.session.pipeline.uids.length - 1 && direction == 1)) { |
---|
390 | alert("Cannot move out of bounds!"); |
---|
391 | return; |
---|
392 | } |
---|
393 | |
---|
394 | // Find the two elements in the editor content display |
---|
395 | var content = document.getElementById("seqContentWrapper"); |
---|
396 | var element = document.getElementById(sequencer.session.pipeline.uids[index]); |
---|
397 | var otherElement = document.getElementById(sequencer.session.pipeline.uids[index+direction]); |
---|
398 | // First buffer the two elements |
---|
399 | var tempElement = element.cloneNode(true); |
---|
400 | var tempOtherElement = otherElement.cloneNode(true); |
---|
401 | var placeHolderElement = document.createElement("div"); |
---|
402 | placeHolderElement.id = "placeholder_element"; |
---|
403 | content.replaceChild(placeHolderElement, otherElement); |
---|
404 | content.replaceChild(tempOtherElement, element); |
---|
405 | content.replaceChild(tempElement, placeHolderElement); |
---|
406 | //This should work. |
---|
407 | // A-B Start positions, backup to tA and tB |
---|
408 | // A-X Replace B with placeholder X |
---|
409 | // B-X Replace A with tB |
---|
410 | // B-A Replace placeholder X with tA. |
---|
411 | // The two elements are now swapped. |
---|
412 | |
---|
413 | // Now swap the array entries. |
---|
414 | sequencer.session.pipeline.uids[index] = sequencer.session.pipeline.uids.splice(index+direction, 1, sequencer.session.pipeline.uids[index])[0]; |
---|
415 | sequencer.session.pipeline.types[index] = sequencer.session.pipeline.types.splice(index+direction, 1, sequencer.session.pipeline.types[index])[0]; |
---|
416 | |
---|
417 | } |
---|