We’ll be building a weather app this time around; this project will serve as a nice introduction to a language that has been embraced by Angular 2, and can prove to be tremendously powerful: TypeScript.
You may have heard of TypeScript before, it’s merely a superset of JavaScript (it get’s compiled to plain JavaScript), but provides us with tools only available to ECMAScript 6 and beyond right now (such as classes!).
It also gives us strong typing, a feature that JavaScript lacks and can prove to be a lifesaver. We are going to take it slow and introduce TypeScript features as we along. If you want to get a feel for TypeScript before we get started, go ahead and take a look at the documentation. If you hate AngularJS, TypeScript or strong typing, stay tuned, a ReactJS + JSX version of this tutorial will come along shortly can be found here.
Building the Weather App
First of all, let’s define what our application must do:
- User Story: I can see the weather in my current location.
- User Story: I can see a different icon or background image depending on the weather.
- User Story: I can push a button to toggle between Fahrenheit and Celsius.
We are going to be using AngularJS for this project. If you are not at least familiar with the framework, I recommend that you wait for the upcoming check out the ReactJS + JSX tutorial or wait for the upcoming plain JavaScript version.
Our application will use a single controller (we’ll call it MainController) and two services:
- Geolocation service: It will give us the users location (coordinates), so we can show him/her the local weather.
- Weather service: It will give us the current weather for a given location (using coordinates).
For the geolocation service, we will take advantage of the browsers geolocation provider, and fall back to the ipinfo API for older browsers.
For the weather service, we’ll use the openweather API.
Let’s start by getting the markup ready, it’s going to be quite simple:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Weather App</title> <link rel="stylesheet" href="styles.css"/> <!-- Stylesheet --> </head> <body>
Here’s the stylesheet (we’ll make it fancier later on):
@import url(https://fonts.googleapis.com/css?family=Montserrat:700|Source+Sans+Pro:200|Work+Sans:100,200); * { margin: 0; padding: 0; box-sizing: border-box; } html { font-family: 'Work Sans', Helvetica, Arial, sans-serif; color: #fff; text-align: center; } body { background: url(img/rain.jpg) no-repeat center center fixed; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; } .main-wrapper { display: flex; justify-content: center; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(10, 10, 10, 0.7); } .forecast-box { align-self: center; width: 100%; max-width: 400px; height: 90vh; } .city-name { font-family: Montserrat, Helvetica, Arial, sans-serif; font-weight: 700; } .country { font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-weight: 200; } .temperature { margin-top: 15px; font-weight: 100; letter-spacing: -0.3rem; font-size: 5rem; } .super-small { cursor: pointer; font-size: 1.5rem; font-weight: 200; letter-spacing: 0; }
Let’s get our hands dirty and start working on the weather app itself. We are going to create a file called app.ts. That’s the extension for TypeScript files, we’ll compile them into regular .js files before deploying.
If you are not familiar with TypeScript and Typings, you can ignore the first line altogether. In this file, we are creating our app as you would normally do in any Angular application. Then, we are creating a class (we’ll leave it empty for now) and then registering that class as a controller for our app:
declare var angular; // Ignore this line for the time being var app = angular.app('weatherApp', []); class MainController {} app.controller('MainController', MainController);
Now, we’ll give our class a few properties, and specify the type for each one of them. We’ll later bind these properties to the view:
declare var angular; // Ignore this line for the time being var app = angular.app('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; } app.controller('MainController', MainController);
You may wonder what in the world those lines mean, it’s actually pretty simple, we are basically saying that MainController has:
- city: A property accessible from outside this class (public) and will contain a string.
- country: A public property that contains a string.
- currentWeather: A public property that contains a string.
- currentTemperature: A public property that contains a number.
- currentUnit: A public property that contains a string.
- availableUnit: A public property that contains a string.
These properties are public because we want to be able to access them from other parts of the application. Properties only accessible within the class itself are private properties.
Now, we are also going to give it two methods:
- fetchWeather: Takes in a temperature unit and calls our future weather service to get weather data using the location provided by the also future geolocation service.
- setWeather: Will take the weather data retrieved from the previous method and set the current data, so the user can see it.
Let’s add these to the code, setWeather will only be called from within the class itself (it doesn’t require user interaction) so we’ll make it private; fetchWeather will be user triggered action though, so we’ll make this one public:
declare var angular; // Ignore this line for the time being var app = angular.app('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; private fetchWeather(units: string): void {} private setWeather(weatherData: any): void {} } app.controller('MainController', MainController);
Notice how we are explicitly stating the type of the input arguments and return values. In the case of fetchWeather, the input argument, units, will be a string, and the output (the return value) will be void, because we are not going to return anything.
Before we finish this guy up, we are going to need our geolocation and weather services though, so let’s get that fixed. Each one of them is going to live in it’s own file, in a folder that we’ll conveniently name services. Here’s a quick overview of the end file structure:
weather-app | | - app.js | - index.html | - styles.css | - services | | - geolocationService.js | | - weatherService.js | | - img | - node_modules
Let’s start with the weather service. Yes, we will need our lovely geolocation service to give us the coordinates (latitude and longitude) that we need to request the weather data, but, for now, let’s just hardcoded coordinate values.
Since we are going to work in an external file, let’s bring in our application first. Then, we are going to create a class with two methods:
- getWeather: Takes in latitude, longitude and units (celsius/fahrenheit) as arguments and returns the weather data returned by openweather.
- getUrl: Takes in latitude, longitude and units, and returns the properly formatted Openweather API URL.
We are also going to define 2 private properties for this class:
- BASE_URL: The base URL for the API call.
- API_KEY: Our API key, obtained from Openweathermap for free when registered.
Here’s our weatherService.ts file:
declare var angular; var app = angular.module('weatherApp'); // We bring in our app class WeatherService { private BASE_URL: string = "http://api.openweathermap.org/data/2.5/weather?"; private API_KEY: string = "YOUR_API_KEY"; public getWeather(lat, lon, units) {} private getUrl(lat, lon, units) { var parsedUnits = units === 'C' ? 'metric' : 'imperial'; return this.BASE_URL + 'units=' + parsedUnits + '&lat=' + lat + '&lon=' + lon + '&appid=' + this.API_KEY + '&callback=JSON_CALLBACK'; } }
First, we set both, BASE_URL and API_KEY. Notice that, we can set the type and initialize them with a value in the same line.
Then, we define getWeather (it does nothing yet) and getUrl, that takes latitude, longitude and units, uses the units (C or F) to then return the API url with the format that OpenWeatherMap specifies. The &callback=JSON_CALLBACK part is AngularJS specific, and required in any call that will return JSON.
Let’s now write getWeather. We are going to need an Angular service for this guy, $http. To inject angular services within our TypeScript class, we need to give our class the $inject property (it’s going to be a static member) that contains an array of strings (each string is the name of an Angular service that we need to bring in) and then pass them to the constructor function in the same order as they appear in the array. Easier seen than explained:
declare var angular; var app = angular.module('weatherApp'); // We bring in our app class WeatherService { private BASE_URL: string = "http://api.openweathermap.org/data/2.5/weather?"; private API_KEY: string = "YOUR_API_KEY"; static $inject = ['$http']; constructor(private $http) {} public getWeather(lat, lon, units) {} private getUrl(lat, lon, units) { var parsedUnits = units === 'C' ? 'metric' : 'imperial'; return this.BASE_URL + 'units=' + parsedUnits + '&lat=' + lat + '&lon=' + lon + '&appid=' + this.API_KEY + '&callback=JSON_CALLBACK'; } }
We can now freely use $http anywhere within our class. Let’s use it to get the weather and then, register our class as a service!
declare var angular; var app = angular.module('weatherApp'); // We bring in our app class WeatherService { private BASE_URL: string = "http://api.openweathermap.org/data/2.5/weather?"; private API_KEY: string = "YOUR_API_KEY"; static $inject = ['$http']; constructor(private $http) {} public getWeather(lat, lon, units) { return this.$http.jsonp(this.getUrl(lat, lon, units)); } private getUrl(lat, lon, units) { var parsedUnits = units === 'C' ? 'metric' : 'imperial'; return this.BASE_URL + 'units=' + parsedUnits + '&lat=' + lat + '&lon=' + lon + '&appid=' + this.API_KEY + '&callback=JSON_CALLBACK'; } } app.service('WeatherService', WeatherService);
Here, we are using the jsonp() method of the $http service, you can read up on it here. We are returning whatever comes back from the $http service, as a promise. Promises let us execute code asynchronously and wait for the return value before processing it. You’ll see how powerful this is once we call this method from the app itself.
Our weather service is now ready, let’s move onto geolocation now! We know that the weather service needs latitude and longitude to request the weather data. This means, that the geolocation service should return exactly that, latitude and longitude.
Remember that we are going to use the HTML5 geolocation service by default, and fall back to the ipinfo.io API for older browsers.
Since we are going to work in an external file, let’s bring in our application first. Then, we are going to create a class with two methods:
- getLocation: Public method that will return the coordinates for the users location.
- getIpLocation: Private method that getLocation will use internally when the browser does not provide HTML5 geolocation.
We’ll finally register this class as an angular service. Here’s what it looks like:
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { public getLocation() {} private getIpLocation() {} } app.service('GeolocationService', GeolocationService);
Let’s start with getLocation. We are going to be using 3 different Angular services in this method, so we first need to inject them into our service. They are going to be:
- $window: A reference to the browsers window object.
- $q: An implementation of promises/deferred objects. It allows asynchronously executing functions and using the return values when they become available.
- $http: This one we’ve used before! It’s basically a wrapper for AJAX and JSONP calls.
We inject do so as we did with $http service before. Remember, that the order of the items in the $inject array must be the same as the order of the arguments passed into the constructor:
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { static $inject = ['$q', '$window', '$http']; constructor(private $q, private $window, private $http) {} public getLocation() {} private getIpLocation() {} } app.service('GeolocationService', GeolocationService);
Now, we can freely use $q, $window and $http within our class. Let’s use a couple of them in the getLocation method. We can access the web browsers’ geolocation service by accessing the $window.navigator.geolocation.getCurrentPosition() method. This is an asynchronous method. It will not return the result immediately, so we need to make sure that our controller waits for the results before it tries to request weather data. We can do that using the $q service. There is a few ways to implement this behaviour, but we’ll use the simplest approach (not the best, but we’ll get to that in the future) and create a deferred object.
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { static $inject = ['$q', '$window']; constructor(private $q, private $window) {} public getLocation() { var deferred = this.$q.defer(); if (this.$window.navigator.geolocation) { this.$window.navigator.geolocation.getCurrentPosition( (location) => { // We got the location! }, (error) => { // We got an error 😦 } ); } else { // If no HTML5 geolocation is available, we use getIpLocation() } return deferred.promise; } private getIpLocation() {} } app.service('GeolocationService', GeolocationService);
Notice those strange looking functions inside getCurrentPosition(). Geolocation takes two arguments. They will both be functions, the first is only executed when a successful result is returned, and the second is only executed when an error comes back. We could do it this way:
window.navigator.getCurrentLocation(successFunction, errorFunction); function successFunction(locationData) { // Do something with the location data. } function errorFunction(errorMessage) { // Do something with the error message, like: console.error(error); }
That’s all good and nice, but it does not answer the question at hand, we are not simply writing those functions inline, we are using the arrow =>. That’s because they are arrow functions! 🙂 Arrow functions allow us to keep the this keyword pointing at whatever the outer scope was rather than the function and additionally, are shorter to write. Look at this example:
function MyClass() { this.text = 'Hello World!'; this.saySomething = function() { setTimeout(function() { console.log(this.text); }); setTimeout(() => { console.log(this.text); }); } } var myClassInstance = new MyClass(); myClassInstance.saySomething(); // undefined, this is a reference to the window object // 'Hello World!', this is a reference to MyClass
We got miscarried, didn’t we, let’s get back to the geolocation service. We’ve got the mainframe ready, let’s start filling in the gaps.
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { static $inject = ['$q', '$window']; constructor(private $q, private $window) {} public getLocation() { var deferred = this.$q.defer(); if (this.$window.navigator.geolocation) { this.$window.navigator.geolocation.getCurrentPosition( (location) => { deferred.resolve({ lat: location.coords.latitude, lon: location.coords. }); }, (error) => { deferred.reject(error); } ); } else { // If no HTML5 geolocation is available, we use getIpLocation() } return deferred.promise; } private getIpLocation() {} } app.service('GeolocationService', GeolocationService);
Now, we are going to implement getIpLocation. It’s very similar to how we fetch weather data from openweather:
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { static $inject = ['$q', '$window']; constructor(private $q, private $window) {} public getLocation() { var deferred = this.$q.defer(); if (this.$window.navigator.geolocation) { this.$window.navigator.geolocation.getCurrentPosition( (location) => { deferred.resolve({ lat: location.coords.latitude, lon: location.coords. }); }, (error) => { deferred.reject(error); } ); } else { // If no HTML5 geolocation is available, we use getIpLocation() } return deferred.promise; } private getIpLocation() { return this.$http.jsonp('http://ipinfo.io/json?callback=JSON_CALLBACK'); } } app.service('GeolocationService', GeolocationService);
The $http service will actually return a promise, since we have to wait for the data to come back from the API call. This is how we wait for this data in the getLocation method:
declare var angular; var app = angular.module('weatherApp'); class GeolocationService { static $inject = ['$q', '$window']; constructor(private $q, private $window) {} public getLocation() { var deferred = this.$q.defer(); if (this.$window.navigator.geolocation) { this.$window.navigator.geolocation.getCurrentPosition( (location) => { deferred.resolve({ lat: location.coords.latitude, lon: location.coords.longitude }); }, (error) => { deferred.reject(error); } ); } else { this.getIpLocation().then( (location) => { // Successfully retrieved location data. deferred.resolve({ lat: location.data.loc.split(',')[0], // We get the coordinates in a string, lon: location.data.loc.split(',')[0] // so we split it and return them separately. }); }, (error) => { deferred.reject(error); } ); } return deferred.promise; } private getIpLocation() { return this.$http.jsonp('http://ipinfo.io/json?callback=JSON_CALLBACK'); } } app.service('GeolocationService', GeolocationService);
We are almost there, we just need to finish up the MainController:
declare var angular; var app = angular.app('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; static $inject = ['GeolocationService', 'WeatherService']; // Remember this? We now use it for own own services! constructor(private GeolocationService, private WeatherService) {} private fetchWeather(units: string): void { // We can use then(), because we are returning the deferred object! It will execute the first function if we resolve() or the second if we reject() this.GeolocationService.getLocation().then( (location) => { this.WeatherService.getWeather(location.lat, location.lon, units).then( (weatherData) => { this.setWeather(weatherData); }, (error) => { // Got an error 😦 } ); } ); } private setWeather(weatherData: any): void {} } app.controller('MainController', MainController);
We need to do something useful with that error now, let’s bring in the Angular $log service (merely a wrapper for console.log) and use it:
declare var angular; var app = angular.app('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; static $inject = ['GeolocationService', 'WeatherService', '$log']; constructor(private GeolocationService, private WeatherService, private $log) {} private fetchWeather(units: string): void { this.GeolocationService.getLocation().then( (location) => { this.WeatherService.getWeather(location.lat, location.lon, units).then( (weatherData) => { this.setWeather(weatherData); }, (error) => { this.$log.error(error); } ); } ); } private setWeather(weatherData: any): void {} } app.controller('MainController', MainController);
We’ll also update the units when we fetch the weather:
private fetchWeather(units: string): void { this.GeolocationService.getLocation().then( (location) => { this.WeatherService.getWeather(location.lat, location.lon, units).then( (weatherData) => { this.currentUnit = units; this.availableUnit = units === 'C' ? 'F' : 'C'; this.setWeather(weatherData); }, (error) => { this.$log.error(error); } ); } } );
We are still missing setWeather, let’s do it now:
private setWeather(weatherData) { this.city = weatherData.data.name; this.country = weatherData.data.sys.country; this.currentTemperature = weatherData.data.main.temp; this.currentWeather = weatherData.data.weather[0].main; }
Finally, we need to call fetchWeather as soon as someone lands on this page, we do that by executing it in the constructor:
declare var angular; var app = angular.module('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; static $inject = ['GeolocationService', 'WeatherService', '$log']; constructor(private GeolocationService, private WeatherService, private $log) { this.fetchWeather('C'); // We call fetchWeather with C as default unit. Because Europe 🙂 } private fetchWeather(units: string): void { this.GeolocationService.getLocation().then( (location) => { this.WeatherService.getWeather(location.lat, location.lon, units).then( (weatherData) => { this.currentUnit = units; this.availableUnit = units === 'C' ? 'F' : 'C'; this.setWeather(weatherData); }, (error) => { this.$log.error(error); } ); } ); } private setWeather(weatherData): void { this.city = weatherData.data.name; this.country = weatherData.data.sys.country; this.currentTemperature = weatherData.data.main.temp; this.currentWeather = weatherData.data.weather[0].main; } } app.controller('MainController', MainController);
We only have one step left. Showing our data in the view! Let’s open up index.html and set the application and controller attributes first:
<!DOCTYPE html> <html lang="en" ng-app="weatherApp"> <head> <meta charset="UTF-8"> <title>Weather App</title> <link rel="stylesheet" href="styles.css"/> <!-- Stylesheet --> </head> <body ng-controller="MainController as vm">
Setting the controller as vm will allow us to access controller properties within the html code using the vm (view-model) object. Let’s update the hard-coded values with real data:
<!DOCTYPE html> <html lang="en" ng-app="weatherApp"> <head> <meta charset="UTF-8"> <title>Weather App</title> <link rel="stylesheet" href="styles.css"/> <!-- Stylesheet --> </head> <body ng-controller="MainController as vm">
Then, we’ll bind the available unit <span> element to trigger a weather fetch using the alternative units. Since we are here, we’ll also bring in the JS files for our services:
<!DOCTYPE html> <html lang="en" ng-app="weatherApp"> <head> <meta charset="UTF-8"> <title>Weather App</title> <link rel="stylesheet" href="styles.css"/> <!-- Stylesheet --> </head> <body ng-controller="MainController as vm">
Nice and dandy, our app is now working! But we can always…
Make it sexy
We are going to change our app a little. Instead of showing a boring, lifeless string (rain, clear, sunny etc.) how about we change the page background to something that relates to the current weather? Sounds like a good idea? Let’s do it!
Our style.css file is already setting the background image to be a picture. Instead of this, we are going to create a new css class for each type of weather, and set an appropriate background image as a background colour. Don’t forget to remove the default background image!
We have a little problem though, there’s quite a few different weather scenarios listed in OpenWeatherMap, you can see them all here. Instead of using the weather name, we are going to take a look at the code that we also get back from the fetchWeather function. We’ll use it in ranges to set the background image and not go crazy in the process, you can see all of the codes in this page, but we can summarize it in:
- 2xx (200-232): Thunderstorm
- 3xx (300-321): Drizzle
- 5xx (500-531): Rain
- 6xx (600-622): Snow
- 7xx (701-781): Atmosphere (this ones a bit special, it encompasses fog, sand, volcanic ash and a few more, we’ll just use fog, you can go ahead and expand on the app later on!)
- 800: Clear
- 80x (801-804): Clouds
- 90x (900-906): Extreme (tornadoes, hail, hurricanes etc.)
- 9xx (951-962): Additional
Let’s open up app.ts and implement this behaviour in the setWeather method. Let’s add a new property to the MainController and add a few if statements to the setWeather function:
declare var angular; var app = angular.module('weatherApp', []); class MainController { public city: string; public country: string; public currentWeather: string; public currentTemperature: number; public currentUnit: string; public availableUnit: string; public weatherClass: string; static $inject = ['GeolocationService', 'WeatherService', '$log']; constructor(private GeolocationService, private WeatherService, private $log) { this.fetchWeather('C'); // We call fetchWeather with C as default unit. Because Europe 🙂 } private fetchWeather(units: string): void { this.GeolocationService.getLocation().then( (location) => { this.WeatherService.getWeather(location.lat, location.lon, units).then( (weatherData) => { this.currentUnit = units; this.availableUnit = units === 'C' ? 'F' : 'C'; this.setWeather(weatherData); }, (error) => { this.$log.error(error); } ); } ); } private setWeather(weatherData): void { this.city = weatherData.data.name; this.country = weatherData.data.sys.country; this.currentTemperature = weatherData.data.main.temp; this.currentWeather = weatherData.data.weather[0].main; var weatherCode = weatherData.data.weather[0].id; if (weatherCode >= 200 && weatherCode < 300) { this.weatherClass = 'thunderstorm'; } else if (weatherCode >= 300 && weatherCode < 400) { this.weatherClass = 'drizzle'; } else if (weatherCode >= 500 && weatherCode < 600) { this.weatherClass = 'rain'; } else if (weatherCode >= 600 && weatherCode < 700) { this.weatherClass = 'snow'; } else if (weatherCode >= 700 && weatherCode < 800) { this.weatherClass = 'atmosphere'; } else if (weatherCode === 800) { this.weatherClass = 'clear'; } else if (weatherCode >= 801 && weatherCode < 900) { this.weatherClass = 'clouds'; } else if (weatherCode >= 900 && weatherCode < 907) { this.weatherClass = 'extreme'; } else if (weatherCode >= 907 && weatherCode < 1000) { this.weatherClass = 'additional'; } else { this.weatherClass = 'unknown'; } } } app.controller('MainController', MainController);
Now, let’s change the body tag in the index.html file:
<!DOCTYPE html> <html lang="en" ng-app="weatherApp"> <head> <meta charset="UTF-8"> <title>Weather App</title> <link rel="stylesheet" href="styles.css"/> <!-- Stylesheet --> </head> <body ng-controller="MainController as vm" ng-class="vm.weatherClass">
Now, the body element will have a different class depending on the weather that we got back from OpenWeatherMap. Let’s create a few styles and grab a few images to display. This is the new styles.css file:
@import url(https://fonts.googleapis.com/css?family=Montserrat:700|Source+Sans+Pro:200|Work+Sans:100,200); * { margin: 0; padding: 0; box-sizing: border-box; } html { font-family: 'Work Sans', Helvetica, Arial, sans-serif; color: #fff; text-align: center; } body { background-repeat: no-repeat; background-position: center center; background-attachment: fixed; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; } .thunderstorm { background-image: url(img/thunderstorm.jpg); } .drizzle { background-image: url(img/drizzle.jpg); } .rain { background-image: url(img/rain.jpg); } .snow { background-image: url(img/snow.jpg); } .atmosphere { background-image: url(img/atmosphere.jpg); } .clear { background-image: url(img/clear.jpg); } .clouds { background-image: url(img/clouds.jpg); } .extreme { background-image: url(img/extreme.jpg); } .additional { background-image: url(img/other.jpg); } .unknown { background-image: url(img/other.jpg); } .main-wrapper { display: flex; justify-content: center; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(10, 10, 10, 0.2); } .forecast-box { align-self: center; width: 100%; max-width: 400px; height: 90vh; } .city-name { font-family: Montserrat, Helvetica, Arial, sans-serif; font-weight: 700; } .country { font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-weight: 200; } .temperature { margin-top: 15px; font-weight: 100; letter-spacing: -0.3rem; font-size: 5rem; } .super-small { cursor: pointer; font-size: 1.5rem; font-weight: 200; letter-spacing: 0; }
There we go! Our weather app is working, you can see a demo here. If you want to make it better looking, go ahead and improve on it, I’m sure it’ll be glorious!