Index: trunk/FACT++/www/schedule/config.template.php
===================================================================
--- trunk/FACT++/www/schedule/config.template.php	(revision 17701)
+++ trunk/FACT++/www/schedule/config.template.php	(revision 17701)
@@ -0,0 +1,9 @@
+<?PHP
+
+$path = "/home/fact/FACT++";
+
+$ldaphost = "161.72.93.133:389";
+$baseDN   = "dc=fact,dc=iac,dc=es";
+$groupDN  = "cn=Operations,ou=Application Groups,".$baseDN;
+
+?>
Index: trunk/FACT++/www/schedule/index.css
===================================================================
--- trunk/FACT++/www/schedule/index.css	(revision 17701)
+++ trunk/FACT++/www/schedule/index.css	(revision 17701)
@@ -0,0 +1,546 @@
+.DynarchCalendar-titleCont
+{
+    width:100%;
+}
+
+.highlight
+{
+    color: #0f0 !important;
+    font-weight: bold;
+}
+
+.selected
+{
+    color: #006 !important;
+    font-weight: bold;
+}
+
+.overlay
+{
+    background-color: rgba(0, 0, 0, 0.5);
+    color: #333;
+    position: fixed;
+    z-index: 400;
+    width: 100%;
+    height: 100%;
+    top: 0px;
+    left: 0px;
+    display:none;
+    overflow:auto;
+    border:1px solid gray;
+}
+
+.close
+{
+    width: 13px;
+    height: 18px;
+    background-color: blue;
+    color:black;
+}
+/*
+table.myTable
+{
+	width:100%;
+}
+
+.myTable td
+{
+	border: solid 1px #cecece;
+}
+
+.myTable td.first
+{
+	width:190px;
+}
+
+.myTable td.last
+{
+	width:50px;
+}
+*/
+*{
+    margin: 0px;
+    padding: 0px;
+}
+
+img, fieldset
+{
+    padding: 0px;
+    border: none;
+    margin: 0px;
+    line-height: 0px;
+}
+
+a
+{
+    color: #ffffff;
+    text-decoration: none;
+    font-weight: bold;
+}
+
+a:hover
+{
+    text-decoration: none;
+    color: #0870d7;
+}
+/*
+h2
+{
+    font: 24px Arial, Helvetica, sans-serif;
+    font-weight: normal;
+    color: #0870d7;
+    padding: 30px 0px 10px 40px;
+}*/
+
+/*h3{
+	background: url(images/title.png) no-repeat top left;
+	font: 18px Arial, Helvetica, sans-serif;
+	color: #ffffff;
+	height: 43px;
+	width: 265px;
+	text-align: center;
+	line-height: 43px;
+	font-weight: normal;
+	
+}*/
+
+body
+{
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 18px;
+    color: #ffffff;
+    background: #000 url(images/bg.jpg) no-repeat center 87px;
+	
+}
+
+#bg2
+{
+    background: url(images/bg2.png) repeat-x top;
+    height: 87px;
+    position: absolute;
+    width: 100%;
+}
+
+#wrapper1
+{
+    position: relative;
+    width:900px;
+    margin: 0 auto;
+}
+
+.clear
+{
+    clear: both;
+}
+
+/*
+	menu
+*/
+
+#header2
+{
+    height: 91px;
+    width: 900px;
+    margin: 0 93px;
+}
+
+#header2 ul
+{
+    padding-left: 0px;
+    list-style: none;
+    width: 800px;
+    margin: 0 auto;
+}
+
+#header2 ul li li2
+{
+    display: inline;
+}
+
+#header2 ul li a
+{
+    font: 24px  Arial, Helvetica, sans-serif;
+    color: #ffffff;
+    text-align: center;
+    font-weight: normal;
+    text-decoration: none;
+    display: block;
+    float: left;
+    width: 620px;
+    height: 120px;
+    line-height: 91px;
+    font-family:broadway;
+    font-size: 3em;
+}
+
+#header2 ul li2
+{
+    font-size: .6em;
+    font-family:broadway;
+    color: #ffffff;
+    width: 620px;
+    height: 120px;
+    line-height: 91px;
+}
+
+#header2 ul li li2 a:hover,  #menu ul li .active
+{
+    color: #FF0;
+}
+
+
+/*logo*/
+/*
+#logo{
+	width: 1032px;
+	margin: 0 auto;
+	height: 112px;
+	padding-top: 50px;
+	padding-left: 60px;
+	
+}
+
+#logo h1 a{
+	font: 14px Arial, Helvetica, sans-serif;
+	color: #ffffff;
+	font-weight: bold;
+	text-decoration: none;
+}
+
+#logo a small{
+	font: 12px Arial, Helvetica, sans-serif;
+	color: #ffffff;
+	font-weight: normal;
+	text-decoration: none;
+}
+
+#prew_img{
+	background: url(images/img_prew.png) no-repeat top left;
+	height: 297px;
+	width: 1054px;
+	margin: 0 auto;
+}
+*/
+
+/*
+	Content
+*/
+#content_bg_top
+{
+    background: url(images/content_top.png) no-repeat top left;
+    height: 10px;
+}
+/*
+#content_bg_bot
+{
+    background: url(images/content_bot.png) no-repeat bottom left;
+    height: 10px;
+}*/
+
+#content_box
+{
+    background: url(images/content_repeat.png);
+    padding:0px 12px;
+}
+
+/*
+header
+*/
+/*
+#header {
+	background: url(images/header.jpg) no-repeat left top;
+	height: 326px;
+	width: 876px;
+}
+*/
+
+/*
+download_box
+*/
+/*
+#download_box{
+	
+	width: 876px;
+	height: 112px;
+	
+}
+
+#download_box p {
+	color: #303030;
+	font: 12px Arial, Helvetica, sans-serif;
+	font-weight: normal;
+}
+#download_box p a {
+	color: #303030;
+	font: 12px Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	
+}
+#download_box p a:hover {
+	color: #0870d7;
+}
+#download_left {
+	padding: 25px 0px 10px 25px;
+	float:left;
+	width: 638px;
+}
+
+#download_right a{
+	font: 18px Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	text-align: center;
+	line-height: 56px;
+	width: 195px;
+	height: 56px;
+	text-transform: uppercase;
+	background: url(images/download_button.png) no-repeat top left;
+	display: block;
+	float: right;
+	margin: 38px 15px 0px 0px;
+}
+
+#download_right a:hover{
+		color: #ffffff;
+}
+*/
+/*
+column_box
+*/
+#column_box
+{
+    padding-left: 15px;
+    padding-bottom: 30px;
+}
+
+#column_box a
+{
+    color: #666666;
+}
+
+#column_box a:hover
+{
+    color: #ffffff;
+}
+
+#column_box p
+{
+    color: #666666;
+    padding-top: 5px;
+}
+
+#column1
+{
+    float: left;
+    width: 280px;
+}
+
+#column2
+{
+    float: left;
+    width: 260px;
+}
+
+#column3
+{
+    float: left;
+    width: 580px;
+}
+
+#column2
+{
+    margin: 0px 40px;
+}
+/*
+	footer_top
+*/
+
+
+
+/*
+#footer_top{
+	padding-top: 35px;	
+}
+
+
+#footer_top a{
+	color: #ffffff;
+	font-weight: bold;
+}
+
+#footer_top a:hover{
+	color: #0870d7;
+}
+
+#footer_top p{
+	color: #ffffff;
+	line-height: 20px;
+}
+
+#footer_column1, #footer_column2, #footer_column3{
+	width: 265px;
+	float: left;
+}
+
+.footer_text{
+	background: url(images/footer_top_border.png) no-repeat top left;
+	padding: 10px 0px 10px 20px;
+}
+
+#footer_column2{
+	padding: 0px 38px 0px 31px;
+}
+
+.foot_pad{
+    padding-left: 45px;
+	padding-top: 10px;
+}
+
+
+.ls{
+    list-style: none;
+    padding-left: 0px;
+}
+
+.ls li{
+    background: url(images/ls1.gif) no-repeat 0px 6px;
+    margin-bottom: 8px;
+    padding-left: 15px;
+}
+*/
+/*
+	footer_bot
+*/
+
+#footer_bot
+{
+    padding: 25px;
+    text-align: center;
+}
+
+.button
+{
+    cursor:pointer;
+    border-top: 1px solid #96d1f8;
+    background: #65a9d7;
+    background: -webkit-gradient(linear, left top, left bottom, from(#3e779d), to(#65a9d7));
+    background: -webkit-linear-gradient(top, #3e779d, #65a9d7);
+    background: -moz-linear-gradient(top, #3e779d, #65a9d7);
+    background: -ms-linear-gradient(top, #3e779d, #65a9d7);
+    background: -o-linear-gradient(top, #3e779d, #65a9d7);
+    padding: 3px 6px;
+    -webkit-border-radius: 6px;
+    -moz-border-radius: 6px;
+    border-radius: 6px;
+    -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+    -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+    box-shadow: rgba(0,0,0,1) 0 1px 0;
+    text-shadow: rgba(0,0,0,.4) 0 1px 0;
+    color: white;
+    font-size: 14px;
+    font-family: 'Lucida Grande', Helvetica, Arial, Sans-Serif;
+    text-decoration: none;
+    vertical-align: middle;
+}
+
+.button:hover
+{
+   border-top-color: #28597a;
+   background: #28597a;
+   color: #ccc;
+}
+
+.button:active
+{
+   border-top-color: #b6c5cf;
+   background: #b6c5cf;
+}
+
+#button
+{
+   border-top: 1px solid #96d1f8;
+   background: #65a9d7;
+   background: -webkit-gradient(linear, left top, left bottom, from(#3e779d), to(#65a9d7));
+   background: -webkit-linear-gradient(top, #3e779d, #65a9d7);
+   background: -moz-linear-gradient(top, #3e779d, #65a9d7);
+   background: -ms-linear-gradient(top, #3e779d, #65a9d7);
+   background: -o-linear-gradient(top, #3e779d, #65a9d7);
+   padding: 3px 6px;
+   -webkit-border-radius: 6px;
+   -moz-border-radius: 6px;
+   border-radius: 6px;
+   -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   box-shadow: rgba(0,0,0,1) 0 1px 0;
+   text-shadow: rgba(0,0,0,.4) 0 1px 0;
+   color: white;
+   font-size: 12px;
+   font-family: 'Lucida Grande', Helvetica, Arial, Sans-Serif;
+   text-decoration: none;
+   vertical-align: middle;
+}
+
+#button:hover
+{
+   border-top-color: #28597a;
+   background: #28597a;
+   color: #ccc;
+}
+
+#button:active
+{
+   border-top-color: #b6c5cf;
+   background: #b6c5cf;
+}
+/*
+ .buttonc {
+	cursor:pointer;
+   border-top: 1px solid #96d1f8;
+  
+   padding: -1px 6px;
+   -webkit-border-radius: 6px;
+   -moz-border-radius: 6px;
+   border-radius: 6px;
+   -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   box-shadow: rgba(0,0,0,1) 0 1px 0;
+   text-shadow: rgba(0,0,0,.4) 0 1px 0;
+   color: black;
+   font-size: 14px;
+   font-family: 'Lucida Grande', Helvetica, Arial, Sans-Serif;
+   text-decoration: none;
+   vertical-align: middle;
+   }
+
+   #buttonc {
+   border-top: 1px solid #96d1f8;
+   background: #65a9d7;
+   background: -webkit-gradient(linear, left top, left bottom, from(#3e779d), to(#65a9d7));
+   background: -webkit-linear-gradient(top, #3e779d, #65a9d7);
+   background: -moz-linear-gradient(top, #3e779d, #65a9d7);
+   background: -ms-linear-gradient(top, #3e779d, #65a9d7);
+   background: -o-linear-gradient(top, #3e779d, #65a9d7);
+   padding: 3px 6px;
+   -webkit-border-radius: 6px;
+   -moz-border-radius: 6px;
+   border-radius: 6px;
+   -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+   box-shadow: rgba(0,0,0,1) 0 1px 0;
+   text-shadow: rgba(0,0,0,.4) 0 1px 0;
+   color: white;
+   font-size: 12px;
+   font-family: 'Lucida Grande', Helvetica, Arial, Sans-Serif;
+   text-decoration: none;
+   vertical-align: middle;
+   }
+#buttonc:hover {
+   border-top-color: #28597a;
+   background: #28597a;
+   color: #ccc;
+   }
+#buttonc:active {
+   border-top-color: #b6c5cf;
+   background: #b6c5cf;
+   }
+ */
Index: trunk/FACT++/www/schedule/index.html
===================================================================
--- trunk/FACT++/www/schedule/index.html	(revision 17701)
+++ trunk/FACT++/www/schedule/index.html	(revision 17701)
@@ -0,0 +1,136 @@
+<!DOCTYLE HTML>
+<html>
+<head>
+   <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+   <title>FACT Scheduling</title>
+   <meta name="keywords" content="" />
+   <meta name="description" content="" />
+
+   <script src="jquery-2.1.0.min.js"></script>
+   <script src="jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
+
+   <script src="JSCal2-1.8/src/js/jscal2.js"></script>
+   <script src="JSCal2-1.8/src/js/lang/en.js"></script>
+
+   <script src="index.js"></script>
+
+   <link rel="stylesheet" href="jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css"/>
+   <link rel="stylesheet" href="index.css" media="screen" />
+
+   <link rel="stylesheet" href="JSCal2-1.8/src/css/jscal2.css" />
+   <link rel="stylesheet" href="JSCal2-1.8/src/css/border-radius.css" />
+   <link rel="stylesheet" href="JSCal2-1.8/src/css/steel/steel.css" />
+</head>
+
+<body style="background-color: #000;">
+<div id="wait" style="background:rgba(0,0,0,0.75) url('images/helium-anim-trans.gif') no-repeat center center;z-index:400;position:fixed;top:0px;left:0px;width:100%;height:100%;text-align:center;vertical-align:middle;"></div>
+<div id="bg2"></div>
+<div id="wrapper1">
+   <div id="header2">
+      <ul>
+         <li><a href="http://www.fact-project.org">FACT <li2>OBSERVATION SCHEDULING</li2></a></li>
+      </ul>
+      <div class="clear"></div>
+   </div>	
+   <div id="content_bg_top"></div>
+   <div id="content_box">
+      <div id="column_box">
+         <div id="column1">
+	    <div id="calendar" align="center">
+ 	       <table>
+                  <tr>
+                     <td valign="top" style="width: 30em;">
+                        <div id="cont"></div>
+                     </td>
+                  </tr>
+               </table>
+            </div>
+	 </div>
+	 <div id="column3"></div>
+         <div class="clear"></div>
+      </div>
+      <div style="width:100%;" align="right" id='btn'>
+         <table width="100%">
+            <tr>
+               <td align="left" width="10px" style="font-weight:bold;">TIME:&nbsp;&nbsp;</td>
+               <td align="left" id="clock" style="font-size:16px; font-weight:bold; color:#FFFF00"></td>
+               <td align="right">
+                  <input type="button" class="button"  id="load" value="Load Night"  title="Click to load given night"/>
+                  <input type="date"                   id="loaddate"                 title="Date to load a schedule from."/>&nbsp;&nbsp;
+                  <input type="button" class="button"  id="save" value="SAVE"        title="Click to save"/>
+                  <input type="date"   disabled="true" readonly="true" id="savedate" title="Date to which the schedule is saved."/>&nbsp;&nbsp;
+                  <input type="button" class="button"  id="help" value="HELP"        title="Click to view help" align="left"/>
+               </td>
+            </tr>
+         </table>
+      </div>
+      <div style="border:2px solid white; width:100%;" id="Scheduling">
+         <table width="100%" id="TableHolder" >
+	    <tr>
+	       <td style="border:1px solid white; color:palegreen; font-size:14px; font-weight:bold;" align="center"><div id="time"></div></td>
+	       <td style="border:1px solid white; color:palegreen; font-size:14px;" align="center">Measurement</td>
+               <td style="border:1px solid white; color:palegreen; font-size:14px;" align="center">Source</td>
+	       <td style="border:1px solid white; color:palegreen; font-size:14px;" align="center">Value</td>
+	    </tr>
+	 </table>
+      </div>
+	
+      <div id="footer_bot">
+         <p>&copy; 2014 FACT Project, Implemented by Western Mindanao State University (<strong><a href="http://www.wmsu.edu.ph">WMSU</a>,Philippines</strong>)</p>
+      </div>
+   </div>
+   <div id="debug"></div>
+</div>
+<div id="Overlay" name="Overlay" class="overlay">
+    <div style="padding-top:25px;padding-bottom:15px;padding-left:25%;padding-right:25%;">
+    <div style="background-color:black;color:white;padding:15px;">
+    <div style="color:white;small-caps;"><span id="close" class="close">X</span> To close click here or press Esc.</div>
+    <BR>
+    <!--<hr><br>-->
+    <H1>HELP</H1>
+    <div style="font-size:17px;padding-top:5px;font-family:'Times New Roman';text-align:justify;">
+    
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Loading a schedule</H3>
+    To load a schedule click on the corresponding date in the calendar. If
+    you click on <B>SAVE</B> the schedule will be saved to the date shown in the box close to the
+    <B>SAVE</B> button. It is always identical with the last calender date you clicked.
+    <p>
+    
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Replacing a schedule</H3>
+    You can overwrite a schedule with the schedule from a different date by entering the
+    date in the box close to <B>Load Night</B> and either pressing Enter or the <B>Load Night</B>
+    button.
+    <p>
+
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Disabled entries</H3>
+    Entries which are at the time the schedule is loaded already in the past, are 
+    disabled and cannot be changed. This is because they might already have been processed
+    by the scheduler and are currently executed.
+
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Color of times</H3>
+    For the current night, entries which are before the current time are
+    displayed in red, entries which are later, are displayed in green. Colors are updated
+    interactively.
+
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Scheduling</H3>
+    Note that the last entry display in red will most probably the first one
+    executed by the scheduler. If the scheduling is running, it will just replace
+    the next entry in the schedule and be executed completely. If the scheduler is not
+    running, still the last red entry will be the first one to be executed, but since
+    it is the first one executed by the script, the script assumes that it failed and
+    was restarted. Therefore, only the last of its observations (usually <I>data</I>
+    is executed.
+
+    <H3 style="font-family:arial;padding-top:20px;padding-bottom:10px;">Options</H3>
+    Options are ecoded as JSON object (search google for details). The meaning of
+    their properties is defined by the task to be executed. The starting and
+    closing brace must be omitted. Note that for a correct JSON object, the property
+    names must be enclosed in double quotation marks ("...").
+    </div>
+    </div>
+    </div>
+</div>
+
+</body>
+
+</html>
Index: trunk/FACT++/www/schedule/index.js
===================================================================
--- trunk/FACT++/www/schedule/index.js	(revision 17701)
+++ trunk/FACT++/www/schedule/index.js	(revision 17701)
@@ -0,0 +1,744 @@
+'use strict';
+
+function pad(n)
+{
+    return (n<10) ? '0' + n : n;
+}
+
+function getYMD(d)
+{
+    return d.getUTCFullYear()+'-'+pad(d.getUTCMonth()+1)+'-'+pad(d.getUTCDate());
+}
+
+function updateClock()
+{
+    var now = new Date();
+    var s = getYMD(now)+' '+pad(now.getUTCHours())+':'+pad(now.getUTCMinutes())+' UTC';
+    $('#clock').html(s);
+
+    var table = document.getElementById("TableHolder");
+
+    if (!table.isTonight)
+        return;
+
+    var rows = table.childNodes;
+
+    for (var i=2; i<rows.length; i++)
+    {
+        var el = rows[i].firstChild.firstChild;
+
+        // FIXME: replace by classes?
+        if (!el.valueAsDate)
+        {
+            el.setAttribute("style", "color:#000000");
+            return;
+        }
+
+        var t0 = now.getTime()%86400000;
+        var t1 = el.valueAsDate%86400000;
+        if (t1<43200000)
+            t1 += 86400000;
+
+        el.setAttribute("style", t1>t0 ? "color:darkgreen" : "color:darkred");
+    }
+};
+
+function addEmptyRow(prev, start)
+{
+    var empty =
+    {
+        fStart: "0000/00/00 "+(start?start:""),
+        fMeasurementTypeKey:0,
+        fMeasurementID:0,
+        fSourceKEY:0,
+    };
+
+    addRow(empty, false, prev);
+}
+
+function debug(txt)
+{
+    var dbg = document.getElementById("debug");
+    dbg.appendChild(document.createTextNode(txt));
+    dbg.appendChild(document.createElement("br"));
+}
+
+function addRow(row, disabled, prev)
+{
+    var table = document.getElementById("TableHolder");
+
+    var sources = table.sources;
+    var measurements = table.measurements;
+
+    var tr = document.createElement("tr");
+    tr.setAttribute("width", "100%");
+
+    if (!prev)
+        table.appendChild(tr);
+    else
+        table.insertBefore(tr, prev.nextSibling);
+
+    // ---------- column 1 ----------
+
+    var input1t = document.createElement("input");
+    input1t.setAttribute("type","time");
+    input1t.setAttribute("size","10");
+    input1t.setAttribute("autofocus","true");
+    if (row.fMeasurementID!=0)
+        input1t.setAttribute("hidden", "true");
+    input1t.setAttribute("value", row.fStart.substr(11));
+    if (disabled)
+        input1t.setAttribute("disabled", "true");
+
+    /*
+    input1t.onblur = function()
+    {
+        var prevRow = tr.previousSibling;
+        while (prevRow && prevRow.firstChild.firstChild.hidden)
+            prevRow = prevRow.previousSibling;
+
+        var nextRow = tr.nextSibling;
+        while (nextRow && nextRow.firstChild.firstChild.hidden)
+            nextRow = nextRow.nextSibling;
+
+        var prevEl = prevRow ? prevRow.firstChild : undefined;
+        var nextEl = nextRow ? nextRow.firstChild : undefined;
+
+        var prevTime = "";
+        if (prevEl && prevEl.firstChild.constructor.name=="HTMLInputElement")
+            prevTime = prevEl.firstChild.value;
+
+        var nextTime = "";
+        if (nextEl && nextEl.firstChild.constructor.name=="HTMLInputElement")
+            nextTime = nextEl.firstChild.value;
+
+        alert(prevTime+"/"+input1t.value+"/"+nextTime);
+    }*/
+
+    var td1 = document.createElement("td");
+    td1.setAttribute("style","white-space:nowrap");
+    td1.setAttribute("align","center");
+    td1.appendChild(input1t);
+
+    // Check if this is the transition from disabled to enabled.
+    // In this case enable all previous [+]
+    if (tr.previousSibling && tr.previousSibling.firstChild.firstChild.disabled && !disabled)
+    {
+        var prevRow = tr.previousSibling;
+
+        var tm = prevRow.firstChild.firstChild.value;
+        while (1)
+        {
+            prevRow.firstChild.childNodes[1].removeAttribute("disabled");
+            prevRow = prevRow.previousSibling;
+            if (prevRow.firstChild.firstChild.value!=tm)
+                break;
+        }
+    }
+
+    var input1p = document.createElement("input");
+    input1p.setAttribute("style","width:20px");
+    input1p.setAttribute("type","button");
+    input1p.setAttribute("value","+");
+    if (row.fMeasurementID!=0)
+        input1p.setAttribute("hidden", "true");
+    // FIXME: Enable if this is the last "+" in tonight
+    if (disabled)
+        input1p.setAttribute("disabled", "true");
+    input1p.onclick = function()
+    {
+        // FiXME: Do not allow deleting of last line
+        var nextRow = tr;
+        while (nextRow.nextSibling)
+        {
+            if (!nextRow.nextSibling.firstChild.firstChild.hidden)
+                break;
+
+            nextRow = nextRow.nextSibling;
+        }
+
+        addEmptyRow(nextRow, input1t.value);
+    }
+    td1.appendChild(input1p);
+
+    var input1m = document.createElement("input");
+    input1m.setAttribute("style","width:20px");
+    input1m.setAttribute("type","button");
+    input1m.setAttribute("value","-");
+    if (row.fMeasurementID!=0)
+        input1m.setAttribute("hidden", "true");
+    if (disabled)
+        input1m.setAttribute("disabled", "true");
+    input1m.onclick = function()
+    {
+        //if (table.childNodes.length==3)
+        //    return;
+
+        var nextRow = tr.nextSibling;
+        table.removeChild(tr);
+
+        while (nextRow)
+        {
+            var inp = nextRow.firstChild.firstChild;
+            if (!inp.hidden)
+                break;
+
+            var xx = nextRow;
+            nextRow = nextRow.nextSibling;
+            table.removeChild(xx);
+        }
+
+        if (table.childNodes.length==2)
+            addEmptyRow();
+    }
+    td1.appendChild(input1m);
+
+    tr.appendChild(td1);
+
+    // ---------- column 2 -----------
+
+    var select2 = document.createElement("select");
+    // select2.setAttribute("style", "width:100px");
+    select2.setAttribute("class", "measurement");
+    if (disabled)
+        select2.setAttribute("disabled", "true");
+
+    for (var i=0; i<measurements.length; i++)
+    {
+        var option = document.createElement("option");
+        select2.appendChild(option);
+        option.setAttribute('value', measurements[i].key);
+        option.appendChild(document.createTextNode(measurements[i].val));
+        if (row.fMeasurementTypeKey==measurements[i].key)
+            select2.selectedIndex = i;
+    }
+
+    var td2 = document.createElement("td");
+    td1.setAttribute("style","white-space:nowrap");
+    td2.setAttribute("align","center");
+    td2.appendChild(select2);
+
+    var input2 = document.createElement("input");
+    input2.setAttribute("type","button");
+    input2.setAttribute("value","+");
+    input2.setAttribute("style","width:20px");
+    if (disabled)
+        input2.setAttribute("disabled", "true");
+    input2.onclick = function()
+    {
+        var empty =
+        {
+            fStart: row.fStart,
+            fMeasurementTypeKey:0,
+            fMeasurementID:-1,
+            fSourceKEY:0,
+        };
+
+        addRow(empty, false, tr);
+    }
+    td2.appendChild(input2);
+
+    input2 = document.createElement("input");
+    input2.setAttribute("type","button");
+    input2.setAttribute("style","width:20px");
+    input2.setAttribute("value","-");
+    if (disabled)
+        input2.setAttribute("disabled", "true");
+    input2.onclick = function()
+    {
+        //if (table.childNodes.length==3)
+        //    return;
+
+        if (!tr.firstChild.childNodes[0].hidden)
+        {
+            var nextRow = tr.nextSibling;
+            if (nextRow)
+            {
+                var e = nextRow.firstChild.childNodes;
+                e[0].removeAttribute("hidden");
+                e[1].removeAttribute("hidden");
+                e[2].removeAttribute("hidden");
+            }
+        }
+        table.removeChild(tr);
+
+        if (table.childNodes.length==2)
+            addEmptyRow();
+    }
+    td2.appendChild(input2);
+
+    tr.appendChild(td2);
+
+    // ---------- column 3 -----------
+
+    var select3 = document.createElement("select");
+    //select3.setAttribute("style", "width:100px");
+    select3.setAttribute("class", "sources");
+    if (disabled)
+        select3.setAttribute("disabled", "true");
+
+    for (var i=0; i<sources.length; i++)
+    {
+        var option = document.createElement("option");
+        select3.appendChild(option);
+        option.setAttribute('value', sources[i].key);
+        option.appendChild(document.createTextNode(sources[i].val));
+        if (row.fSourceKey==sources[i].key)
+            select3.selectedIndex = i;
+    }
+
+    var td3 = document.createElement("td");
+    td3.setAttribute("style","white-space:nowrap");
+    td3.setAttribute("align","center");
+    td3.appendChild(select3);
+
+    tr.appendChild(td3);
+
+    // ---------- column 4 ------------
+
+    var input4 = document.createElement("input");
+    input4.setAttribute("type","text");
+    input4.setAttribute("class","selected");
+    input4.setAttribute("size","63%");
+    input4.setAttribute("placeholder","JSON object");
+    if (row.fData)
+        input4.setAttribute("value",row.fData);
+    if (disabled)
+        input4.setAttribute("disabled","true");
+
+    var td4 = document.createElement("td");
+    //td4.setAttribute("width","50%");
+    td4.setAttribute("align","center");
+    td4.appendChild(input4);
+
+    tr.appendChild(td4);
+}
+
+function setupCalendar(result, date)
+{
+    debug("cal="+date);
+
+    date = date.replace('-','');
+    date = date.replace('-','');
+
+    var dates = { };
+
+    for (var i=0; i<result.length; i++)
+    {
+        result[i].d = result[i].d.replace('-','');
+        result[i].d = result[i].d.replace('-','');
+
+        dates[result[i].d] = { klass: "highlight", tooltip: "Schedule set." };
+    }
+
+    dates[date] = { klass: "selected", tooltip: "Currently selected." };
+
+    function getDateInfo(date, wantsClassName)
+    {
+        var as_number = Calendar.dateToInt(date);
+        return dates[as_number];
+    };
+
+    var cont = document.getElementById("cont");
+    while (cont.firstChild)
+        cont.removeChild(cont.firstChild);
+
+    var setup =
+    {
+        cont: "cont",
+        selectionType: Calendar.SEL_MULTIPLE,
+        bottomBar: false,
+        date:parseInt(date),
+        dateInfo:getDateInfo
+    };
+
+    debug("cal.date="+date);
+
+    var cal = Calendar.setup(setup); 
+    cal.addEventListener("onSelect", function(){ loadDay(this.selection.print("%Y-%m-%d")); });
+}
+
+
+function onDataReceived(result)
+{
+    var table = document.getElementById("TableHolder");
+    if (!table.currentDay)
+        return;
+
+    // Split the results of the different queries
+    // They are separated by newlines
+    var data = result.split('\\n');
+    if (data.length<5)
+    {
+        alert("Malformed result returned["+data.length+"]:\n"+data[data.length-1]);
+        return;
+    }
+
+    debug("table.currentDay="+table.currentDay);
+
+    // Decode the results into variables
+
+    // Does that work in all browsers, or do we need "YYYY-MM-DDTHH:MM:SSZ" ?
+    //data[0] = data[0].replace('-', '/');
+    //data[0] = data[0].replace('-', '/');
+
+    // year, month, day, hours, minutes, seconds, milliseconds
+    var tonight      = new String(table.currentDay);
+    var day          = new Date(table.currentDay);
+    var dates        = JSON.parse(data[0]);
+    var sources      = JSON.parse(data[1]);
+    var measurements = JSON.parse(data[2]);
+    var schedule     = JSON.parse(data[3]);
+
+    if (data[4])
+        alert(data[4]);
+
+    var ld = document.getElementById("loaddate");
+    ld.setAttribute("size","10");
+
+    ld.value = table.prevDay;
+
+    // First update the calender
+    setupCalendar(dates, tonight);
+
+    // Add a fake source to the list of sources to allow 'deselection' of source
+    sources.splice(0, 0, { key: 0, val: "---" });
+
+    table.sources = sources;
+    table.measurements = measurements;
+
+    // Enable or disable the SAVE and LoadPrev button
+    var save  = document.getElementById("save");
+    var load  = document.getElementById("load");
+    var ldate = document.getElementById("loaddate");
+
+    if (day.getTime()+36*3600*1000>table.loadTime)
+    {
+        save.removeAttribute("disabled");
+        load.removeAttribute("disabled");
+        ldate.removeAttribute("disabled");
+
+
+        // If this is a dayin the future, but no schedule is in the db,
+        // create an empty one
+        if (schedule.length==0)
+            addEmptyRow();
+    }
+    else
+    {
+        save.setAttribute("disabled", "true");
+        load.setAttribute("disabled", "true");
+        ldate.setAttribute("disabled", "true");
+    }
+
+    // Update the header of the date/time column
+    var tm = document.getElementById("time");
+    while (tm.firstChild)
+        tm.removeChild(tm.firstChild);
+
+    var nxt = new Date(day.getTime()+24*3600*1000);
+
+    var d1 = pad(day.getUTCMonth()+1)+'-'+pad(day.getUTCDate());
+    var d2 = pad(nxt.getUTCMonth()+1)+'-'+pad(nxt.getUTCDate());
+
+    tm.appendChild(document.createTextNode(d1+ " / "+d2));
+
+    var offset = new Date(table.loadedDay).getTime();
+
+    // other day loaded and tonight
+
+    if (!table.isTonight || !table.loadedDay)
+        table.cutTime = "12:00:00";
+
+    // Now loop over all rows and add them oine by one to the table
+    for (var i=0; i<schedule.length; i++)
+    {
+        schedule[i].fStart = schedule[i].fStart.replace('-', '/');
+        schedule[i].fStart = schedule[i].fStart.replace('-', '/');
+
+        var stamp = new Date(schedule[i].fStart+" UTC");
+
+        if (table.loadedDay)
+            stamp = new Date(stamp.getTime()+day.getTime()-offset);
+
+        var disabled = stamp.getTime()<table.loadTime && !table.loadedDay;
+        if (disabled)
+            table.cutTime = pad(stamp.getUTCHours())+":"+pad(stamp.getUTCMinutes())+":"+pad(stamp.getUTCSeconds());
+
+        addRow(schedule[i], disabled);
+    }
+
+    debug("currentDay="+table.currentDay);
+    debug("nextDay="+table.nextDay);
+    debug("isTonight="+table.isTonight);
+    debug("cutTime="+table.cutTime);
+}
+
+function loadDay(date, dateToLoad)
+{
+    // Clean elements from table before new elements are added
+    var table = document.getElementById("TableHolder");
+
+    // In very rare cases (fast and frequent clicks on a date with a long schedule)
+    // the event listened of the calender returns a wrong value
+    if (new String(date).length!=10)
+        return;
+
+    var dbg = document.getElementById("debug");
+    while (dbg.firstChild)
+        dbg.removeChild(dbg.firstChild);
+
+    var count = 0;
+
+    var cut;
+    while (table.childNodes.length>2)
+    {
+        var cols = table.lastChild.childNodes;
+        var time = cols[0].firstChild;
+
+        if (time.disabled)
+        {
+            debug("disabled="+time.value);
+
+            cut = time.value;
+            if (dateToLoad)
+                break;
+        }
+
+        count++;
+        table.removeChild(table.lastChild);
+    }
+
+    debug(count+" lines removed ");
+
+    document.getElementById("savedate").value = date;
+
+    // it helps to know if this is tonight or not
+    var now   = new Date();
+    var day   = new Date(date);
+    var night = getYMD(new Date(now.getTime()-12*3600*1000));
+
+    table.isTonight  = date==night;
+    table.currentDay = date;
+    table.loadedDay  = dateToLoad;
+    table.loadTime   = now.getTime();
+
+    if (!table.isTonight || !table.loadedDay)
+        table.cutTime = undefined;
+
+    // remember the currently displayed day (FIXME: Move to table property?)
+    table.prevDay = getYMD(new Date(day.getTime()-24*3600*1000));
+    table.nextDay = getYMD(new Date(day.getTime()+24*3600*1000));
+
+    debug("day="+date+"|"+date.length);
+    debug("dayToLoad="+table.loadedDay);
+    debug("cut="+cut);
+
+    var data = "n="+(dateToLoad?dateToLoad:date);
+    if (cut && (!table.isTonight || table.loadedDay))
+        data += "&t="+cut;
+
+    debug("data="+data);
+
+    // request data from the datanbase and on reception, display the data
+    $.ajax({
+        type:    "POST",
+        cache:   false,
+        url:     "load.php",
+        data:    data,
+        success: onDataReceived,
+        error:   function(xhr) { if (xhr.status==0) alert("ERROR[0] - Request failed!"); else alert("ERROR[0] - "+xhr.statusText+" ["+xhr.status+"]"); }
+    });
+}
+
+function onLoad()
+{
+    /*---------------------------------------------------------------------------------------------
+     Prevent any user interaction during AJAX requests
+     ----------------------------------------------------------------------------------------------*/
+
+    $(document).ajaxStart(function() { $('#wait').fadeIn(); }).ajaxStop(function() { $('#wait').fadeOut(200); });
+
+    /*---------------------------------------------------------------------------------------------
+     Check if a dedicated day was requested, if not start with the current day.
+     Load the data from the database and display the calendar and the data.
+     ----------------------------------------------------------------------------------------------*/
+
+    var reg = /^\?day=(20[123][0-9]-[01][0-9]-[0123][0-9])/;
+    var res = reg.exec(location.search);
+
+    var day;
+    if (!res)
+    {
+        var now = new Date();
+
+        if (now.getUTCHours()<12)
+            now = new Date(now.getTime()-12*3600*1000);
+
+        day = getYMD(now);
+    }
+    else
+        day = res[1];
+
+    loadDay(day);
+
+    /*---------------------------------------------------------------------------------------------
+     Start updating the clock
+     ----------------------------------------------------------------------------------------------*/
+
+    updateClock();
+    setInterval(updateClock, 1000);
+
+    /*---------------------------------------------------------------------------------------------
+     Loading of previous data. Get the previous date with existing data through PreviousData.php
+     Same Shedule.ph passed to load on the controls with extra parameter 'prev' to indicate that
+     data is from previous schedule.
+     ----------------------------------------------------------------------------------------------*/
+
+    // FIXME: Do not overwrite the disabled part of the schedule if it is TONIGHT!!!!
+
+    function onLoad()
+    {
+        var cd = document.getElementById("TableHolder");
+        var dt = document.getElementById("loaddate");
+        loadDay(cd.currentDay, dt.value);
+    }
+
+    $('#load').click(onLoad);
+    $('#loaddate').keypress(function(event) { if (event.which==13) { onLoad(); } event.preventDefault(); });
+
+    /*---------------------------------------------------------------------------------------------
+     Savng and updating of schedule. Data array is generated from the current table to be submitted
+     to saveSchedule.php for the execution of queries.
+     ----------------------------------------------------------------------------------------------*/
+
+    function onSaveClick()
+    {
+        var table = document.getElementById("TableHolder");
+
+        var rows = table.childNodes;
+
+        var schedule = [];
+
+        // cutTime is the last time of a measurement which is past
+        // and should not be updated. This assumes that all
+        // time values are sequential.
+        //var cutTime  = "12:00:00";
+        //var prevTime = new Date(currentDay+" 12:00:00");
+
+        // FIXME: Make sure dates are sequentiel
+
+        for (var i=2; i<rows.length; i++)
+        {
+            var cols = rows[i].childNodes;
+
+            var time     = cols[0].firstChild.value;
+            var measure  = cols[1].firstChild.value;
+            var source   = cols[2].firstChild.value;
+            var value    = cols[3].firstChild.value;
+            var hidden   = cols[0].firstChild.hidden;
+            var disabled = cols[0].firstChild.disabled;
+
+            if (!hidden && !time)
+            {
+                alert("ERROR - Invalid time fields detected.");
+                return;
+            }
+
+            /*
+            if (!hidden)
+            {
+                // This is just to check the times... theoretically,
+                // these times could be sent, so that the php does not
+                // have to do that again, or should the php check things
+                // to ensure that it cannot be hacked?
+                var t = time;
+                t = t.replace(':','');
+                t = t.replace(':','');
+
+                t = new Date(t<120000 ? currentDay+" "+time : nextDay+" "+time);
+
+                if (t.getTime()<prevTime.getTime())
+                {
+                    alert("Times not sequential... cannot save schedule.");
+                    return;
+                }
+
+                prevTime = t;
+            }
+
+            if (disabled)
+            {
+                // if (cutTime>time) // no time yet!
+                cutTime = time;
+                continue;
+            }*/
+
+            if (disabled)
+                continue;
+
+            if (hidden)
+                time = null;
+
+            schedule.push([ time, measure, source, value ]);
+        }
+
+        if (schedule.length==0)
+        {
+            alert("Schedule empty - nothing to be saved.");
+            return;
+        }
+
+        //alert(table.isTonight+"/"+table.cutTime+"/"+schedule.length);
+
+        var data = "n="+table.currentDay+"&d="+JSON.stringify(schedule);
+        if (table.isTonight)
+            data += "&t="+table.cutTime;
+
+        $.ajax({
+           type:    "POST",
+           cache:   false,
+           url:     "save.php",
+           data:    data,
+           success: function(result) { if (result.length==0) { /*alert("Success.");*/ loadDay(table.currentDay); } else alert("ERROR - "+result); },
+           error:   function(xhr) { if (xhr.status==0) alert("ERROR[1] - Unauthorized!"); else alert("ERROR[1] - "+xhr.statusText+" ["+xhr.status+"]"); }
+        });
+    }
+
+    $('#save').click(onSaveClick);
+
+
+    function onHelp()
+    {
+        var ov  = $("#Overlay");
+        var pos = $("#help").offset();
+        //var doc = $(document);
+        ov.css({
+           left:   pos.left + 'px',
+           top:    pos.top + 'px',
+           width:  0,
+           height: 0
+        })
+        .show()
+        .animate({
+           left:   0,
+           top:    0,
+           width:  '100%',
+           height: '100%'
+        }, "slow");
+
+        event.preventDefault();
+    }
+
+    function onClose()
+    {
+        $("#Overlay").hide("slow");
+    }
+
+    $('#help').click(onHelp);
+    $(document).keydown(function(event) { if (event.which==27) { onClose(); event.preventDefault(); } });
+    $('#close').click(onClose);
+
+}
+
+$('document').ready(onLoad);
Index: trunk/FACT++/www/schedule/load.php
===================================================================
--- trunk/FACT++/www/schedule/load.php	(revision 17701)
+++ trunk/FACT++/www/schedule/load.php	(revision 17701)
@@ -0,0 +1,63 @@
+<?php
+
+
+if (!isset($_POST['n']))
+   return header('HTTP/1.0 400 Syntax error.');
+
+// ================================================================
+//                       Database interaction
+// ================================================================
+
+function query($query)
+{
+   $sql = mysql_query($query);
+   if (!$sql)
+   	die(mysql_error());
+
+   $rows = array();
+   while($row = mysql_fetch_assoc($sql))
+      $rows[] = $row;
+
+   print(json_encode($rows).'\n');
+}
+
+// ----------------------------------------------------------------
+
+$cut = "";
+if (isset($_POST['t']))
+{
+    $cut = $_POST['n'].' '.$_POST['t'];
+
+    $d = new DateTime($cut);
+    if ($d->format("His")<120000)
+    {
+        $d->add(new DateInterval('P1D'));  // PnYnMnDTnHnMnS
+        $cut = $d->format("Y-m-d H:i:s");
+    }
+
+    $cut = " AND fStart>'".$cut."' ";
+}
+
+// ----------------------------------------------------------------
+
+require_once 'config.php';
+
+$db = mysql_connect($dbhost,$dbuser,$dbpass);
+if (!$db)
+   die(mysql_error());
+
+if (!mysql_select_db($dbname, $db))
+   die(mysql_error());
+
+/*mixed date_sunrise ( int $timestamp [, int $format = SUNFUNCS_RET_STRING [, float $latitude = ini_get("date.default_latitude") [, float $longitude = ini_get("date.default_longitude") [, float $zenith = ini_get("date.sunrise_zenith") [, float $gmt_offset = 0 ]]]]] )*/
+
+query("SELECT DISTINCT(DATE(ADDTIME(fStart, '-12:00'))) AS 'd' FROM Schedule");
+query("SELECT fSourceKEY AS 'key', fSourceName AS 'val' FROM Source WHERE fSourceTypeKEY=1");
+query("SELECT fMeasurementKEY AS 'key', fMeasurement AS 'val' FROM measurement");
+query("SELECT * FROM Schedule WHERE DATE(ADDTIME(fStart, '-12:00:00')) = '".$_POST['n']."'".$cut."ORDER BY fScheduleID ASC, fMeasurementID ASC");
+
+//sleep(3);
+
+//print($test1."|".$test2."|".$test3."|SELECT * FROM Schedule WHERE DATE(ADDTIME(fStart, '-12:00:00')) = '".$_POST['day']."'".$cut."ORDER BY fScheduleID ASC, fMeasurementID ASC\n");
+
+?>
Index: trunk/FACT++/www/schedule/save.php
===================================================================
--- trunk/FACT++/www/schedule/save.php	(revision 17701)
+++ trunk/FACT++/www/schedule/save.php	(revision 17701)
@@ -0,0 +1,237 @@
+<?php
+
+if (!isset($_POST['n']) || !isset($_POST['d']))
+    return header('HTTP/1.0 400 Syntax error.');
+
+require_once 'config.php';
+
+function login()
+{
+    global $ldaphost;
+    global $baseDN;
+    global $groupDN;
+
+    $username = $_SERVER['PHP_AUTH_USER'];
+    $password = $_SERVER['PHP_AUTH_PW'];
+
+    if (!isset($username) || !isset($password))
+        return "Unauthorized.";
+
+    $con = @ldap_connect($ldaphost);
+    if (!$con)
+        return "ldap_connect failed to ".$ldaphost;
+
+    //------------------ Look for user common name
+    $attributes = array('cn', 'mail');
+    $dn         = 'ou=People,'.$baseDN;
+    $filter     = '(uid='.$username.')';
+
+    $sr = @ldap_search($con, $dn, $filter, $attributes);
+    if (!$sr)
+        return "ldap_search failed for dn=".$dn.": ".ldap_error($con);
+
+    $srData = @ldap_get_entries($con, $sr);
+    if ($srData["count"]==0)
+        return "No results returned by ldap_get_entries for dn=".$dn.".";
+
+    $email         =$srData[0]['mail'][0];
+    $userCommonName=$srData[0]['cn'][0];
+    $userDN        =$srData[0]['dn'];
+
+    //------------------ Authenticate user
+    if (!@ldap_bind($con, $userDN, $password))
+        return "ldap_bind failed: ".ldap_error($con);
+
+    //------------------ Check if the user is in FACT ldap group
+    $attributes= array("member");
+    $filter= '(objectClass=*)';
+
+    // Get all members of the group.
+    $sr = @ldap_read($con, $groupDN, $filter, $attributes);
+    if (!$sr)
+        return "ldap_read failed for dn=".$groupDN.": ".ldap_error($con);
+
+    // retrieve the corresponding data
+    $srData = @ldap_get_entries($con, $sr);
+    if ($srData["count"]==0)
+        return "No results returned by ldap_get_entries for dn=".$dn.".";
+
+    @ldap_unbind($con);
+
+    $found = false;
+    foreach ($srData[0]['member'] as $member)
+        if (strpos($member, "cn=".$userCommonName.",")===0)
+            return "";
+
+    return "Authorization failed.";
+}
+
+// --------------------------------------------------------------------
+
+if (isset($_GET['logout']))
+{
+    if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))
+        return;
+
+    return header('HTTP/1.0 401 Successfull logout!');
+}
+
+// --------------------------------------------------------------------
+
+$rc = login();
+if ($rc!="")
+{
+    header('WWW-Authenticate: Basic realm="FACT Schedule"');
+    header('HTTP/1.0 401 '.$rc);
+}
+
+// ====================================================================
+
+// This is the day/night from which the data is to be deleted
+// and to which the data is to be submitted
+$day  = $_POST['n'];
+
+// This is the time of the last diabled entry (or the time from which
+// on the data should be deleted/submitted)
+// Note that there is no sanity check yet, therefore the data and the
+// time variable must be consistent
+$time = isset($_POST['t']) ? $_POST['t'] : "12:00:00";
+
+// The data to be submitted
+$data = json_decode($_POST['d']);
+
+// Get user
+$user = $_SERVER['PHP_AUTH_USER'];
+
+// FIXME: Make sure that the date is valid (in the future)?
+
+// ----------------------------------------------------------------
+
+// Calculate the date for the next day, to have the correct
+//date after midnight as well
+$date = new DateTime($day);
+$date->add(new DateInterval('P1D'));  // PnYnMnDTnHnMnS
+$nextDay = $date->format('Y-m-d');
+
+// ----------------------------------------------------------------
+
+// Calculate the lower limit from which on data should be deleted.
+// This is either noon (if the date is in the future) or the provided
+// time (different from 12:00:00) during the night
+$cut = $day." ".$time;
+
+$d = new DateTime($cut);
+
+// If the time lays before noon, it belongs to the next day
+if ($d->format("His")<120000)
+{
+    $d->add(new DateInterval('P1D'));  // PnYnMnDTnHnMnS
+    $cut = $d->format("Y-m-d H:i:s");
+}
+
+// ----------------------------------------------------------------
+
+// Now create the queries with the correct dates (date and time)
+// from the posted data and the times therein
+$queries = array();
+
+array_push($queries, "LOCK TABLES Schedule WRITE");
+array_push($queries, "DELETE FROM Schedule WHERE fStart>'".$cut."' AND DATE(ADDTIME(fStart, '-12:00')) = '".$day."'");
+
+// ----------------------------------------------------------------
+
+foreach ($data as $row)
+{
+    $t = $row[0]; // time
+    $m = $row[1]; // measurement
+    $s = $row[2]; // source
+    $v = $row[3]; // value
+
+    // If there is a time set, remember the time, if not
+    // this is just a measurement within an observation
+    // so duplicate the time
+    if (!isset($t))
+    {
+        $t = $save;
+        $id++;
+    }
+    else
+    {
+        $save = $t;
+        $id = 0;
+    }
+
+    // Check if the time is before noon. If it is before noon,
+    // it belongs to the next day
+    $d = date_parse($t);
+    $t = $d['hour']<12 ? $nextDay." ".$t : $day." ".$t;
+
+    if (!isset($last))
+    {
+        $last = $t;
+    }
+    else
+    {
+        if ($last>$t)
+            die("Times not sequential\n[".$last."|".$t."]");
+        $last = $t;
+    }
+
+    // Compile query
+    $query = "INSERT INTO Schedule SET";
+    $query .= " fStart='".$t."'";
+    $query .= ",fMeasurementID=".$id;
+    $query .= ",fMeasurementTypeKey=".$m;
+    $query .= ",fUser='".$user."'";
+    if ($s>0)
+        $query .= ",fSourceKey=".$s;
+
+    // Check if this is a valid JSON object
+    if (!json_decode('{'.$v.'}'))
+    {
+        switch (json_last_error())
+        {
+        case JSON_ERROR_NONE:             break;
+        case JSON_ERROR_DEPTH:            $err = 'Maximum stack depth exceeded'; break;
+        case JSON_ERROR_STATE_MISMATCH:   $err = 'Invalid or malformed JSON';    break;
+        case JSON_ERROR_CTRL_CHAR:        $err = 'Unexpected control character'; break;
+        case JSON_ERROR_SYNTAX:           $err = 'Syntax error';                 break;
+        case JSON_ERROR_UTF8:             $err = 'Malformed UTF-8 characters';   break;
+        default:                          $err = 'Unknown error';                break;
+        }
+
+        if (isset($err))
+            die($err." at ".$t." [entry #".($id+1)."]:\n".$v);
+    }
+
+    // PHP >= 5.5.0
+    // if (!json_decode('{'.$v.'}'))
+    //    die("Invalid option at ".$t.": ".$v." [JSON - ".json_last_error_msg()."]");
+
+
+    $query .= ",fData='".$v."'";
+
+    // add query to the list of queries
+    array_push($queries, $query);
+}
+
+array_push($queries, "UNLOCK TABLES");
+
+// ================================================================
+//                       Database interaction
+// ================================================================
+
+$db = mysql_connect($dbhost,$dbuser,$dbpass);
+if (!$db)
+    die(mysql_error());
+
+if (!mysql_select_db($dbname, $db))
+    die(mysql_error());
+
+foreach ($queries as $query)
+    if (!mysql_query($query))
+        die(mysql_error());
+
+mysql_close($db);
+
+?>
