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

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