Index: /trunk/src/cptms/cptms_map.html
===================================================================
--- /trunk/src/cptms/cptms_map.html	(revision 293)
+++ /trunk/src/cptms/cptms_map.html	(revision 302)
@@ -2,7 +2,12 @@
 <html>
   <head>
+<!-- Launch with  python -m CGIHTTPServer 8080  -->
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
-    <title>CPTMS Map v0.5.5</title> 
+    <title>CPTMS Map v0.5.6</title> 
     <style>
+        @font-face {
+          font-family: Scoreboard;
+          src: url('scoreboard.ttf');
+        }
       /* Set the size of the div element that contains the map */
       #mapdiv {
@@ -16,4 +21,19 @@
         padding: 0;
       }
+        textarea {
+           height: 33px;
+           width: 272px;
+           resize: none;
+           font-family: Scoreboard;
+           font-size: xx-large;
+           background-color: #2F4F4F;
+           color: yellow;
+        }
+       input {
+           border: thin solid #333;
+           padding: 2px;
+           font-family: monospace;
+           font-size: large;
+        }
       #search-input {
         background-color: #17263c;  /* #CD853F;  /*#E6E6FA; /* lavender */
@@ -40,9 +60,72 @@
         cursor: pointer;
       }
+      #cms-info-label {
+           height: 20px;
+           width: 540px;
+           overflow: hidden;
+           background-color: #A8C5FF;  /*#ECECFB; */
+           border: thin solid #BDBDBD;
+           padding: 5px;
+       }
+      #message-display {
+           height: 172px;
+           width: 300px;
+           overflow: hidden;
+           float: left;
+       }
+      #message-input {
+           height: 122px;
+           width: 135px;
+           background-color: #729FFF;
+           float: left;
+       }
+       #buttonPanel {
+           height: 122px;
+           width: 100px;
+           background-color: #729FFF;
+           border-left: none;
+           float: left;
+           padding: 20px;
+        }
+       .wrapper {
+           position: relative;\
+        }
+       #dialog {
+          position: absolute;
+          top: 10%;
+          right: 20%;
+          background-color: #729FFF; /* #ECECFB; */
+          margin: auto;
+          padding: 20px;
+          border: 1px solid #888;
+          width: 52%;
+          display: none;           
+        }
+      .unstyled-button {
+        border: 0 none;
+        padding: 0;
+        background: none;
+        cursor: pointer;
+      }    
+    /* The Close Button */
+    .close {
+      color: orange;
+      float: right;
+      font-size: 20px;
+      font-weight: bold;
+    }
+    .close:hover,
+    .close:focus {
+      color: red;
+      text-decoration: none;
+      cursor: pointer;
+    }
 
     </style>
   </head>
   <body>
-    <!-- Version 5.5 renames title to CPTMS, loads static data on startup and dynamic data
+    <!-- 
+         Version 5.6 integrates CMS features
+         Version 5.5 renames title to CPTMS, loads static data on startup and dynamic data
          every ten seconds.  
          Version 5.4 adds Search box and Center button
@@ -61,6 +144,33 @@
     <!--The div element where the map appears -->
     <div id="mapdiv"></div>
-    <!--The div element where the center button appears -->
-    <div id="ctrButton">&#x2295;</div>
+    <!--The div element where the buttons appears -->
+    <div id="ctrButton" class="unstyled-button">&#x2295;</div>
+    <button id="cmsButton" class="unstyled-button"><img id="cmsBtnImg" src="images/CPTMSImages/btnDepressed_CMS.png"></button>
+    <button id="vdsButton" class="unstyled-button"><img id="vdsBtnImg" src="images/CPTMSImages/btnDepressed_VDS.png"></button>
+    <!-- The div element for the popup dialog -->
+    <div id="dialog" style="display:none;">
+        <span class="close">&times;</span>
+        <br>
+        <div id="cms-info-label" style="font-family:monospace">CMS ID: xxx LOCATION: </div>
+        <br>
+        <div><pre>Proposed:                              Current:</pre></div>
+        <input id='cmsID' value="" type='hidden'/>
+        <div id="message-input">
+        <input id="msgcontent1" size="16" maxlength="16" type="text"/><br><br>
+        <input id="msgcontent2" size="16" maxlength="16"/><br><br>
+        <input id="msgcontent3" size="16" maxlength="16"/>
+        </div>        
+        <div id="buttonPanel"    style="display: block;">
+        <button onclick="handleSubmit();">Send >></button><br>
+        <button onclick="handleClear();">Clear >></button><br>
+        <button onclick="handleClose();">Close </button>
+        </div>
+        <div id="message-display"  style="display: block;">
+         <textarea readonly id="msgdisplay1" maxlength="16" rows="1" cols="16"></textarea>
+         <textarea readonly id="msgdisplay2" maxlength="16" rows="1" cols="16"></textarea>
+         <textarea readonly id="msgdisplay3" maxlength="16" rows="1" cols="16"></textarea>
+        </div>
+    </div>
+
     <script>
     // a global variable for the google map
@@ -84,4 +194,12 @@
     // white means a disabled spot
     var colorZvalues = {"white":5,"lime":10,"yellow":20,"red":30};
+var kMapStartupFile = "cmsStatusD12.json";
+var blueFlag = "images/CPTMSImages/icon_cmsBlue.png";
+var yellowFlag = "images/CPTMSImages/icon_cmsYellow.png";
+var messageList;
+var cms_info;
+var markerList = [];
+var cms_showing = true;
+var vds_showing = true;
 
     // Build a solid colored icon to use instead of the classic pin
@@ -197,25 +315,27 @@
      }
 
-    // Load the dynamic highways file via ajax
+    // Load the dynamic json file for highways, etc.
     // Ref: https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript
-     function loadJSON(callback) {   
-
-        var xobj = new XMLHttpRequest();
-            xobj.overrideMimeType("application/json");
-        xobj.open('GET', kMapPointsFile, true); 
-        xobj.onreadystatechange = function () {
-              if (xobj.readyState == 4 && xobj.status == "200") {
-                // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
-                callback(xobj.responseText);
-              }
-        };
-        xobj.send(null);  
-     }
-
+function loadJSON(inFile, callback)
+{
+    var xobj = new XMLHttpRequest();
+    xobj.overrideMimeType("application/json");
+    xobj.open('GET', inFile, true);
+    xobj.onreadystatechange = function()
+    {
+        if (xobj.readyState == 4 && xobj.status == "200")
+        {
+            callback(xobj.responseText);
+        }
+    };
+    // We want ajax to ignore any cached responses
+    xobj.setRequestHeader('If-Modified-Since', 'Sat, 01 Jan 2000 01:01:01 GMT')
+    xobj.send(null);
+}
      // Load the highways dynamic json file and update the map
      function updateMap()
      {
         var parsed_JSON;
-        loadJSON(function(response)
+        loadJSON(kMapPointsFile,function(response)
         {
             // Parse JSON string into object
@@ -313,4 +433,204 @@
           placePins = [];
     }
+// Initialize the view/hide buttons 
+function initButton()
+{
+    var cmsBtnDiv = document.getElementById('cmsButton');
+    map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(cmsBtnDiv)
+    cmsBtnDiv.title = 'Click to toggle cms view';
+
+    // Setup the click event listeners to toggle icon display
+    cmsBtnDiv.addEventListener('click', function() {
+            cms_showing = !cms_showing;
+            // reveal or hide all the icons
+            for (var i = 0; i < markerList.length; i++)
+            {
+                markerList[i].setVisible(cms_showing);
+            }
+            // Determine which button image to show
+            if (cms_showing)
+            {
+                pic = "images/CPTMSImages/btnDepressed_CMS.png"
+            }
+            else
+            {
+                pic = "images/CPTMSImages/btnReady_CMS.png"
+            }
+            document.getElementById('cmsBtnImg').src=pic;
+    });
+    var vdsBtnDiv = document.getElementById('vdsButton');
+    map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(vdsBtnDiv)
+    vdsBtnDiv.title = 'Click to toggle vds view';
+
+    // Setup the click event listeners to toggle icon display
+    vdsBtnDiv.addEventListener('click', function() {
+            vds_showing = !vds_showing;
+            // reveal or hide all the icons
+            alert("Not implemented yet")
+            // Determine which button image to show
+            if (vds_showing)
+            {
+                pic = "btnDepressed_VDS.png"
+            }
+            else
+            {
+                pic = "btnReady_VDS.png"
+            }
+            document.getElementById('vdsBtnImg').src=pic;
+    });
+}
+function setMarkers()
+{
+    var simpleImage = "";
+    loadcmsJSON(function(response)
+    {
+        // Parse JSON string into object
+        cms_info = JSON.parse(response);
+        console.log(cms_info.data[0].cms);
+        // Process each new marker 
+        for (var i = 0; i < cms_info.data.length; i++)
+        {
+            var cms = cms_info.data[i].cms;
+            var currLat = Number(cms.location.latitude);
+            var currLong = Number(cms.location.longitude);
+            // load a yellow flag if there's currently no message
+            if (messageList[i] == "||")
+                simpleImage = yellowFlag;
+            else
+                simpleImage = blueFlag;
+            var directionCode = cms.location.direction.charAt(0);
+            var locationInfo = directionCode + " " + cms.location
+                .route + " " + cms.location.postmile + " " + cms
+                .location.locationName
+            markerList[i] = new google.maps.Marker(
+            {
+                position:
+                {
+                    lat: currLat,
+                    lng: currLong
+                },
+                map: map,
+                icon: simpleImage,
+                title: "#"+i+" " +locationInfo,
+                cmsid: "" + i,
+                location: locationInfo
+            });
+            google.maps.event.addListener(markerList[i], 'click',
+                function()
+                {
+                    var dialog = document.getElementById('dialog');
+                    dialog.style.display = 'block';
+                    // fetch the sequential msg #
+                    cmsID = Number(this.cmsid);
+                    // Assign to the hidden field
+                    document.getElementById('cmsID').value = cmsID;
+                    getMessage(cmsID); // note: this is async
+                    document.getElementById('cms-info-label').innerHTML = "CMS ID: " +
+                        cmsID + "&nbsp;&nbsp;&nbsp;LOCATION: " + this.location;
+                    // clear input fields
+                    document.getElementById('msgcontent1').value = "";
+                    document.getElementById('msgcontent2').value = "";
+                    document.getElementById('msgcontent3').value = "";
+                    document.getElementById('msgcontent1').focus();
+                    var span = document.getElementsByClassName("close")[0]
+                    // When the user clicks on <span> (x), close the modal
+                    span.onclick = function() {
+                      handleClose();
+                    }
+                });
+        }
+    });
+}
+
+function loadcmsJSON(callback)
+{
+    var xobj = new XMLHttpRequest();
+    xobj.overrideMimeType("application/json");
+    xobj.open('GET', kMapStartupFile, true);
+    xobj.onreadystatechange = function()
+    {
+        if (xobj.readyState == 4 && xobj.status == "200")
+        {
+            callback(xobj.responseText);
+        }
+    };
+    // We want ajax to ignore any cached responses
+    xobj.setRequestHeader('If-Modified-Since', 'Sat, 01 Jan 2000 01:01:01 GMT')
+    xobj.send(null);
+}
+
+function handleSubmit()
+{
+    // recover the user's response
+    var response1 = document.getElementById('msgcontent1').value;
+    var response2 = document.getElementById('msgcontent2').value;
+    var response3 = document.getElementById('msgcontent3').value;
+    var newMsg = response1+response2+response3;
+    if (newMsg.length == 0)
+    {
+        alert("Nothing to Send ... Proposed is empty.");
+    }
+    else
+    {
+        document.getElementById('msgdisplay1').value = response1;
+        document.getElementById('msgdisplay2').value = response2;
+        document.getElementById('msgdisplay3').value = response3;
+        saveMessage(response1 + "|" + response2 + "|" + response3);
+    }
+}
+
+function handleClose()
+{
+    // hide the display
+    document.getElementById('dialog').style.display = 'none'
+}
+
+function handleClear()
+{
+    document.getElementById('msgdisplay1').value = "";
+    document.getElementById('msgdisplay2').value = "";
+    document.getElementById('msgdisplay3').value = "";
+    saveMessage("||");
+}
+// retrieve the current cms message file
+function getMessage(cmsID)
+{
+    loadJSON("http://localhost:8080/messagefile.txt", function(response)
+    {
+        // Parse JSON string into object
+        messageList = JSON.parse(response);
+        // select a message from json for the given cmsID
+        console.log("get by cmsID=" + cmsID);
+        var cmsSign = document.getElementById('msgdisplay1');
+        messageparts = messageList[cmsID].split("|");
+        cmsSign.value = messageparts[0];
+        document.getElementById('msgdisplay2').value = messageparts[1];
+        document.getElementById('msgdisplay3').value = messageparts[2];
+    });
+}
+// Save an updated cms message to the file
+// NB: cms id's are one-based, json array is zero-based.
+function saveMessage(outMessage, cmsID)
+{
+    var cmsID = document.getElementById('cmsID').value;
+    console.log("Saving " + outMessage + " for cmsID " + cmsID)
+    messageList[cmsID] = outMessage;
+    // Change icon if something was saved
+    if (outMessage == "||")
+        markerList[cmsID].setIcon(yellowFlag);
+    else
+        markerList[cmsID].setIcon(blueFlag);
+
+    var xhttp = new XMLHttpRequest();
+    xhttp.open("GET", "http://localhost:8080/cgi-bin/saveMessage.py?msg=" + JSON
+        .stringify(messageList), true);
+    xhttp.send();
+    // Using POST might be a better idea ... haven't tried this yet
+    //      var xhr = new XMLHttpRequest();
+    //      xhr.open("POST", "/cgi-bin/saveMessage.py?", true);
+    //      xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
+    // send the collected data as JSON
+    //      xhr.send(JSON.stringify(messageList));
+} 
 
     // Initialize the map and load the points
@@ -329,4 +649,7 @@
         initSearch();
         initCenter();
+        initButton();
+        getMessage(1); // load the current message file
+        setMarkers();
 
         loadMapData();  // go load the map data
