harunpehlivan
3/12/2018 - 1:01 AM

FCC Local Weather App

FCC Local Weather App

<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.css" rel="stylesheet" />
body { background-color: #333; }
#background-images {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

/*   opacity: 0;
  -webkit-transition: opacity 2.0s ease-in-out;
  -o-transition: opacity 2.0s ease-in-out;
  transition: opacity 2.0s ease-in-out; */
}
#bgImg1, #bgImg2 {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  background-attachment: fixed;
  background-position-x: center;
  background-position-y: center;
  background-size: cover;
  background-repeat: no-repeat;
}
#bgImg1 { 
  background-image: url("https://aboutme.imgix.net/background/users/h/a/r/harun.pehlivan_1511592423_808.jpg?q=40&dpr=2&auto=format&fit=max&w=620&h=413.3333333333333&rect=255,0,2754,1836");
  background-color: #333;
}
#bgImg2 { 
  background-image: url("https://tebm.files.wordpress.com/2010/02/arge-proje-egitim-esnasc4b1nda-hallerim-3.jpg");
  background-color: #333;
}

#background-images div.front {
  z-index: 3;
}
#background-images div.back {
  z-index: 2;
}

#main {
/*   opacity: 0; */
  -webkit-transition: opacity 1.0s ease-in-out;
  -o-transition: opacity 1.0s ease-in-out;
  transition: opacity 1.0s ease-in-out;
  z-index: 10;
  position: relative;
}

.ui.masthead.segment {
  /* min-height: 50vh; */
  border-bottom: none;
}

#content {
  background-color: #EEE;
  /* opacity: 0.8; */
  border: 10px solid #666;
  border-radius: 20px;
  padding: 20px;
  position: relative;
  /* color: #fff; */
}
#debug-btn {
  /* padding-bottom: 6px; */
  /* vertical-align: top; */
  position: absolute;
  top: 10px;
  right: 10px;
}

.ui.grid {
  margin-top: 0;
}
/* .ui.grid > .column:not(.row) { padding-top: 0; } */

/* Compass left panel */
#compass-label {
  margin: 0;
}
/* #compass-deg { margin-bottom:6px; } */

.compass {
  margin: 10px 0;
  position: relative;
  width: 100%;
  /* height:200px; */
}
.arrow {
  position: absolute;
  z-index: 300;
  /* width: 160px;
        height: 160px; */
  -webkit-transform: rotate(0);
  -ms-transform: rotate(0);
  transform: rotate(0);
  -webkit-transition: -webkit-transform 0.5s ease-in-out;
  transition: -webkit-transform 0.5s ease-in-out;
  -o-transition: transform 0.5s ease-in-out;
  transition: transform 0.5s ease-in-out;
  transition: transform 0.5s ease-in-out, -webkit-transform 0.5s ease-in-out;
  width: 100%;
  max-width: 180px;
}
.arrow:hover {
  -webkit-transform: rotate(225deg);
  -ms-transform: rotate(225deg);
  transform: rotate(225deg);
  -webkit-transition: -webkit-transform 0.7s ease-in-out;
  transition: -webkit-transform 0.7s ease-in-out;
  -o-transition: transform 0.7s ease-in-out;
  transition: transform 0.7s ease-in-out;
  transition: transform 0.7s ease-in-out, -webkit-transform 0.7s ease-in-out;
}
.disc {
  /* position: absolute; */
  z-index: 200;
  /* width: 160px;
        height: 160px; */
  width: 100%;
  max-width: 180px;
}
#compass-stats {
  margin: 10px 0;
  padding: 0.8em 0.6em;
}

/* weather center panel 'temp-group' */
#city {
  margin: 20px 0 0 0;
  display: inline-block;
}
#icon {
  margin-bottom: -6px;
}
#temp {
  display: inline-block;
  /* margin: 24px 0; */
  margin: 6px 0 0;
  font-size: 4em;
  position: relative;
  color: darkgreen;
}
#temp::after {
  content: "\00b0";
  color: darkgreen;
  font-size: 28px;
  position: absolute;
  top: -9px;
  right: -14px;
}
#temp-deg {
  font-size: 0.8em;
  vertical-align: top;
}
#high-low {
  margin-bottom: 24px;
  margin-top: -5px;
}
#sky, #humidity, #wind-dir, #wind-speed, #sunrise, #sunset, #vis {
  color: darkgreen;
  /* font-weight: bold;  */
  font-style: italic;
}

/* City buttons right column */
#city-buttons {
  /* margin-top: 20px; */
  z-index: 400;
}
#city-buttons .ui.button {
  width: 120px;
  /* margin: 1px 0; */
}
.ui.buttons .or::before {
  line-height: 1.60em;
  border: 1px solid #999;
  font-size: 14px;
  top: 55%;
  background-color: #f6f6f6;
}
.ui.button, .ui.buttons .button {
  border: 1px solid #666;
  margin: 1px 0;
}

/* Next row */
/* temp toggle left column */
#temp-group {
  /* margin-top: 20px;  */
  border: 1px solid #666;
  padding-top: 6px;
}
#temp-toggle {
  border-left: unset;
}
#temp-toggle .button:first-child {
  border-left: 1px solid #666;
  border-top-left-radius: .28571429rem;
  border-bottom-left-radius: .28571429rem;
}

/* debug panel */
#debug-container {
  display: none;
}
#debug-content {
  background: #eee;
  padding: 10px 20px 20px 20px;
  border: 1px solid #999;
}
.debug {
  clear: both;
}
#hard-coded {
  position: relative;
}
#hard-coded dl {
  /* float: left; */
  /* padding: 0 40px 0 30px; */
}
#hard-coded dl:last-child {
  padding-right: 10px;
}

.hidden {
  display: none;
}

#traverse-json, #json-fields-loop {
  height: 270px;
}
#debug-content {
  height: 380px;
}
#traverse-json, #json-fields-loop {
  overflow-y: scroll;
}
.ui.popup.wide.center.transition {
  width: 340px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.js"></script>
// Let's start with Los Angeles cause... why not?
let lat = 34.052234;
let lon = -118.243685;

// Google Maps Geocoding API key
const GeocodeAPIKey = "AIzaSyATszLTrO3Njt52156ddkyk85WcFRzgZEg"; // will use this later...
const TimeZoneAPIKey = "AIzaSyB80idMRjgP_qHfbqMZaYOuJSnGwME5LSY";
const DaysOfWeek = ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat", "Sun"];
const crossFadeTime = 1200;
const pathToImages = "https://local-weather-app.netlify.com/images/";

// json vars
let interval = null; // used to stop setInterval in whatTimeIsIt function so we can set a new time
let currentUnit = "imperial"; // keep track of current unit

let temp = 0;
let high = 0;
let low = 0;
let windSpeed = 0;
let windDegree = 0; // default val in case it isn't returned in json
let vis = 0;

// locations for each of the buttons
var locations = {
  nairobi: { lat: -1.292066, lon: 36.821946 },
  frankfurt: { lat: 50.110922, lon: 8.682127 },
  lagos: { lat: 6.524379, lon: 3.379206 },
  cape_code: { lat: 41.668948, lon: -70.293295 }, // Hyannis
  paris: { lat: 48.858547, lon: 2.294449 }, // Tour Eiffel
  // paris: { lat: 48.856614, lon: 2.352222 }, // Paris proper
  // paris: { lat: 48.873792, lon: 2.295028 }, // Arc de Triomphe
  // london: { lat: 51.520093, lon: -0.122097 }, // London's West End
  london: { lat: 51.513676, lon: -0.12671 }, // London's theatre district
  los_angeles: { lat: 34.052234, lon: -118.243685 } // my hood
};

// allows us to change background images
var bgImages = [
  "clear-d.jpeg",
  "clear-n.jpg",
  "clouds-n.jpeg",
  "cloudy-sky.jpg",
  "clouds-pink.jpeg",
  "drizzle-d.jpg",
  "fog.jpg",
  "night-fog1.jpg",
  "night-fog3.jpg",
  "purple-tree.jpg",
  // "rain-d.jpeg",
  "rain1.jpg",
  "rain-thunder.jpg",
  "snow-d.jpeg",
  "snow-n.jpeg",
  "wind-d.jpg",
  "wind-n.jpeg"
];

// lets get started!
$(document).ready(function () {
  // do semantic-ui calls before my init() function
  $('.menu .item').tab(); // create the debug tabs
  shoutOutToMyPeeps() // set up the callouts

  init(); // let's start the show!
});

function init() {
  // pre-load bgImages
  for (var i = 0; i < bgImages.length; ++i) {
    var img = new Image();
    img.src = pathToImages + bgImages[i];
  }
  // for initial load
  $("#main").css("opacity", "1.0");
  $("#background-images").css("opacity", "1.0");

  // setup button click event handlers

  // Imperial or Metric? Set click eventhandler to toggle values
  $(".cf-toggle").on("click", function(event) {
    // console.log(event.target.id);
    if (event.target.id != currentUnit) {
      $(".cf-toggle").toggleClass("positive active");
      // console.log(event.target.id);
      if (event.target.id === "metric") {
        toggleUnits("metric");
        currentUnit = "metric";
      } else {
        toggleUnits("imperial");
        currentUnit = "imperial";
      }
    }
  });

  // Geolocate me!
  $("#use-my-location").on("click", getGeolocation);

  // city buttons (lets write long form js)
  document.getElementById("btn-Nairobi").addEventListener("click", function() {
    getWeather(locations.nairobi.lat, locations.nairobi.lon);
  });
  document
    .getElementById("btn-Frankfurt")
    .addEventListener("click", function() {
      getWeather(locations.frankfurt.lat, locations.frankfurt.lon);
    });
  document.getElementById("btn-Lagos").addEventListener("click", function() {
    getWeather(locations.lagos.lat, locations.lagos.lon);
  });
  document.getElementById("btn-Cape-Cod").addEventListener("click", function() {
    getWeather(locations.cape_code.lat, locations.cape_code.lon);
  });
  document.getElementById("btn-London").addEventListener("click", function() {
    getWeather(locations.london.lat, locations.london.lon);
  });
  document.getElementById("btn-Paris").addEventListener("click", function() {
    getWeather(locations.paris.lat, locations.paris.lon);
  });
  document
    .getElementById("btn-Los-Angeles")
    .addEventListener("click", function() {
      getWeather(locations.los_angeles.lat, locations.los_angeles.lon);
    });

  // loop through bgImages array & create an event handler for each bgImage button
  // need to use 'let' in for..loop for block level scope: https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
  /*for (let i = 0; i < bgImages.length; i++) {
    var btn = document.getElementById("bgImage-btn" + i);
    btn.title = bgImages[i]; // set tooltip with image name
    btn.setAttribute("rel", bgImages[i]); // set attribute for use in updateBackgroundImage()

    btn.addEventListener("click", function() {
      updateBackgroundImage(bgImages[i]);
    });
  }*/
  bgImages.forEach(function changeImages(el, index) {
    var btn = document.getElementById("bgImage-btn" + index);
    btn.title = el; // set tooltip with image name
    btn.setAttribute("rel", el); // set attribute for use in updateBackgroundImage()

    btn.addEventListener("click", function () {
      updateBackgroundImage(el);
    });
  });
  // Above code can be replaced by creating bg-links dynamically - see crossfade.html #107-#114
  // $("bg-controls a.label").click(function () {
  //     updateBackgroundImage(bgImages[i]);
  // });

  // debug panel
  $("#debug-btn").click(function() {
    $("#debug-container").transition("slide up");
  });

  // do the magic
  getWeather(lat, lon);
}

// where am I?
function getGeolocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
      lat = (position.coords.latitude).toFixed(6);
      lon = (position.coords.longitude).toFixed(6);
      console.log("lon:", lon, "lat", lat);
      getWeather(lat, lon);
    });
  }
}

// ajax call using jquery (we use straight javascript ajax further down)
function getWeather(lat1, lon1) {
  lat = lat1;
  lon = lon1;

  $.ajax({
    url: "https://fcc-weather-api.glitch.me/api/current",
    dataType: "jsonp",
    data: {
      lat: lat,
      lon: lon,
      units: "imperial"
    },
    jsonpCallback: "displayWeather"
  });
}

// throw it up on the page
function displayWeather(json) {
  console.log(json);
  console.log("global temp ", json.main.temp);

  // assign to global vars
  temp = json.main.temp;
  high = json.main.temp_max;
  low = json.main.temp_min;
  windSpeed = json.wind.speed;
  windDegree = json.wind.deg || 0; // in case json.wind.deg is null or undefined
  vis = json.visibility || 0;

  // output temp & distance items in metric or imperial units
  toggleUnits(currentUnit);

  // fill page with the remaining data (using vanilla js here)
  document.getElementById("city").innerHTML = json.name;
  if (typeof json.weather[0].icon === "undefined") {
    document.getElementById("icon").style.display = "none";
  }else {
    document.getElementById("icon").style.display = "inline-block";
  }
  document.getElementById("icon").src = json.weather[0].icon || "";
  document.getElementById("sky").innerHTML =
    json.weather[0].description.charAt(0).toUpperCase() +
    json.weather[0].description.slice(1);
  document.getElementById("humidity").innerHTML = json.main.humidity + "%";
  document.getElementById("wind-dir").innerHTML = windDirection(windDegree);
  document.getElementById("sunrise").innerHTML = new Date(
    Number(json.sys.sunrise + "000")
  ).toLocaleTimeString();
  document.getElementById("sunset").innerHTML = new Date(
    Number(json.sys.sunset + "000")
  ).toLocaleTimeString();
  document.getElementById("compass-deg").innerHTML =
    windDegree.toFixed(2) + "&deg;";
  document.getElementById("conditionCode").innerHTML = json.weather[0].id;
  document.getElementById("lat").innerHTML = lat; // json.coord.lat is truncated to 2 decimal places - don't use
  document.getElementById("lon").innerHTML = lon; // json.coord.lon is truncated to 2 decimal places - don't use

  // point me in the right direction
  setCompass(windDegree);
  // console.log(json.wind.deg);
  // console.log(windDegree);

  // set background image based on weather condition
  // FIX: This is now moved to whatTimeIsIt() function so it can be based on time of day also
  // doWeatherCondition(json.weather[0].id, timeOfDay);

  // show me local time & set background image
  whatTimeIsIt(lat, lon, "localTime", json.weather[0].id);

  //  build debug panel
  buildDebugPanel(json);
}

// make the magic with css transform, rotate, & degree - this is cool
function setCompass(degree) {
  setTimeout(function() {
    $(".arrow").css("transform", "rotate(" + degree + "deg)");
  }, 1000);

  //$(".arrow:hover").css("transform", "rotate(225deg)")
  // var prev = $(".arrow").css("transform");
  $(".arrow").hover(
    function() {
      $(".arrow").css("transform", "rotate(0deg)");
      // console.log("hover in");
    },
    function() {
      $(".arrow").css("transform", "rotate(" + degree + "deg");
      // console.log("hover out");
    }
  );
}

// Display localized time for a chosen city
function whatTimeIsIt(lat, lon, tagId, conditionCode) {
  // console.log("lat:", lat);
  // console.log("lon:", lon);
  // http://www.javascriptkit.com/dhtmltutors/local-time-google-time-zone-api.shtml
  // http://www.javascriptkit.com/dhtmltutors/live-local-time-google-time-zone-api.shtml

  var container = document.getElementById(tagId);
  var targetDate = new Date(); // Current date/time of user computer
  var timestamp =
    targetDate.getTime() / 1000 + targetDate.getTimezoneOffset() * 60; // Current UTC date/time expressed as seconds since midnight, January 1, 1970 UTC
  var apiCall =
    "https://maps.googleapis.com/maps/api/timezone/json?location=" +
    lat +
    ", " +
    lon +
    "&timestamp=" +
    timestamp +
    "&key=" +
    TimeZoneAPIKey;
  // set new time
  clearInterval(interval);

  var xhr = new XMLHttpRequest(); // create new XMLHttpRequest2 object
  xhr.open("GET", apiCall); // open GET request
  xhr.onload = function() {
    if (xhr.status === 200) {
      // if Ajax request successful
      var output = JSON.parse(xhr.responseText); // convert returned JSON string to JSON object
      // console.log(output.status); // log API return status for debugging purposes
      if (output.status === "OK") {
        // if API reports everything was returned successfully
        var offsets = output.dstOffset * 1000 + output.rawOffset * 1000; // get DST and time zone offsets in milliseconds
        var localDate = new Date(timestamp * 1000 + offsets); // Date object containing current time of city(timestamp + dstOffset + rawOffset)
        // console.log("in: ", localDate.toLocaleString()); // Display current citydate and time

        var refreshDate = new Date(); // get current date again to calculate time elapsed between targetDate and now
        var millisecondsElapsed = refreshDate - targetDate; // get amount of time elapsed between targetDate and now
        localDate.setMilliseconds(
          localDate.getMilliseconds() + millisecondsElapsed
        ); // update localDate to account for any time elapsed
        interval = setInterval(function() {
          localDate.setSeconds(localDate.getSeconds() + 1);
          container.innerHTML =
            localDate.toLocaleTimeString() +
            " (" +
            DaysOfWeek[localDate.getDay()] +
            ")";
        }, 1000);

        // set background image based on weather condition
        var timeOfDay = "d";
        if (localDate.getHours() < 7 || localDate.getHours() > 17) {
          timeOfDay = "n";
          console.log("night", "hour", localDate.getHours());
        } else {
          console.log("day", "hour", localDate.getHours());
        }
        doWeatherCondition(conditionCode, timeOfDay);
      }
    } else {
      alert("Request failed.  Returned status of " + xhr.status);
    }
  };
  xhr.send(); // send request
}

// functions to toggle between Celcius & Fahrenheit
function toggleUnits(unit) {
  if (unit === "imperial") {
    // console.log("imperial", convertCtoF(temp));
    document.getElementById("temp").innerHTML = convertCtoF(temp);
    document.getElementById("high").innerHTML = convertCtoF(high) + "&deg;";
    document.getElementById("low").innerHTML = convertCtoF(low) + "&deg;";
    document.getElementById("wind-speed").innerHTML = convertWindToMiles(
      windSpeed
    );
    document.getElementById("vis").innerHTML = convertVisToMiles(vis);
  } else if (unit === "metric") {
    // console.log("metric", temp);
    // console.log("vis",vis);
    document.getElementById("temp").innerHTML = temp.toFixed();
    document.getElementById("high").innerHTML = high + "&deg;";
    document.getElementById("low").innerHTML = low + "&deg;";
    document.getElementById("wind-speed").innerHTML = windSpeed + " m/s";
    document.getElementById("vis").innerHTML = vis.toLocaleString() + " m";
  }
}

function convertCtoF(cel) {
  return Math.floor(cel * 1.8 + 32);
}

function convertVisToMiles(meters) {
  return (meters / 1609.344).toFixed(2) + " mi";
}

function convertWindToMiles(meters) {
  return (meters * 2.2369).toFixed(2) + " mph";
}

function windDirection(degree) {
  var dir = "";
  switch (true) {
    case degree < 11.25:
      dir = "North";
      break;
    case degree < 33.75:
      dir = "North NW";
      break;
    case degree < 56.25:
      dir = "NorthEast";
      break;
    case degree < 78.75:
      dir = "East NE";
      break;
    case degree < 101.25:
      dir = "East";
      break;
    case degree < 123.75:
      dir = "East SE";
      break;
    case degree < 146.25:
      dir = "SouthEast";
      break;
    case degree < 168.75:
      dir = "South SE";
      break;
    case degree < 191.25:
      dir = "South";
      break;
    case degree < 213.75:
      dir = "South SW";
      break;
    case degree < 236.25:
      dir = "SouthWest";
      break;
    case degree < 258.75:
      dir = "West SW";
      break;
    case degree < 281.25:
      dir = "West";
      break;
    case degree < 303.75:
      dir = "West NW";
      break;
    case degree < 326.25:
      dir = "NorthWest";
      break;
    case degree < 348.75:
      dir = "North NE";
      break;
    default:
      dir = "North";
  }
  return dir;
}

function doWeatherCondition(conditionCode, timeDay) {
  var bgImage = "";

  switch (true) {
    case conditionCode < 240: //Thunderstorm
      if (timeDay === "d") {
        bgImage = "thunder-d.jpeg";
      } else {
        bgImage = "thunder-n.jpg";
      }
      break;
    case conditionCode < 330: //Drizzle
      bgImage = "drizzle-d.jpg";
      break;
    case conditionCode < 540: //Rain
      if (timeDay === "d") {
        // bgImage = "rain-d.jpeg";
        bgImage = "rain1.jpg";
      } else {
        bgImage = "rain-on-black.jpg";
      }
      break;
    case conditionCode < 630: //Snow
      if (timeDay === "d") {
        bgImage = "snow-d.jpeg";
      } else {
        bgImage = "snow-n.jpeg";
      }
      break;
    case conditionCode < 790: //Atmospheric - haze, fog, mist
      if (timeDay === "d") {
        bgImage = "fog.jpg";
      } else {
        bgImage = "night-fog3.jpg";
      }
      break;
    case conditionCode === 800: //Clear
      if (timeDay === "d") {
        bgImage = "clear-d.jpeg";
      } else {
        bgImage = "clear-n.jpg";
      }
      break;
    case conditionCode < 805: //Clouds
      if (timeDay === "d") {
        bgImage = "cloudy-sky.jpg";
      } else {
        bgImage = "clouds-n.jpeg";
      }
      break;
    case conditionCode < 910: //Extreme - tropical storm, hail, hurricane, tornado
      bgImage = "rain-thunder.jpg";
      break;
    case conditionCode < 970: //Additional - breeze, wind, gale
      if (timeDay === "d") {
        bgImage = "wind-d.jpg";
      } else {
        bgImage = "wind-n.jpeg";
      }
      break;
    default:
      bgImage = "";
  }
  // bgImage = "cloudy-sky.jpg";
  updateBackgroundImage(bgImage);
}

function updateBackgroundImage(img) {
  // $("body").css("background-image", "url('" + pathToImages + img + "')");
  if ($("#background-images div.front").attr("rel") !== img) {
    // if this isn't already the bg image
    $("background-images div.back").attr("rel", img); // set rel to image name
    $("#background-images div.back").css(
      "background-image",
      "url('" + pathToImages + img + "')"
    ); //set background-image on .back div

    crossFadeImages(); // do the magic
  }
}

function crossFadeImages() {
  var $front = $("#background-images .front");
  var $back = $("#background-images .back");
  $front.fadeOut(crossFadeTime, function() {
    //fade out the top image
    $front.addClass("back").removeClass("front").show(); //remove class which resets z-index to 1 and unhide the image (which is now behind 'back') with show()
    $back.addClass("front").removeClass("back"); // give new image z-index of 3 from z-index 2
  });
}

// Debug panel, object iteration, & raw json output
function buildDebugPanel(json) {
  var html = "";

  html += "<p>" + new Date(Number(json.dt + "000")).toLocaleString() + "</p>";

  html += "<div class='ui horizontal segments'>";

  html += "<dl class='ui segment'>";
  html += "<dt>Location:</dt><dd>" + json.name + "</dd>"; // name
  html += "<dt>Temp:</dt><dd>" + convertCtoF(json.main.temp) + "</dd>"; //main.temp
  html += "<dt>High:</dt><dd>" + convertCtoF(json.main.temp_max) + "</dd>"; //main.temp_max
  html += "<dt>Low:</dt><dd>" + convertCtoF(json.main.temp_min) + "</dd>"; //main.temp_min
  // html += "<dt>Date/Time run:</dt><dd>" + new Date(Number(json.dt + '000')).toLocaleString() + "</dd>"; // dt
  html += "</dl>";

  html += "<dl class='ui segment'>";
  html += "<dt>Conditions:</dt><dd>" + json.weather[0].main + "</dd>"; // weather[0].main
  html += "<dt>Description:</dt><dd>" + json.weather[0].description + "</dd>"; //weather[0].description
  html +=
    "<dt>Icon:</dt><dd>" +
    "<img id='icon' src='" +
    (json.weather[0].icon || "#") +
    "'></dd>"; //weather[0].icon
  html += "</dl>";

  html += "<dl class='ui segment'>";
  html +=
    "<dt>Sunrise:</dt><dd>" +
    new Date(Number(json.sys.sunrise + "000")).toLocaleTimeString() +
    "</dd>"; //sys.sunrise
  html +=
    "<dt>Sunset:</dt><dd>" +
    new Date(Number(json.sys.sunset + "000")).toLocaleTimeString() +
    "</dd>"; //sys.sunset
  html += "</dl>";

  html += "<dl class='ui segment'>";
  html +=
    "<dt>Wind:</dt><dd>" +
    convertWindToMiles(json.wind.speed) +
    " " +
    windDirection(json.wind.deg) +
    "</dd>"; //wind.speed
  // html += "<dt>Direction:</dt><dd>" + windDirection(json.wind.deg) + "</dd>"; //wind.deg
  html += "<dt>Pressure:</dt><dd>" + json.main.pressure + " hPa</dd>"; //main.pressure
  html += "<dt>Humidity:</dt><dd>" + json.main.humidity + "%<br />"; //main.humidity
  html +=
    "<dt>Visibility:</dt><dd>" + convertVisToMiles(json.visibility) + "</dd>"; //visibility
  html += "</dl>";

  html += "<dl class='ui segment'>";
  html += "<dt>Country:</dt><dd>" + json.sys.country + "</dd>"; //sys.country
  html += "<dt>Latitude:</dt><dd>" + json.coord.lat + "</dd>"; // coord.lat
  html += "<dt>Longitude:</dt><dd>" + json.coord.lon + "</dd>"; // coord.lon
  html += "</dl>";

  html += "</div>";

  $("#hard-coded").html(html);

  // vanilla JS expression for fun
  document.getElementById("traverse-json").innerHTML = traverse(json, "");

  // jQuery expression for fun
  $("#json-fields-loop").html(jsonFieldsLoop(json, ""));

  $("#output").html(JSON.stringify(json));
}

// jQuery object loop
function jsonFieldsLoop(json, htmlStr) {
  // Loop done with jQuery
  $.each(json, function(k, v) {
    if (typeof v === "object") {
      htmlStr += k + "<br />";
      htmlStr = jsonFieldsLoop(v, htmlStr);
    } else {
      htmlStr += k + ": " + v + "<br />";
    }
  });

  return htmlStr;
}

// Vanilla JS object loop
function traverse(obj, htmlStr) {
  htmlStr += "<ul>";

  // Loop done with for..in which needs hasOwnProperty check
  for (var property in obj) {
    if (obj.hasOwnProperty(property)) {
      if (!!obj[property] && typeof obj[property] === "object") {
        htmlStr += "<li><b>" + property + "</b>";
        htmlStr = traverse(obj[property], htmlStr);
        htmlStr += "</li>";
      } else {
        switch (property) {
          case "sunrise":
          case "sunset":
            obj[property] = new Date(
              Number(obj[property] + "000")
            ).toLocaleTimeString();
            break;
          case "temp":
          case "temp_min":
          case "temp_max":
            obj[property] = convertCtoF(obj[property]);
            break;
        }
        htmlStr += "<li>" + property + ": " + obj[property] + "</li>";
      }
    }
  }
  htmlStr += "</ul>";
  return htmlStr;
}

// res ipsa ...
function shoutOutToMyPeeps() {
  $("#btn-Nairobi").popup({
    title: "Go Hufflepuff!ツ",
    content:
      "This is for my friend in Kenya who's gonna run her own game dev company one day!πŸ‘πŸ˜",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-Frankfurt").popup({
    html:
      "<div class='header'>She codes the best Bear on a Waffle!πŸ˜‚πŸ˜‚</div><div class='content'>This is really for my favorite cat Cuddle Bear!😍<br />His owner is pretty cool too. She has <strong>mad</strong> CSS skills!😎😁</div>",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-Lagos").popup({
    // title: "Representing Africa's West Side",
    html:
      "<div class='header'>Representing Africa's West Side</div><div class='content'>This is for my friend in Nigeria who likes πŸ”₯SimiπŸ”₯ and the finer things in life! πŸ’πŸ’ŽπŸ·πŸ˜Ž<br /><br/ >(BTW, 'Original Baby', 'One Kain', 'Smile For Me', 'Love Don't Care', & 'Jamb Question' all get πŸ‘πŸ‘)</div>",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-Cape-Cod").popup({
    title: "Living the dream!",
    content:
      "This is for my Twitter friend and fellow countryman on the East Coast who always has words of encouragement and support for his fellow coders.πŸ‘πŸ™πŸ™†πŸ‘Œ",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-London").popup({
    html:
      "<div class='header'>And the award goes to...πŸ‘πŸ˜ŽπŸ†</div><div class='content'>This is for my friend in London who's a star of the stage🎭🎢 and computer screen!πŸ’»πŸ˜<br /><br />I use her post on how to break down arrow functions regularly!πŸ‘πŸ˜ƒ</div>",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-Paris").popup({
    title: "For my Ryan Reynolds😍 Fan Clubβ„’ friends..",
    html:
      "<div class='header'>For my Ryan Reynolds😍 Fan Clubβ„’ friends..</div><div class='content'>I don't know where you two live* but I think Paris is the perfect place for soul mates to meet for coffee.😝😁<br><br>* Laniakea... Really? the Local Supercluster home to our Milky Way Galaxy?... Doesn't really narrow it down, does it... and what is this place called 'Germany' for that matter?😏😜",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
  $("#btn-Los-Angeles").popup({
    title: "πŸ™‹βœŒ My Hood. 😎😜",
    content:
      "This is for me so I can see what the β›…weather's like before a run πŸŽ½πŸƒor hikeπŸŽ’πŸ‘Ÿ.",
    variation: "wide",
    position: "left center",
    inline: "true"
  });
}
<div id="background-images">
  <div class="bgImages front" id="bgImg1"><div class="view"><!--Img 1 - front--></div></div>
  <div class="bgImages back" id="bgImg2"><br><div class="view"><!--Img 2 - back--></div></div>
</div>

<main class="ui fluid container" role="main" id="main">
  <section class="ui vertical masthead center aligned segment">
    <div class="ui text container" id="content">
      <h1 id="header">Local Weather App</h1>
      <i id="debug-btn" class="link bordered setting icon" ></i>

      <p><i>Check out some of the cities I have Twitter friends in or click "Use my location".</i></p>

      <div class="ui three column stackable grid" id="row">

        <div class="four wide column">
          <div id="compass-label">Wind Direction</div>
          <div class="animate" id="compass-deg">0.00</div>
          <div class="compass">
            <img class="arrow" src="https://local-weather-app.netlify.com/images/arrow.svg" alt="arrow">
            <img class="disc" src="https://local-weather-app.netlify.com/images/disc.svg" alt="disc"> 
          </div>
          <div class="ui segment" id="compass-stats">
            <div class="hidden">Code: <span id="conditionCode"></span></div>
            <div>Lat: <span class="animate" id="lat">0.000000</span></div>
            <div>Lon: <span class="animate" id="lon">0.000000</span></div>
          </div>
        </div>
        <div class="eight wide column">
          <div class="ui segment" id="temp-group">
            <div class="animate" id="loc-header-container">
              <div class="ui large header" id="city">City</div>
              <img id="icon" src="#" alt="icon">
            </div>
            <div class="animate" id="localTime">Time &amp; Day</div>
            <div class="ui huge header animate" id="temp">0</div>
            <div id="high-low"><span id="high" class="ui red label animate" data-tooltip="Today's High">Hi</span> <span id="low" class="ui blue label animate" data-tooltip="Today's Low">Low</span></div>
            <div><span class="animate" id="sky">Condition</span> with <span class="animate" id="humidity">0%</span> humidity</div>
            <div>Wind: <span class="animate" id="wind-dir">Direction</span> at <span class="animate" id="wind-speed">0 mph</span></div>
            <div>Visibility: <span class="animate" id="vis">0.00 mi</span></div>
            <div class="hidden">Sunrise: <span id="sunrise"></span> - Sunset: <span id="sunset"></span></div>
          </div>
        </div>
        <div class="four wide column" id="city-buttons">
          <button class="ui button" id="btn-Nairobi">Nairobi</button>
          <button class="ui button" id="btn-Frankfurt">Frankfurt</button>
          <button class="ui button" id="btn-Lagos">Lagos</button>
          <button class="ui button" id="btn-Cape-Cod">Cape Cod</button>
          <button class="ui button" id="btn-London">London</button>
          <button class="ui button" id="btn-Paris">Paris</button>
          <button class="ui button" id="btn-Los-Angeles">Los Angeles</button>
        </div>

        <div class="four wide column">
          <div class="ui buttons tiny" id="temp-toggle">
            <button class="ui button cf-toggle" id="metric">C</button>
            <div class="or"></div>
            <button class="ui button cf-toggle active positive" id="imperial">F</button>
          </div>
        </div>  
        <div class="eight wide column bg-controls">
          <a class="ui red empty circular label" id="bgImage-btn0"></a>
          <a class="ui orange empty circular label" id="bgImage-btn1"></a>
          <a class="ui yellow empty circular label" id="bgImage-btn2"></a>
          <a class="ui olive empty circular label" id="bgImage-btn3"></a>
          <a class="ui green empty circular label" id="bgImage-btn4"></a>
          <a class="ui teal empty circular label" id="bgImage-btn5"></a>
          <a class="ui blue empty circular label" id="bgImage-btn6"></a>
          <a class="ui violet empty circular label" id="bgImage-btn7"></a>
          <a class="ui purple empty circular label" id="bgImage-btn8"></a>
          <a class="ui pink empty circular label" id="bgImage-btn9"></a>
          <a class="ui brown empty circular label" id="bgImage-btn10"></a>
          <a class="ui grey empty circular label" id="bgImage-btn11"></a>
          <a class="ui black empty circular label" id="bgImage-btn12"></a>
          <a class="ui red empty circular label" id="bgImage-btn13"></a>
          <a class="ui orange empty circular label" id="bgImage-btn14"></a>
          <a class="ui yellow empty circular label" id="bgImage-btn15"></a>
        </div>
        <div class="four wide column">
          <button class="ui button positive tiny" id="use-my-location">Use my location</button>
        </div>
      </div>

    </div> 
  </section>

  <div class="ui text container" id="debug-container">
    <h2 class ="ui small center aligned top attached block header">Debug Info</h2>
    <section class="ui attached segment" id="debug-content">

      <div class="ui top attached tabular stackable menu">
        <a class="active item column" data-tab="first">Hard coded</a>
        <a class="item column" data-tab="second">Traverse JSON</a>
        <a class="item column" data-tab="third">JSON Fields Loop</a>
        <a class="item column" data-tab="fourth">Raw Output</a>
      </div>

      <div class="ui bottom attached active tab segment" data-tab="first" id="hard-coded">

      </div>
      <div class="ui bottom attached tab segment " data-tab="second">
        <div id="traverse-json"></div>
      </div>
      <div class="ui bottom attached tab segment" data-tab="third">
        <!-- <h3>JSON Fields loop</h3> -->
        <div id="json-fields-loop"></div>
      </div>
      <div class="ui bottom attached tab segment" data-tab="fourth">
        <!-- <h3>Raw Output</h3> -->
        <div id="output"></div>
      </div>
    </section>
  </div>

</main>

FCC Local Weather App

Uses Semantic-UI, jQuery, Ajax, JSONP, Web APIs, SVG & CSS transitions. Free Code Camp Intermediate Front End Development Project.

A Pen by HARUN PEHLΔ°VAN on CodePen.

License.