<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Ivan Hayes]]></title><description><![CDATA[Ivan Hayes]]></description><link>https://ivanhayes.com/blog/</link><image><url>http://ivanhayes.com/blog/favicon.png</url><title>Ivan Hayes</title><link>https://ivanhayes.com/blog/</link></image><generator>Ghost 1.22</generator><lastBuildDate>Thu, 26 Mar 2026 21:00:29 GMT</lastBuildDate><atom:link href="https://ivanhayes.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Creating a Web-app with Grunt – Part 2]]></title><description><![CDATA[This is the final instalment of a two-part tutorial, in which we have been creating a web-app – Breeze – that will load and display temperature information for various locations using data from the OpenWeatherMap api.]]></description><link>https://ivanhayes.com/blog/creating-a-web-app-with-grunt-part-2/</link><guid isPermaLink="false">5af1c08d7ce7f61014b7fbaf</guid><category><![CDATA[javascript]]></category><category><![CDATA[sass]]></category><category><![CDATA[grunt]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Sun, 25 Jan 2015 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>This is the final instalment of a two-part tutorial, in which we have been creating a web-app – Breeze – that will load and display temperature information for various locations using data from <a href="http://openweathermap.org/">OpenWeatherMap</a>.</p>
<!--more-->
<p>In this instalment, we will build on what we covered previously during <a href="http://ivanhayes.com/blog/creating-a-web-app-with-grunt-part-1/">the first part of the tutorial</a> in order to create the logic of the <em>Breeze</em> application – loading and displaying current temperature information for various locations by making AJAX requests to the <a href="http://openweathermap.org/api">OpenWeatherMap API</a>. We’ll also improve the Grunt workflow and briefly look at SCSS variables and mixins.</p>
<p>Whether you’re short on time and just want to see the code for the complete project, or simply want to follow along, then you can <a href="https://github.com/munkychop/breeze/fork">fork the repo on Github</a> and switch to the ’tutorial-part-2’ branch.</p>
<p>If, for whatever reason, you don’t want to fork or clone the project, but want to get at the code, then you can <a href="https://github.com/munkychop/breeze/archive/tutorial-part-2.zip">download the whole project as a zip file</a>.</p>
<p>Once you have the project, open Terminal and ensure that you change to the ‘breeze’ directory before running the following command to install dependencies:</p>
<pre><code class="language-sh">npm install
</code></pre>
<h2 id="improvingthegrunttaskworkflow">Improving the Grunt Task Workflow</h2>
<p>There are quite a few things that we could be doing differently in order to make the build workflow and overall code quality of our project better; by updating the Gruntfile setup we will be improving the process of loading tasks, as well as the browser coverage of our compiled CSS code. We will also automate more tasks in order to make development simpler and faster.</p>
<h3 id="loadinggrunttaskssimultaneously">Loading Grunt Tasks Simultaneously</h3>
<p>Currently, within our Gruntfile, we’ve been loading tasks one at a time. Instead of doing this, we’ll start using a module called <a href="https://www.npmjs.org/package/load-grunt-tasks">load-grunt-tasks</a>, which allows us to load all the necessary Grunt tasks that are defined as dev dependencies within the <em>package.json</em> file.</p>
<p>Using Terminal, ensure that you have changed to the ‘breeze’ directory – we will be running all commands from this directory throughout the tutorial – and then run the following command:</p>
<pre><code class="language-sh">npm install load-grunt-tasks --save-dev
</code></pre>
<p>This installs the <em>load-grunt-tasks</em> module into the <em>node-modules</em> folder and adds it as a development dependency within the <em>package.json</em> file.</p>
<p>Once that’s done, we need to update part of our Gruntfile to make use of the module.</p>
<p>This is what the relevant part of the Gruntfile looked like before:</p>
<pre><code class="language-javascript">grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);
grunt.loadNpmTasks(&quot;grunt-contrib-uglify&quot;);
grunt.loadNpmTasks(&quot;grunt-contrib-connect&quot;);
grunt.loadNpmTasks(&quot;grunt-contrib-watch&quot;);
</code></pre>
<p>We are going to replace all of that with the following single line instead:</p>
<pre><code class="language-javascript">require(&quot;load-grunt-tasks&quot;)(grunt);
</code></pre>
<p>The next time we run the <code>grunt breeze</code> command, all of our npm module dependencies should be loaded, just like before, but with a much more neat and concise syntax.</p>
<h3 id="automaticcssprefixes">Automatic CSS Prefixes</h3>
<p>Normally, in order to ensure a web application works in various browsers, we’d need to add vendor-specific prefixes, for example, <code>-webkit-transition</code>, <code>-moz-transition</code>, and <code>-ms-transition</code> would be used in addition to the standard spec of <code>transition</code>.</p>
<p>Instead of manually adding prefixes, we’ll start using a module called <a href="https://www.npmjs.com/package/grunt-autoprefixer">grunt-autoprefixer</a>, which allows us to write our SCSS using just the standard spec, as it will automatically add the necessary vendor prefixes where appropriate during the Grunt build step. This is a great option, because it allows us to keep our SCSS code simple and clean, rather than having it littered with prefixes.</p>
<p>Using Terminal, run the following command to install autoprefixer:</p>
<pre><code class="language-sh">npm install grunt-autoprefixer --save-dev
</code></pre>
<p>Now we need to add a config to the Gruntfile for autoprefixer:</p>
<pre><code class="language-javascript">autoprefixer: {

    dist : {
        options: {
            // add prefixes to support the last 2 browser versions,
            // as well as IE9+ and Android 4 stock browsers
            browsers: [&quot;last 2 versions&quot;, &quot;ie &gt;= 9&quot;, &quot;Android 4&quot;],
            map: true
        },

        files: {
            &quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
        }
    }
}
</code></pre>
<p>Note that, within the scope of this tutorial, we’ll only really need to run the autoprefixer task when creating a distribution build, as when developing locally we should be using a good browser, such as Chrome, which supports a lot of the CSS spec by default. If you need to test in not-so-modern browsers during development, then the autoprefixer task should be run on every build of the SCSS code, and you may also want to look at the documentation for autoprefixer, which provides <a href="https://www.npmjs.com/package/grunt-autoprefixer#options">many configuration options</a>.</p>
<h3 id="autoupdatingstylechangesinthebrowser">Auto-updating Style Changes in the Browser</h3>
<p>The <em>watch</em> task supports an option called ‘livereload’, which can automatically reload the browser when any of the watched files are changed. To use this option, we first need to install the <em>livereload</em> browser <a href="https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei">extension for Chrome</a> or follow <a href="http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-">the simple installation guide</a> if you want to use Firefox or Safari.</p>
<p>Once livereload is installed in the browser, we then need to update the Gruntfile. Modify the config for the <em>watch</em> task so that it includes an ‘options’ object containing a ’livereload’ property:</p>
<pre><code class="language-javascript">watch: {

    options: {
        livereload: true,
    },

    html: {
        files: [&quot;index.html&quot;],
    },

    js: {
        files: [&quot;src/js/**/*.js&quot;],
        tasks: [&quot;uglify:dev&quot;]
    },

    scss: {
        files: [&quot;src/scss/**/*.scss&quot;],
        tasks: [&quot;sass:dev&quot;]
    },

    img: {
        files: [&quot;src/img/**/*.*&quot;],
        tasks: [&quot;copy:img&quot;]
    }
}
</code></pre>
<p>Now when we run <code>grunt breeze</code> we will be able to turn on <em>livereload</em> for the page running in the browser. To do this, simply click on the livereload browser icon and its very subtle centre circle should turn black for Chrome/Safari, or red for Firefox, meaning that it has successfully connected to the running Grunt task.</p>
<p>In Chrome/Safari:</p>
<p><img src="https://ivanhayes.com/blog/content/images/2018/06/chrome-livereload-icon-300x178.png" alt="chrome-livereload-icon-300x178"></p>
<p>And in Firefox:</p>
<p><img src="https://ivanhayes.com/blog/content/images/2018/06/firefox-livereload-icon.png" alt="firefox-livereload-icon"></p>
<h2 id="implementingtheopenweathermapapi">Implementing the OpenWeatherMap API</h2>
<p>So far, in terms of JavaScript, we've only added an alert message. In this section we will use the HTML5 geolocation API to get the coordinates of a user and the <em>atomic</em> AJAX library to get weather data from the OpenWeatherMap API, based on the coordinates we obtain.</p>
<p>Before we get started on this section, <a href="http://openweathermap.org/register">create a free OpenWeatherMap account</a> in order to obtain a developer API key. This key will be needed later when we start making requests to the API.</p>
<h3 id="mainapplicationlogic">Main Application Logic</h3>
<p>The logic of the application will be fairly simple, and we can break it into two clearly defined parts – getting weather data for the current location coordinates, and getting weather data using a specific search term. There will also be some logic for displaying the weather information, which will be shared by both these parts.</p>
<h4 id="logicusingcoordinates">Logic Using Coordinates</h4>
<ol>
<li>Attempt to get the coordinates of the current user with the HTML5 geolocation API</li>
<li>If we get the coordinates, then make an API request to OpenWeatherMap for weather data using the coordinates</li>
<li>Run ‘shared logic’ below</li>
</ol>
<h4 id="logicusingasearchterm">Logic Using a Search Term</h4>
<ol>
<li>Listen for when the user submits the search form</li>
<li>Make an api request using the text they entered into the search text field</li>
<li>Run ‘shared logic’ below</li>
</ol>
<h4 id="sharedlogic">Shared Logic</h4>
<ol>
<li>Parse the JSON data received from the API</li>
<li>If the location is found, display temperature information using the parsed data</li>
<li>If the location is not found, or if we get an error when making the AJAX request, then display an error message to notify the user.</li>
</ol>
<h3 id="gettingstarted">Getting Started</h3>
<p>Firstly, remove the alert that we added in the previous part of this tutorial, as it was just a temporary placeholder for our actual code.</p>
<p>Next, it’s a good idea to create an immediately invoked closure which will contain all our code. Doing this is good practice, as it will stop our variables polluting the global (window) scope:</p>
<pre><code class="language-javascript">(function (){

	// code...

})();
</code></pre>
<p>If you’d like more information about closures, <a href="http://code.tutsplus.com/tutorials/closures-front-to-back--net-24869">this article</a> is a pretty good one.</p>
<p>We want to ensure the DOM has fully loaded before we start running any of our logic. To do this, we add a listener for the ‘DOMContentLoaded’ event and once this occurs we run an initialisation function for the application:</p>
<pre><code class="language-javascript">(function (){
	
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		// code...
	}

})();
</code></pre>
<p>Now that we have the basics set up, let’s move on to the first part of the app logic – getting weather data for the current coordinates.</p>
<h3 id="gettingcoordinated">Getting Coordinated</h3>
<p>In order to request location information for the current user, we can use the <code>getCurrentPosition</code> method of the <code>navigator.geolocation</code> object:</p>
<pre><code class="language-javascript">(function (){
	
	// Add an event listener to ensure the DOM has loaded before
	// initialising the application.
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		navigator.geolocation.getCurrentPosition(geolocationSuccessHandler, geolocationErrorHandler);
	}

	function geolocationSuccessHandler (position)
	{
		// get lat and lon values from the position object.
		var lat = position.coords.latitude;
		var lon = position.coords.longitude;
	}

	function geolocationErrorHandler (error)
	{
		// The user has disallowed sharing their geolocation, or there was a general error obtaining the info.
		console.log(&quot;geolocationErrorHandler&quot;);
	}

})();
</code></pre>
<p>Running this method causes a notification to be displayed within the browser. Notice that we also provide two functions as parameters – the first will be run if the user allows our web app to use location information, and the second runs if the user does not allow this, or if there is an error obtaining the information. Note that, at the time of writing, Firefox will not run the error function if a user chooses the ‘Not now’ option within the location notification – it simply fails silently in this case, which is <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=675533">a known Mozilla bug</a> that has been open for a long time. Firefox does, however, run the error handler if a user chooses the ‘Never’ option.</p>
<p>Also, it’s useful to note that, if coordinates are returned, they are sometimes precise and sometimes approximate. This is because some browsers, such as Chrome, give users a choice as to whether or not a precise location should be used.</p>
<p>We’re now ready to make an API request to get weather data for the coordinates we obtained from the <code>position</code> object.</p>
<h3 id="requestingweatherdata">Requesting Weather Data</h3>
<p>Next, add variables to store the API URL, the type of units you want (metric or imperial), and your OpenWeatherMap API key, as well as a few for alerting error messages, and a function that will get weather data from coordinates passed as parameters. You must replace the value of the <code>API_KEY</code> variable – ’YOUR_API_KEY_HERE’ – with the API key you got after signing up to OpenWeatherMap, ensuring that you keep the quotation marks as they are:</p>
<pre><code class="language-javascript">(function (){
	
	var API_KEY = &quot;YOUR_API_KEY_HERE&quot;;
	var API_UNIT_TYPE = &quot;metric&quot;; // otherwise change to 'imperial'.
	var API_URL = &quot;http://api.openweathermap.org/data/2.5/weather?APPID=&quot; + API_KEY + &quot;&amp;units=&quot; + API_UNIT_TYPE;

	var MESSAGE_LOCATION_NOT_FOUND = &quot;Location not found.&quot;;
	var MESSAGE_API_REQUEST_ERROR = &quot;API Error.&quot;;

	// Add an event listener to ensure the DOM has loaded before
	// initialising the application.
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		navigator.geolocation.getCurrentPosition(geolocationSuccessHandler, geolocationErrorHandler);
	}

	function geolocationSuccessHandler (position)
	{
		// get lat and lon values from the position object.
		var lat = position.coords.latitude;
		var lon = position.coords.longitude;

		// Load weather data using the lat and lon values, now that we have the user's position.
		getWeatherDataFromCoords(lat, lon);
	}

	function geolocationErrorHandler (error)
	{
		// The user has disallowed sharing their geolocation, or there was a general error obtaining the info.
		console.log(&quot;geolocationErrorHandler&quot;);
	}

	function getWeatherDataFromCoords (lat, lon)
	{
		console.log(&quot;getting weather data...&quot;, lat, lon);
		
		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;lat=&quot; + lat + &quot;&amp;lon=&quot; + lon;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function apiRequestSuccess (data, xhr)
	{
		console.log(&quot;weather data received.&quot;, data);

		if (typeof data.cod !== &quot;undefined&quot; &amp;&amp; data.cod === &quot;404&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}
	}

	function apiRequestError (data, xhr)
	{
		alert(MESSAGE_API_REQUEST_ERROR);
	}

})();
</code></pre>
<p>We have now built a function to request weather data – <code>getWeatherDataFromCoords</code> – and we are calling it after we have obtained the user’s coordinates, from within the <code>geolocationSuccessHandler</code> function.</p>
<p>If you open up the JavaScript console on your browser, you should see the log messages from the application, including an object containing weather information.</p>
<h2 id="displayingweatherinformation">Displaying Weather Information</h2>
<p>Logging the data we got from the API is all well and good, but it would be great to update the HTML in order to show the weather information to the user, so let’s go ahead and do that now:</p>
<pre><code class="language-javascript">(function (){
	
	var API_KEY = &quot;YOUR_API_KEY_HERE&quot;;
	var API_UNIT_TYPE = &quot;metric&quot;; // otherwise change to 'imperial'.
	var API_URL = &quot;http://api.openweathermap.org/data/2.5/weather?APPID=&quot; + API_KEY + &quot;&amp;units=&quot; + API_UNIT_TYPE;

	// Use '°C' for metric units, or  '°F' for imperial units.
	var UNITS_SUFFIX = API_UNIT_TYPE === &quot;metric&quot; ? &quot;°C&quot; : &quot;°F&quot;;

	var MESSAGE_LOCATION_NOT_FOUND = &quot;Location not found. Please search for something else.&quot;;
	var MESSAGE_API_REQUEST_ERROR = &quot;There was a problem getting weather data. Please try again.&quot;;

	var _locationNameDisplay;
	var _temperatureDisplay;

	// Add an event listener to ensure the DOM has loaded before
	// initialising the application.
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		_locationNameDisplay = document.querySelector(&quot;.location-name-display&quot;);
		_temperatureDisplay = document.querySelector(&quot;.temperature-display&quot;);

		// Get the geo position of the current user (if they allow this).
		navigator.geolocation.getCurrentPosition(geolocationSuccessHandler, geolocationErrorHandler);
	}

	function geolocationSuccessHandler (position)
	{
		// Get lat and lon values from the position object.
		var lat = position.coords.latitude;
		var lon = position.coords.longitude;

		// Load weather data using the lat and lon values, now that we have the user's position.
		getWeatherDataFromCoords(lat, lon);
	}

	function geolocationErrorHandler (error)
	{
		// The user has disallowed sharing their geolocation, or there was a general error obtaining the info.
		console.log(&quot;geolocationErrorHandler&quot;);
	}

	function getWeatherDataFromCoords (lat, lon)
	{
		console.log(&quot;getting weather data...&quot;, lat, lon);

		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;lat=&quot; + lat + &quot;&amp;lon=&quot; + lon;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function apiRequestSuccess (data, xhr)
	{
		console.log(&quot;weather data received.&quot;, data);

		// The data returned by the API contains a property called 'cod',
		// rather than 'code', for the response status code. This is not a typo!
		if (typeof data.cod !== &quot;undefined&quot; &amp;&amp; data.cod === &quot;404&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		// Get the location name, country code, and temperature from the data object
		// returned by the API, ensuring that we round it to the nearest integer.
		var locationName = data.name;
		var countryCode = data.sys.country;
		var temperature = Math.round(data.main.temp);

		// Don't display anything if there is no name and country code.
		if (locationName === &quot;&quot; &amp;&amp; countryCode === &quot;&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		updateLocationDisplay(locationName, countryCode, temperature);
	}

	function apiRequestError (data, xhr)
	{
		alert(MESSAGE_API_REQUEST_ERROR);
	}

	function updateLocationDisplay(locationName, countryCode, temperature)
	{
		if (locationName !== &quot;&quot;)
		{
			_locationNameDisplay.innerHTML = locationName + &quot;, &quot; + countryCode;
		}
		else
		{
			_locationNameDisplay.innerHTML = countryCode;
		}

		_temperatureDisplay.innerHTML = temperature + UNITS_SUFFIX;
	}

})();
</code></pre>
<p>Firstly, we are checking if the status code in the data object returned by the API is 404 (not found) before proceeding. If it is then we need to alert the user so they know there was a problem with the request. We then grab the location name, country code, and temperature from the API data object. Next, we check if both the location name and country code are empty strings, and if this is the case we again alert the user, since we would have nothing to display. Lastly, we call the <code>updateLocationDisplay</code> function, within which we display either the location name, country code, or both, dependant on what is available.</p>
<h2 id="addingsearchfunctionality">Adding Search Functionality</h2>
<p>At this point, we have successfully displayed weather information for a user’s coordinates, and we can further improve our application by adding a search feature, so that we can get temperature information for practically any location in the world, based on user-entered text.</p>
<p>The input text field already exists in the HTML code, but is currently not functional. In this section we’ll go through the logic for using a search term. To reiterate, we want to do the following:</p>
<ol>
<li>Listen for when the user submits the search form</li>
<li>Make an api request using the text they entered into the search text field</li>
<li>Parse the JSON data received from the API</li>
<li>If the location is found, display temperature information using the parsed data</li>
</ol>
<p>We have already written the logic for <em>#3</em> and <em>#4</em>, and we should re-use this instead of creating duplicate functionality. So, we just need to create the logic for <em>#1</em> and <em>#2</em>:</p>
<pre><code class="language-javascript">(function (){

	var API_KEY = &quot;YOUR_API_KEY_HERE&quot;;
	var API_UNIT_TYPE = &quot;metric&quot;; // otherwise change to 'imperial'.
	var API_URL = &quot;http://api.openweathermap.org/data/2.5/weather?APPID=&quot; + API_KEY + &quot;&amp;units=&quot; + API_UNIT_TYPE;

	// Use '°C' for metric units, or  '°F' for imperial units.
	var UNITS_SUFFIX = API_UNIT_TYPE === &quot;metric&quot; ? &quot;°C&quot; : &quot;°F&quot;;

	var MESSAGE_LOCATION_NOT_FOUND = &quot;Location not found. Please search for something else.&quot;;
	var MESSAGE_API_REQUEST_ERROR = &quot;There was a problem getting weather data. Please try again.&quot;;
	var MESSAGE_SEARCH_NOT_SPECIFIED = &quot;Please specify a search term.&quot;;

	var _locationSearchForm;
	var _locationSearchInput;
	var _locationNameDisplay;
	var _temperatureDisplay;
	var _waitingForData;

	// Add an event listener to ensure the DOM has loaded before
	// initialising the application.
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		_locationSearchForm = document.querySelector(&quot;#location-search-form&quot;);
		_locationSearchInput = document.querySelector(&quot;.location-search-input&quot;);
		_locationNameDisplay = document.querySelector(&quot;.location-name-display&quot;);
		_temperatureDisplay = document.querySelector(&quot;.temperature-display&quot;);

		_waitingForData = false;

		// Add a listener for when the search form is submitted.
		_locationSearchForm.addEventListener(&quot;submit&quot;, locationSearchFormSubmitHandler);

		// Get the geo position of the current user (if they allow this).
		navigator.geolocation.getCurrentPosition(geolocationSuccessHandler, geolocationErrorHandler);
	}

	function geolocationSuccessHandler (position)
	{
		// Get lat and lon values from the position object.
		var lat = position.coords.latitude;
		var lon = position.coords.longitude;

		// Load weather data using the lat and lon values, now that we have the user's position.
		getWeatherDataFromCoords(lat, lon);
	}

	function geolocationErrorHandler (error)
	{
		// The user has disallowed sharing their geolocation, or there was a general error obtaining the info.
		// Even still, they will still be able to search for weather information.
		console.log(&quot;geolocationErrorHandler&quot;);
	}

	function locationSearchFormSubmitHandler (event)
	{
		event.preventDefault();
		
		console.log(&quot;form submit.&quot;, _locationSearchInput.value);

		if (_locationSearchInput.value !== &quot;&quot;)
		{
			getWeatherDataFromLocationName(_locationSearchInput.value);

			_locationSearchInput.value = &quot;&quot;;
		}
		else
		{
			alert(MESSAGE_SEARCH_NOT_SPECIFIED);
		}
	}

	function getWeatherDataFromCoords (lat, lon)
	{
		// Don't do anything if we are waiting on the response of a previous API request.
		if (_waitingForData)
		{
			return false;
		}

		// Disable API requests until we receive a response.
		_waitingForData = true;

		console.log(&quot;getting weather data...&quot;, lat, lon);

		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;lat=&quot; + lat + &quot;&amp;lon=&quot; + lon;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function getWeatherDataFromLocationName (locationName)
	{
		// Don't do anything if we are waiting on the response of a previous API request.
		if (_waitingForData)
		{
			return false;
		}

		// Disable API requests until we receive a response.
		_waitingForData = true;

		console.log(&quot;getting weather data...&quot;, locationName);

		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;q=&quot; + locationName;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function apiRequestSuccess (data, xhr)
	{
		console.log(&quot;weather data received.&quot;, data);

		// Reenable data lookups now that the API response has been received.
		_waitingForData = false;

		// The data returned by the API contains a property called 'cod',
		// rather than 'code', for the response status code. This is not a typo!
		if (typeof data.cod !== &quot;undefined&quot; &amp;&amp; data.cod === &quot;404&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		// Get the location name, country code, and temperature from the data object
		// returned by the API, ensuring that we round it to the nearest integer.
		var locationName = data.name;
		var countryCode = data.sys.country;
		var temperature = Math.round(data.main.temp);

		// Don't display anything if there is no name and country code.
		if (locationName === &quot;&quot; &amp;&amp; countryCode === &quot;&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		updateLocationDisplay(locationName, countryCode, temperature);
	}

	function apiRequestError (data, xhr)
	{
		alert(MESSAGE_API_REQUEST_ERROR);

		// Reenable data lookups so that we can retry requesting weather data.
		_waitingForData = false;
	}

	function updateLocationDisplay(locationName, countryCode, temperature)
	{
		if (locationName !== &quot;&quot;)
		{
			_locationNameDisplay.innerHTML = locationName + &quot;, &quot; + countryCode;
		}
		else
		{
			_locationNameDisplay.innerHTML = countryCode;
		}

		_temperatureDisplay.innerHTML = temperature + UNITS_SUFFIX;
	}

})();
</code></pre>
<p>We have stored references to the input text field and the form within which it is contained, allowing us to listen out for when a user submits the form, so that we can run the <code>locationSearchFormSubmitHandler</code> function. Within this function, the first piece of code to get executed is <code>event.preventDefault()</code>. This stops the default form submission action so that we can write our own implementation without the page being refreshed. We then check if the input text field is empty, and if so we alert the user, otherwise the <code>getWeatherDataFromLocationName</code> function is run with the value of the input text field provided as a parameter. We then clear the contents of the input text field to make it easier for the user to search weather data for somewhere else next time.</p>
<p>The <code>getWeatherDataFromLocationName</code> function is similar to <code>getWeatherDataFromCoords</code>, but accepts a single string as a parameter, rather than coordinates. Notice also that we have added conditional logic to both of those functions, which checks the state of the <code>_waitingForData</code> boolean variable. This check is done so that only a single API request can be performed at any given time – the value of the variable is set to ‘true’ when an API request is in progress, and then back to ‘false’ when we receive a response or error.</p>
<p>We then reuse the previously created logic to parse and display the weather information.</p>
<h2 id="makingitpretty">Making It Pretty</h2>
<p>In this section we'll create some logic to dynamically update the class names of elements and then use some basic SCSS to style the application and make it look slick with CSS3 transitions.</p>
<h3 id="logictofacilitatestyling">Logic to Facilitate Styling</h3>
<p>Our final JavaScript code is as follows:</p>
<pre><code class="language-javascript">(function (){
	
	var API_KEY = &quot;YOUR_API_KEY_HERE&quot;;
	var API_UNIT_TYPE = &quot;metric&quot;; // otherwise change to 'imperial'.
	var API_URL = &quot;http://api.openweathermap.org/data/2.5/weather?APPID=&quot; + API_KEY + &quot;&amp;units=&quot; + API_UNIT_TYPE;

	var TEMPERATURES = {

		FREEZING	: {MAX : 0, CLASS_NAME : &quot;freezing&quot;},
		COLD		: {MAX : 8, CLASS_NAME : &quot;cold&quot;},
		WARM		: {MAX : 16, CLASS_NAME : &quot;warm&quot;},
		HOT			: {MAX : 24, CLASS_NAME : &quot;hot&quot;},
		BLAZING		: {MAX : Number.POSITIVE_INFINITY, CLASS_NAME : &quot;blazing&quot;}
	};

	// Use '°C' for metric units, or  '°F' for imperial units.
	var UNITS_SUFFIX = API_UNIT_TYPE === &quot;metric&quot; ? &quot;°C&quot; : &quot;°F&quot;;

	var MESSAGE_LOCATION_NOT_FOUND = &quot;Location not found. Please search for something else.&quot;;
	var MESSAGE_API_REQUEST_ERROR = &quot;There was a problem getting weather data. Please try again.&quot;;
	var MESSAGE_SEARCH_NOT_SPECIFIED = &quot;Please specify a search term.&quot;;

	var _mainContainer;
	var _locationSearchForm;
	var _locationSearchSubmitWrapper;
	var _locationSearchInput;
	var _currentTemperatureClassName;
	var _locationNameDisplay;
	var _temperatureDisplay;
	var _waitingForData;

	// Add an event listener to ensure the DOM has loaded before
	// initialising the application.
	document.addEventListener(&quot;DOMContentLoaded&quot;, init);

	function init ()
	{
		_mainContainer = document.querySelector(&quot;.main-container&quot;);
		_locationSearchForm = document.querySelector(&quot;#location-search-form&quot;);
		_locationSearchSubmitWrapper = document.querySelector(&quot;.location-search-submit-wrapper&quot;);
		_locationSearchInput = document.querySelector(&quot;.location-search-input&quot;);
		_locationNameDisplay = document.querySelector(&quot;.location-name-display&quot;);
		_temperatureDisplay = document.querySelector(&quot;.temperature-display&quot;);

		_waitingForData = false;

		// Add a listener for when the search form is submitted.
		_locationSearchForm.addEventListener(&quot;submit&quot;, locationSearchFormSubmitHandler);

		// Get the geo position of the current user (if they allow this).
		navigator.geolocation.getCurrentPosition(geolocationSuccessHandler, geolocationErrorHandler);
	}

	function geolocationSuccessHandler (position)
	{
		// Get lat and lon values from the position object.
		var lat = position.coords.latitude;
		var lon = position.coords.longitude;

		// Load weather data using the lat and lon values, now that we have the user's position.
		getWeatherDataFromCoords(lat, lon);
	}

	function geolocationErrorHandler (error)
	{
		// The user has disallowed sharing their geolocation, or there was a general error obtaining the info.
		// Even still, they will still be able to search for weather information.
		console.log(&quot;geolocationErrorHandler&quot;);
	}

	function locationSearchFormSubmitHandler (event)
	{
		event.preventDefault();
		
		console.log(&quot;form submit.&quot;, _locationSearchInput.value);

		if (_locationSearchInput.value !== &quot;&quot;)
		{
			getWeatherDataFromLocationName(_locationSearchInput.value);

			_locationSearchSubmitWrapper.classList.add(&quot;loading&quot;);
			_locationSearchInput.value = &quot;&quot;;
		}
		else
		{
			alert(MESSAGE_SEARCH_NOT_SPECIFIED);
		}
	}

	function getWeatherDataFromCoords (lat, lon)
	{
		// Don't do anything if we are waiting on the response of a previous API request.
		if (_waitingForData)
		{
			return false;
		}

		// Disable API requests until we receive a response.
		_waitingForData = true;

		console.log(&quot;getting weather data...&quot;, lat, lon);

		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;lat=&quot; + lat + &quot;&amp;lon=&quot; + lon;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function getWeatherDataFromLocationName (locationName)
	{
		// Don't do anything if we are waiting on the response of a previous API request.
		if (_waitingForData)
		{
			return false;
		}

		// Disable API requests until we receive a response.
		_waitingForData = true;

		console.log(&quot;getting weather data...&quot;, locationName);

		// Build a request URL based on the current lat and lon values.
		var requestURL = API_URL + &quot;&amp;q=&quot; + locationName;

		// Make an AJAX request to the API.
		atomic.get(requestURL)
			.success(apiRequestSuccess)
			.error(apiRequestError);
	}

	function apiRequestSuccess (data, xhr)
	{
		console.log(&quot;weather data received.&quot;, data);

		// Reenable data lookups now that the API response has been received.
		_waitingForData = false;
		_locationSearchSubmitWrapper.classList.remove(&quot;loading&quot;);

		// The data returned by the API contains a property called 'cod',
		// rather than 'code', for the response status code. This is not a typo!
		if (typeof data.cod !== &quot;undefined&quot; &amp;&amp; data.cod === &quot;404&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		// Get the location name, country code, and temperature from the data object
		// returned by the API, ensuring that we round it to the nearest integer.
		var locationName = data.name;
		var countryCode = data.sys.country;
		var temperature = Math.round(data.main.temp);

		// Don't display anything if there is no name and country code.
		if (locationName === &quot;&quot; &amp;&amp; countryCode === &quot;&quot;)
		{
			alert(MESSAGE_LOCATION_NOT_FOUND);
			return;
		}

		updateLocationDisplay(locationName, countryCode, temperature);
		updateBackgroundColor(temperature);
	}

	function apiRequestError (data, xhr)
	{
		alert(MESSAGE_API_REQUEST_ERROR);

		// Reenable data lookups so that we can retry requesting weather data.
		_waitingForData = false;
	}

	function updateLocationDisplay(locationName, countryCode, temperature)
	{
		if (locationName !== &quot;&quot;)
		{
			_locationNameDisplay.innerHTML = locationName + &quot;, &quot; + countryCode;
		}
		else
		{
			_locationNameDisplay.innerHTML = countryCode;
		}

		_temperatureDisplay.innerHTML = temperature + UNITS_SUFFIX;
	}

	function updateBackgroundColor (temperature)
	{
		// If we previously set a temperature class name on the main
		// container element then remove it here.
		if (typeof _currentTemperatureClassName !== &quot;undefined&quot;)
		{
			_mainContainer.classList.remove(_currentTemperatureClassName);
		}

		// Set the '_currentTemperatureClassName' variable based on the
		// constants we defined earlier.
		if (temperature &lt;= TEMPERATURES.FREEZING.MAX)
		{
			_currentTemperatureClassName = TEMPERATURES.FREEZING.CLASS_NAME;
		}
		else if (temperature &lt;= TEMPERATURES.COLD.MAX)
		{
			_currentTemperatureClassName = TEMPERATURES.COLD.CLASS_NAME;
		}
		else if (temperature &lt;= TEMPERATURES.WARM.MAX)
		{
			_currentTemperatureClassName = TEMPERATURES.WARM.CLASS_NAME;
		}
		else if (temperature &lt;= TEMPERATURES.HOT.MAX)
		{
			_currentTemperatureClassName = TEMPERATURES.HOT.CLASS_NAME;
		}
		else
		{
			_currentTemperatureClassName = TEMPERATURES.BLAZING.CLASS_NAME;
		}

		// Set a temperature class name on the main container element.
		_mainContainer.classList.add(_currentTemperatureClassName);
	}

})();
</code></pre>
<p>We’ve added a function called <code>updateBackgroundColor</code> that accepts a single numerical parameter – the current temperature. Also, we’ve created a ‘TEMPERATURES’ object, which contains information that we’ll use within the <code>updateBackgroundColor</code> function to determine which CSS class gets added to the main container <code>div</code> element of the application. If the temperature parameter value is less than or equal to the maximum ‘freezing’ temperature, then we add the ‘freezing’ class name, otherwise, if the temperature parameter value is less than or equal to the maximum ‘cold’ temperature, then we add the ‘cold’ class name, and so on. Additionally, there is a <code>_currentTemperatureClassName</code> variable for keeping track of the current class name that has been added to the main container, so that the class name can be removed the next time the function runs. This ensures we don’t end up with multiple temperature class names on the main container.</p>
<p>Now that the main container has specific temperature class names added and removed, we target those class names using our SCSS code, so that the background colour is updated when we search for different locations, based on on temperature.</p>
<p>Lastly, we have created a <code>_locationSearchSubmitWrapper</code> variable. this will allow us to target an element wrapping the form submit button so that we can add a transition to show when the app waiting for a response to an API request. This will provide visual feedback to the user so that they understand what  the application is doing, rather than blindly waiting for something to happen. We add and remove a ‘loading’ class to this element where appropriate.</p>
<h3 id="stylingandtransitions">Styling and Transitions</h3>
<p>Let’s take a brief look at the way we will be styling the application using SCSS to illustrate some of the basic concepts of the language.</p>
<h4 id="partials">Partials</h4>
<p>In SCSS, partials are simply files that have been broken up into logical segments, which we can include in our code using the <code>@import</code> directive. By convention, partial files are named with a leading underscore. Here is a basic example:</p>
<p>Main file – <em>app.scss</em></p>
<pre><code class="language-scss">body {
	background-color: blue;
}

@import &quot;some-amazing-partial&quot;;
</code></pre>
<p>Partial file – <em>some-amazing-partial.scss</em></p>
<pre><code class="language-scss">body {
	background-color: green;
}
</code></pre>
<p>In the first file – <em>app.scss</em> – we simply set the background colour of the <code>body</code> element to blue and then import <em>some-amazing-partial.scss</em>, which then changes the background colour to green, overriding the previously set value. Note also that we don’t provide the ‘scss’ file extension when importing partials.<br>
If we were to compile the above two files into pure CSS it would look as follows:</p>
<pre><code class="language-css">body {
	background-color: blue;
}

body {
	background-color: green;
}
</code></pre>
<p>so we are effectively just inserting all the content from a partial file wherever we use the <code>@import</code> directive.</p>
<h4 id="variables">Variables</h4>
<p>We can also make use of simple variables in SCSS, the definitions for which start with a dollar sign. Variables make updating things such as colour schemes way easier than when using CSS alone. Consider the following standard CSS code:</p>
<pre><code class="language-css">.first-element {
	background-color: blue;
}

.second-element {
	background-color: blue;
}

.third-element {
	background-color: blue;
}
</code></pre>
<p>With standard CSS, if we wanted to change the background colour of all the targeted elements then we’d have to do so individually. Now that’s not so bad when there are only three elements, but imagine how long this simple update would take if there were hundreds of places throughout the styling code where this colour was defined! “Find and replace”, I hear you say, but what if there are some other elements that also use this colour, which we don’t want to change? Now that’s a lot of work!<br>
With SCSS, we could simply define some logically-named variables for our colours, and when we need to update a particular colour all that would be required is a quick change to a single variable value. Here’s a simple example:</p>
<pre><code class="language-scss">$color-nav: blue;
$color-examples: blue;

.navigation-element {
	background-color: $color-nav;
}

.example-element-1 {
	background-color: $color-examples;
}

.example-element-2 {
	background-color: $color-examples;
}
</code></pre>
<p>Here we simply create two variables for colours, one for navigation and one for some example elements. Now if we want to change the colour of our navigation we can do so very easily, and without affective the colour of the example elements.</p>
<p>On a side note, it’s normally a good idea to include the majority of your variables in a separate file (as we have done for this project), or group of files, so that they are always easy to find.</p>
<h4 id="mixins">Mixins</h4>
<p>In SCSS, <em>mixins</em> are effectively used to include snippets of code that we use regularly, in a similar way to JavaScript functions that return a value (but not quite the same way as SCSS functions). We can also pass parameters to mixins to alter the code the return. A good use case for a mixing would be something like including a <a href="http://www.sitepoint.com/clearing-floats-overview-different-clearfix-methods/">clearfix</a>:</p>
<p>Here’s how we could fix the issue with CSS alone:</p>
<pre><code class="language-css">.some-element-1:after {
	content: &quot;&quot;;
	display: table;
	clear: both;
}

.some-element-2:after {
	content: &quot;&quot;;
	display: table;
	clear: both;
}
</code></pre>
<p>Now obviously if we have many elements that need a clearfix then we’d have to be writing this a lot. Mixins to the rescue – here’s the same thing written in SCSS:</p>
<p><em>mixins.scss</em><br>
A file to potentially contain many types of mixins</p>
<pre><code class="language-scss">@mixin clearfix {
	&amp;:after {
		content: &quot;&quot;;
		display: table;
		clear: both;
	}
}
</code></pre>
<pre><code class="language-scss">*app.scss*

.some-element-1 {
	@include clearfix;
}

.some-element-2 {
	@include clearfix;
}
</code></pre>
<p>Now that’s much neater :)</p>
<h4 id="transitions">Transitions</h4>
<p>Now, moving back to our application, we aren’t going to go into too much detail about the SCSS implementation, but one thing worth looking at briefly is CSS3 transitions. We’ll use transitions in order to give the application a nice slick look and feel – fading between background colours when the user searches for locations. To do that we simply add a transition property to the main container <code>div</code> element:</p>
<pre><code class="language-scss">.main-container {
	transition: background-color 1s linear;
}
</code></pre>
<p>This basically states that whenever the <code>background-color</code> property is updated for this element, the browser should transition for 1 second between the old value and the new value. Very simple, yet very effective.</p>
<p>Finally, we will also add another transition for showing and hiding a loading icon in place of the form submit button, for when the application needs to show that it is loading:</p>
<pre><code class="language-scss">.location-search-submit, .location-search-cloud-icon {
	transition: left 0.4s ease;
	transition-delay: 0.5s;
}
</code></pre>
<p>That just about wraps it up. If you have any questions about the application, including the logic, styling, or any of the concepts we’ve covered, then feel free to post a comment.</p>
<p>And as always, happy coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Minimal Weather with Breeze]]></title><description><![CDATA[A sneak-peek at the finished product of the 'Creating a Web-app with Grunt' tutorial series.]]></description><link>https://ivanhayes.com/blog/minimal-weather-with-breeze/</link><guid isPermaLink="false">5af1bef57ce7f61014b7fbab</guid><category><![CDATA[javascript]]></category><category><![CDATA[sass]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Fri, 12 Dec 2014 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><img src="https://ivanhayes.com/blog/content/images/2018/05/breeze.png" alt="breeze"></p>
<p>Recently, I wrote the first part of a tutorial on creating a web-app, walking you through the necessary steps to use Grunt as a build tool. In the second part of the tutorial, the implementation of the web-app – Breeze – will be explained, but as a sneak peek, you can find the finished product here:</p>
<p><a href="https://ivanhayes.com/breeze">https://ivanhayes.com/breeze</a></p>
<p>You can also <a href="https://github.com/munkychop/breeze/fork">fork the repo on Github</a> and switch to the ‘tutorial-part-2’ branch.</p>
<p>Stay tuned for the second part of the tutorial, coming soon!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Creating a Web-app with Grunt – Part 1]]></title><description><![CDATA[This is the first of a two-part tutorial, in which we’ll be creating a web-app – Breeze – that will load and display temperature information for various cities using data from OpenWeatherMap]]></description><link>https://ivanhayes.com/blog/creating-a-web-app-with-grunt-part-1/</link><guid isPermaLink="false">5af1baaf7ce7f61014b7fb9e</guid><category><![CDATA[javascript]]></category><category><![CDATA[sass]]></category><category><![CDATA[grunt]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Mon, 27 Oct 2014 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>This is the first of a two-part tutorial, in which we’ll be creating a web-app – Breeze – that will load and display temperature information for various cities using data from <a href="http://openweathermap.org/">OpenWeatherMap</a>. In this part of the tutorial, we’ll be setting up a simple build workflow using <a href="http://gruntjs.com/">Grunt</a> and <a href="https://www.npmjs.org/">npm</a>.</p>
<!--more-->
<p>If you’re short on time and just want to see the complete code for this part of the tutorial then you can <a href="https://github.com/munkychop/breeze/fork">fork the repo on Github</a> and switch to the ‘tutorial-part-1’ branch.</p>
<h3 id="anoteforwindowsusers">A Note for Windows Users</h3>
<p>Throughout this tutorial I will be referring to the Mac command line application – <em>Terminal</em>, so if you are on Windows then you’ll simply need to use <em>Command Prompt</em> instead.</p>
<h2 id="developmentenvironmentsetup">Development Environment Setup</h2>
<p>Firstly we need to have our dev environment setup so that we can use npm and Grunt. To do this we just need to ensure Node.js is installed, as npm comes bundled with it by default. So if you haven’t already got this up and running then installing one of the <a href="http://nodejs.org/download/">pre-built packages from the Node.js website</a> is probably the simplest method.</p>
<p>Next we need to <a href="http://sass-lang.com/install">install Sass</a>. If you are using Windows then you’ll first need to <a href="http://rubyinstaller.org/">install Ruby</a>, whereas if you are using a Mac then Ruby comes pre-installed. To install Sass, open Terminal and run the following command:</p>
<pre><code class="language-sh">gem install sass
</code></pre>
<p>This can take a few minutes to complete, so be patient, but if you are using a Mac and the above command is failing then you could use <code>sudo gem install sass</code> instead.</p>
<h2 id="projectsetup">Project Setup</h2>
<p>You will need to <a href="https://raw.githubusercontent.com/toddmotto/atomic/master/src/atomic.js">download a small library called ‘atomic’</a>, which our project will use for making AJAX requests to the <a href="http://openweathermap.org/api">OpenWeatherMap API</a>.</p>
<p>Now we need to setup the project files and folders – create the following structure:</p>
<pre>

breeze
│
├──• Gruntfile.js
├──• index.html
├──• package.json
│
└───src
     │
     ├───js
     │    ├───app
     │    │    │
     │    │    └──• app.js
     │    │
     │    └───libs
     │         │
     │         └──• atomic.js
     └───scss
          │
          └──• app.scss


</pre>
<p>The <em>breeze</em> directory will serve as the document root of our application.</p>
<p>Now that we have the basic structure, let’s add some content to the files we just created. The contents of <em>index.html</em> should be as follows:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;

&lt;html&gt;
	&lt;head&gt;
		&lt;title&gt;Breeze&lt;/title&gt;
		&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=“dist/css/app.min.css&quot;&gt;
	&lt;/head&gt;

	&lt;body&gt;
		&lt;div class=&quot;main-container&quot;&gt; &lt;/div&gt;

		&lt;script src=“dist/js/app.min.js&quot;&gt;&lt;/script&gt;
	&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Astute readers may have noticed that – within the HTML markup – we are referencing directories and files that don’t exist – <em>dist/css/app.min.css</em> and <em>dist/js/app.min.js</em>. These directories and files will be created automatically by Grunt once we set it up and run a build task.</p>
<p>For the contents of <em>app.js</em>, we will temporarily just add an alert message:</p>
<pre><code class="language-js">alert(&quot;Breeze&quot;);
</code></pre>
<p>And finally, the contents of <em>app.scss</em> will currently just contain the following styles:</p>
<pre><code class="language-css">html, body, div {
	margin: 0;
	padding: 0;
}
</code></pre>
<h2 id="installingdevelopmentdependencies">Installing Development Dependencies</h2>
<p>We are going to use npm for installing development dependencies – those used by Grunt for the build step of our application.</p>
<p>The first thing we need to install is the CLI (command line interface) for Grunt. This will be a global install; it isn’t specific to our project and will be available system-wide, so you’ll only need to do this once per-system:</p>
<p><strong>Mac users</strong><br>
You will need to run this command with <em>sudo</em>.</p>
<pre><code class="language-sh">sudo npm install -g grunt-cli
</code></pre>
<p><strong>Windows users</strong></p>
<pre><code class="language-sh">npm install -g grunt-cli
</code></pre>
<p>Next up, within the <em>breeze</em> directory, open the <em>package.json</em> file that we created earlier and add the following:</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;breeze&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;
}
</code></pre>
<p>This simply defines some basic information about our application which is required by npm. Save and close the file.</p>
<p>Although the <em>package.json</em> file has many uses, within the scope of this tutorial – and other than the required <em>name</em> and <em>version</em> properties – it will simply be used to define the development dependencies of our application.</p>
<p>Note that the command <code>npm init</code> could instead be used to automatically generate a <em>package.json</em> file, but doing so adds many properties that we won’t need to use in this tutorial, so creating it manually is a simpler option in this case.</p>
<p>Now that we have our <em>package.json</em> file, using Terminal, change to the <em>breeze</em> directory. We will need to run all remaining commands from this directory. Install the development dependencies by running the following command:</p>
<pre><code class="language-sh">npm install grunt grunt-contrib-connect grunt-contrib-sass grunt-contrib-uglify grunt-contrib-watch grunt-devtools --save-dev
</code></pre>
<p>This installs all of our development dependencies into a <em>node-modules</em> folder within the <em>breeze</em> directory. A ‘devDependencies’ property is also added to <em>package.json</em>, listing the names and current version numbers of our application’s development dependencies:</p>
<pre><code class="language-sh">{
  &quot;name&quot;: &quot;breeze&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;devDependencies&quot;: {
    &quot;grunt&quot;: &quot;^0.4.5&quot;,
    &quot;grunt-contrib-connect&quot;: &quot;^0.8.0&quot;,
    &quot;grunt-contrib-sass&quot;: &quot;^0.8.1&quot;,
    &quot;grunt-contrib-uglify&quot;: &quot;^0.6.0&quot;,
    &quot;grunt-contrib-watch&quot;: &quot;^0.6.1&quot;,
    &quot;grunt-devtools&quot;: &quot;^0.2.1&quot;
  }
}
</code></pre>
<h2 id="doingthegruntwork">Doing the Grunt Work</h2>
<p>We will be using <a href="http://gruntjs.com/">Grunt</a> – a JavaScript task runner – to carry out the build step of our application. Grunt works by running various user-defined tasks when instructed to do so. We are going to set it up in such a way that our tasks run when a change to JavaScript or SCSS files is detected.</p>
<p>To use Grunt, we first need to open <em>Gruntfile.js</em> that we created earlier within the <em>breeze</em> directory and paste in the following code, which we will discuss in more detail below:</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.initConfig({

	});
};
</code></pre>
<p>When we use the Grunt CLI (more on this below), the version of the Grunt library specified within the <em>devDependencies</em>  property inside <em>package.json</em> is loaded and the configuration within the Gruntfile is automatically applied. Currently our Gruntfile doesn’t contain any configuration options, so we’ll start adding these now.</p>
<h3 id="sass">Sass</h3>
<p>First, we’ll add a config for compiling our SCSS code using the <em>sass</em> task (<a href="https://github.com/gruntjs/grunt-contrib-sass">grunt-contrib-sass</a>) – a wrapper for the version of Sass installed on our system i.e. the Sass Ruby gem we installed earlier:</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);

	grunt.initConfig({
		sass: {

			dev : {
				options: {
					style: &quot;compressed&quot;,
					sourcemap : true
				},

				files: {
					&quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
				}
			}
		}
	});
};
</code></pre>
<p>Here, we first tell Grunt to load the <em>sass</em> task and then we define the config for this task within the object that gets passed into Grunt’s <em>initConfig</em> method. Within the <em>sass</em> task we define a ‘dev’ configuration object. We can define however many configurations we like and call them whatever we like, but within these configuration objects we need to follow the rules of the task we are using, which in this case is <em>sass</em>. The <em>sass</em> task allows us to define an ‘options’ object containing various properties that affect how our SCSS is converted into CSS. In this case we are simply stating that we want the CSS to be compressed into one line with any empty space removed. The ‘files’ object contains two file path strings – the one on the left is the file path to which we want Grunt to output our compiled CSS, and the one on the right is the main SCSS file we want to use to start the compilation process.</p>
<p>To test that everything is working as expected, run the following command in Terminal:</p>
<pre><code class="language-sh">grunt sass:dev
</code></pre>
<p>Doing that should compile the SCSS into an <em>app.min.css</em> file within the <em>dist/css/</em> directory.</p>
<h3 id="uglify">Uglify</h3>
<p>Next up, we want to use the <em>uglify</em> (<a href="https://github.com/gruntjs/grunt-contrib-uglify">grunt-contrib-uglify</a>) task to concatenate all of our JavaScript into a single file and then minify this file when complete so that it loads quickly in web browsers. This is also a good way to reduce the number of HTTP requests an application makes when developing with more than one JavaScript file; rather than using multiple <em>script</em> tags within the HTML markup we will only need to use a single one that points to the compiled file containing all of our JavaScript code – <em>app.min.js</em>. To carry out the concatenation and minification process we will add a configuration object for the <em>uglify</em> task:</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-uglify&quot;);

	grunt.initConfig({

		sass: {

			dev : {
				options: {
					style: &quot;compressed&quot;,
					sourcemap : true
				},

				files: {
					&quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
				}
			}
		},

		uglify : {

			dev : {
				options : {
					compress : true,
					mangle : true,
					preserveComments : false
				},

				files: {
					&quot;dist/js/app.min.js&quot; : [&quot;src/js/libs/atomic.js&quot;, &quot;src/js/app/app.js&quot;]
				}
			}
		}
	});
};
</code></pre>
<p>We have now also loaded the <em>uglify</em> task and defined a <em>dev</em> config for it within the object passed into Grunt’s <em>initConfig</em> method.</p>
<p>The ‘files’ object for <em>uglify</em> works in exactly the same way as the one within the <em>sass</em> config, with the exception that we are passing in an array of JavaScript file paths on the right. All of these files will be concatenated, minified and output to a single JavaScript file. When set to ‘true’, the <em>compress</em> option shortens variable and function names, and removes empty space in an attempt to reduce the overall file size. Similarly, When set to ‘true’, the <em>mangle</em> option performs various optimisations such as removing unreferenced functions and variables. We have set the <em>preserveComments</em> option to ‘false’ so that all comments will be removed from the minified file.</p>
<p>To test that everything is working as expected, run the following command in Terminal:</p>
<pre><code class="language-sh">grunt uglify:dev
</code></pre>
<p>Doing that should compile the JavaScript into an <em>app.min.js</em> file within the <em>dist/js/</em> directory.</p>
<p>A word of warning here – the files are concatenated in the same order that they are specified in the array of file paths, so you’ll need to ensure that dependencies are ordered correctly.</p>
<p>With all that working as expected, the project directory structure should now look like this:</p>
<pre>

breeze
│
├──• Gruntfile.js
├──• index.html
├──• package.json
│
├───src
│    │
│    ├───js
│    │    │
│    │    ├───app
│    │    │    │
│    │    │    └──• app.js
│    │    │
│    │    └───libs
│    │         │
│    │         └──• atomic.js
│    └───scss
│         │
│         └──• app.scss
│
└───dist
     │
     ├───css
     │    │
     │    └──• app.min.css
     └───js
          │
          └──• app.min.js


</pre>
<h3 id="connect">Connect</h3>
<p>Now that we’ve setup a build for our SCSS and JavaScript, we are going to create a <em>connect</em> (<a href="https://github.com/gruntjs/grunt-contrib-connect">grunt-contrib-connect</a>) config that allows us to quickly start a basic server in order to run our application, which will be launched in the default browser. This is way quicker than configuring Apache, for example. And like Apache, the <em>connect</em> server runs our application using the <em>http://</em> protocol – which allows us to use AJAX – rather than the <em>file://</em> protocol which does not.</p>
<p>We will set this up in a similar way to the other configs:</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-uglify&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-connect&quot;);

	grunt.initConfig({

		sass: {

			dev: {
				options: {
					style: &quot;compressed&quot;,
					sourcemap : true
				},

				files : {
					&quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
				}
			}
		},

		uglify: {

			dev: {
				options: {
					compress: true,
					mangle: true,
					preserveComments: false
				},

				files: {
					&quot;dist/js/app.min.js&quot; : [&quot;src/js/libs/atomic.js&quot;, &quot;src/js/app/app.js&quot;]
				}
			}
		},

		connect: {

			server : {
				options: {
					open: true,
					keepalive: true
				}
			}
		}
	});
};
</code></pre>
<p>As with the other tasks, we have loaded the <em>connect</em> task and defined a config, which this time we have called ‘server’. The only two options we need to use at this point are ‘open’ and ‘keepalive’ – both of which we have set to ‘true’. The <em>open</em> option simply launches the default web browser on our system and loads the index page within the same directory as the Gruntfile. By default, the <em>connect</em> task only keeps the server active for as long as our other tasks are running, so since we have no continually-running tasks, we use the <em>keepalive</em> option to force the server to stay active until it is manually stopped.</p>
<p>Run the following command to launch our application in the default browser:</p>
<pre><code class="language-sh">grunt connect:server
</code></pre>
<p>Notice that, within the Terminal window, we can no longer run any commands now that we have the server running. To stop the server, on the keyboard hold the <code>ctrl</code> key and press <code>c</code>.</p>
<p>If you want to run Grunt commands while the server is active then you can open another Terminal window and once again change to the <em>breeze</em> directory.</p>
<h2 id="runningmultipletaskswithonecommand">Running Multiple Tasks with One Command</h2>
<p>So far, we have set things up so that we can run tasks one at a time, but what if we want to run multiple tasks? We could run them one after the other in Terminal, but there is an easier way – we can register our own tasks with Grunt, which is much more straightforward than it sounds. Let’s start by creating a task that will first run <em>sass:dev</em>, followed by <em>uglify:dev</em>, and then finally <em>connect:server</em>. We’ll call this task ‘breeze’:</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-uglify&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-connect&quot;);

	grunt.initConfig({

		sass: {

			dev: {
				options: {
					style: &quot;compressed&quot;,
					sourcemap : true
				},

				files : {
					&quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
				}
			}
		},

		uglify: {

			dev: {
				options: {
					compress: true,
					mangle: true,
					preserveComments: false
				},

				files: {
					&quot;dist/js/app.min.js&quot; : [&quot;src/js/libs/atomic.js&quot;, &quot;src/js/app/app.js&quot;]
				}
			}
		},

		connect: {

			server : {
				options: {
					open: true,
					keepalive: true
				}
			}
		}
	});

	grunt.registerTask(&quot;breeze&quot;, [&quot;sass:dev&quot;, &quot;uglify:dev&quot;, &quot;connect:server”]);
};
</code></pre>
<p>So now instead of sequentially running each task manually, in Terminal, we now simply need to run:</p>
<pre><code class="language-sh">grunt breeze
</code></pre>
<p>This will run all the tasks we specified in the array for the second parameter of Grunt’s <em>registerTask</em> method.</p>
<h2 id="automatingbuildtasks">Automating Build Tasks</h2>
<p>Now everything is set up and ready for us to manually run builds of our SCSS and JavaScript, and view our app within the browser. But, wouldn’t it be nice if we could go one step further and have Grunt run relevant tasks automatically when any of our development files is changed? That’s precisely what we’ll be doing in this section of this tutorial.</p>
<h3 id="watch">Watch</h3>
<p>To automate the running of the tasks that we have already defined, we first need to setup a <em>watch</em> (<a href="https://github.com/gruntjs/grunt-contrib-watch">grunt-contrib-watch</a>) task which watches specific files and runs relevant tasks if any of the files changes.</p>
<p>Setting up the <em>watch</em> task is very similar to the way in which we setup all the other tasks (this is now the complete Gruntfile):</p>
<pre><code class="language-js">module.exports = function(grunt) {

	&quot;use strict&quot;;

	grunt.loadNpmTasks(&quot;grunt-contrib-sass&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-uglify&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-connect&quot;);
	grunt.loadNpmTasks(&quot;grunt-contrib-watch&quot;);

	grunt.initConfig({

		sass: {

			dev: {
				options: {
					style: &quot;compressed&quot;,
					sourcemap : true
				},

				files : {
					&quot;dist/css/app.min.css&quot;: &quot;src/scss/app.scss&quot;
				}
			}
		},

		uglify: {

			dev: {
				options: {
					compress: true,
					mangle: true,
					preserveComments: false
				},

				files: {
					&quot;dist/js/app.min.js&quot; : [&quot;src/js/libs/atomic.js&quot;, &quot;src/js/app/app.js&quot;]
				}
			}
		},

		connect: {

			server : {
				options: {
					open: true
				}
			}
		},

		watch: {
			
			js: {
				files: [&quot;src/js/**/*.js&quot;],
				tasks: [&quot;uglify:dev&quot;]
			},

			scss: {
				files: [&quot;src/scss/**/*.scss&quot;],
				tasks: [&quot;sass:dev&quot;]
			}
		}
	});

	grunt.registerTask(&quot;breeze&quot;, [&quot;sass:dev&quot;, &quot;uglify:dev&quot;, &quot;connect:server&quot;, &quot;watch&quot;]);
};
</code></pre>
<p>We have loaded the <em>watch</em> task and created two configs – ‘js’ and ‘scss’. These configs could have been called anything else, but the names were chosen so that the nature of what the configs will be used for is clear. Within each config, using the <em>files</em> option, we specify an array of file paths that are to be watched for changes, and then, using the <em>tasks</em> option, we specify a list of tasks to run when any of the listed files is changed. In the <em>js</em> config we are watching all files that have a <em>.js</em> file extension within the <em>src/js</em> directory and its subdirectories. When any of these files is changed, the <em>uglify:dev</em> task is run automatically. Similarly, with the <em>scss</em> config we are watching all files that have a <em>.scss</em> file extension within the <em>src/scss</em> directory and its subdirectories. When any of these files is changed, the <em>sass:dev</em> task is run automatically.</p>
<p>At this point, it is important to note that we have removed the <em>keepalive</em> property from the 'server' config of the <em>connect</em> task, since it would have stopped the <em>watch</em> task – or any task, for that matter – from running afterwards. The <em>keepalive</em> property is no longer needed because we are now using the continually-running <em>watch</em> task, which will keep the server alive automatically.</p>
<p>Notice that – within the <em>breeze</em> task we created earlier – ‘watch’ has been added to the array of tasks that Grunt will run for us sequentially.</p>
<p>Now we simply need to once again run our <code>grunt breeze</code> task using Terminal (be sure to cancel any running tasks using <code>ctrl + c</code> first) and our app will launch in the browser, with the <em>watch</em> task waiting for changes on the lists of files we specified. To check this is working change the alert message in <em>app.js</em> to the following:</p>
<pre><code class="language-js">alert(&quot;Grunt is a breeze&quot;);
</code></pre>
<p>Save the file and Grunt should automatically run the tasks we specified in the <em>watch:js</em> config, so refresh the web browser and you should see the new alert.</p>
<h2 id="nextsteps">Next steps</h2>
<p>So far we have covered how to setup Sass and npm, and how to use Grunt to setup a simple workflow for the build step of a web application.</p>
<p>In the next instalment of this two-part tutorial, we will cover the logic of the <em>breeze</em> application – loading and displaying current temperature information for various cities by making AJAX requests to the <a href="http://openweathermap.org/api">OpenWeatherMap API</a>. We’ll also briefly cover SCSS variables and mixins.</p>
<p>Feel free to ask questions in the comments below.</p>
<p>Happy coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Getting to Grips with Google Glass and iBeacons]]></title><description><![CDATA[Google Glass is a relatively new platform which enables people to view and interact with an optical head mounted display using either touch or voice recognition.]]></description><link>https://ivanhayes.com/blog/google-glass-and-ibeacons/</link><guid isPermaLink="false">5af1bb1a7ce7f61014b7fba0</guid><category><![CDATA[android]]></category><category><![CDATA[java]]></category><category><![CDATA[tech]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Wed, 15 Oct 2014 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Google Glass is a relatively new platform which enables people to view and interact with an optical head mounted display using either touch or voice recognition.</p>
<!--more-->
<h2 id="theidea">The Idea</h2>
<p>We wanted to test the capabilities of Google Glass in combination with iBeacons, and create a simple contextually aware application. The app would look for specific iBeacons and, when detected, display information related to the area in which the found iBeacon was placed.</p>
<h2 id="wheretostart">Where to Start?</h2>
<p>When we set out to make Glassware, I had absolutely no experience with Android development, or the Java programming language in general. Luckily, due to extensive exposure to ActionSript 3.0, I had much experience with using a strict, statically typed language. So where to start? RTFM! Yes the documentation for Glass was my first port of call in order to gain more of an understanding of how to approach a Glassware project. There are various routes one can take, two of which being ‘activities’ suggested by the good folks at Google – Live Cards and Immersion.</p>
<p>So before deciding upon which route was right for us, I needed to take a step back and think about what exactly we were hoping to achieve, which in this case was a fairly simple contextually aware application. This would give us a clearer idea of how the app would function and allow me to focus on learning Java via the Android SDK and GDK add-on, based on our end goal.</p>
<p>With that in mind, one of the first questions I asked myself was “has this been done before?”, so I had a look online for similar projects and found a few, most of which being very old, with ‘very old’ in this case meaning a few months old due to the rapidly changing APIs for Glass and the introduction of their new development tool, <a href="https://developer.android.com/sdk/installing/studio.html">Android Studio</a>.</p>
<p>There was one project on Github – <a href="https://github.com/advantej/Glasstimote">Glasstimote</a> – which was very closely aligned to what we wanted to achieve, so instead of reinventing the wheel, I forked the project and got to work analysing every little part of the code to gain an understanding of how Android projects are pieced together.</p>
<p>The neat and tidy approach Google have taken with Android in terms of the XML format manifest, layouts, and string values, is very intuitive and I picked it up in no time.</p>
<p>In terms of the activity, I decided on an immersion approach, as we wanted the application to be constantly running and stop the Google Glass from going into sleep mode while in use. In order to ensure the application views would remain open, the <code>android:keepScreenOn</code> flag was set to ‘true’ within the XML file for each view layout.</p>
<h2 id="data">Data</h2>
<p>We experimented with a version of the app which updated its views by using async tasks to retrieve data from a MySQL database, with the advantage of this being dynamic data; it would be simple to update the view data without recompiling the application. This worked well in very controlled conditions, but suffered with the lack of a simple user experience, as when WiFi dropped out – something that happened frequently – the user would have to rejoin a network and resume the previously failed data update. It felt like this approach wasn’t quite right for our intended purpose, and so we shelved the prototype and went back to using static data.</p>
<p>A great user experience is key; the application could be doing all kinds of fancy things in the background, but if this makes for a lousy user experience then nobody will want to use the app to start with. Generally the users won’t care how the application gets its data if they’ve had a good experience.</p>
<h2 id="optimisation">Optimisation</h2>
<p>Glass is only a small little thing and we want to avoid putting too much computational pressure on it, so optimising our application as much as possible seemed like a good option, since it was already necessary to rewrite much of the logic within the forked project in order for it to meet our requirements.</p>
<p>The original application worked by continually scanning for and displaying information about as many iBeacons as it could find. This behaviour didn’t correspond to our functional goals, for which we only needed to find one of three beacons at a time. Therefore, it made sense for our version to only scan for iBeacons until one specific to the application was found. In order for this to work I set up three distinct spatial regions within the output range of each iBeacon. The application would then stop scanning for iBeacons when in range of one of the regions and display information about that specific region. So, for example, it starts by scanning for three beacons, each with a named overall region – ‘kitchen’, ‘library’ and ‘creative’ – and when one is found, such as ‘kitchen’, and if within the entry region, it then stops scanning for the other two. If the application then detects that it is far enough outside the ‘kitchen’ entry region to be within, or further away than the exit region, it once again starts scanning for all three iBeacons. This simple optimisation saves us tons of unnecessary processing, in turn prolonging battery life of the Google Glass.</p>
<p>Another small optimisation was to use string values defined in the XML for updating views with region location information, instead of using string literals to display this data. This helps avoid unnecessary object allocation.</p>
<h2 id="testing">Testing</h2>
<p>iBeacons in general seem to be fairly unreliable in terms of calculating distance. This is due to the changing nature of many environmental and atmospheric conditions, such as the number of people in a room, WiFi interference, and even the weather.</p>
<p>It’s also very difficult to debug Glassware efficiently when iBeacon regions are involved, due to the need for physical testing; much of the time I was walking around armed with a MacBook Pro, just so that I could read the logs in realtime.</p>
<p>I experimented with setting various signal strengths on the iBeacons and in the end found it easiest to use a strong signal in order to emit at a fairly long distance, but to have the application logic look for smaller ranges within this large area; the iBeacons were set to emit at a range of 30m and within this total emitted range a 2m radius was used for entry regions and a 3.5m radius was used for exit regions.</p>
<p>In the end the app worked really well, detecting iBeacons fairly reliably and providing a great experience for <a href="http://tmwlabs.tumblr.com/post/99559985355/incubator-expo">TMW Incubator Expo</a> attendees. The final version of the app is open-source and can be found on the TMW Github repo: <a href="https://github.com/tmwagency/Glasstimote">https://github.com/tmwagency/Glasstimote</a>.</p>
<h2 id="finalthoughts">Final Thoughts</h2>
<p>With interesting apps such as World Lens, which translates words in realtime both with and without an Internet connection, and the ability to display contextually relevant information based on the user‘s location via iBeacons, Google Glass has a lot of potential. It also seems to be very attractive to Android developers since porting existing apps to Glassware was made much easier after the release of the GDK, but it becomes apparent that there is a tough road ahead for the £1000 glasses when compared to extension-focused wearables such as smart watches from Samsung and the soon-to-be-released Apple iWatch, since the latter – in addition to being much more subtle in appearance – offers a less intrusive option for people wanting to interact with their digital world, as the watches are currently a simplified extension of smartphones and tablets; in contrast to Glass, they are literally not in your face when worn. Yet, Google Glass retains a certain charm, seeing as it is the first of its kind, and we at TMW are hoping the competition will spawn some even greater products from the technology giants.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Bullet pub-sub Library]]></title><description><![CDATA[Bullet is an ultra lightweight and simple to use pub-sub library with zero dependencies, AMD/module support and an intuitive API.
It was built to facilitate a simple and consistent system of communication across web applications and includes only the bare essentials typically needed to achieve this.]]></description><link>https://ivanhayes.com/blog/bullet-pub-sub-library/</link><guid isPermaLink="false">5af1b7407ce7f61014b7fb96</guid><category><![CDATA[javascript]]></category><category><![CDATA[library]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Wed, 10 Sep 2014 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Bullet is an ultra lightweight and simple to use pub-sub library with zero dependencies, AMD/module support and an intuitive API.<br>
It was built to facilitate a simple and consistent system of communication across web applications and includes only the bare essentials typically needed to achieve this.</p>
<!--more-->
<h3 id="usage">Usage</h3>
<p>Firstly, grab either the <a href="https://raw.githubusercontent.com/munkychop/bullet/master/dist/bullet.min.js">minified</a>, or <a href="https://raw.githubusercontent.com/munkychop/bullet/master/dist/bullet.js">non-minified</a> source from Github.</p>
<p>Alternatively, you can install via npm with the following command in your command prompt:</p>
<pre><code class="language-sh">npm install bullet-pubsub --save
</code></pre>
<p>Or you can install via Bower instead, if that's your thing:</p>
<pre><code class="language-sh">bower install bullet
</code></pre>
<h3 id="includebulletinyourapplication">Include Bullet in your application</h3>
<pre><code class="language-html">&lt;script type=&quot;application/javascript&quot; src=&quot;path/to/bullet.min.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p>If using CommonJS then simply require Bullet as per usual:</p>
<pre><code class="language-javascript">var Bullet = require(&quot;bullet-pubsub&quot;);
</code></pre>
<h3 id="api">API</h3>
<h4 id="on"><strong>.on()</strong></h4>
<pre><code class="language-javascript">Bullet.on(&quot;someMessageName&quot;, callback);
</code></pre>
<p>Register a callback function to get called whenever the specified message is triggered.</p>
<p><strong>Example usage:</strong></p>
<pre><code class="language-javascript">function helloCallback () {
    console.log(&quot;hello there :)&quot;);
}
 
// Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:
    
Bullet.on(&quot;hello&quot;, helloCallback);
    

// Somewhere later in the application...
    
    
// Trigger the 'hello' message – Bullet will call the 'helloCallback' function:
    
Bullet.trigger(&quot;hello&quot;);
</code></pre>
<hr>
<h4 id="off"><strong>.off()</strong></h4>
<pre><code class="language-javascript">Bullet.off(&quot;someMessageName&quot;[, callback]);
</code></pre>
<p>Remove either all callback functions or a specific callback function registered against the specified message.</p>
<p><strong>Example usage:</strong></p>
<pre><code class="language-javascript">function helloCallback () {
    console.log(&quot;hello there :)&quot;);
}

function anotherCallback () {
    console.log(&quot;hello again :)&quot;);
}


Bullet.on(&quot;hello&quot;, helloCallback);
Bullet.on(&quot;hello&quot;, anotherCallback);


// Somewhere later in the application...


// Trigger the 'hello' message – Bullet will call both the 'helloCallback' and 'anotherCallback' functions:

Bullet.trigger(&quot;hello&quot;);


// Remove all callback functions associated with the 'hello' message:
Bullet.off(&quot;hello&quot;);

// Attempt to trigger the 'hello' message again – Bullet won't call any functions:
Bullet.trigger(&quot;hello&quot;);
</code></pre>
<p><strong>Example usage removing a specific callback:</strong></p>
<pre><code class="language-javascript">function helloCallback () {
    console.log(&quot;hello there :)&quot;);
}

function anotherCallback () {
    console.log(&quot;hello again :)&quot;);
}


Bullet.on(&quot;hello&quot;, helloCallback);
Bullet.on(&quot;hello&quot;, anotherCallback);


// Somewhere later in the application...


// Trigger the 'hello' message – Bullet will call both the 'helloCallback' and 'anotherCallback' functions:

Bullet.trigger(&quot;hello&quot;);


// Remove only the 'anotherCallback' function associated with the 'hello' message:
Bullet.off(&quot;hello&quot;, anotherCallback);

// Trigger the 'hello' message again – Bullet will only call the 'helloCallback' function:
Bullet.trigger(&quot;hello&quot;);
</code></pre>
<hr>
<h4 id="once"><strong>.once()</strong></h4>
<pre><code class="language-javascript">Bullet.once(&quot;someMessageName&quot;, callback);
</code></pre>
<p>This function behaves in the same way as the the <strong>'on'</strong> function, except that – once registered – the callback function will only be called a single time when the specified message is triggered.</p>
<p><strong>Example usage:</strong></p>
<pre><code class="language-javascript">function helloCallback () {
    console.log(&quot;hello there :)&quot;);
}


// Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:

Bullet.once(&quot;hello&quot;, helloCallback);


// Somewhere later in the application...


// Trigger the 'hello' message – Bullet will call the 'helloCallback' function:

Bullet.trigger(&quot;hello&quot;);


// Attempt to trigger the 'hello' message again – Bullet won't call any functions this time:

Bullet.trigger(&quot;hello&quot;);
</code></pre>
<hr>
<h4 id="trigger"><strong>.trigger()</strong></h4>
<pre><code class="language-javascript">Bullet.trigger(&quot;someMessageName&quot;[, data]);
</code></pre>
<p>This function will call all callback functions registered against the specified message, optionally passing in custom data as a payload.</p>
<p><strong>Example usage:</strong></p>
<pre><code class="language-javascript">function helloCallback () {
    console.log(&quot;hello there :)&quot;);
}


// Register the 'helloCallback' function to be called whenever the 'hello' message is triggered:

Bullet.on(&quot;hello&quot;, helloCallback);


// Somewhere later in the application...


// Trigger the 'hello' message – Bullet will call the 'helloCallback' function:

Bullet.trigger(&quot;hello&quot;);
</code></pre>
<p><strong>Example usage with custom data:</strong></p>
<pre><code class="language-javascript">function userAddedCallback (data) {
    console.log(data);
}


// Register the 'userAddedCallback' function to be called whenever the 'user-added' message is triggered:

Bullet.on(&quot;user-added&quot;, userAddedCallback);


// Somewhere later in the application...


// Create some custom data:

var customData = {
    someProp : &quot;bro&quot;,
    someOtherProp : &quot;awesome!&quot;
};


// Trigger the 'user-added' message – Bullet will call the 'helloCallback' function and
// pass in the custom data that you created, which will be sent to the function as a parameter:

Bullet.trigger(&quot;user-added&quot;, customData);
</code></pre>
<h2 id="abouttheproject">About the Project</h2>
<p>Bullet was developed and is currently maintained by <a href="https://twitter.com/munkychop">Ivan Hayes</a>.</p>
<p>Head over to the Github <a href="https://github.com/munkychop/bullet">project page</a> to find out more. Go ahead and star the project to keep up with its latest developments :-)</p>
</div>]]></content:encoded></item><item><title><![CDATA[Faster Tapping with SwiftClick]]></title><description><![CDATA[SwiftClick is a library created to eliminate the 300ms click event delay on touch devices that support orientation change.]]></description><link>https://ivanhayes.com/blog/faster-tapping-with-swiftclick/</link><guid isPermaLink="false">5af1b2a17ce7f61014b7fb8d</guid><category><![CDATA[javascript]]></category><category><![CDATA[library]]></category><dc:creator><![CDATA[Ivan Hayes]]></dc:creator><pubDate>Fri, 10 Jan 2014 12:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>SwiftClick is a library created to eliminate the 300ms click event delay on touch devices that support orientation change and is designed to be super lightweight — weighing in at only 957 bytes minified &amp; gzipped!</p>
<!--more-->
<p>As many of you will have experienced when using touch devices such as iPhones, tapping on HTML elements sometimes feels a bit sluggish. This is because there is a <a href="http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away">300ms delay</a> before click events are fired. As a user experience this is clunky behaviour and so the exploration of potential solutions to this problem worked its way into TMW labs. Since the issue only occurs on devices that support touch events, the initial approach was to simply run a basic test for touch support and create a variable for the event name to use in click listeners – the value of which being either 'touchstart' for devices that support touch, or 'click' for those that don't:</p>
<pre><code class="language-js">var clickEventType = &quot;ontouchstart&quot; in window ? &quot;touchstart&quot; : &quot;click&quot;;
</code></pre>
<p>The problem with this detection method is that laptops or monitors that support touch would expect a touch event, meaning that if a mouse was being used then click events wouldn't work. Because of this the detection method was refined to check if the device supports both touch and orientation change and only use touch events if both conditions are met:</p>
<pre><code class="language-js">var clickEventType = &quot;onorientationchange&quot; in window &amp;&amp; &quot;ontouchstart&quot; in window ? &quot;touchstart&quot; : &quot;click&quot;;
</code></pre>
<p>This detection method worked well in the majority of situations, with the <code>'clickEventType'</code> variable being used in any event listeners that needed to get a click event:</p>
<pre><code class="language-js">someEl.addEventListener(clickEventType, fn, false);
</code></pre>
<p>This worked pretty nicely, with the main drawback being the fact that the 'clickEventType' variable would have to be used in any place where a click listener is needed, which could potentially become problematic when projects are picked up by new developers who may not necessarily realise that this variable is being used at all.</p>
<p>Other options were explored, including a really nice utility called <a href="https://github.com/ftlabs/fastclick">FastClick</a>, developed by <a href="https://github.com/ftlabs">FT Labs</a>. When an HTML element is touched, FastClick hijacks the touch event and creates a synthesised click event if necessary, which is then dispatched by the touched element. This allows JavaScript event listeners to be registered using the normal 'click' event type, but has the advantage of these events being fired earlier than normal – effectively removing the 300ms delay. For the most part this worked really well, but it eventually became apparent that the util sometimes exhibited strange behaviour, such as ghost-clicks, or events not firing at all when links were clicked and then firing later on, at the same time as other click events when different elements were clicked. And at 25kb non-minified (2.4kb minified &amp; gzipped) it is also a bit heavy in file size when considering the nature of the task for which it was created to solve. Therefore, the time was right to create a custom solution – <a href="https://github.com/tmwagency/swiftclick">SwiftClick</a>.</p>
<p>SwiftClick is inspired by FastClick, but has been designed from the ground up to be super lightweight — weighing in at a mere 6.8KB non-minified and a teeny-tiny 957 bytes (yes, bytes) minified &amp; gzipped!</p>
<p>In contrast with FastClick, which does a lot under the hood to fix obscure bugs in many crappy old mobile browsers for complex elements like <code>&lt;form&gt;</code>, <code>&lt;select&gt;</code>, and <code>&lt;textarea&gt;</code>, by default SwiftClick focuses on basic element types that are typically used in modern interactive development, providing an option to add in additional element types when necessary.</p>
<h2 id="usage">Usage</h2>
<p>Firstly, grab either the <a href="https://raw2.github.com/tmwagency/swiftclick/master/js/dist/swiftclick.min.js">minified</a>, or <a href="https://raw2.github.com/tmwagency/swiftclick/master/js/libs/swiftclick.js">non-minified</a> source from Github, or install via Bower using the following command in your command prompt:</p>
<pre><code class="language-sh">bower install swiftclick
</code></pre>
<h3 id="includeswiftclickinyourapplication">Include SwiftClick in your application</h3>
<pre><code class="language-html">&lt;script type=&quot;application/javascript&quot; src=&quot;path/to/swiftclick.min.js&quot;&gt;&lt;/script&gt;
</code></pre>
<h3 id="setupswiftclick">Setup SwiftClick</h3>
<p>Setting up SwiftClick is a very easy process, which mirrors that of FastClick in that instances must be attached to a context element. Touch events from all elements within the context element are automatically captured and converted to click events when necessary, minus the delay.</p>
<p>Start by creating a reference to a new instance of SwiftClick using the 'attach' helper method and attach it to a context element. Attaching to document.body is easiest if you only need a single instance of SwiftClick:</p>
<pre><code class="language-js">var swiftclick = SwiftClick.attach(document.body);
</code></pre>
<p>If necessary, multiple instances of SwiftClick can be created for specific context elements which, although not really necessary in most cases, can sometimes be useful for optimising applications with a large amount of HTML:</p>
<pre><code class="language-js">var navigationSwiftClick = SwiftClick.attach(someNavElement);
var uiSwiftClick = SwiftClick.attach(someOtherElement);
</code></pre>
<h3 id="defaultelements">Default Elements</h3>
<p>Once attached, by default SwiftClick will track events originating from the following element types:</p>
<ul>
<li><code>&lt;a&gt;</code></li>
<li><code>&lt;div&gt;</code></li>
<li><code>&lt;span&gt;</code></li>
<li><code>&lt;button&gt;</code></li>
</ul>
<h3 id="addingnondefaultelementtypes">Adding non-default element types</h3>
<p>If necessary you can make SwiftClick track events originating from additional element types by adding an array of node names. This requires a reference to an instance of SwiftClick:</p>
<pre><code class="language-js">var swiftclick = SwiftClick.attach(someElement);
swiftclick.addNodeNamesToTrack([&quot;p&quot;, &quot;h1&quot;, &quot;nav&quot;]);
</code></pre>
<h3 id="replacingallstorednodenamestotrack">Replacing all stored node names to track</h3>
<pre><code class="language-js">var swiftclick = SwiftClick.attach(someElement);
swiftclick.replaceNodeNamesToTrack([&quot;a&quot;, &quot;div&quot;]);
</code></pre>
<p>Doing this will remove all default node names, as well as any that have been added, and replace them with the node names within the array that is passed in, resulting in only the new node names being tracked.</p>
<h3 id="automaticallydisabledwhennotneeded">Automatically disabled when not needed</h3>
<p>SwiftClick only intercepts events for touch devices that support orientation change, otherwise it just sits there looking pretty.</p>
<h2 id="abouttheproject">About the Project</h2>
<p>SwiftClick was developed and is currently maintained by <a href="https://twitter.com/munkychop">Ivan Hayes</a>.</p>
<p>Head over to the Github <a href="https://github.com/tmwagency/swiftclick">project page</a> to find out more. Go ahead and star the project to keep up with its latest developments :-)</p>
</div>]]></content:encoded></item></channel></rss>