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

Last change on this file since 18398 was 18389, checked in by tbretz, 9 years ago
Added a tooltip containing ID and last update time.
File size: 22.5 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 onLoad()
565{
566 if (location.href.search('debug')==-1)
567 $("#debug").hide();
568
569 /*---------------------------------------------------------------------------------------------
570 Initialize jQuery tooltips
571 ----------------------------------------------------------------------------------------------*/
572
573 $(document).ready(function(){$('[data-toggle="tooltip"]').tooltip();});
574
575 /*---------------------------------------------------------------------------------------------
576 Prevent any user interaction during AJAX requests
577 ----------------------------------------------------------------------------------------------*/
578
579 $(document).ajaxStart(function() { $('#wait').fadeIn(); }).ajaxStop(function() { $('#wait').fadeOut(200); });
580
581 /*---------------------------------------------------------------------------------------------
582 Check if a dedicated day was requested, if not start with the current day.
583 Load the data from the database and display the calendar and the data.
584 ----------------------------------------------------------------------------------------------*/
585
586 var reg = /^\?day=(20[123][0-9]-[01][0-9]-[0123][0-9])/;
587 var res = reg.exec(location.search);
588
589 var day;
590 if (!res)
591 {
592 var now = new Date();
593
594 if (now.getUTCHours()<12)
595 now = new Date(now.getTime()-12*3600*1000);
596
597 day = getYMD(now);
598 }
599 else
600 day = res[1];
601
602 loadDay(day);
603
604 /*---------------------------------------------------------------------------------------------
605 Start updating the clock
606 ----------------------------------------------------------------------------------------------*/
607
608 updateClock();
609 setInterval(updateClock, 1000);
610
611 /*---------------------------------------------------------------------------------------------
612 Loading of previous data. Get the previous date with existing data through PreviousData.php
613 Same Shedule.ph passed to load on the controls with extra parameter 'prev' to indicate that
614 data is from previous schedule.
615 ----------------------------------------------------------------------------------------------*/
616
617 // FIXME: Do not overwrite the disabled part of the schedule if it is TONIGHT!!!!
618
619 function onLoad()
620 {
621 var cd = document.getElementById("TableHolder");
622 var dt = document.getElementById("loaddate");
623 loadDay(cd.currentDay, dt.value);
624 }
625
626 $('#load').click(onLoad);
627 $('#loaddate').keypress(function(event) { if (event.which==13) { onLoad(); } event.preventDefault(); });
628
629 /*---------------------------------------------------------------------------------------------
630 Savng and updating of schedule. Data array is generated from the current table to be submitted
631 to saveSchedule.php for the execution of queries.
632 ----------------------------------------------------------------------------------------------*/
633
634 function onSaveClick()
635 {
636 var table = document.getElementById("TableHolder");
637
638 var rows = table.childNodes;
639
640 var schedule = [];
641
642 // cutTime is the last time of a measurement which is past
643 // and should not be updated. This assumes that all
644 // time values are sequential.
645 //var cutTime = "12:00:00";
646 //var prevTime = new Date(currentDay+" 12:00:00");
647
648 // FIXME: Make sure dates are sequentiel
649
650 for (var i=2; i<rows.length; i++)
651 {
652 var cols = rows[i].childNodes;
653
654 var time = cols[0].firstChild.value;
655 var measure = cols[1].firstChild.value;
656 var source = cols[2].firstChild.value;
657 var value = cols[3].firstChild.value;
658 var hidden = cols[0].firstChild.hidden;
659 var disabled = cols[0].firstChild.disabled;
660
661 if (!hidden && !time)
662 {
663 alert("ERROR - Invalid time fields detected.");
664 return;
665 }
666
667 /*
668 if (!hidden)
669 {
670 // This is just to check the times... theoretically,
671 // these times could be sent, so that the php does not
672 // have to do that again, or should the php check things
673 // to ensure that it cannot be hacked?
674 var t = time;
675 t = t.replace(':','');
676 t = t.replace(':','');
677
678 t = new Date(t<120000 ? currentDay+" "+time : nextDay+" "+time);
679
680 if (t.getTime()<prevTime.getTime())
681 {
682 alert("Times not sequential... cannot save schedule.");
683 return;
684 }
685
686 prevTime = t;
687 }
688
689 if (disabled)
690 {
691 // if (cutTime>time) // no time yet!
692 cutTime = time;
693 continue;
694 }*/
695
696 if (disabled)
697 continue;
698
699 if (hidden)
700 time = null;
701
702 schedule.push([ time, measure, source, value ]);
703 }
704
705 if (schedule.length==0)
706 {
707 alert("No active tasks - nothing to be saved.");
708 return;
709 }
710
711 //alert(table.isTonight+"/"+table.cutTime+"/"+schedule.length);
712
713 var data = "n="+table.currentDay+"&d="+JSON.stringify(schedule);
714 if (table.isTonight)
715 data += "&t="+table.cutTime;
716
717 $.ajax({
718 type: "POST",
719 cache: false,
720 url: "save.php",
721 data: data,
722 success: function(result) { if (result.length==0) { /*alert("Success.");*/ loadDay(table.currentDay); } else alert("ERROR - "+result); },
723 error: function(xhr) { if (xhr.status==0) alert("ERROR[1] - Unauthorized!"); else alert("ERROR[1] - "+xhr.statusText+" ["+xhr.status+"]"); }
724 });
725 }
726
727 $('#save').click(onSaveClick);
728
729
730 function onHelp()
731 {
732 var ov = $("#Overlay");
733 var pos = $("#help").offset();
734 //var doc = $(document);
735 ov.css({
736 left: pos.left + 'px',
737 top: pos.top + 'px',
738 width: 0,
739 height: 0
740 })
741 .show()
742 .animate({
743 left: 0,
744 top: 0,
745 width: '100%',
746 height: '100%'
747 }, "slow");
748
749 event.preventDefault();
750 }
751
752 function onClose()
753 {
754 $("#Overlay").hide("slow");
755 }
756
757 $('#help').click(onHelp);
758 $(document).keydown(function(event) { if (event.which==27) { onClose(); event.preventDefault(); } });
759 $('#close').click(onClose);
760
761}
762
763$('document').ready(onLoad);
Note: See TracBrowser for help on using the repository browser.