//Declaring global variables representing DOM elements which will be used throughout the application

var mapDom = document.getElementById('qd30map-cesium');
var placeholderDom = document.getElementById('loading-placeholder');
var navContainer = document.getElementById('nav-ui-wrap');
var navParent = document.getElementById('nav-arrows');
var navReset = document.getElementById('nav-reset');
var mapSearchBtn = document.getElementById('map-search');
var navElements = navParent.getElementsByTagName('button');
var searchForm = document.getElementById('map-ui-form');
var appContainer = document.getElementById('map-search');

//These will represent the coordinates of the App and Form, which will be used in centering functions (this is
//especially helpful for mobile use
var appX = null;
var appY = null;
var formX= null;
var formY = null;


navElements[0].tabIndex = 3;
navElements[1].tabIndex = 4;
navElements[2].tabIndex = 5;
navElements[3].tabIndex = 6;
navReset.tabIndex = 7;
mapSearchBtn.tabIndex = 8;
//Iterate over the navigation elements, add listener and add the appropriate camera function
for (var i=0; i < navElements.length; i++) {
  navElements[i].addEventListener('mousedown', function(param) {
    switch(param.target.id) {
      case 'nav-up':
        camera.moveUp(100);
        break;

      case 'nav-down':
        camera.moveDown(100);
        break;

      case 'nav-left':
        camera.moveLeft(100);
        break;

      case 'nav-right':
        camera.moveRight(100);
        break;
    }
  });
  navElements[i].addEventListener('keydown', function(param) {
    switch(param.target.id) {
      case 'nav-up':

        if (param.keyCode == 13) {
          camera.moveUp(100);
        }
        break;

      case 'nav-down':
        if (param.keyCode == 13) {
          camera.moveDown(100);
        }
        break;

      case 'nav-left':
        if (param.keyCode == 13) {
          camera.moveLeft(100);
        }
        break;

      case 'nav-right':
        if (param.keyCode == 13) {
          camera.moveRight(100);
        }
        break;
    }
  });
//Reset the view
  navReset.onclick = function(param) {
    gotoQD30();
  };
  navReset.onkeyup = function(param) {
    if (param.keyCode == 13) {
      gotoQD30();
    }
  }
}
//Center the form -> especially for mobile users, as the map will take up the entire screen
mapSearchBtn.onclick = function() {
  centerForm();
};

mapSearchBtn.onkeydown = function(param) {
  if (param.keyCode == 13) {
    centerForm();
  }
};

var zoomParent = document.getElementById('zoom');

var zoomElements = zoomParent.getElementsByTagName('button');
zoomElements[0].tabIndex = 1;
zoomElements[1].tabIndex = 2;


for (var a=0; a < zoomElements.length; a++) {
  zoomElements[a].addEventListener('mousedown', function(param) {
    var y = camera.position.y;

    switch(param.target.id) {
      case 'zoom-in':
        camera.moveForward(250);
        break;

      case 'zoom-out':
        camera.moveBackward(250);
        break;
    }
  });

  zoomElements[a].addEventListener('keydown', function(param) {
    var y = camera.position.y;
    switch(param.target.id) {
      case 'zoom-in':
        if (param.keyCode == 13) {
          camera.moveForward(100);
        }
        break;

      case 'zoom-out':
        if (param.keyCode == 13) {
          camera.moveBackward(100);
        }
        break;
    }
  });
}
//To check version
// console.log(Cesium.VERSION);

Cesium.BingMapsApi.defaultKey = 'mqrOOSbN2SMpDuWhHx0W~HVi_IW2A0UwRAV1xLIZuTQ~AnSezO-xLZ4_s1rf8ydB6Wf0aRdKtw_znPffJQ9qKbntyOPWFvitPVAvxT0v6dib';
//Instantiate the viewer. This is the primary object from which most functionality is derived
var viewer = new Cesium.Viewer('qd30map-cesium', {
  animation : false,
  homeButton : false,
  vrButton : false,
  infoBox : true,
  geocoder : false,
  sceneModePicker: false,
  selectionIndicator: true,
  timeline : false,
  navigationHelpButton : false,
  navigationInstructionsInitiallyVisible: false,
  scene3DOnly : false,
  shadows : true,
  terrainShadows : true,
  baseLayerPicker : false
});
var camera = viewer.camera;
var scene = viewer.scene;
var ellipsoid = scene.globe.ellipsoid;
var canvas = viewer.canvas;
var layers = viewer.scene.imageryLayers;
var globe = viewer.scene.globe;
//Reset the globe's imagery layers and begin with a light grey base
globe.imageryLayers.removeAll();
globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
//Add a map tiling service over top of the base layer, to show the city street details
var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
  url : 'http://tile.stamen.com/toner/'
}));
tonerLayer.alpha = 0.2;

// var terrainProvider = new Cesium.CesiumTerrainProvider({
//   url : '//assets.agi.com/stk-terrain/world',
//   requestVertexNormals: true
// });
//Remove tab index from the canvas, as this won't be necessary for keyboard only users
canvas.setAttribute('tabindex', -1);

var reset = document.getElementById('nav-reset');
reset.addEventListener('click', function() {
  gotoQD30();
});

//Load the datasource as a promise which triggers the "then" function upon loading

var dataSource = Cesium.GeoJsonDataSource.load('/sites/quartierdix30/files/geojson/quartier_en.json').then(function(data)  {
  viewer.dataSources.add(data);
  gotoQD30();
  //entities.values provides the entities as individual features, as found in the geoJson file. Iterate over these and
  //endow them with the appropriate properties for display
  var entities = data.entities.values;
  for (var i = 0; i < entities.length; i++) {
    var entity = entities[i];
    //Buildings should have a black outline by default
    entity.polygon.outline = true;
    entity.polygon.outlineWidth = 1.2;
    entity.polygon.outlineColor = Cesium.Color.BLACK;

    //use extrudedHeight to stretch buildings to their appropriate height, as per their level property (this is
    //adjustable for each node)
    if (entity.properties.hasOwnProperty('level')) {
      entity.polygon.extrudedHeight = 0.5 * (entity.properties.level * 10);
    }
    //The value of colour for each feature is dependent on which sector the entity has been assigned to.
    //Boutique Node is paired with a Zone Node, and Zone Nodes are organized by Sector, which is how they derive their colour.
    //The value found in the json is added dynamically everytime the map data is rebuilt
    if (entity.properties.hasOwnProperty("colour")) {
      if (entity.properties.hasOwnProperty("event")) {
        //If the boutique has a special event, we use the StripeMaterialProperty to make it stand out
        entity.polygon.material = new Cesium.StripeMaterialProperty({
          evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
          oddColor : Cesium.Color.BLACK,
          repeat : 10
        });
      } else {
        entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
      }
    }
    //The description property provides all of the markup for the infobox which pops up when a boutique is selected
    if (entity.properties.hasOwnProperty("description")) {
      entity.description = '<div>' + entity.properties.description
        + '</div>';
    }
  }
});
//This function takes us to the default view of the map
function gotoQD30() {
  var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(new Cesium.Cartesian3(
    1278149.1996018, -4297423.8092246,
    4522591.15943));
  pos.latitude = Cesium.Math.toDegrees(pos.latitude);
  pos.longitude = Cesium.Math.toDegrees(pos.longitude);

  viewer.camera.flyTo({
    destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, pos.height),
    orientation : {
      heading : 5.51747,
      pitch : -1.419427,
      roll : 6.2675
    },
    duration: 0.5
  });
}
//Not currently used
function clearLayers() {
  globe.imageryLayers.removeAll();
  globe.baseColor = new Cesium.Color(0, 0, 0.5, 1);
}
//Not currently used
function customLayer() {
  globe.imageryLayers.removeAll();
  globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
  var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
    url : 'http://tile.stamen.com/toner/'
  }));
  tonerLayer.alpha = 0.2;
}
//Not currently used
function highlight(chosenColour) {
  for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
    var entity = viewer.dataSources.get(0).entities.values[j];
    if (entity.properties.hasOwnProperty('colour')) {
      if (entity.properties.colour.toLowerCase() == chosenColour.toLowerCase()) {
        entity.polygon.material = Cesium.Color.BLACK;
        entity.polygon.outlineWidth = 2.0;
      }
    }
  }
}
//Resets all boutiques to their default initial colour as per their colour and event properties
function colourReset() {
  // for (z = viewer.entities.values.length -1; z >= 0; z--) {
  //     if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
  //         viewer.entities.remove(viewer.entities.values[z]);
  //     }
  // }
  for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
    var entity = viewer.dataSources.get(0).entities.values[j];
    if (entity.properties.hasOwnProperty('colour')) {
      if (entity.properties.hasOwnProperty("event")) {
        entity.polygon.material = new Cesium.StripeMaterialProperty({
          evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
          oddColor : Cesium.Color.BLACK,
          repeat : 10
        });
      } else {
        entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
        entity.polygon.outlineWidth = 1.2;
        entity.polygon.outlineColor = Cesium.Color.BLACK;
      }
    }
  }
}
//Centers the view over the map canvas
function centerApp() {
  window.scrollTo(appX, appY);
}
//Centers the view over the form
function centerForm() {
  window.scrollTo(formX, formY);
}
//Helper function to determine position of an element
function getPosition(el) {
  var xPos = 0;
  var yPos = 0;

  while (el) {
    if (el.tagName == "BODY") {
      // deal with browser quirks with body/window/document and page scroll
      var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
      var yScroll = el.scrollTop || document.documentElement.scrollTop;

      xPos += (el.offsetLeft - xScroll + el.clientLeft);
      yPos += (el.offsetTop - yScroll + el.clientTop);
    } else {
      // for all other non-BODY elements
      xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
      yPos += (el.offsetTop - el.scrollTop + el.clientTop);
    }

    el = el.offsetParent;
  }
  return {
    x: xPos,
    y: yPos
  };
}
//Not currently used
function getOffset( el ) {
  var _x = 0;
  var _y = 0;
  while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    _x += el.offsetLeft - el.scrollLeft;
    _y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
  }
  return { top: _y, left: _x };
}
//Determines whether or not an element is visible
function domIsVisible(element) {
  var rect = element.getBoundingClientRect();
  var html = document.documentElement;
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || html.clientHeight) &&
    rect.right <= (window.innerWidth || html.clientWidth)
  );
}


window.onload = function() {
  mapDom.style.display = 'inline-block';
  searchForm.style.display = 'inline-block';
  navContainer.style.display = 'block';
  placeholderDom.style.display = 'none';
  //Set the form and canvas X and Y values after loading the window
  var formPosition = getPosition(searchForm);
  formX = formPosition.x;
  formY = formPosition.y;

  var appPosition = getPosition(appContainer);
  appX = appPosition.x;
  appY = appPosition.y ;
  centerApp();

  // var formAnchorTags = Array.from(searchForm.querySelectorAll("a"));
  // formAnchorTags.forEach(function(anchor) {
  //   if (!anchor.children[0].classList.contains('hidden-nid')) {
  //     console.dir(anchor.parentElement);
  //     console.dir(anchor.parentNode);
  //   }
  //   if (anchor.parentElement.nodeName == 'SUMMARY') {
  //     anchor.parentElement.tabIndex = -1;
  //   }
  //
  // });
  //Reload the stylesheet for the infobox, as it is an iframe and its styling needs to be enforced after the window has loaded
  var frame = viewer.infoBox.frame;
  var cssLink = frame.contentDocument.createElement('link');
  cssLink.href = Cesium.buildModuleUrl('../../../../css/qd30map.css');
  cssLink.rel = 'stylesheet';
  cssLink.type = 'text/css';
  frame.contentDocument.head.appendChild(cssLink);
  //Remove tabindex from elements within the header
  var header = document.getElementById('header');
  var headChilds = header.getElementsByTagName('*');
  for (var c = 0; c < headChilds.length; c++) {
    headChilds[c].tabIndex = -1;
  }
  var qdTitle = document.getElementById('block-quartierdix30-page-title');
  var titleChilds = qdTitle.getElementsByTagName('*');
  for (var d = 0; d < titleChilds.length; d++) {
    titleChilds[d].tabIndex = -1;
  }
  //Set a variable to represent the primary datasource, which contains all of the geojson features
  var data = viewer.dataSources.get(0);
  var subBtn = document.getElementById('edit-mapsubmit');
  //Set a listener for the form's submit button, so submissions can be overridden
  subBtn.onclick = function(v) {
    v.preventDefault();
    handleSubmit();
  };
  //Set the same type of listener for the autocomplete field, whose submit logic must also be overridden
  var autoComplete = document.getElementById('ui-id-1');
  autoComplete.onclick = function() {
    handleSubmit();
  };
  autoComplete.onkeydown = function(param) {
    if (param.keyCode == 13) {
      handleSubmit();
    }
  };
  //Override selections based on clicking objects within the WebGL Canvas
  clickHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  clickHandler.setInputAction(function(movement) {
    var pickedObject = scene.pick(movement.position);
    if (Cesium.defined(pickedObject)) {
      for (var i = 0; i < data.entities.values.length; i++)  {
        var entity = data.entities.values[i];
        if (entity.properties.nid != undefined) {
          if (entity.properties.nid == pickedObject.id.properties.nid) {
            exploreStore(entity, data);
          }
        }
      }
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

  //Iterate over all the anchor elements in the form and setup appropriate click/keyboard listeners to select stores
  var anchorElements = searchForm.querySelectorAll('.form-item');


  for (var i = 0; i < anchorElements.length; i++) {
    anchorElements[i].tabIndex = 0;
  }

  for (var k=0; k < anchorElements.length; k++) {
    anchorElements[k].addEventListener('click', function(param) {
      for (var g=0; g < anchorElements.length; g++) {
        //Adding the selected-store class to the elements will cause them to appear highlighted within the form
        //We must first remove this from the previous selected element
        anchorElements[g].classList.remove('selected-store');
      }
      var choice = param.target.innerHTML.trim();
      for (var i = 0; i < data.entities.values.length; i++)  {
        var entity = data.entities.values[i];
        if (entity.properties.nid != undefined) {
          var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf('</spa'));
          //Form selection occurs by comparing the nid value hidden within the form element with the nid property of the geojson feature
          if (formNid == entity.properties.nid) {
            if (entity.polygon.hierarchy._value.positions.length > 1) {
              selectStore(entity, data);
            } else {
              gotoQD30();
            }

            if (!domIsVisible(appContainer)) {
              centerApp();
            }
            this.classList.add('selected-store');
          }
        }
      }
    });
    //This logic is the same above, but for selection via keyboard
    anchorElements[k].addEventListener('keyup', function(param) {
      for (var g=0; g < anchorElements.length; g++) {
        anchorElements[g].classList.remove('selected-store');
      }
      if (param.keyCode == 13) {
        var choice = param.target.innerHTML.trim();
        for (var i = 0; i < data.entities.values.length; i++) {
          var entity = data.entities.values[i];
          if (entity.properties.nid != undefined) {
            var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf('</spa'));

            if (formNid == entity.properties.nid) {
              if (entity.polygon.hierarchy._value.positions.length > 1) {
                selectStore(entity, data);
              } else {
                gotoQD30();
              }

              if (!domIsVisible(appContainer)) {
                centerApp();
              }
              this.classList.add('selected-store');
            }
          }
        }
      }
    });
  }
  //This listener is used to highlight multiple boutiques by store type
  var types = Array.from(document.querySelectorAll('summary > a.details-title'));
  types.forEach(function(type) {
    type.addEventListener('click', function(param) {
      var text = param.srcElement.lastChild.textContent.toLowerCase();
      highlightByType(text, data);
    });
  });
  //this function is used to override form submission
  function handleSubmit() {

    var search = document.getElementById('edit-search');
    var formNid = search.value.substring(search.value.indexOf('(') + 1, search.value.indexOf(')'));
    var length = data.entities.values.length;
    for (var i = 0; i < length; i++)  {
      var entity = data.entities.values[i];
      if (entity.properties.nid != undefined) {
        console.log(entity.properties.nid);
        console.log(formNid);
        if (formNid == entity.properties.nid) {
          if (entity.polygon.hierarchy._value.positions.length > 1) {
            selectStore(entity, data);
          } else {
            gotoQD30();
          }
        }
      }
    }
  }
  //The primary function for selecting a boutique: finds the average point between the feature's coordinates, flies the camera
  //at an appropriate angle, colours the building and sets the viewer.selectedEntity property to it, triggering the display
  //of the infobox popup
  function selectStore(entity, data) {
    var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
    var avgPoint = Math.round(pCoords.positions.length / 2);
    var topPoint = 0;

    for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
      if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
        topPoint = xy;
      }
    }

    var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
    pos.latitude = Cesium.Math.toDegrees(pos.latitude);
    pos.longitude = Cesium.Math.toDegrees(pos.longitude);

    viewer.camera.flyTo({
      destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, 500),
      orientation : {
        heading : 5.51747,
        pitch : -1.419427,
        roll : 6.2675
      },
      duration: 1
    });

    colourReset();
    //Previously, we wrapped highlighted buildings with a glowing polyline to improve contrast
    // viewer.entities.add({
    //   name : 'selectionOutline',
    //   polyline : {
    //     positions : pCoords.positions,
    //     width : 4,
    //     material : new Cesium.PolylineGlowMaterialProperty({
    //       glowPower : 0.5,
    //       color : Cesium.Color.fromCssColorString('#008B8B'),
    //       extrudedHeight : entity.polygon.extrudedHeight + 20
    //     })
    //   }
    // });

    entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
    entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
    entity.polygon.material.outlineWidth = 5;
    viewer.selectedEntity = entity;
  }
  //this function is similar to above, but used for clicking on the canvas. This preserves the zoom height which the user
  //is previously using, such as to avoid the confusion of changing height each time a building is clicked on
  function exploreStore(entity, data) {
    var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
    var cartographic = new Cesium.Cartographic();
    var camHeight = ellipsoid.cartesianToCartographic(camera.position, cartographic).height;
    // var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[0]);
    // pos.latitude = Cesium.Math.toDegrees(pos.latitude);
    // pos.longitude = Cesium.Math.toDegrees(pos.longitude);

    var avgPoint = Math.round(pCoords.positions.length / 2);


    var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
    pos.latitude = Cesium.Math.toDegrees(pos.latitude);
    pos.longitude = Cesium.Math.toDegrees(pos.longitude);

    // var topPoint = 0;
    //
    // for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
    //     if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
    //         topPoint = xy;
    //     }
    // }

    viewer.camera.flyTo({
      destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, camHeight),
      orientation : {
        heading : camera.heading
      },
      duration: 1
    });

    colourReset();

    // pCoords.positions.forEach(function(position) {
    //     position.x += 0.33;
    //     position.y += 0.33;
    // });

    // var selectionOutline = viewer.entities.add({
    //   name : 'selectionOutline',
    //   polyline : {
    //     positions : pCoords.positions,
    //     width : 4,
    //     material : new Cesium.PolylineGlowMaterialProperty({
    //       glowPower : 0.5,
    //       color : Cesium.Color.fromCssColorString('#008B8B'),
    //       extrudedHeight : entity.polygon.extrudedHeight + 20
    //     })
    //   }
    // });
    entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
    entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
    entity.polygon.material.outlineWidth = 5;
    viewer.selectedEntity = entity;
  }
  //This is the function used to highlight multiple boutiques based on their store type (restaurant, entertainment, "accepts gift cards", etc)
  function highlightByType(type, data) {
    colourReset();
    // for (z = 0; z < viewer.entities.values.length; z++) {
    //     if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
    //         viewer.entities.remove(viewer.entities.values[z]);
    //     }
    // }
    for (var j = 0; j < data.entities.values.length; j++) {
      var entity = data.entities.values[j];
      if (entity.properties.hasOwnProperty('storetypes')) {
        if (entity.properties.storetypes.toLowerCase().match(type)) {
          var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
          matched = true;
          entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
          entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
          entity.polygon.material.outlineWidth = 5;

          // viewer.entities.add({
          //   name : 'selectionOutline',
          //   polyline : {
          //     positions : pCoords.positions,
          //     width : 4,
          //     material : new Cesium.PolylineGlowMaterialProperty({
          //       glowPower : 0.5,
          //       color : Cesium.Color.fromCssColorString('#008B8B'),
          //       extrudedHeight : entity.polygon.extrudedHeight + 20
          //     })
          //   }
          // });
        }
      }
    }
  }

  //Logic to close all non-target details elements
  //This was important to keep the use of the form from becoming confusing, as there are many collapsible details elements
  var details = Array.from(document.querySelectorAll("details"));
  details.forEach(function(detail) {
    if (detail.constructor.name === 'HTMLDetailsElement') {
      detail.addEventListener('click', function() {

        for (f = 0; f < details.length; f++) {
          details[f].classList.remove('selected-detail');
        }

        this.classList.add('selected-detail');
        subDet = this.querySelectorAll('details');

        for (g = 0; g < subDet.length; g++) {subDet[g].classList.add('selected-detail');}
        closeOthers(this);
      });
    }
  });


  //helper function used in the above listeners
  function closeOthers(elem) {
    for (var i = 0; i < details.length; i++) {
      if (!details[i].contains(elem)) {
        var selectedFound = false;
        var elems = details[i].querySelectorAll('*');
        for (z = 0; z < elems.length; z++) {
          if (elems[z].classList.contains('selected-detail') || elems[z].classList.contains('selected-store')) {
            selectedFound = true;
          }
        }
        if (selectedFound == false) {
          details[i].removeAttribute('open');
        }
      } else {
        details[i].setAttribute('open', 'open');
      }
    }
  }


  //This helper function is no longer used, but was helpful during development to find the visible coordinates on the canvas
  function getExtentView() {
    var cl2 = new Cesium.Cartesian2(0, 0);
    var leftTop = viewer.scene.camera.pickEllipsoid(cl2, ellipsoid);

    cr2 = new Cesium.Cartesian2(viewer.scene.canvas.width, viewer.scene.canvas.height);
    var rightDown = viewer.scene.camera.pickEllipsoid(cr2, ellipsoid);

    if (leftTop != null && rightDown != null) {
      leftTop = ellipsoid.cartesianToCartographic(leftTop);
      rightDown = ellipsoid.cartesianToCartographic(rightDown);
      return new Cesium.Rectangle(leftTop.longitude, rightDown.latitude, rightDown.longitude, leftTop.latitude);
    } else {
      console.log("Sky is visible");
      return null;
    }
  }

};

//It is important to update the values of the form and app positions upon window resize
window.onresize = function() {
  var formPosition = getPosition(searchForm);
  formX = formPosition.x;
  formY = formPosition.y;

  var appPosition = getPosition(appContainer);
  appX = appPosition.x;
  appY = appPosition.y;
};
//This jQuery code was being used for the map legend
(function($, Drupal) {
  Drupal.behaviors.qd30map = {
    attach: function(context, settings) {
      $('.closemenu').on('mouseenter', function() {
        $(this).find('span').show();
      }).on('mouseleave', function() {
        $(this).find('span').hide();
      }).on('click tap', function() {
        $(this).parent().parent().children('div').fadeOut(150);
        $(this).parent().parent().delay(50).animate({width: 'toggle'}, 300);
        if ($(this).parent().hasClass('closeinfo')) {
          infoOpen = false;
          $('.selected').removeClass('selected');
        }
      });

      $('#open-legend').on('click tap', function() {
        $('.map-legend-info').animate({width:'toggle'}, 300);
        $('.map-legend-info > div').delay(150).fadeIn(150);
      });
    }}})(jQuery, Drupal);
//Helper function not currently being used
function fade(element) {
  var op = 1;  // initial opacity
  var timer = setInterval(function() {
    if (op <= 0.1) {
      clearInterval(timer);
      element.style.display = 'none';
    }
    element.style.opacity = op;
    element.style.filter = 'alpha(opacity=' + op * 100 + ")";
    op -= op * 0.1;
  }, 150);
}
