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

Last change on this file since 17810 was 17724, checked in by tbretz, 11 years ago
The error message wasn't that clear for the case a schedule of tonight was saved, but no active tasks were there.
File size: 21.9 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 return;
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)
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 oine by one to the table
456 for (var i=0; i<schedule.length; i++)
457 {
458 schedule[i].fStart = schedule[i].fStart.replace('-', '/');
459 schedule[i].fStart = schedule[i].fStart.replace('-', '/');
460
461 var stamp = new Date(schedule[i].fStart+" UTC");
462
463 if (table.loadedDay)
464 stamp = new Date(stamp.getTime()+day.getTime()-offset);
465
466 var disabled = stamp.getTime()<table.loadTime && !table.loadedDay;
467 if (disabled)
468 table.cutTime = pad(stamp.getUTCHours())+":"+pad(stamp.getUTCMinutes())+":"+pad(stamp.getUTCSeconds());
469
470 addRow(schedule[i], disabled);
471 }
472
473 debug("currentDay="+table.currentDay);
474 debug("nextDay="+table.nextDay);
475 debug("isTonight="+table.isTonight);
476 debug("cutTime="+table.cutTime);
477}
478
479function loadDay(date, dateToLoad)
480{
481 // Clean elements from table before new elements are added
482 var table = document.getElementById("TableHolder");
483
484 // In very rare cases (fast and frequent clicks on a date with a long schedule)
485 // the event listened of the calender returns a wrong value
486 if (new String(date).length!=10)
487 return;
488
489 var dbg = document.getElementById("debug");
490 while (dbg.firstChild)
491 dbg.removeChild(dbg.firstChild);
492
493 var count = 0;
494
495 var cut;
496 while (table.childNodes.length>2)
497 {
498 var cols = table.lastChild.childNodes;
499 var time = cols[0].firstChild;
500
501 if (time.disabled)
502 {
503 debug("disabled="+time.value);
504
505 cut = time.value;
506 if (dateToLoad)
507 break;
508 }
509
510 count++;
511 table.removeChild(table.lastChild);
512 }
513
514 debug(count+" lines removed ");
515
516 document.getElementById("savedate").value = date;
517
518 // it helps to know if this is tonight or not
519 var now = new Date();
520 var day = new Date(date);
521 var night = getYMD(new Date(now.getTime()-12*3600*1000));
522
523 table.isTonight = date==night;
524 table.currentDay = date;
525 table.loadedDay = dateToLoad;
526 table.loadTime = now.getTime();
527
528 if (!table.isTonight || !table.loadedDay)
529 table.cutTime = undefined;
530
531 // remember the currently displayed day (FIXME: Move to table property?)
532 table.prevDay = getYMD(new Date(day.getTime()-24*3600*1000));
533 table.nextDay = getYMD(new Date(day.getTime()+24*3600*1000));
534
535 debug("day="+date+"|"+date.length);
536 debug("dayToLoad="+table.loadedDay);
537 debug("cut="+cut);
538
539 var data = "n="+(dateToLoad?dateToLoad:date);
540 if (cut && (!table.isTonight || table.loadedDay))
541 data += "&t="+cut;
542
543 debug("data="+data);
544
545 // request data from the datanbase and on reception, display the data
546 $.ajax({
547 type: "POST",
548 cache: false,
549 url: "load.php",
550 data: data,
551 success: onDataReceived,
552 error: function(xhr) { if (xhr.status==0) alert("ERROR[0] - Request failed!"); else alert("ERROR[0] - "+xhr.statusText+" ["+xhr.status+"]"); }
553 });
554}
555
556function onLoad()
557{
558 if (location.href.search('debug')==-1)
559 $("#debug").hide();
560
561 /*---------------------------------------------------------------------------------------------
562 Prevent any user interaction during AJAX requests
563 ----------------------------------------------------------------------------------------------*/
564
565 $(document).ajaxStart(function() { $('#wait').fadeIn(); }).ajaxStop(function() { $('#wait').fadeOut(200); });
566
567 /*---------------------------------------------------------------------------------------------
568 Check if a dedicated day was requested, if not start with the current day.
569 Load the data from the database and display the calendar and the data.
570 ----------------------------------------------------------------------------------------------*/
571
572 var reg = /^\?day=(20[123][0-9]-[01][0-9]-[0123][0-9])/;
573 var res = reg.exec(location.search);
574
575 var day;
576 if (!res)
577 {
578 var now = new Date();
579
580 if (now.getUTCHours()<12)
581 now = new Date(now.getTime()-12*3600*1000);
582
583 day = getYMD(now);
584 }
585 else
586 day = res[1];
587
588 loadDay(day);
589
590 /*---------------------------------------------------------------------------------------------
591 Start updating the clock
592 ----------------------------------------------------------------------------------------------*/
593
594 updateClock();
595 setInterval(updateClock, 1000);
596
597 /*---------------------------------------------------------------------------------------------
598 Loading of previous data. Get the previous date with existing data through PreviousData.php
599 Same Shedule.ph passed to load on the controls with extra parameter 'prev' to indicate that
600 data is from previous schedule.
601 ----------------------------------------------------------------------------------------------*/
602
603 // FIXME: Do not overwrite the disabled part of the schedule if it is TONIGHT!!!!
604
605 function onLoad()
606 {
607 var cd = document.getElementById("TableHolder");
608 var dt = document.getElementById("loaddate");
609 loadDay(cd.currentDay, dt.value);
610 }
611
612 $('#load').click(onLoad);
613 $('#loaddate').keypress(function(event) { if (event.which==13) { onLoad(); } event.preventDefault(); });
614
615 /*---------------------------------------------------------------------------------------------
616 Savng and updating of schedule. Data array is generated from the current table to be submitted
617 to saveSchedule.php for the execution of queries.
618 ----------------------------------------------------------------------------------------------*/
619
620 function onSaveClick()
621 {
622 var table = document.getElementById("TableHolder");
623
624 var rows = table.childNodes;
625
626 var schedule = [];
627
628 // cutTime is the last time of a measurement which is past
629 // and should not be updated. This assumes that all
630 // time values are sequential.
631 //var cutTime = "12:00:00";
632 //var prevTime = new Date(currentDay+" 12:00:00");
633
634 // FIXME: Make sure dates are sequentiel
635
636 for (var i=2; i<rows.length; i++)
637 {
638 var cols = rows[i].childNodes;
639
640 var time = cols[0].firstChild.value;
641 var measure = cols[1].firstChild.value;
642 var source = cols[2].firstChild.value;
643 var value = cols[3].firstChild.value;
644 var hidden = cols[0].firstChild.hidden;
645 var disabled = cols[0].firstChild.disabled;
646
647 if (!hidden && !time)
648 {
649 alert("ERROR - Invalid time fields detected.");
650 return;
651 }
652
653 /*
654 if (!hidden)
655 {
656 // This is just to check the times... theoretically,
657 // these times could be sent, so that the php does not
658 // have to do that again, or should the php check things
659 // to ensure that it cannot be hacked?
660 var t = time;
661 t = t.replace(':','');
662 t = t.replace(':','');
663
664 t = new Date(t<120000 ? currentDay+" "+time : nextDay+" "+time);
665
666 if (t.getTime()<prevTime.getTime())
667 {
668 alert("Times not sequential... cannot save schedule.");
669 return;
670 }
671
672 prevTime = t;
673 }
674
675 if (disabled)
676 {
677 // if (cutTime>time) // no time yet!
678 cutTime = time;
679 continue;
680 }*/
681
682 if (disabled)
683 continue;
684
685 if (hidden)
686 time = null;
687
688 schedule.push([ time, measure, source, value ]);
689 }
690
691 if (schedule.length==0)
692 {
693 alert("No active tasks - nothing to be saved.");
694 return;
695 }
696
697 //alert(table.isTonight+"/"+table.cutTime+"/"+schedule.length);
698
699 var data = "n="+table.currentDay+"&d="+JSON.stringify(schedule);
700 if (table.isTonight)
701 data += "&t="+table.cutTime;
702
703 $.ajax({
704 type: "POST",
705 cache: false,
706 url: "save.php",
707 data: data,
708 success: function(result) { if (result.length==0) { /*alert("Success.");*/ loadDay(table.currentDay); } else alert("ERROR - "+result); },
709 error: function(xhr) { if (xhr.status==0) alert("ERROR[1] - Unauthorized!"); else alert("ERROR[1] - "+xhr.statusText+" ["+xhr.status+"]"); }
710 });
711 }
712
713 $('#save').click(onSaveClick);
714
715
716 function onHelp()
717 {
718 var ov = $("#Overlay");
719 var pos = $("#help").offset();
720 //var doc = $(document);
721 ov.css({
722 left: pos.left + 'px',
723 top: pos.top + 'px',
724 width: 0,
725 height: 0
726 })
727 .show()
728 .animate({
729 left: 0,
730 top: 0,
731 width: '100%',
732 height: '100%'
733 }, "slow");
734
735 event.preventDefault();
736 }
737
738 function onClose()
739 {
740 $("#Overlay").hide("slow");
741 }
742
743 $('#help').click(onHelp);
744 $(document).keydown(function(event) { if (event.which==27) { onClose(); event.preventDefault(); } });
745 $('#close').click(onClose);
746
747}
748
749$('document').ready(onLoad);
Note: See TracBrowser for help on using the repository browser.