• Edit
  • Download
  • <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <title>ZingSoft Demo</title>
    
      <script nonce="undefined" src="https://cdn.zingchart.com/zingchart.min.js"></script>
      <style>
        html,
        body {
          height: 100%;
          width: 100%;
          margin: 0;
          padding: 0;
        }
    
        #myChart {
          height: 100%;
          width: 100%;
          min-height: 700px;
          position: relative;
          z-index: 500;
        }
    
        #crosshair-image {
          position: absolute;
          z-index: 1000;
          left: 908px;
          height: 50px;
          width: 50px;
          background-image: transparent;
          background-repeat: no-repeat;
        }
    
        /* split up the images into 4 moon phases 25px 25px pngs. You can crop your own */
    
        .phase_1 {
          background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_1.png");
        }
    
        .phase_2 {
          background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_2.png");
        }
    
        .phase_3 {
          background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_3.png");
        }
    
        .phase_4 {
          background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_4.png");
        }
      </style>
    </head>
    
    <body>
      <div id="myChart">
        <div id="crosshair-image"></div>
      </div>
      <script>
        ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"]; /* Globals */
        var MILLISECONDS_IN_A_DAY = 86400000;
        var MILLISECONDS_IN_A_HOUR = 3600000;
        var MILLISECONDS_IN_A_MINUTE = 60000;
        var MILLISECONDS_IN_A_SECOND = 1000;
        var START_OF_2016_TIMESTAMP = 1451635200000;
        //var START_OF_2017_TIMESTAMP = 1451635200;
        var currentYearTimestamp = START_OF_2016_TIMESTAMP;
        var currentStringYear = '2016';
        var DEGREES_SYMBOL = '°';
        var MONTH_LABELS = ['Jan 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Feb 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Mar 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Apr 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'May 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'June 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'July 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Aug 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Sep 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Oct 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Nov 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Dec 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']; //length is 366
        var SCALE_MARKER_COLOR = '#bdbdbd';
        var SCALE_MARKER_LINE_WIDTH = 1;
        var SCALE_MARKER_ALPHA = .7;
        var SCALE_MARKERS = [ // scale markers used for more precise vertical guidelines
          {
            type: 'line',
            range: [31],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [60],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [91],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [121],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [152],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [182],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [213],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [244],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [274],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [305],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          },
          {
            type: 'line',
            range: [335],
            lineColor: SCALE_MARKER_COLOR,
            lineWidth: SCALE_MARKER_LINE_WIDTH,
            alpha: SCALE_MARKER_ALPHA
          }
        ];
    
        var myConfig = {
          layout: '3x1',
          graphset: [{ // start sunrise/sunset graph
              id: 'sunrise_sunset_chart',
              type: 'line',
              utc: true,
              plot: {
                maxTrackers: 1000, // max amount of nodes to be drawn. Also applies event listeners to 1000 nodes so faster if this is off
                highlightState: { // legend hover line state
                  lineWidth: 5
                },
                marker: { // turn nodes off so its just the line
                  visible: false
                }
              },
              plotarea: {
                margin: '10 80 60 80'
              },
              legend: {
                highlightPlot: true, // hover highlight
                marginBottom: 0,
                marginRight: 80,
                layout: 'h'
              },
              scaleX: {
                minValue: currentYearTimestamp,
                maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
                labels: MONTH_LABELS,
                maxItems: 366, // dont allow more than this amount of items
                itemsOverlap: true, // force items to not disappear
                step: 'day',
                guide: { //hide the guide lines
                  visible: false
                },
                tick: { // hide ticks
                  visible: false
                },
                transform: {
                  type: 'date',
                  all: '%M %d'
                },
                label: {
                  text: 'Days'
                },
                markers: SCALE_MARKERS // these are your new guidelines
              },
              scaleY: {
                minValue: 19800000, // 05:30:00 am
                maxValue: 26100000, // 07:15:00 am
                step: (MILLISECONDS_IN_A_MINUTE * 15),
                maxItems: 8, // force 8 items
                itemsOverlap: true, // helps force 8 items
                lineColor: '#ff5722',
                tick: {
                  lineColor: '#ff5722'
                },
                guide: {
                  visible: false
                },
                label: {
                  text: 'Sunrise Time (AM)'
                },
                transform: {
                  type: 'date',
                  all: '%h:%i %A'
                }
              },
              scaleY2: {
                minValue: 63000000, // 05:30:00 pm
                maxValue: 69300000, // 07:15:00 pm
                step: (MILLISECONDS_IN_A_MINUTE * 15),
                maxItems: 8, // force 8 items
                itemsOverlap: true, // helps force 8 items
                lineColor: '#ff9800',
                tick: {
                  lineColor: '#ff9800'
                },
                guide: {
                  visible: false
                },
                label: {
                  text: 'Sunset Time (PM)'
                },
                transform: {
                  type: 'date',
                  all: '%h:%i %A'
                }
              },
              tooltip: {
                visible: false
              }, // turn off tooltip
              crosshairX: {
                shared: true,
                plotLabel: { // crosshair tooltip
                  headerText: '%kv ' + currentStringYear,
                  text: '<span style="color:%color">%t:</span> %vv',
                  fontSize: 15,
                  padding: 10,
                  borderRadius: 5
                },
                scaleLabel: {
                  visible: false
                }, // highlight x-axis off
                marker: { // highlight marker
                  type: 'circle',
                  size: 4
                }
              },
              series: []
            },
            {
              id: 'azimuth_chart',
              type: 'line',
              utc: true,
              plot: {
                maxTrackers: 1000,
                highlightState: {
                  lineWidth: 5
                },
                marker: {
                  visible: false
                }
              },
              plotarea: {
                margin: '10 80 60 80'
              },
              legend: {
                highlightPlot: true,
                marginBottom: 0,
                marginRight: 80,
                layout: 'h'
              },
              scaleX: {
                minValue: currentYearTimestamp,
                maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
                labels: MONTH_LABELS,
                maxItems: 366,
                itemsOverlap: true,
                step: 'day',
                guide: {
                  visible: false
                },
                tick: {
                  visible: false
                },
                transform: {
                  type: 'date',
                  all: '%M %d'
                },
                label: {
                  text: 'Days'
                },
                markers: SCALE_MARKERS
              },
              scaleY: {
                values: '60:120:10',
                maxItems: 7,
                itemsOverlap: true,
                format: '%v' + DEGREES_SYMBOL,
                guide: {
                  visible: false
                },
                lineColor: '#ff5722',
                tick: {
                  lineColor: '#ff5722'
                },
                label: {
                  text: 'Sunrise Direction'
                }
              },
              scaleY2: {
                values: '240:300:10',
                maxItems: 7,
                itemsOverlap: true,
                format: '%v' + DEGREES_SYMBOL,
                guide: {
                  visible: false
                },
                lineColor: '#ff9800',
                tick: {
                  lineColor: '#ff9800'
                },
                label: {
                  text: 'Sunset Direction'
                }
              },
              tooltip: {
                visible: false
              },
              crosshairX: {
                shared: true,
                plotLabel: {
                  headerText: '%kv ' + currentStringYear,
                  text: '<span style="color:%color">%t:</span> %v' + DEGREES_SYMBOL,
                  fontSize: 15,
                  padding: 10,
                  borderRadius: 5
                },
                scaleLabel: {
                  visible: false
                },
                marker: {
                  type: 'circle',
                  size: 4
                }
              },
              series: []
            }, // end azimuth graph
            { // start sunrise/sunset graph
              id: 'moonrise_moonset_chart',
              type: 'line',
              utc: true,
              plot: {
                maxTrackers: 1000,
                highlightState: {
                  lineWidth: 5
                },
                marker: {
                  visible: false
                }
              },
              plotarea: {
                margin: '10 80 60 80'
              },
              legend: {
                highlightPlot: true,
                marginBottom: 0,
                marginRight: 80,
                layout: 'h'
              },
              scaleX: {
                minValue: currentYearTimestamp,
                maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
                labels: MONTH_LABELS,
                maxItems: 366,
                itemsOverlap: true,
                step: 'day',
                guide: {
                  visible: false
                },
                tick: {
                  visible: false
                },
                transform: {
                  type: 'date',
                  all: '%M %d'
                },
                label: {
                  text: 'Days'
                },
                markers: SCALE_MARKERS
              },
              scaleX2: {
                minValue: currentYearTimestamp,
                maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
                maxItems: 400,
                itemsOverlap: true,
                step: 'day',
                guide: {
                  visible: false
                },
                tick: {
                  visible: false
                },
              },
              scaleY: {
                minValue: 0,
                maxValue: MILLISECONDS_IN_A_DAY,
                step: MILLISECONDS_IN_A_HOUR * 2,
                maxItems: 7,
                itemsOverlap: true,
                guide: {
                  visible: false
                },
                label: {
                  text: 'Moonrise Time'
                },
                transform: {
                  type: 'date',
                  all: '%h:%i %A'
                }
              },
              scaleY2: {
                minValue: 0,
                maxValue: MILLISECONDS_IN_A_DAY,
                step: MILLISECONDS_IN_A_HOUR * 2,
                maxItems: 7,
                itemsOverlap: true,
                guide: {
                  visible: false
                },
                label: {
                  text: 'Moonset Time'
                },
                transform: {
                  type: 'date',
                  all: '%h:%i %A'
                }
              },
              tooltip: {
                visible: false
              },
              crosshairX: {
                shared: true,
                plotLabel: {
                  headerText: '%kv ' + currentStringYear,
                  text: '<span style="color:%color">%t:</span> %vv',
                  fontSize: 15,
                  padding: 10,
                  borderRadius: 5,
                },
                scaleLabel: {
                  visible: false
                },
                marker: {
                  type: 'circle',
                  size: 4
                }
              },
              series: []
            }
          ]
        };
    
        zingchart.render({
          id: 'myChart',
          data: myConfig,
          height: '100%',
          width: '100%'
        });
    
        /*
         *
         * Example time: "113.63°"
         */
        function parseDegrees(sDegrees) {
          return Number(sDegrees.slice(0, sDegrees.length - 1));
        }
    
        /*
         *
         * Example time: "1, 5:50:56 AM"
         * returns milliseconds value
         */
        function parseStringToTimestamp(sTime) {
          if (sTime.length <= 1) {
            return null; // null value if blank cell 
          }
          /*
           * Y axis are plotted from 0 - 24hrs in milliseconds.
           * Since x-axis already plots day we don't need the exact timestamp
           * for this day of the year. Just the offset in milliseconds.
           *
           */
          var aTime = sTime.split(/\s/); // separate am/pm
          parsedTime = aTime[0]; // asign time
          parsedTime = parsedTime.split(':'); // split time
    
          if (aTime[1].toLowerCase() == 'pm') {
            if (Number(parsedTime[0]) !== 12)
              parsedTime[0] = Number(parsedTime[0]) + 12;
          } else { // if am
            if (Number(parsedTime[0]) === 12)
              parsedTime[0] = 0; // convert 12:00AM to Unix time
          }
          parsedTime[0] = Number(parsedTime[0]) * MILLISECONDS_IN_A_HOUR;
          parsedTime[1] = Number(parsedTime[1]) * MILLISECONDS_IN_A_MINUTE;
          parsedTime[2] = Number(parsedTime[2]) * MILLISECONDS_IN_A_SECOND;
          return parsedTime[0] + parsedTime[1] + parsedTime[2];
        }
    
    
        function parseDataAndRenderChart() {
          var arrayOfRows = this.responseText.split('\n');
          var azimuthSeries = [];
          var sunriseSunsetSeries = [];
          var moonriseMoonsetSeries = [];
    
          // turn rows as a string into an array
          arrayOfRows = arrayOfRows.map(function(obj) {
            return obj.split(',');
          });
    
          // initialize azimuth chart series
          azimuthSeries[0] = {
            text: 'Sunrise Direction',
            values: [],
            scales: 'scale-x, scale-y',
            lineColor: '#ff5722'
          };
          azimuthSeries[1] = {
            text: 'Sunset Direction',
            values: [],
            scales: 'scale-x, scale-y-2',
            lineColor: '#ff9800'
          };
    
          // initialize sunrise/sunset chart series
          sunriseSunsetSeries[0] = {
            text: 'Sunrise',
            values: [],
            scales: 'scale-x, scale-y',
            lineColor: '#ff5722'
          };
          sunriseSunsetSeries[1] = {
            text: 'Sunset',
            values: [],
            scales: 'scale-x, scale-y-2',
            lineColor: '#ff9800'
          };
    
          // initialize moonrise/moonset chart series
          moonriseMoonsetSeries[0] = {
            text: 'Moonrise',
            values: [],
            scales: 'scale-x, scale-y',
            lineColor: '#9e9e9e'
          };
          moonriseMoonsetSeries[1] = {
            text: 'Moonset',
            values: [],
            scales: 'scale-x, scale-y-2',
            lineColor: '#212121'
          };
    
    
          /*
           * ignore arrayOfRows[0] those are the titles
           * assign millisecond timestamp value and string value to float
           * [[]] array of arrays
           */
          for (var i = 1; i < arrayOfRows.length; i++) {
    
            // compile azimuth data
            azimuthSeries[0].values.push( // push to series 0
              parseDegrees(arrayOfRows[i][2])
            );
            azimuthSeries[1].values.push( // push to series 1
              parseDegrees(arrayOfRows[i][4])
            );
    
            // compile sunrise/sunset data
            sunriseSunsetSeries[0].values.push( // push to series 0
              parseStringToTimestamp(arrayOfRows[i][1])
            );
            sunriseSunsetSeries[1].values.push( // push to series 1
              parseStringToTimestamp(arrayOfRows[i][3])
            );
    
            // compile moonrise/moonset data
            moonriseMoonsetSeries[0].values.push( // push to series 0
              parseStringToTimestamp(arrayOfRows[i][5])
            );
            moonriseMoonsetSeries[1].values.push( // push to series 1
              parseStringToTimestamp(arrayOfRows[i][6])
            );
    
          }
    
    
          // load data into chart
          zingchart.exec('myChart', 'setseriesdata', {
            graphid: 'azimuth_chart',
            update: false,
            data: azimuthSeries
          });
          zingchart.exec('myChart', 'setseriesdata', {
            graphid: 'sunrise_sunset_chart',
            update: false,
            data: sunriseSunsetSeries
          });
          zingchart.exec('myChart', 'appendseriesdata', {
            graphid: 'moonrise_moonset_chart',
            update: false,
            data: moonriseMoonsetSeries
          });
          zingchart.exec('myChart', 'update');
        }
    
    
    
    
    
    
        // request data from url
        var oReq = new XMLHttpRequest();
        oReq.addEventListener("load", parseDataAndRenderChart);
        oReq.open("GET", 'https://app.zingsoft.com/api/file/GSNQC6UG/xqIP1B9ZRXiOmCCmLfCU_sun_moon_rise_azimuth.csv'); // exported google sheets to csv (thats it)
        oReq.send();
    
    
    
    
    
        /*
         * Event listener for crosshair and how to change image.
         * This will change the class (check CSS tab) on the div (positioned absolute over the chart)
         * it will update the image based on the day (nodeindex).
         *
         * eg. nodeindex 32 is february 1st
         */
        var imageContainer = null;
    
        function bindImageOnMove(e) {
          // event is fired for all 3 graphs. For speed only do this once.
          if (e.graphid === "moonrise_moonset_chart") {
            // easy way to cache the reference to the element
            imageContainer = (imageContainer != null) ? imageContainer : document.getElementById('crosshair-image');
    
            // used CSS modifications over chart modifcations for faster speed.
            imageContainer.style.left = e.guide.x + 'px';
    
            // if node index exists for at least one plot
            if (e.items[0] && e.items[0].nodeindex) {
              switch (e.items[0].nodeindex) { // nodeindex = day
                case 1:
                case 31:
                case 62:
                  imageContainer.className = "phase_1";
                  break;
                case 9:
                case 39:
                case 70:
                  imageContainer.className = "phase_2";
                  break;
                case 17:
                case 47:
                case 78:
                  imageContainer.className = "phase_3";
                  break;
                case 25:
                case 55:
                case 86:
                  imageContainer.className = "phase_4";
                  break;
                default:
                  imageContainer.className = ""; // remove image
              }
            }
          }
        }
    
        zingchart.bind('myChart', 'guide_mousemove', bindImageOnMove);
        /*
         * since the height of the chart is dynamic in my example (can resize window for chart size),
         * this function is used to dynamically set the height of the moon phase image on chart load (set it once)
         */
        zingchart.bind('myChart', 'complete', function(e) {
          // assign tooltip to top of graph by getting the bottom charts info
          var graphInfo = zingchart.exec('myChart', 'getobjectinfo', {
            graphid: 'moonrise_moonset_chart',
            object: 'graph'
          });
          imageContainer = document.getElementById('crosshair-image');
          imageContainer.style.top = (graphInfo.y + 10) + 'px';
        });
      </script>
    </body>
    
    </html>
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <title>ZingSoft Demo</title>
    
      <script src="https://cdn.zingchart.com/zingchart.min.js"></script>
    </head>
    
    <body>
      <div id="myChart">
        <div id="crosshair-image"></div>
      </div>
    </body>
    
    </html>
    html,
    body {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0;
    }
    
    #myChart {
      height: 100%;
      width: 100%;
      min-height: 700px;
      position: relative;
      z-index: 500;
    }
    
    #crosshair-image {
      position: absolute;
      z-index: 1000;
      left: 908px;
      height: 50px;
      width: 50px;
      background-image: transparent;
      background-repeat: no-repeat;
    }
    
    /* split up the images into 4 moon phases 25px 25px pngs. You can crop your own */
    
    .phase_1 {
      background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_1.png");
    }
    
    .phase_2 {
      background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_2.png");
    }
    
    .phase_3 {
      background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_3.png");
    }
    
    .phase_4 {
      background-image: url("//demos.zingchart.com/view/67J40ZDF/moon_phase_4.png");
    }
    /* Globals */
    var MILLISECONDS_IN_A_DAY = 86400000;
    var MILLISECONDS_IN_A_HOUR = 3600000;
    var MILLISECONDS_IN_A_MINUTE = 60000;
    var MILLISECONDS_IN_A_SECOND = 1000;
    var START_OF_2016_TIMESTAMP = 1451635200000;
    //var START_OF_2017_TIMESTAMP = 1451635200;
    var currentYearTimestamp = START_OF_2016_TIMESTAMP;
    var currentStringYear = '2016';
    var DEGREES_SYMBOL = '°';
    var MONTH_LABELS = ['Jan 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Feb 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Mar 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Apr 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'May 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'June 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'July 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Aug 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Sep 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Oct 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Nov 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'Dec 1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']; //length is 366
    var SCALE_MARKER_COLOR = '#bdbdbd';
    var SCALE_MARKER_LINE_WIDTH = 1;
    var SCALE_MARKER_ALPHA = .7;
    var SCALE_MARKERS = [ // scale markers used for more precise vertical guidelines
      {
        type: 'line',
        range: [31],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [60],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [91],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [121],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [152],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [182],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [213],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [244],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [274],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [305],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      },
      {
        type: 'line',
        range: [335],
        lineColor: SCALE_MARKER_COLOR,
        lineWidth: SCALE_MARKER_LINE_WIDTH,
        alpha: SCALE_MARKER_ALPHA
      }
    ];
    
    var myConfig = {
      layout: '3x1',
      graphset: [{ // start sunrise/sunset graph
          id: 'sunrise_sunset_chart',
          type: 'line',
          utc: true,
          plot: {
            maxTrackers: 1000, // max amount of nodes to be drawn. Also applies event listeners to 1000 nodes so faster if this is off
            highlightState: { // legend hover line state
              lineWidth: 5
            },
            marker: { // turn nodes off so its just the line
              visible: false
            }
          },
          plotarea: {
            margin: '10 80 60 80'
          },
          legend: {
            highlightPlot: true, // hover highlight
            marginBottom: 0,
            marginRight: 80,
            layout: 'h'
          },
          scaleX: {
            minValue: currentYearTimestamp,
            maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
            labels: MONTH_LABELS,
            maxItems: 366, // dont allow more than this amount of items
            itemsOverlap: true, // force items to not disappear
            step: 'day',
            guide: { //hide the guide lines
              visible: false
            },
            tick: { // hide ticks
              visible: false
            },
            transform: {
              type: 'date',
              all: '%M %d'
            },
            label: {
              text: 'Days'
            },
            markers: SCALE_MARKERS // these are your new guidelines
          },
          scaleY: {
            minValue: 19800000, // 05:30:00 am
            maxValue: 26100000, // 07:15:00 am
            step: (MILLISECONDS_IN_A_MINUTE * 15),
            maxItems: 8, // force 8 items
            itemsOverlap: true, // helps force 8 items
            lineColor: '#ff5722',
            tick: {
              lineColor: '#ff5722'
            },
            guide: {
              visible: false
            },
            label: {
              text: 'Sunrise Time (AM)'
            },
            transform: {
              type: 'date',
              all: '%h:%i %A'
            }
          },
          scaleY2: {
            minValue: 63000000, // 05:30:00 pm
            maxValue: 69300000, // 07:15:00 pm
            step: (MILLISECONDS_IN_A_MINUTE * 15),
            maxItems: 8, // force 8 items
            itemsOverlap: true, // helps force 8 items
            lineColor: '#ff9800',
            tick: {
              lineColor: '#ff9800'
            },
            guide: {
              visible: false
            },
            label: {
              text: 'Sunset Time (PM)'
            },
            transform: {
              type: 'date',
              all: '%h:%i %A'
            }
          },
          tooltip: {
            visible: false
          }, // turn off tooltip
          crosshairX: {
            shared: true,
            plotLabel: { // crosshair tooltip
              headerText: '%kv ' + currentStringYear,
              text: '<span style="color:%color">%t:</span> %vv',
              fontSize: 15,
              padding: 10,
              borderRadius: 5
            },
            scaleLabel: {
              visible: false
            }, // highlight x-axis off
            marker: { // highlight marker
              type: 'circle',
              size: 4
            }
          },
          series: []
        },
        {
          id: 'azimuth_chart',
          type: 'line',
          utc: true,
          plot: {
            maxTrackers: 1000,
            highlightState: {
              lineWidth: 5
            },
            marker: {
              visible: false
            }
          },
          plotarea: {
            margin: '10 80 60 80'
          },
          legend: {
            highlightPlot: true,
            marginBottom: 0,
            marginRight: 80,
            layout: 'h'
          },
          scaleX: {
            minValue: currentYearTimestamp,
            maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
            labels: MONTH_LABELS,
            maxItems: 366,
            itemsOverlap: true,
            step: 'day',
            guide: {
              visible: false
            },
            tick: {
              visible: false
            },
            transform: {
              type: 'date',
              all: '%M %d'
            },
            label: {
              text: 'Days'
            },
            markers: SCALE_MARKERS
          },
          scaleY: {
            values: '60:120:10',
            maxItems: 7,
            itemsOverlap: true,
            format: '%v' + DEGREES_SYMBOL,
            guide: {
              visible: false
            },
            lineColor: '#ff5722',
            tick: {
              lineColor: '#ff5722'
            },
            label: {
              text: 'Sunrise Direction'
            }
          },
          scaleY2: {
            values: '240:300:10',
            maxItems: 7,
            itemsOverlap: true,
            format: '%v' + DEGREES_SYMBOL,
            guide: {
              visible: false
            },
            lineColor: '#ff9800',
            tick: {
              lineColor: '#ff9800'
            },
            label: {
              text: 'Sunset Direction'
            }
          },
          tooltip: {
            visible: false
          },
          crosshairX: {
            shared: true,
            plotLabel: {
              headerText: '%kv ' + currentStringYear,
              text: '<span style="color:%color">%t:</span> %v' + DEGREES_SYMBOL,
              fontSize: 15,
              padding: 10,
              borderRadius: 5
            },
            scaleLabel: {
              visible: false
            },
            marker: {
              type: 'circle',
              size: 4
            }
          },
          series: []
        }, // end azimuth graph
        { // start sunrise/sunset graph
          id: 'moonrise_moonset_chart',
          type: 'line',
          utc: true,
          plot: {
            maxTrackers: 1000,
            highlightState: {
              lineWidth: 5
            },
            marker: {
              visible: false
            }
          },
          plotarea: {
            margin: '10 80 60 80'
          },
          legend: {
            highlightPlot: true,
            marginBottom: 0,
            marginRight: 80,
            layout: 'h'
          },
          scaleX: {
            minValue: currentYearTimestamp,
            maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
            labels: MONTH_LABELS,
            maxItems: 366,
            itemsOverlap: true,
            step: 'day',
            guide: {
              visible: false
            },
            tick: {
              visible: false
            },
            transform: {
              type: 'date',
              all: '%M %d'
            },
            label: {
              text: 'Days'
            },
            markers: SCALE_MARKERS
          },
          scaleX2: {
            minValue: currentYearTimestamp,
            maxValue: currentYearTimestamp + (MILLISECONDS_IN_A_DAY * 365),
            maxItems: 400,
            itemsOverlap: true,
            step: 'day',
            guide: {
              visible: false
            },
            tick: {
              visible: false
            },
          },
          scaleY: {
            minValue: 0,
            maxValue: MILLISECONDS_IN_A_DAY,
            step: MILLISECONDS_IN_A_HOUR * 2,
            maxItems: 7,
            itemsOverlap: true,
            guide: {
              visible: false
            },
            label: {
              text: 'Moonrise Time'
            },
            transform: {
              type: 'date',
              all: '%h:%i %A'
            }
          },
          scaleY2: {
            minValue: 0,
            maxValue: MILLISECONDS_IN_A_DAY,
            step: MILLISECONDS_IN_A_HOUR * 2,
            maxItems: 7,
            itemsOverlap: true,
            guide: {
              visible: false
            },
            label: {
              text: 'Moonset Time'
            },
            transform: {
              type: 'date',
              all: '%h:%i %A'
            }
          },
          tooltip: {
            visible: false
          },
          crosshairX: {
            shared: true,
            plotLabel: {
              headerText: '%kv ' + currentStringYear,
              text: '<span style="color:%color">%t:</span> %vv',
              fontSize: 15,
              padding: 10,
              borderRadius: 5,
            },
            scaleLabel: {
              visible: false
            },
            marker: {
              type: 'circle',
              size: 4
            }
          },
          series: []
        }
      ]
    };
    
    zingchart.render({
      id: 'myChart',
      data: myConfig,
      height: '100%',
      width: '100%'
    });
    
    /*
     *
     * Example time: "113.63°"
     */
    function parseDegrees(sDegrees) {
      return Number(sDegrees.slice(0, sDegrees.length - 1));
    }
    
    /*
     *
     * Example time: "1, 5:50:56 AM"
     * returns milliseconds value
     */
    function parseStringToTimestamp(sTime) {
      if (sTime.length <= 1) {
        return null; // null value if blank cell 
      }
      /*
       * Y axis are plotted from 0 - 24hrs in milliseconds.
       * Since x-axis already plots day we don't need the exact timestamp
       * for this day of the year. Just the offset in milliseconds.
       *
       */
      var aTime = sTime.split(/\s/); // separate am/pm
      parsedTime = aTime[0]; // asign time
      parsedTime = parsedTime.split(':'); // split time
    
      if (aTime[1].toLowerCase() == 'pm') {
        if (Number(parsedTime[0]) !== 12)
          parsedTime[0] = Number(parsedTime[0]) + 12;
      } else { // if am
        if (Number(parsedTime[0]) === 12)
          parsedTime[0] = 0; // convert 12:00AM to Unix time
      }
      parsedTime[0] = Number(parsedTime[0]) * MILLISECONDS_IN_A_HOUR;
      parsedTime[1] = Number(parsedTime[1]) * MILLISECONDS_IN_A_MINUTE;
      parsedTime[2] = Number(parsedTime[2]) * MILLISECONDS_IN_A_SECOND;
      return parsedTime[0] + parsedTime[1] + parsedTime[2];
    }
    
    
    function parseDataAndRenderChart() {
      var arrayOfRows = this.responseText.split('\n');
      var azimuthSeries = [];
      var sunriseSunsetSeries = [];
      var moonriseMoonsetSeries = [];
    
      // turn rows as a string into an array
      arrayOfRows = arrayOfRows.map(function(obj) {
        return obj.split(',');
      });
    
      // initialize azimuth chart series
      azimuthSeries[0] = {
        text: 'Sunrise Direction',
        values: [],
        scales: 'scale-x, scale-y',
        lineColor: '#ff5722'
      };
      azimuthSeries[1] = {
        text: 'Sunset Direction',
        values: [],
        scales: 'scale-x, scale-y-2',
        lineColor: '#ff9800'
      };
    
      // initialize sunrise/sunset chart series
      sunriseSunsetSeries[0] = {
        text: 'Sunrise',
        values: [],
        scales: 'scale-x, scale-y',
        lineColor: '#ff5722'
      };
      sunriseSunsetSeries[1] = {
        text: 'Sunset',
        values: [],
        scales: 'scale-x, scale-y-2',
        lineColor: '#ff9800'
      };
    
      // initialize moonrise/moonset chart series
      moonriseMoonsetSeries[0] = {
        text: 'Moonrise',
        values: [],
        scales: 'scale-x, scale-y',
        lineColor: '#9e9e9e'
      };
      moonriseMoonsetSeries[1] = {
        text: 'Moonset',
        values: [],
        scales: 'scale-x, scale-y-2',
        lineColor: '#212121'
      };
    
    
      /*
       * ignore arrayOfRows[0] those are the titles
       * assign millisecond timestamp value and string value to float
       * [[]] array of arrays
       */
      for (var i = 1; i < arrayOfRows.length; i++) {
    
        // compile azimuth data
        azimuthSeries[0].values.push( // push to series 0
          parseDegrees(arrayOfRows[i][2])
        );
        azimuthSeries[1].values.push( // push to series 1
          parseDegrees(arrayOfRows[i][4])
        );
    
        // compile sunrise/sunset data
        sunriseSunsetSeries[0].values.push( // push to series 0
          parseStringToTimestamp(arrayOfRows[i][1])
        );
        sunriseSunsetSeries[1].values.push( // push to series 1
          parseStringToTimestamp(arrayOfRows[i][3])
        );
    
        // compile moonrise/moonset data
        moonriseMoonsetSeries[0].values.push( // push to series 0
          parseStringToTimestamp(arrayOfRows[i][5])
        );
        moonriseMoonsetSeries[1].values.push( // push to series 1
          parseStringToTimestamp(arrayOfRows[i][6])
        );
    
      }
    
    
      // load data into chart
      zingchart.exec('myChart', 'setseriesdata', {
        graphid: 'azimuth_chart',
        update: false,
        data: azimuthSeries
      });
      zingchart.exec('myChart', 'setseriesdata', {
        graphid: 'sunrise_sunset_chart',
        update: false,
        data: sunriseSunsetSeries
      });
      zingchart.exec('myChart', 'appendseriesdata', {
        graphid: 'moonrise_moonset_chart',
        update: false,
        data: moonriseMoonsetSeries
      });
      zingchart.exec('myChart', 'update');
    }
    
    
    
    
    
    
    // request data from url
    var oReq = new XMLHttpRequest();
    oReq.addEventListener("load", parseDataAndRenderChart);
    oReq.open("GET", 'https://app.zingsoft.com/api/file/GSNQC6UG/xqIP1B9ZRXiOmCCmLfCU_sun_moon_rise_azimuth.csv'); // exported google sheets to csv (thats it)
    oReq.send();
    
    
    
    
    
    /*
     * Event listener for crosshair and how to change image.
     * This will change the class (check CSS tab) on the div (positioned absolute over the chart)
     * it will update the image based on the day (nodeindex).
     *
     * eg. nodeindex 32 is february 1st
     */
    var imageContainer = null;
    
    function bindImageOnMove(e) {
      // event is fired for all 3 graphs. For speed only do this once.
      if (e.graphid === "moonrise_moonset_chart") {
        // easy way to cache the reference to the element
        imageContainer = (imageContainer != null) ? imageContainer : document.getElementById('crosshair-image');
    
        // used CSS modifications over chart modifcations for faster speed.
        imageContainer.style.left = e.guide.x + 'px';
    
        // if node index exists for at least one plot
        if (e.items[0] && e.items[0].nodeindex) {
          switch (e.items[0].nodeindex) { // nodeindex = day
            case 1:
            case 31:
            case 62:
              imageContainer.className = "phase_1";
              break;
            case 9:
            case 39:
            case 70:
              imageContainer.className = "phase_2";
              break;
            case 17:
            case 47:
            case 78:
              imageContainer.className = "phase_3";
              break;
            case 25:
            case 55:
            case 86:
              imageContainer.className = "phase_4";
              break;
            default:
              imageContainer.className = ""; // remove image
          }
        }
      }
    }
    
    zingchart.bind('myChart', 'guide_mousemove', bindImageOnMove);
    /*
     * since the height of the chart is dynamic in my example (can resize window for chart size),
     * this function is used to dynamically set the height of the moon phase image on chart load (set it once)
     */
    zingchart.bind('myChart', 'complete', function(e) {
      // assign tooltip to top of graph by getting the bottom charts info
      var graphInfo = zingchart.exec('myChart', 'getobjectinfo', {
        graphid: 'moonrise_moonset_chart',
        object: 'graph'
      });
      imageContainer = document.getElementById('crosshair-image');
      imageContainer.style.top = (graphInfo.y + 10) + 'px';
    });