source: branches/testFACT++branch/www/schedule/index.js@ 19915

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