Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
executable file 229 lines (207 sloc) 6.76 KB
<meta charset="utf-8" />
<!-- Include Bootstrap 4 CSS -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<!-- Include Font Awesome 5 -->
<link rel="stylesheet" href="" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<title>PBS 83 — Currency Converter</title>
<!-- The Page header (a Jumbotron) -->
<header class="container mt-5">
<div class="jumbotron">
<h1 class="display-4">PBS 83 <small class="text-muted">Currency Converter</small></h1>
<p class="lead">A sample solution to the challenge set in <a href="" target="_blank">instalment 83</a> of the <a href="" target="_blank">Programming by Stealth series</a>.</p>
<!-- The main page body -->
<main class="container">
<div class="row">
<div class="col">
<h1 class="display-4">
<i class="fas fa-coins"></i>
Current Exchange Rates
<div class="row no-gutters" id="currency_cards">
<div class="col text-center">
<div class="spinner-border m-5" role="status">
<span class="sr-only">Loading...</span>
<div class="row">
<div class="col text-center text-muted">
<small>Currency data from <a href="" target="_blank" rel="noopener"></a></small>
<!-- Include Bootstrap JavaScript from CDNs -->
<script src="" integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
<script src="" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<!-- Include Mustache.js -->
<script src="" integrity="sha256-srhz/t0GOrmVGZryG24MVDyFDYZpvUH2+dnJ8FbpGi0=" crossorigin="anonymous"></script>
<!-- This Page's Local JavaScript Code -->
<script type="text/javascript">
// Define helper variables & functions
// constants
var CURRENCIES = { // a dicrionary of currency data indexed by code
AUD: {
name: 'Australian Dollar',
symbol: '$',
icon: '<i class="fas fa-dollar-sign"></i>'
CAD: {
name: 'Canadian Dollar',
symbol: '$',
icon: '<i class="fas fa-dollar-sign"></i>'
EUR: {
name: 'Euro',
symbol: '',
icon: '<i class="fas fa-euro-sign"></i>'
GBP: {
name: 'British Pound',
symbol: '£',
icon: '<i class="fas fa-pound-sign"></i>'
NZD: {
name: 'New Zealand Dollar',
symbol: '$',
icon: '<i class="fas fa-dollar-sign"></i>'
USD: {
name: 'US Dollar',
symbol: '$',
icon: '<i class="fas fa-dollar-sign"></i>'
var DEFAULT_CURRENCIES = ['EUR', 'GBP', 'USD']; // the default currencies to load
var DISPLAY_CURRENCIES = ['AUD', 'EUR', 'USD', 'GBP', 'CAD', 'NZD']; // the currencies to display the rates against
// globally scoped helper variables
var CURRENCY_CARD_TPL = ''; // loaded by document ready handler
// Document ready handler
// load the templates
CURRENCY_CARD_TPL = $('#currencyCardTpl').html();
// load the currencies
const cardPromises = [];
for(const cur of DEFAULT_CURRENCIES){
function(cards){ // success handler
const $cards = $('#currency_cards');
for(const card of cards){
function(err){ // error handler
console.error('failed to load currencies with error:', err);
renderError('Failed to load currency data 🙁');
// Helper functions
* An asynchtonous function to generate the HTML for a card for a given
* currency.
* @param {string} curCode - The three-letter code for the currency to load.
* @return {string} Returns the HTML for a currency card.
* @throws {TypeError} A type error is throw if the currency code is not valid.
async function buildCurrencyCard(curCode){
// validate the currency code
curCode = String(curCode).toUpperCase();
throw new TypeError(`Invalid country code: ${curCode}`);
// fetch the data for the currency
const curData = await $.ajax({ // could throw Error
method: 'GET',
cache: false,
data: {
base: curCode
console.debug(`received currency data for '${curCode}': `, curData);
// build the view for the card
const cardView = {
base: {
code: curData.base,
rates: []
for(const cur of DISPLAY_CURRENCIES){
if(cur === curData.base) continue; // skip self
code: cur,
rate: curData.rates[cur],
console.debug('generated view:', cardView);
// generate and return the HTML
return Mustache.render(CURRENCY_CARD_TPL, cardView);
* A function to render an error message to the user
* @param {string} message
function renderError(message){
.append(Mustache.render($('#errorCardTpl').html(), {message}));
<!-- The Error template -->
<script type="text/html" id="errorCardTpl">
<div class="col">
<div class="card bg-danger text-white">
<h2 class="card-header h4">Error</h2>
<div class="card-body">
<p class="card-text">{{message}}</p>
<!-- The card template -->
<script type="text/html" id="currencyCardTpl">
<div class="col-12 col-md-6 col-lg-4">
<div class="card m-3">
<h2 class="card-header h4">
{{{base.icon}}} {{}}
<small class="badge badge-primary badge-pill float-right">{{base.code}}</small>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<h3 class="d-inline h6 mr-2 text-secondary">{{code}}</h3>
{{base.symbol}}1 = {{symbol}}{{rate}}
<small class="text-muted">{{name}}</small>
You can’t perform that action at this time.