source: trunk/FACT++/www/schedule/index.js@ 18368

Last change on this file since 18368 was 18368, checked in by tbretz, 9 years ago
Added a missing sanity check.
File size: 22.1 KB
Line 
1'use strict';
2
3function pad(n)
4{
5 return (n<10) ? '0' + n : n;
6}
7
8function getYMD(d)
9{
10 return d.getUTCFullYear()+'-'+pad(d.getUTCMonth()+1)+'-'+pad(d.getUTCDate());
11}
12
13function updateClock()
14{
15 var now = new Date();
16 var s = getYMD(now)+' '+pad(now.getUTCHours())+':'+pad(now.getUTCMinutes())+' UTC';
17 $('#clock').html(s);
18
19 var table = document.getElementById("TableHolder");
20
21 if (!table.isTonight)
22 return;
23
24 var rows = table.childNodes;
25
26 for (var i=2; i<rows.length; i++)
27 {
28 var el = rows[i].firstChild.firstChild;
29
30 // FIXME: replace by classes?
31 if (!el.valueAsDate)
32 {
33// el.setAttribute("style", "color:#000000");
34 continue;
35 }
36
37 var t0 = now.getTime()%86400000;
38 var t1 = el.valueAsDate%86400000;
39 if (t1<43200000)
40 t1 += 86400000;
41
42 el.setAttribute("style", t1>t0 ? "color:darkgreen" : "color:darkred");
43 }
44};
45
46function addEmptyRow(prev, start)
47{
48 var empty =
49 {
50 fStart: "0000/00/00 "+(start?start:""),
51 fMeasurementTypeKey:0,
52 fMeasurementID:0,
53 fSourceKEY:0,
54 };
55
56 addRow(empty, false, prev);
57}
58
59function debug(txt)
60{
61 var dbg = document.getElementById("debug");
62 dbg.appendChild(document.createTextNode(txt));
63 dbg.appendChild(document.createElement("br"));
64}
65
66function addRow(row, disabled, prev)
67{
68 var table = document.getElementById("TableHolder");
69
70 var sources = table.sources;
71 var measurements = table.measurements;
72
73 var tr = document.createElement("tr");
74 tr.setAttribute("width", "100%");
75
76 if (!prev)
77 table.appendChild(tr);
78 else
79 table.insertBefore(tr, prev.nextSibling);
80
81 // ---------- column 1 ----------
82
83 var input1t = document.createElement("input");
84 input1t.setAttribute("type","time");
85 input1t.setAttribute("size","10");
86 input1t.setAttribute("autofocus","true");
87 if (row.fMeasurementID!=0)
88 input1t.setAttribute("hidden", "true");
89 input1t.setAttribute("value", row.fStart.substr(11));
90 if (disabled)
91 input1t.setAttribute("disabled", "true");
92
93 /*
94 input1t.onblur = function()
95 {
96 var prevRow = tr.previousSibling;
97 while (prevRow && prevRow.firstChild.firstChild.hidden)
98 prevRow = prevRow.previousSibling;
99
100 var nextRow = tr.nextSibling;
101 while (nextRow && nextRow.firstChild.firstChild.hidden)
102 nextRow = nextRow.nextSibling;
103
104 var prevEl = prevRow ? prevRow.firstChild : undefined;
105 var nextEl = nextRow ? nextRow.firstChild : undefined;
106
107 var prevTime = "";
108 if (prevEl && prevEl.firstChild.constructor.name=="HTMLInputElement")
109 prevTime = prevEl.firstChild.value;
110
111 var nextTime = "";
112 if (nextEl && nextEl.firstChild.constructor.name=="HTMLInputElement")
113 nextTime = nextEl.firstChild.value;
114
115 alert(prevTime+"/"+input1t.value+"/"+nextTime);
116 }*/
117
118 var td1 = document.createElement("td");
119 td1.setAttribute("style","white-space:nowrap;padding-left:4px;padding-right:2px;");
120 td1.setAttribute("align","center");
121 td1.appendChild(input1t);
122
123 // Check if this is the transition from disabled to enabled.
124 // In this case enable all previous [+]
125 if (tr.previousSibling && tr.previousSibling.firstChild.firstChild.disabled && !disabled)
126 {
127 var prevRow = tr.previousSibling;
128
129 var tm = prevRow.firstChild.firstChild.value;
130 while (1)
131 {
132 prevRow.firstChild.childNodes[1].removeAttribute("disabled");
133 prevRow = prevRow.previousSibling;
134 if (prevRow.firstChild.firstChild.value!=tm)
135 break;
136 }
137 }
138
139 var input1p = document.createElement("input");
140 input1p.setAttribute("style","width:20px");
141 input1p.setAttribute("type","button");
142 input1p.setAttribute("value","+");
143 if (row.fMeasurementID!=0)
144 input1p.setAttribute("hidden", "true");
145 // FIXME: Enable if this is the last "+" in tonight
146 if (disabled && ! (table.isTonight && row.last))
147 input1p.setAttribute("disabled", "true");
148 input1p.onclick = function()
149 {
150 // FiXME: Do not allow deleting of last line
151 var nextRow = tr;
152 while (nextRow.nextSibling)
153 {
154 if (!nextRow.nextSibling.firstChild.firstChild.hidden)
155 break;
156
157 nextRow = nextRow.nextSibling;
158 }
159
160 addEmptyRow(nextRow, input1t.value);
161 }
162 td1.appendChild(input1p);
163
164 var input1m = document.createElement("input");
165 input1m.setAttribute("style","width:20px");
166 input1m.setAttribute("type","button");
167 input1m.setAttribute("value","-");
168 if (row.fMeasurementID!=0)
169 input1m.setAttribute("hidden", "true");
170 if (disabled)
171 input1m.setAttribute("disabled", "true");
172 input1m.onclick = function()
173 {
174 //if (table.childNodes.length==3)
175 // return;
176
177 var nextRow = tr.nextSibling;
178 table.removeChild(tr);
179
180 while (nextRow)
181 {
182 var inp = nextRow.firstChild.firstChild;
183 if (!inp.hidden)
184 break;
185
186 var xx = nextRow;
187 nextRow = nextRow.nextSibling;
188 table.removeChild(xx);
189 }
190
191 if (table.childNodes.length==2)
192 addEmptyRow();
193 }
194 td1.appendChild(input1m);
195
196 tr.appendChild(td1);
197
198 // ---------- column 2 -----------
199
200 var select2 = document.createElement("select");
201 // select2.setAttribute("style", "width:100px");
202 select2.setAttribute("class", "measurement");
203 if (disabled)
204 select2.setAttribute("disabled", "true");
205
206 for (var i=0; i<measurements.length; i++)
207 {
208 var option = document.createElement("option");
209 select2.appendChild(option);
210 option.setAttribute('value', measurements[i].key);
211 option.appendChild(document.createTextNode(measurements[i].val));
212 if (row.fMeasurementTypeKey==measurements[i].key)
213 select2.selectedIndex = i;
214 }
215
216 var td2 = document.createElement("td");
217 td2.setAttribute("style","white-space:nowrap;padding-left:2px;padding-right:2px;");
218 td2.setAttribute("align","center");
219 td2.appendChild(select2);
220
221 var input2 = document.createElement("input");
222 input2.setAttribute("type","button");
223 input2.setAttribute("value","+");
224 input2.setAttribute("style","width:20px");
225 if (disabled)
226 input2.setAttribute("disabled", "true");
227 input2.onclick = function()
228 {
229 var empty =
230 {
231 fStart: row.fStart,
232 fMeasurementTypeKey:0,
233 fMeasurementID:-1,
234 fSourceKEY:0,
235 };
236
237 addRow(empty, false, tr);
238 }
239 td2.appendChild(input2);
240
241 input2 = document.createElement("input");
242 input2.setAttribute("type","button");
243 input2.setAttribute("style","width:20px");
244 input2.setAttribute("value","-");
245 if (disabled)
246 input2.setAttribute("disabled", "true");
247 input2.onclick = function()
248 {
249 //if (table.childNodes.length==3)
250 // return;
251
252 if (!tr.firstChild.childNodes[0].hidden)
253 {
254 var tm = tr.firstChild.childNodes[0].value;
255
256 var nextRow = tr.nextSibling;
257 if (nextRow)
258 {
259 var e = nextRow.firstChild.childNodes;
260 e[0].removeAttribute("hidden");
261 e[1].removeAttribute("hidden");
262 e[2].removeAttribute("hidden");
263 e[0].value = tm;
264 }
265 }
266 table.removeChild(tr);
267
268 if (table.childNodes.length==2)
269 addEmptyRow();
270 }
271 td2.appendChild(input2);
272
273 tr.appendChild(td2);
274
275 // ---------- column 3 -----------
276
277 var select3 = document.createElement("select");
278 //select3.setAttribute("style", "width:100px");
279 select3.setAttribute("class", "sources");
280 if (disabled)
281 select3.setAttribute("disabled", "true");
282
283 for (var i=0; i<sources.length; i++)
284 {
285 var option = document.createElement("option");
286 select3.appendChild(option);
287 option.setAttribute('value', sources[i].key);
288 option.appendChild(document.createTextNode(sources[i].val));
289 if (row.fSourceKey==sources[i].key)
290 select3.selectedIndex = i;
291 }
292
293 var td3 = document.createElement("td");
294 td3.setAttribute("style","white-space:nowrap;padding-left:2px;padding-right:2px;");
295 td3.setAttribute("align","center");
296 td3.appendChild(select3);
297
298 tr.appendChild(td3);
299
300 // ---------- column 4 ------------
301
302 var input4 = document.createElement("input");
303 input4.setAttribute("type","text");
304 input4.setAttribute("style","width:100%");
305 input4.setAttribute("placeholder","JSON object");
306 if (row.fData)
307 input4.setAttribute("value",row.fData);
308 if (disabled)
309 input4.setAttribute("disabled","true");
310
311 var td4 = document.createElement("td");
312 td4.setAttribute("style", "padding-left:2px;padding-right:4px;");
313 td4.setAttribute("align","center");
314 td4.appendChild(input4);
315
316 tr.appendChild(td4);
317}
318
319function setupCalendar(result, date)
320{
321 debug("cal="+date);
322
323 date = date.replace('-','');
324 date = date.replace('-','');
325
326 var dates = { };
327
328 for (var i=0; i<result.length; i++)
329 {
330 result[i].d = result[i].d.replace('-','');
331 result[i].d = result[i].d.replace('-','');
332
333 dates[result[i].d] = { klass: "highlight", tooltip: "Schedule set." };
334 }
335
336 dates[date] = { klass: "selected", tooltip: "Currently selected." };
337
338 function getDateInfo(date, wantsClassName)
339 {
340 var as_number = Calendar.dateToInt(date);
341 return dates[as_number];
342 };
343
344 var cont = document.getElementById("cont");
345 while (cont.firstChild)
346 cont.removeChild(cont.firstChild);
347
348 var setup =
349 {
350 cont: "cont",
351 selectionType: Calendar.SEL_MULTIPLE,
352 bottomBar: false,
353 date:parseInt(date),
354 dateInfo:getDateInfo
355 };
356
357 debug("cal.date="+date);
358
359 var cal = Calendar.setup(setup);
360 cal.addEventListener("onSelect", function(){ loadDay(this.selection.print("%Y-%m-%d")); });
361}
362
363
364function onDataReceived(result)
365{
366 var table = document.getElementById("TableHolder");
367 if (!table.currentDay)
368 return;
369
370 // Split the results of the different queries
371 // They are separated by newlines
372 var data = result.split('\\n');
373 if (data.length<5)
374 {
375 alert("Malformed result returned["+data.length+"]:\n"+data[data.length-1]);
376 return;
377 }
378
379 debug("table.currentDay="+table.currentDay);
380
381 // Decode the results into variables
382
383 // Does that work in all browsers, or do we need "YYYY-MM-DDTHH:MM:SSZ" ?
384 //data[0] = data[0].replace('-', '/');
385 //data[0] = data[0].replace('-', '/');
386
387 // year, month, day, hours, minutes, seconds, milliseconds
388 var tonight = new String(table.currentDay);
389 var day = new Date(table.currentDay);
390 var dates = JSON.parse(data[0]);
391 var sources = JSON.parse(data[1]);
392 var measurements = JSON.parse(data[2]);
393 var schedule = JSON.parse(data[3]);
394
395 if (data[4])
396 alert(data[4]);
397
398 var ld = document.getElementById("loaddate");
399 ld.setAttribute("size","10");
400
401 ld.value = table.prevDay;
402
403 // First update the calender
404 setupCalendar(dates, tonight);
405
406 // Add a fake source to the list of sources to allow 'deselection' of source
407 sources.splice(0, 0, { key: 0, val: "---" });
408
409 table.sources = sources;
410 table.measurements = measurements;
411
412 // Enable or disable the SAVE and LoadPrev button
413 var save = document.getElementById("save");
414 var load = document.getElementById("load");
415 var ldate = document.getElementById("loaddate");
416
417 if (day.getTime()+36*3600*1000>table.loadTime)
418 {
419 save.removeAttribute("disabled");
420 load.removeAttribute("disabled");
421 ldate.removeAttribute("disabled");
422
423
424 // If this is a dayin the future, but no schedule is in the db,
425 // create an empty one
426 if (schedule.length==0)
427 addEmptyRow();
428 }
429 else
430 {
431 save.setAttribute("disabled", "true");
432 load.setAttribute("disabled", "true");
433 ldate.setAttribute("disabled", "true");
434 }
435
436 // Update the header of the date/time column
437 var tm = document.getElementById("time");
438 while (tm.firstChild)
439 tm.removeChild(tm.firstChild);
440
441 var nxt = new Date(day.getTime()+24*3600*1000);
442
443 var d1 = pad(day.getUTCMonth()+1)+'-'+pad(day.getUTCDate());
444 var d2 = pad(nxt.getUTCMonth()+1)+'-'+pad(nxt.getUTCDate());
445
446 tm.appendChild(document.createTextNode(d1+ " / "+d2));
447
448 var offset = new Date(table.loadedDay).getTime();
449
450 // other day loaded and tonight
451
452 if (!table.isTonight || !table.loadedDay)
453 table.cutTime = "12:00:00";
454
455 // Now loop over all rows and add them one by one to the table
456 for (var i=0; i<schedule.length; i++)
457 {
458 schedule[i].last = schedule[schedule.length-1].fStart==schedule[i].fStart;
459 schedule[i].fStart = schedule[i].fStart.replace('-', '/');
460 schedule[i].fStart = schedule[i].fStart.replace('-', '/');
461
462 var stamp = new Date(schedule[i].fStart+" UTC");
463
464 if (table.loadedDay)
465 stamp = new Date(stamp.getTime()+day.getTime()-offset);
466
467 var disabled = stamp.getTime()<table.loadTime && !table.loadedDay;
468 if (disabled)
469 table.cutTime = pad(stamp.getUTCHours())+":"+pad(stamp.getUTCMinutes())+":"+pad(stamp.getUTCSeconds());
470
471 addRow(schedule[i], disabled);
472 }
473
474 debug("currentDay="+table.currentDay);
475 debug("nextDay="+table.nextDay);
476 debug("isTonight="+table.isTonight);
477 debug("cutTime="+table.cutTime);
478}
479
480function loadDay(date, dateToLoad)
481{
482 // Clean elements from table before new elements are added
483 var table = document.getElementById("TableHolder");
484
485 // In very rare cases (fast and frequent clicks on a date with a long schedule)
486 // the event listened of the calender returns a wrong value
487 if (new String(date).length!=10)
488 return;
489
490 var dbg = document.getElementById("debug");
491 while (dbg.firstChild)
492 dbg.removeChild(dbg.firstChild);
493
494 debug("loadDay="+date+"|"+dateToLoad);
495
496 var count = 0;
497
498 var cut;
499 while (table.childNodes.length>2)
500 {
501 var cols = table.lastChild.childNodes;
502 var time = cols[0].firstChild;
503
504 if (time.disabled)
505 {
506 debug("disabled="+time.value);
507
508 if (dateToLoad)
509 {
510 cut = time.value;
511 break;
512 }
513 }
514
515 count++;
516 table.removeChild(table.lastChild);
517 }
518
519 debug(count+" lines removed ");
520
521 document.getElementById("savedate").value = date;
522
523 // it helps to know if this is tonight or not
524 var now = new Date();
525 var day = new Date(date);
526 var night = getYMD(new Date(now.getTime()-12*3600*1000));
527
528 table.isTonight = date==night;
529 table.currentDay = date;
530 table.loadedDay = dateToLoad;
531 table.loadTime = now.getTime();
532
533 if (!table.isTonight || !table.loadedDay)
534 table.cutTime = undefined;
535
536 // remember the currently displayed day (FIXME: Move to table property?)
537 table.prevDay = getYMD(new Date(day.getTime()-24*3600*1000));
538 table.nextDay = getYMD(new Date(day.getTime()+24*3600*1000));
539
540 debug("day="+date+"|"+date.length);
541 debug("dayToLoad="+table.loadedDay);
542 debug("cut="+cut);
543
544 var data = "n="+(dateToLoad?dateToLoad:date);
545 if (cut && (!table.isTonight || table.loadedDay))
546 data += "&t="+cut;
547
548 debug("data="+data);
549
550 // request data from the datanbase and on reception, display the data
551 $.ajax({
552 type: "POST",
553 cache: false,
554 url: "load.php",
555 data: data,
556 success: onDataReceived,
557 error: function(xhr) { if (xhr.status==0) alert("ERROR[0] - Request failed!"); else alert("ERROR[0] - "+xhr.statusText+" ["+xhr.status+"]"); }
558 });
559}
560
561function onLoad()
562{
563 if (location.href.search('debug')==-1)
564 $("#debug").hide();
565
566 /*---------------------------------------------------------------------------------------------
567 Prevent any user interaction during AJAX requests
568 ----------------------------------------------------------------------------------------------*/
569
570 $(document).ajaxStart(function() { $('#wait').fadeIn(); }).ajaxStop(function() { $('#wait').fadeOut(200); });
571
572 /*---------------------------------------------------------------------------------------------
573 Check if a dedicated day was requested, if not start with the current day.
574 Load the data from the database and display the calendar and the data.
575 ----------------------------------------------------------------------------------------------*/
576
577 var reg = /^\?day=(20[123][0-9]-[01][0-9]-[0123][0-9])/;
578 var res = reg.exec(location.search);
579
580 var day;
581 if (!res)
582 {
583 var now = new Date();
584
585 if (now.getUTCHours()<12)
586 now = new Date(now.getTime()-12*3600*1000);
587
588 day = getYMD(now);
589 }
590 else
591 day = res[1];
592
593 loadDay(day);
594
595 /*---------------------------------------------------------------------------------------------
596 Start updating the clock
597 ----------------------------------------------------------------------------------------------*/
598
599 updateClock();
600 setInterval(updateClock, 1000);
601
602 /*---------------------------------------------------------------------------------------------
603 Loading of previous data. Get the previous date with existing data through PreviousData.php
604 Same Shedule.ph passed to load on the controls with extra parameter 'prev' to indicate that
605 data is from previous schedule.
606 ----------------------------------------------------------------------------------------------*/
607
608 // FIXME: Do not overwrite the disabled part of the schedule if it is TONIGHT!!!!
609
610 function onLoad()
611 {
612 var cd = document.getElementById("TableHolder");
613 var dt = document.getElementById("loaddate");
614 loadDay(cd.currentDay, dt.value);
615 }
616
617 $('#load').click(onLoad);
618 $('#loaddate').keypress(function(event) { if (event.which==13) { onLoad(); } event.preventDefault(); });
619
620 /*---------------------------------------------------------------------------------------------
621 Savng and updating of schedule. Data array is generated from the current table to be submitted
622 to saveSchedule.php for the execution of queries.
623 ----------------------------------------------------------------------------------------------*/
624
625 function onSaveClick()
626 {
627 var table = document.getElementById("TableHolder");
628
629 var rows = table.childNodes;
630
631 var schedule = [];
632
633 // cutTime is the last time of a measurement which is past
634 // and should not be updated. This assumes that all
635 // time values are sequential.
636 //var cutTime = "12:00:00";
637 //var prevTime = new Date(currentDay+" 12:00:00");
638
639 // FIXME: Make sure dates are sequentiel
640
641 for (var i=2; i<rows.length; i++)
642 {
643 var cols = rows[i].childNodes;
644
645 var time = cols[0].firstChild.value;
646 var measure = cols[1].firstChild.value;
647 var source = cols[2].firstChild.value;
648 var value = cols[3].firstChild.value;
649 var hidden = cols[0].firstChild.hidden;
650 var disabled = cols[0].firstChild.disabled;
651
652 if (!hidden && !time)
653 {
654 alert("ERROR - Invalid time fields detected.");
655 return;
656 }
657
658 /*
659 if (!hidden)
660 {
661 // This is just to check the times... theoretically,
662 // these times could be sent, so that the php does not
663 // have to do that again, or should the php check things
664 // to ensure that it cannot be hacked?
665 var t = time;
666 t = t.replace(':','');
667 t = t.replace(':','');
668
669 t = new Date(t<120000 ? currentDay+" "+time : nextDay+" "+time);
670
671 if (t.getTime()<prevTime.getTime())
672 {
673 alert("Times not sequential... cannot save schedule.");
674 return;
675 }
676
677 prevTime = t;
678 }
679
680 if (disabled)
681 {
682 // if (cutTime>time) // no time yet!
683 cutTime = time;
684 continue;
685 }*/
686
687 if (disabled)
688 continue;
689
690 if (hidden)
691 time = null;
692
693 schedule.push([ time, measure, source, value ]);
694 }
695
696 if (schedule.length==0)
697 {
698 alert("No active tasks - nothing to be saved.");
699 return;
700 }
701
702 //alert(table.isTonight+"/"+table.cutTime+"/"+schedule.length);
703
704 var data = "n="+table.currentDay+"&d="+JSON.stringify(schedule);
705 if (table.isTonight)
706 data += "&t="+table.cutTime;
707
708 $.ajax({
709 type: "POST",
710 cache: false,
711 url: "save.php",
712 data: data,
713 success: function(result) { if (result.length==0) { /*alert("Success.");*/ loadDay(table.currentDay); } else alert("ERROR - "+result); },
714 error: function(xhr) { if (xhr.status==0) alert("ERROR[1] - Unauthorized!"); else alert("ERROR[1] - "+xhr.statusText+" ["+xhr.status+"]"); }
715 });
716 }
717
718 $('#save').click(onSaveClick);
719
720
721 function onHelp()
722 {
723 var ov = $("#Overlay");
724 var pos = $("#help").offset();
725 //var doc = $(document);
726 ov.css({
727 left: pos.left + 'px',
728 top: pos.top + 'px',
729 width: 0,
730 height: 0
731 })
732 .show()
733 .animate({
734 left: 0,
735 top: 0,
736 width: '100%',
737 height: '100%'
738 }, "slow");
739
740 event.preventDefault();
741 }
742
743 function onClose()
744 {
745 $("#Overlay").hide("slow");
746 }
747
748 $('#help').click(onHelp);
749 $(document).keydown(function(event) { if (event.which==27) { onClose(); event.preventDefault(); } });
750 $('#close').click(onClose);
751
752}
753
754$('document').ready(onLoad);
Note: See TracBrowser for help on using the repository browser.