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

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