Tag Archiv: php

Freebie: 4 Wonderful JavaScript Option Menus

4-wonderful-javascript-option-menus

In this iteration of your freebie giveaway, we’ve prepared for you 4 awesome option menus! They are made entirely our of vanilla HTML, CSS and JavaScript, and don’t require any libraries or frameworks (except for some jQuery).

The Menus

These menus are designed in such a way, that they can quickly show all the needed options for your app. They are hidden offscreen and only appear when a user requests them with the push of a button. Once open, the menus can easily be hidden away again via a close button.

All you have to do, to include any of these menus in your project, is to copy the HTML from the demo examples and place the code anywhere on your page. There are also a few lines of JavaScript, and a specific CSS file for each menu. Both the JavaScript and CSS are self contained and shouldn’t cause any side effects to your existing content.

We’ve also added event handlers that call alerts when a option in the menus is selected. You can use this to effortlessly add your own functionality to all the links and buttons.

Side Option Menu

Side Option Menu

Free for Commercial Use

The menus can be downloaded from the button on the top of the page. You have all rights to customize them and use them however you want, in both personal and commercial projects (our license page). Enjoy!

Learn the Bootstrap Grid in 15 Minutes

learn-bootstrap-grid

Bootstrap is the most widely used frontend framework right now. When it comes to building responsive websites and apps, it’s the first choice of both professionals and hobbyists because of how simple it is to work with. Anybody who knows HTML, CSS and a bit of JavaScript can learn Bootstrap in no time.

In this quick lesson we’re going to cover the grid system, one of the fundamental concepts every Bootstrap developer needs to master. It allows us to create responsive page layouts which can change and adapt depending on the screen size of the device the user is on.

1. Rows and Columns

The grid consists of rows and columns. This allows us to freely position elements vertically and horizontally.

Rows are block level. This means, that when we create a row, it takes up the entire width of the element it is in. You can think of rows as new lines in your layout.

The horizontal alignment in the grid is done via columns. Only columns can be the direct children of a row and all content should go inside them. Placing content directly within a row will break the layout.

<!-- Wrong -->
<div class="row">
    Some content
</div>

<!-- Correct -->
<div class="row">
    <div class="col-md-12">Content Goes Here</div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

        .row div{
            background-color: #2196F3;
            border: 1px solid #fff;
            height: 125px;
            padding: 15px;
            color: #fff;
        }

    </style>

</head>

<body>

    <div class="container">
        <div class="row">
            <div class="col-md-6">First row, first column</div>
            <div class="col-md-6">First row, second column</div>
        </div>

        <div class="row">
            <div class="col-md-4">Second row, first column</div>
            <div class="col-md-4">Second row, second column</div>
            <div class="col-md-4">Second row, third column</div>
        </div>
    </div>

</body>
</html>

Note: Rows and columns have a special relationship. Columns have 15px left and right padding so that their content is properly spaced out. However, this pushes the first and last column’s content 15px away from the parent. To compensate, the row has negative left and right 15px margins. This is why you should always place columns within rows.

2. Rows are Divided in 12

Rows are divided horizontally into 12 equal parts. When we place a column inside a row, we have to specify the number of parts it is going to take up.

This is done by applying a specific class .col-md-NUMBER, where NUMBER can be an integer from 1 to 12. Depending on the number, a column will occupy a percentage of the full row width: 6 will be 50% (12/6), 3 will be 25% (12/3) and so on. The following example should make things clearer:

<div class="row">
    <div class="col-md-12">Full width</div>
</div>
<div class="row">
    <div class="col-md-3">25%</div>
    <div class="col-md-3">25%</div>
    <div class="col-md-6">50%</div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

        .row div{
            background-color: #2196F3;
            border: 1px solid #fff;
            height: 125px;
            padding: 15px;
            color: #fff;
        }

    </style>

</head>

<body>

        <div class="container">
	    <div class="row">
	        <div class="col-md-12">Full width</div>
	    </div>
	    <div class="row">
	        <div class="col-md-3">25%</div>
	        <div class="col-md-3">25%</div>
	        <div class="col-md-6">50%</div>
	    </div>
	</div>

</body>
</html>

3. Column Wrapping

We always have to take into consideration, that there are only 12 available spaces in a line. If we sum up the space required by a couple of adjacent columns, and the result exceeds 12, the last columns in that group will have to move to the next line. Let’s take a look at a practical example:

The first two columns have sizes of respectively 8 and 4 (8+4=12), which makes the first line full. There isn’t enough space for the third cell there, so it will have to wrap to the next line.

<div class="row">
    <div class="col-xs-8"></div>
    <div class="col-xs-4"></div>
    <div class="col-xs-9">This column will move to the next line.</div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}


        .row div{
            background-color: #2196F3;
            border: 1px solid #fff;
            height: 125px;
            padding: 15px;
            color: #fff;
        }

    </style>

</head>

<body>

        <div class="container">
	    <div class="row">
	        <div class="col-xs-8"> 8 </div>
	        <div class="col-xs-4"> 4 </div>
	        <div class="col-xs-9"> 9 </div>
	    </div>
	</div>

</body>
</html>

4. Screen Size Classes

Remember when we wrote .col-md-NUMBER in step 2? That -md- stands for medium. Bootstrap has a number of these classes for different screen sizes:

  • xs – Extra small screens like smartphones. Use it as .col-xs-NUMBER
  • sm – Small screen devices like tablets. .col-sm-NUMBER
  • md – Medium sized screens such as low dpi desktops and laptops.  .col-md-NUMBER
  • lg – Large, high resolution screens. .col-lg-NUMBER

Bootstrap takes the screen resolution and dpi into account when deciding which classes are active (learn more here). This is a powerful way how to control how layouts render on different devices.

When we define a rule for any device size, this rule will be inherited and applied to all bigger sizes, unless we overwrite it by supplying a new one. Hit the Run button on the following example and try resizing your browser to see the layout adapt.

<div class="row">
    <div class="col-xs-12 col-md-6"><p>Try resizing the browser to see this text and the image rearrange for optimal viewing. </p></div>
    <div class="col-xs-12 col-md-6"><img src="city.jpg" class="img-responsive"></div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

    </style>

</head>

<body>

        <div class="container">
	    <div class="row">
	        <div class="col-xs-12 col-md-6">
	            <p>Try resizing the browser to see this text and the image rearrange for optimal viewing.</p>
	            <p> On extra small screens both the text and the image take up the whole width of the row. </p>
	            <p> When the browser size gets into the medium category, they can move in together and share a single row. They only need half of the row, since there is more space horizontally on the screen.</p>
	        </div>
	        <div class="col-xs-12 col-md-6"><img src="http://cdn.tutorialzine.com/wp-content/uploads/2015/10/city.jpg" class="img-responsive"></div>
	    </div>
	</div>

</body>
</html>

5. Clearfix

In some scenarios, when a column has much more content and a bigger height then the ones after it, the layout will break. The columns will all pile up under each other, instead of moving to the next line as they should.

To prevent this, we add a helper div with the clearfix class. It will force all columns after it move to a new line, solving the issue.

<div class="row">
    <div class="col-xs-6 tall-column">A column much taller than the rest.</div>
    <div class="col-xs-6"></div>
    <div class="clearfix"></div>
    <div class="col-xs-6"></div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

        .row{
            margin-bottom: 50px;
        }

        .row .col-xs-6{
            background-color: #2196F3;
            border: 1px solid #fff;
            height: 125px;
            padding: 15px;
            color: #fff;
        }

        .row .tall-column{
            height: 300px;
        }

    </style>

</head>

<body>

         <div class="container">

	    <div class="row">
	        <div class="col-xs-6 tall-column">All columns are 6 units wide, but this one is too tall!</div>
	        <div class="col-xs-6"></div>
	        <div class="col-xs-6">This column shoudln't be here.</div>
	        <div class="col-xs-6">This column shoudln't be here.</div>
	    </div>

	    <div class="row">
	        <div class="col-xs-6 tall-column">By adding a clearfix between the second and third columns, everything will go where it should.</div>
	        <div class="col-xs-6"></div>
	        <div class="clearfix"></div>
	        <div class="col-xs-6">All better now.</div>
	        <div class="col-xs-6"></div>
	    </div>

	</div>

</body>
</html>

You can use Bootstrap’s responsive utility classes to control when clearfix is active.

6. Offsets Are Your Friend

By default, columns stick to each other without leaving any space, floating to the left. Any excess space remaining in that row stays empty on the right.

To create margins on the left of columns we can use the offset classes. Applying a .col-md-offset-2 class to any column will move it to the right, as if there is an invisible .col-md-2 cell there. You can have different offsets for the different screen sizes thanks to the xs, sm, md and lg prefixes.

You can use offsets to easily center columns:

<div class="row">
    <div class="col-md-6 col-md-offset-3"></div>
    <div class="col-md-10 col-md-offset-1"></div>
</div>
<div class="container">

    <div class="row">
        <div class="col-xs-5 col-xs-offset-4">col-xs-5 col-xs-offset-4</div>
        <div class="col-xs-9 col-xs-offset-2">col-xs-9 col-xs-offset-2</div>
        <div class="col-xs-6 col-xs-offset-1">col-xs-6 col-xs-offset-1</div>
        <div class="col-xs-4 col-xs-offset-1">col-xs-4 col-xs-offset-1</div>
        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
        <div class="col-xs-10">col-xs-10</div>
        <div class="col-xs-7">col-xs-7</div>
    </div>
    <div class="row">
        <div class="col-xs-5">col-xs-5</div>
    </div>
    <div class="row">
        <div class="col-xs-7">col-xs-7</div>
        <div class="col-xs-10">col-xs-10</div>
        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
        <div class="col-xs-9 col-xs-offset-2">col-xs-9 col-xs-offset-2</div>
        <div class="col-xs-5 col-xs-offset-4">col-xs-5 col-xs-offset-4</div>
    </div>

</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

        .row{
            padding: 0 250px;
        }

        .row div{
            background-color: #2196F3;
            height: 50px;
            padding: 15px;
            color: #fff;
        }

        @media(max-width: 992px){

            .row{
                padding: 0 100px;
            }

            .row div{
                height: 25px;
            }
        }

    </style>

</head>

<body>

        <div class="container">

	    <div class="row">
	        <div class="col-xs-5 col-xs-offset-4">col-xs-5 col-xs-offset-4</div>
	        <div class="col-xs-9 col-xs-offset-2">col-xs-9 col-xs-offset-2</div>
	        <div class="col-xs-6 col-xs-offset-1">col-xs-6 col-xs-offset-1</div>
	        <div class="col-xs-4 col-xs-offset-1">col-xs-4 col-xs-offset-1</div>
	        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
	        <div class="col-xs-10">col-xs-10</div>
	        <div class="col-xs-7">col-xs-7</div>
	    </div>
	    <div class="row">
	        <div class="col-xs-5">col-xs-5</div>
	    </div>
	    <div class="row">
	        <div class="col-xs-7">col-xs-7</div>
	        <div class="col-xs-10">col-xs-10</div>
	        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
	        <div class="col-xs-11 col-xs-offset-1">col-xs-11 col-xs-offset-1</div>
	        <div class="col-xs-9 col-xs-offset-2">col-xs-9 col-xs-offset-2</div>
	        <div class="col-xs-5 col-xs-offset-4">col-xs-5 col-xs-offset-4</div>
	    </div>

	</div>

</body>
</html>

7. Push and Pull

The push and pull classes allow us to reorder columns depending on screen size. Push moves a column to the right, and pull to the left. This is different from offset as push and pull use position: relative and don’t shift other columns.

Push and pull classes have the following format: .col-SIZE-push-NUMBER, and .col-SIZE-pull-NUMBER. Possible SIZE values are sm, xs, md and lg. This represents in which of the 4 screen size scenarios we want the swap to occur. NUMBER tells Bootstrap how many positions we want to move.

Hit Run on the code below and resize the pop-out to see how the two cells change places when the window becomes small.

<div class="row">
    <div class="col-xs-4 col-md-push-8">On laptop and desktop screens this text will go to the right and the image will go to the left, changing places.</div>
    <div class="col-xs-8 col-md-pull-4"><img src="city.jpg" class="img-responsive"></div>
</div>
<!DOCTYPE html>
<html>

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Learn the bootstrap grid in 15 minutes</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>

        body{ padding-top:50px;}

    </style>

</head>

<body>

       <div class="container">
	    <div class="row">
	        <div class="col-xs-4 col-md-push-8">On laptop and desktop screens this text will go to the right and the image will go to the left, changing places.</div>
	        <div class="col-xs-8 col-md-pull-4"><img src="http://cdn.tutorialzine.com/wp-content/uploads/2015/10/city.jpg" class="img-responsive"></div>
	    </div>
	</div>

</body>
</html>

Conclusion

Great job! You now know the most important part of the Bootstrap framework, and you’re ready to build your next stellar responsive design. But there is a bit more left to learn to achieve true mastery. Check out Bootstrap’s extensive documentation for more.

Comparing The Top Frameworks For Building Hybrid Mobile Apps

comparing-the-top-frameworks

Only a few years ago, developing mobile apps via JavaScript was nothing more then a quirky experiment. The idea that you could build iOS and Android apps, without having to operate with Java and Objective C, seemed appealing to many web devs. Now we have a plethora of frameworks bringing us closer than ever to creating a native mobile experience using only web technologies.

What are Hybrid Mobile Apps?

A hybrid app is just a regular mobile optimized website, written in CSS, HTML and JavaScript, that is displayed in a webview (this is a basically a stripped down web browser). The advantage is that you only need to write a single application, which in most cases runs unmodified on Android, iOS and Windows Phone. Most of the frameworks listed in this article do this using Cordova or PhoneGap, which give you a bridge to the device APIs in JavaScript.

In this article we’re going to compare the most popular JavaScript frameworks for building hybrid and native mobile apps. Scroll to the bottom of the article to see the scores.

1. Ionic

Ionic

Ionic

Ionic is the most popular framework on our list and probably the first choice of many developers. You can use the CSS portion of the framework to create native looking designs, but to harness the full potential of Ionic, it’s best to pair it with AngularJS. A big bonus you get with Ionic is the command-line interface which is full of awesome features including integrated emulators and a Cordova based app packager.

Pros:

  • Works with predefined components
  • Great community
  • Command Line Interface with lots of useful features

Cons:

  • You need to know AngularJS to do anything complex

2. Onsen UI

Onsen UI

Onsen UI

This is an open source framework that allows developers to build apps by combining native-looking components. It’s fairly simple to use, can work with or without AngularJS, and has great documentation that includes lots of examples and layouts for the most common app structures. A disadvantage of Onsen UI is that it currently offers only an iOS theme, although the next version promises Material Design support.

Pros:

  • Works with predefined components
  • Excellent documentation with examples

Cons:

  • PhoneGap/Cordova builder not included, but supported
  • No support for Material Design (yet)

3. Framework 7

Framework 7

Framework 7

The cool thing about Framework 7 is that it is completely framework agnostic (doesn’t have external dependencies like Angular or React) and still manages to make apps look and feel native, with properly styled components and animations. Anyone who understands HTML, CSS and JavaScript can create an app without making the code convoluted. Framework 7 doesn’t include any tools for emulation or app packaging so you will need to combine it with Cordova or PhoneGap.

Pros:

  • Simple to use, relies only on HTML, CSS and JavaScript
  • Good performance
  • Can be combined with any JavaScript framework of choice

Cons:

  • PhoneGap/Cordova builder not included, but supported

4. React Native

React Native

React Native

As the name implies, React Native’s purpose is to build proper native apps, instead of creating hybrid ones that run in a Webview. Development, however, is still done completely via JavaScript and React. This frameworks isn’t tailored towards beginners in web development, but on the plus side, there is a huge community behind it that will help you in every part of the way. Recently the framework rolled support for Android, so you can have real cross-platform apps.

Pros:

  • Native-like performance
  • Huge community

Cons:

  • Steep learning curve
  • The development tools only work on OS X at the moment

5. jQuery Mobile

jQuery Mobile

jQuery Mobile

The grandpa of all mobile frameworks, jQuery Mobile doesn’t try to make apps that look like Android or iOS. Instead, it’s purpose is to help develop web apps that will work equally well on all mobile browsers (including oldies such as Windows Phone, Blackberry, and Symbian). As such, it is very lightweight, depends only on jQuery and is quite easy to learn, while still offering good touch recognition and PhoneGap/Cordova support.

Pros:

  • Support for wide range of mobile browsers
  • Simple to use

Cons:

  • Dated styles that don’t resemble either iOS or Android
  • PhoneGap/Cordova builder not included, but supported

6. Native Script

NativeScript

NativeScript

Native script’s biggest feature is that it allows you to write the functionality of your app in JavaScript once, which then will be transformed accordingly to Android, iOS and Windows Phone. Packaged, the compiled applications start in a native fashion, without opening and running in a browser. This frameworks does require some coding skills, but compensates for it with an extensive, in-depth documentation.

Pros:

  • Write once, use everywhere approach.
  • Great documentation

Cons:

  • Steep learning curve
  • Small community

7. Famous

Famous

Famous

Famous has a unique approach to web and mobile development. It combines the DOM tree (your HTML) with WebGL, displaying everything in a canvas, similar to what HTML game engines do. This novel technique allows the framework to run your apps in 60 fps, which is as smooth as most native apps. Sadly, this project is no longer being actively developed and doesn’t have good docs for reference.

Pros:

  • Native-like performance

Cons:

  • No longer actively developed
  • Documentation not full
  • Small community

The Rundown

Conclusion

There is no best framework – all of them have their pros and cons and it’s up to you to decide depending on what you plan on using them for.

We hope this quick comparison has been of great use to you! If you have experience with any of the listed frameworks or similar ones, please leave a comment and share your opinion on hybrid app development!

Quick Tip: The Simplest Way To Center Elements Vertically And Horizontally

the-simple-way-to-center

Flexbox is a relatively new addition to the CSS world and we keep finding more and more excellent applications for it. We can even solve the age-old problem with centering an element both vertically and horizontally with CSS. It is easier than you can imagine – it is only three lines of code, doesn’t require you to specify the size of the centered element and is responsive-friendly!

This technique works in all modern browsers – IE10+, Chrome, Firefox and Safari (with the -webkit- prefix). See the full compatibility table here.

(Play with our code editor on Tutorialzine.com)

The HTML

The idea, of course, revolves around flexbox. First, we create a container in which we want everything to be centered:

<div class="container">
    <!--// Any content in here will be centered.-->
    <img src="fireworks.jpg" alt="fireworks">
</div>

You can place this container div anywhere you want. In the live example above we’ve made it take up the whole width and height of the page.

The CSS

As we said earlier, we will be using only three lines of code. Here they are:

.container{
    display: flex;
    justify-content: center;
    align-items: center;
}

Every flex container has two axis for positioning elements. The main axis is declared with the flex-direction property (can be row or column, see docs). By omitting this rule, we’ve left the flex direction to its default row value.

Now all that we need to do is center both axis. It couldn’t get any simpler:

  1. Make the display type flex, so that we activate flexbox mode.
  2. justify-content defines where flex items will align according to the main axis (horizontally in our case).
  3. align-items does the same with the axis perpendicular to the main one (vertically in our case).

Now that we have set the rules for the vertical and the horizontal alignment to center, any element we add inside the container will be positioned perfectly in the middle. We don’t need to know its dimensions beforehand, the browser will do all the hard work!

Conclusion

There are lots of other techniques for centering content with CSS, but flexbox makes this a whole lot simpler and more elegant. If you wish to learn more about it, check out these resources:

  • A complete guide to flexbox – here.
  • MDN: Using CSS flexible boxes (a long read) – here.
  • Flexbox in 5 minutes – here.

Making Your First HTML5 Game With Phaser

html5-game-phaser

Everybody loves classic games. How many of you remember the retro snake game from old Nokia phones? We sure do. This is why for this lesson we decided to recreate it using HTML5. There is a great open source game development framework called Phaser that we will use.

You will learn about sprites, game states and how to use the preload, create and update methods. Here’s what the final version of the game we’re building looks like:

Finished Game

Setup

Download a zip archive with the file structure of the game here. It contains all the image assets needed for the game but no code. We will be writing this next.

Now open index.html, add a title for your page and create links to all the JS files. Later, to play the game, just open this file in your browser.

<!doctype html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Snake</title>
    <script src="assets/js/phaser.min.js"></script>
    <script src="assets/js/menu.js"></script>
    <script src="assets/js/game.js"></script>
    <script src="assets/js/game_over.js"></script>
    <script src="assets/js/main.js"></script>
</head>
<body>

</body>
</html>

Your directory should look like this:

Directory Tree

Directory Tree

How the game is organized

Phaser games are organized around states. Think of states in Phaser as the different parts of a game. Here are the states of our game:

  • The Menu state. It is handled by menu.js, and only displays the start image. When it is clicked, the game transitions to the Game state.
  • Game state. It is handled by game.js. This is the actual play area of the game. You control the snake, eat apples and have lots of fun. When you die, you transition to the Game_Over state.
  • Game_Over state. It shows gameover.png and displays your last score. When it is clicked you transition to the Game state.

main.js is our main JavaScript file. This is the place where we will create a new game instance and add a menu state.

1. Loading an image

Right now, our game doesn’t do anything. Let’s code the Menu state, and make it display the title screen.

During the setup we included the Phaser library in our HTML file. This gives us a global object called Phaser. Through it, we can access the library’s methods and functions for building games.

Now we will use the global Phaser object, and create a new game instance. This is an object which represents our entire game. We will add the states to it.

main.js

var game;

// Create a new game instance 600px wide and 450px tall:
game = new Phaser.Game(600, 450, Phaser.AUTO, '');

// First parameter is how our state will be called.
// Second parameter is an object containing the needed methods for state functionality
game.state.add('Menu', Menu);

game.state.start('Menu');

Now we need to initialize our Menu state object. In menu.js define a new object and add the functions below. When the state is started, the preload function will be called first, loading all the needed assets for our game. Once preloading finishes, create gets called, initializing the playing field and everything we want on it:

menu.js

var Menu = {

    preload : function() {
        // Loading images is required so that later on we can create sprites based on the them.
        // The first argument is how our image will be refered to, 
        // the second one is the path to our file.
        game.load.image('menu', './assets/images/menu.png');
    },

    create: function () {
        // Add a sprite to your game, here the sprite will be the game's logo
        // Parameters are : X , Y , image name (see above) 
        this.add.sprite(0, 0, 'menu');
    }

};

Because of browser security restrictions, to start the game you’ll need a locally running web server. This page from the Phaser guidelines has plenty of options for all operating systems – here. In other words, it won’t work if you simply double click your index.html.

If everything has been done right, a page with the the start screen should appear in your browser.

Rendering an image

2. Drawing the snake

As we mentioned earlier, the Game state is where the actual game play happens. This is also the place where we will draw the snake. Like we did with the Menu state, we need to register the Game state with the global game object in main.js. Here is how your code should look like:

main.js

var game;

game = new Phaser.Game(600, 450, Phaser.AUTO, '');

game.state.add('Menu', Menu);

// Adding the Game state.
game.state.add('Game', Game);

game.state.start('Menu');

We’ll also want to add some code in menu.js to let us start the Game state when it is clicked. For this purpose, we will replace the sprite with a button. Adding a button is basically the same as adding a sprite, you only need to provide a function to be called when it is clicked. Here is the final menu.js code:

menu.js

var Menu = {

    preload : function() {
        // Load all the needed resources for the menu.
        game.load.image('menu', './assets/images/menu.png');
    },

    create: function () {

        // Add menu screen.
        // It will act as a button to start the game.
        this.add.button(0, 0, 'menu', this.startGame, this);

    },

    startGame: function () {

        // Change the state to the actual game.
        this.state.start('Game');

    }

};

We can now proceed with coding the Game state and drawing the snake. The structure is similar to the one of the Menu state.

game.js

var snake, apple, squareSize, score, speed,
    updateDelay, direction, new_direction,
    addNew, cursors, scoreTextValue, speedTextValue, 
    textStyle_Key, textStyle_Value;

var Game = {

    preload : function() {
        // Here we load all the needed resources for the level.
        // In our case, that's just two squares - one for the snake body and one for the apple.
        game.load.image('snake', './assets/images/snake.png');
        game.load.image('apple', './assets/images/apple.png');
    },

    create : function() {

        // By setting up global variables in the create function, we initialise them on game start.
        // We need them to be globally available so that the update function can alter them.

        snake = [];                     // This will work as a stack, containing the parts of our snake
        apple = {};                     // An object for the apple;
        squareSize = 15;                // The length of a side of the squares. Our image is 15x15 pixels.
        score = 0;                      // Game score.
        speed = 0;                      // Game speed.
        updateDelay = 0;                // A variable for control over update rates.
        direction = 'right';            // The direction of our snake.
        new_direction = null;           // A buffer to store the new direction into.
        addNew = false;                 // A variable used when an apple has been eaten.

        // Set up a Phaser controller for keyboard input.
        cursors = game.input.keyboard.createCursorKeys();

        game.stage.backgroundColor = '#061f27';

        // Generate the initial snake stack. Our snake will be 10 elements long.
        // Beginning at X=150 Y=150 and increasing the X on every iteration.
        for(var i = 0; i < 10; i++){
            snake[i] = game.add.sprite(150+i*squareSize, 150, 'snake');  // Parameters are (X coordinate, Y coordinate, image)
        }


        // Genereate the first apple.
        this.generateApple();


        // Add Text to top of game.
        textStyle_Key = { font: "bold 14px sans-serif", fill: "#46c0f9", align: "center" };
        textStyle_Value = { font: "bold 18px sans-serif", fill: "#fff", align: "center" };

        // Score.
        game.add.text(30, 20, "SCORE", textStyle_Key);
        scoreTextValue = game.add.text(90, 18, score.toString(), textStyle_Value);
        // Speed.
        game.add.text(500, 20, "SPEED", textStyle_Key);
        speedTextValue = game.add.text(558, 18, speed.toString(), textStyle_Value);

    },



    update: function() {
        // The update function is called constantly at a high rate (somewhere around 60fps),
        // updating the game field every time.
        // We are going to leave that one empty for now.
    },


    generateApple: function(){

        // Chose a random place on the grid.
        // X is between 0 and 585 (39*15)
        // Y is between 0 and 435 (29*15)

        var randomX = Math.floor(Math.random() * 40 ) * squareSize,
            randomY = Math.floor(Math.random() * 30 ) * squareSize;

        // Add a new apple.
        apple = game.add.sprite(randomX, randomY, 'apple');
    }

};

Here is how the snake and apple should look like:

Drawing the snake

3. Movement and control

To make the snake move we’ll be working in the update function of game.js.

First of all, we create event listeners for controlling the direction of the snake with the arrow keys.

The actual movement is a bit complex, since update is triggered at a very quick rate, and if we move the snake every time it’s called we would end up with one uncontrollably speedy reptile. To change this, we’ve set up an if statement that checks weather this is the 10th consecutive call of update() using a counter variable called updateDelay. 

If it really is the 10th call, we remove the last square of our snake (first element in the stack), give it new coordinates according to the current direction and place it in front of the head of the snake (top of the stack). Here’s how the code looks like:

update: function() {

    // Handle arrow key presses, while not allowing illegal direction changes that will kill the player.

    if (cursors.right.isDown && direction!='left')
    {
        new_direction = 'right';
    }
    else if (cursors.left.isDown && direction!='right')
    {
        new_direction = 'left';
    }
    else if (cursors.up.isDown && direction!='down')
    {
        new_direction = 'up';
    }
    else if (cursors.down.isDown && direction!='up')
    {
        new_direction = 'down';
    }


    // A formula to calculate game speed based on the score.
    // The higher the score, the higher the game speed, with a maximum of 10;
    speed = Math.min(10, Math.floor(score/5));
    // Update speed value on game screen.
    speedTextValue.text = '' + speed;

    // Since the update function of Phaser has an update rate of around 60 FPS,
    // we need to slow that down make the game playable.

    // Increase a counter on every update call.
    updateDelay++;

    // Do game stuff only if the counter is aliquot to (10 - the game speed).
    // The higher the speed, the more frequently this is fulfilled,
    // making the snake move faster.
    if (updateDelay % (10 - speed) == 0) {


        // Snake movement

        var firstCell = snake[snake.length - 1],
            lastCell = snake.shift(),
            oldLastCellx = lastCell.x,
            oldLastCelly = lastCell.y;

        // If a new direction has been chosen from the keyboard, make it the direction of the snake now.
        if(new_direction){
            direction = new_direction;
            new_direction = null;
        }


        // Change the last cell's coordinates relative to the head of the snake, according to the direction.

        if(direction == 'right'){

            lastCell.x = firstCell.x + 15;
            lastCell.y = firstCell.y;
        }
        else if(direction == 'left'){
            lastCell.x = firstCell.x - 15;
            lastCell.y = firstCell.y;
        }
        else if(direction == 'up'){
            lastCell.x = firstCell.x;
            lastCell.y = firstCell.y - 15;
        }
        else if(direction == 'down'){
            lastCell.x = firstCell.x;
            lastCell.y = firstCell.y + 15;
        }


        // Place the last cell in the front of the stack.
        // Mark it the first cell.

        snake.push(lastCell);
        firstCell = lastCell;

    }
    
}

Try and control the snake via the arrow keys on your keyboard.

Movement and control

 4. Collision detection

A game in which the snake is roaming freely in the playing field is not much fun. We need to detect when the snake comes in contact with a wall, an apple or itself. This is called collision detection.

This is usually done by using a physics engine, of which the Phaser framework supports a few. But they are too complex for a simple game like this. We will instead do our own collision detection by comparing coordinates.

In the update function, after the code for moving the snake, we call a number of methods. They will compare coordinates to tell us if a collision has occurred.

update: function() {

        // Snake movement
        // ...
        // End of snake movement

        // Increase length of snake if an apple had been eaten.
        // Create a block in the back of the snake with the old position of the previous last block
        // (it has moved now along with the rest of the snake).

        if(addNew){
            snake.unshift(game.add.sprite(oldLastCellx, oldLastCelly, 'snake'));
            addNew = false;
        }

        // Check for apple collision.
        this.appleCollision();

        // Check for collision with self. Parameter is the head of the snake.
        this.selfCollision(firstCell);

        // Check with collision with wall. Parameter is the head of the snake.
        this.wallCollision(firstCell);
    }


},

appleCollision: function() {

    // Check if any part of the snake is overlapping the apple.
    // This is needed if the apple spawns inside of the snake.
    for(var i = 0; i < snake.length; i++){
        if(snake[i].x == apple.x && snake[i].y == apple.y){

            // Next time the snake moves, a new block will be added to its length.
            addNew = true;

            // Destroy the old apple.
            apple.destroy();

            // Make a new one.
            this.generateApple();

            // Increase score.
            score++;

            // Refresh scoreboard.
            scoreTextValue.text = score.toString();

        }
    }

},

selfCollision: function(head) {

    // Check if the head of the snake overlaps with any part of the snake.
    for(var i = 0; i < snake.length - 1; i++){
        if(head.x == snake[i].x && head.y == snake[i].y){

            // If so, go to game over screen.
            game.state.start('Game_Over');
        }
    }

},

wallCollision: function(head) {

    // Check if the head of the snake is in the boundaries of the game field.

    if(head.x >= 600 || head.x < 0 || head.y >= 450 || head.y < 0){


        // If it's not in, we've hit a wall. Go to game over screen.
        game.state.start('Game_Over');
    }

}

When the snake collides with the apple we increase the score and the length of the snake. But when a collision with the wall or the snake body occurs, we should end the game. To do this, we need to make the Game_Over state. Again, we need to register it with main.js. Add this line near the bottom of that file:

main.js

game.state.add('Game_Over', Game_Over);

And the state itself:

game_over.js

var Game_Over = {

    preload : function() {
        // Load the needed image for this game screen.
        game.load.image('gameover', './assets/images/gameover.png');
    },

    create : function() {

        // Create button to start game like in Menu.
        this.add.button(0, 0, 'gameover', this.startGame, this);

        // Add text with information about the score from last game.
        game.add.text(235, 350, "LAST SCORE", { font: "bold 16px sans-serif", fill: "#46c0f9", align: "center"});
        game.add.text(350, 348, score.toString(), { font: "bold 20px sans-serif", fill: "#fff", align: "center" });

    },

    startGame: function () {

        // Change the state back to Game.
        this.state.start('Game');

    }

};

Collision detection

That’s it! Our game is ready!

Further reading

We hope that you liked our simple game and learned a lot while following our tutorial. There is much more to learn about Phaser:

Making Your First Webapp with React

creating-your-first-web-app-with-react

React has gained a lot of popularity recently and has attracted a large and active community. This results in a vast wealth of reusable components for it that will save you time when coding. The library itself encourages writing loosely coupled code that is modular and composable.

In this tutorial, I will show you how to create a small application and how to split it into discrete components that talk to each other. As a base, we will take the npm-driven website example from last month, but we’ll do it the React way. It is interesting to compare the results – the React version has a few more lines of code than the jQuery version, but we can both agree that it is much better organized.

What you need to know about React

  • It is a popular client-side library/framework for building user interfaces, which is developed and used by Facebook.
  • With it, you organize your application around discrete components, with each handling its own rendering and state. Components can be nested within each other.
  • React is fast because it minimizes the number of writes to the DOM (the slowest part of any client-side application).
  • The recommended way to write React code is by using JSX – an extension to JavaScript which presents components as HTML elements. JSX needs to be compiled to JS in order to work in browsers.
  • It hasn’t hit version 1.0 as of this writing, so there might be changes in the future.
  • We have a nice article with examples for learning react which you can check out. Also there is the official getting started guide here.

What we will be building

We will create a simple web app, which invites people to search for locations and to store them in their browsers’ localStorage. The locations will be presented on a Google Map with the help of the GMaps plugin. We will use Bootstrap with the Flatly theme for the interface. In the process, we will break the application down into logical components and make them talk to each other.

Running the demo

If you don’t want to read the entire tutorial, you can go ahead and download the source code from the download button above. To run it, you need to have Node.js and npm installed. Assuming that you have, here is what you need to do:

  1. Download the zip with the source code from the button above.
  2. Extract it to a folder somewhere on your computer.
  3. Open a new terminal (command prompt), and navigate to that folder.
  4. Execute npm install. This will download and install all dependencies that are needed.
  5. Execute npm run build. This will compile the react components down to a regular JavaScript file named compiled.js.
  6. Open index.html in your browser. You should see the app.

There is one more npm command that I’ve prepared for you to make your development easier:

npm run watch

This will compile the JSX code down to JavaScript and will continue to monitor it for changes. If you change a file, the code will be recompiled automatically for you. You can see these commands in the package.json file.

The source code is easy to follow and has plenty of comments, so for those of you who prefer to read the source, you can skip the rest of the article.

Setting things up

As I mentioned, the recommended way to write React code is by using a JavaScript extension called JSX, which needs to be transformed to JavaScript. There are a few tools that can do this but the one I recommend is reactify – a browserify transform. So in addition to compiling JSX down to JavaScript, you get access to the require() node.js call and with it the ability to install and use libraries from npm.

To set up reactify, browserify and the rest, run this command:

npm install browserify reactify watchify uglify-js react

To create a production ready and minified JavaScript file, which you can put online, run this command in your terminal:

NODE_ENV=production browserify -t [ reactify --es6 ] main.js | uglifyjs > compiled.min.js

Reactify supports a limited set of the new ES6 features with the --es6 flag, which I’ve used in the source code (you will see it in a moment).

While developing, use the following command:

watchify -v -d -t [ reactify --es6 ] main.js -o compiled.js

Watchify will monitor your files for changes and recompile your source code if it is needed. It also enables source maps, so you can use the Chrome Debugger to step through your code.

Great! You can now write React modules, require() npm libraries and even use some ES6 features. You are ready for writing some code!

The code

Here are the components that we will be writing:

  • App is the main component. It contains methods for the actions that can be performed by the user like searching, adding a location to favorites and more. The other components are nested inside it.
  • CurrentLocation presents the currently visited address in the map. Addresses can be added or removed from favorites by clicking the star icon.
  • LocationList renders all favorite locations. It creates a LocationItem for each.
  • LocationItem is an individual location. When it is clicked, its corresponding address is searched for and highlighted in the map.
  • Map integrates with the GMaps library, and renders a map from Google Maps.
  • Search is a component that wraps around the search form. When it is submitted, a search for the location is triggered.
Components Breakdown

Components Breakdown

App.js

First up is App. In addition to the lifecycle methods that React requires, it has a few additional ones that reflect the main actions that can be performed by the user like adding and removing an address from favorites and searching. Notice that I am using the shorter ES6 syntax for defining functions in objects.

var React = require('react');

var Search = require('./Search');
var Map = require('./Map');
var CurrentLocation = require('./CurrentLocation');
var LocationList = require('./LocationList');


var App = React.createClass({

	getInitialState(){

		// Extract the favorite locations from local storage

		var favorites = [];

		if(localStorage.favorites){
			favorites = JSON.parse(localStorage.favorites);
		}

		// Nobody would get mad if we center it on Paris by default

		return {
			favorites: favorites,
			currentAddress: 'Paris, France',
			mapCoordinates: {
				lat: 48.856614,
				lng: 2.3522219
			}
		};
	},

	toggleFavorite(address){

		if(this.isAddressInFavorites(address)){
			this.removeFromFavorites(address);
		}
		else{
			this.addToFavorites(address);
		}

	},

	addToFavorites(address){

		var favorites = this.state.favorites;

		favorites.push({
			address: address,
			timestamp: Date.now()
		});

		this.setState({
			favorites: favorites
		});

		localStorage.favorites = JSON.stringify(favorites);
	},

	removeFromFavorites(address){

		var favorites = this.state.favorites;
		var index = -1;

		for(var i = 0; i < favorites.length; i++){

			if(favorites[i].address == address){
				index = i;
				break;
			}

		}

		// If it was found, remove it from the favorites array

		if(index !== -1){
			
			favorites.splice(index, 1);

			this.setState({
				favorites: favorites
			});

			localStorage.favorites = JSON.stringify(favorites);
		}

	},

	isAddressInFavorites(address){

		var favorites = this.state.favorites;

		for(var i = 0; i < favorites.length; i++){

			if(favorites[i].address == address){
				return true;
			}

		}

		return false;
	},

	searchForAddress(address){
		
		var self = this;

		// We will use GMaps' geocode functionality,
		// which is built on top of the Google Maps API

		GMaps.geocode({
			address: address,
			callback: function(results, status) {

				if (status !== 'OK') return;

				var latlng = results[0].geometry.location;

				self.setState({
					currentAddress: results[0].formatted_address,
					mapCoordinates: {
						lat: latlng.lat(),
						lng: latlng.lng()
					}
				});

			}
		});

	},

	render(){

		return (

			<div>
				<h1>Your Google Maps Locations</h1>

				<Search onSearch={this.searchForAddress} />

				<Map lat={this.state.mapCoordinates.lat} lng={this.state.mapCoordinates.lng} />

				<CurrentLocation address={this.state.currentAddress} 
					favorite={this.isAddressInFavorites(this.state.currentAddress)} 
					onFavoriteToggle={this.toggleFavorite} />

				<LocationList locations={this.state.favorites} activeLocationAddress={this.state.currentAddress} 
					onClick={this.searchForAddress} />

			</div>

		);
	}

});

module.exports = App;

In the render method, we initialize the other components. Each component receives only the data that it needs to get its job done, as attributes. In some places, we also pass methods which the child components will call, which is a good way for components to communicate while keeping them isolated from one another.

CurrentLocation.js

Next is CurrentLocation. This component presents the address of the currently displayed location in an H4 tag, and a clickable star icon. When the icon is clicked, the App’s toggleFavorite method is called.

var React = require('react');

var CurrentLocation = React.createClass({

	toggleFavorite(){
		this.props.onFavoriteToggle(this.props.address);
	},

	render(){

		var starClassName = "glyphicon glyphicon-star-empty";

		if(this.props.favorite){
			starClassName = "glyphicon glyphicon-star";
		}

		return (
			<div className="col-xs-12 col-md-6 col-md-offset-3 current-location">
				<h4 id="save-location">{this.props.address}</h4>
				<span className={starClassName} onClick={this.toggleFavorite} aria-hidden="true"></span>
			</div>
		);
	}

});

module.exports = CurrentLocation;

LocationList.js

LocationList takes the array with favorite locations that was passed to it, creates a LocationItem object for each and presents it in a Bootstrap list group.

var React = require('react');
var LocationItem = require('./LocationItem');

var LocationList = React.createClass({

	render(){

		var self = this;

		var locations = this.props.locations.map(function(l){

			var active = self.props.activeLocationAddress == l.address;

			// Notice that we are passing the onClick callback of this
			// LocationList to each LocationItem.

			return <LocationItem address={l.address} timestamp={l.timestamp} 
					active={active} onClick={self.props.onClick} />
		});

		if(!locations.length){
			return null;
		}

		return (
			<div className="list-group col-xs-12 col-md-6 col-md-offset-3">
				<span className="list-group-item active">Saved Locations</span>
				{locations}
			</div>
		)

	}

});

module.exports = LocationList;

LocationItem.js

LocationItem represents an individual favorite location. It uses the moment library to calculate the relative time since the location was added as a favorite.

var React = require('react');
var LocationItem = require('./LocationItem');
var moment = require('moment');

var LocationItem = React.createClass({

	handleClick(){
		this.props.onClick(this.props.address);
	},

	render(){

		var cn = "list-group-item";

		if(this.props.active){
			cn += " active-location";
		}

		return (
			<a className={cn} onClick={this.handleClick}>
				{this.props.address}
				<span className="createdAt">{ moment(this.props.timestamp).fromNow() }</span>
				<span className="glyphicon glyphicon-menu-right"></span>
			</a>
		)

	}

});

module.exports = LocationItem;

Map.js

Map is a special component. It wraps the Gmaps plugin, which is not a React component by itself. By hooking to the Map’s componentDidUpdate method, we can initialize a real map inside the #map div whenever the displayed location is changed.

var React = require('react');

var Map = React.createClass({

	componentDidMount(){

		// Only componentDidMount is called when the component is first added to
		// the page. This is why we are calling the following method manually. 
		// This makes sure that our map initialization code is run the first time.

		this.componentDidUpdate();
	},

	componentDidUpdate(){

		if(this.lastLat == this.props.lat && this.lastLng == this.props.lng){

			// The map has already been initialized at this address.
			// Return from this method so that we don't reinitialize it
			// (and cause it to flicker).

			return;
		}

		this.lastLat = this.props.lat;
		this.lastLng = this.props.lng

		var map = new GMaps({
			el: '#map',
			lat: this.props.lat,
			lng: this.props.lng
		});

		// Adding a marker to the location we are showing
		
		map.addMarker({
			lat: this.props.lat,
			lng: this.props.lng
		});
	},

	render(){

		return (
			<div className="map-holder">
				<p>Loading...</p>
				<div id="map"></div>
			</div>
		);
	}

});

module.exports = Map;

Search.js

The Search component consists of a Bootstrap form with an input group. When the form is submitted the App’s searchForAddress method is called.

var React = require('react');

var Search = React.createClass({

	getInitialState() {
		return { value: '' };
	},

	handleChange(event) {
		this.setState({value: event.target.value});
	},

	handleSubmit(event){
		
		event.preventDefault();
		
		// When the form is submitted, call the onSearch callback that is passed to the component

		this.props.onSearch(this.state.value);

		// Unfocus the text input field
		this.getDOMNode().querySelector('input').blur();
	},

	render() {

		return (
			<form id="geocoding_form" className="form-horizontal" onSubmit={this.handleSubmit}>
				<div className="form-group">
					<div className="col-xs-12 col-md-6 col-md-offset-3">
						<div className="input-group">
							<input type="text" className="form-control" id="address" placeholder="Find a location..." 
							value={this.state.value} onChange={this.handleChange} />
							<span className="input-group-btn">
								<span className="glyphicon glyphicon-search" aria-hidden="true"></span>
							</span>
						</div>
					</div>
				</div>
			</form>
		);

	}
});

module.exports = Search;

main.js

All that is left is to add the App component to the page. I am adding it to a container div with the #main id (you can see this element in index.html in the downloadable zip file).

var React = require('react');
var App = require('./components/App');

React.render(
  <App />,
  document.getElementById('main')
);

In addition to these files, I have included the GMaps library and the Google Maps JavaScript API on which it depends, as <script> tags in index.html.

We’re done!

I hope that this tutorial gave you a better understanding of how to structure React applications. There is much more you can do with the library, including server-side rendering, which we hope to cover in the future.

Smartphone Remote Control with Node.js and Socket.io

smartphone-remote-control-for-your-presentation

Wouldn’t it be cool to use your smartphone as a remote control? It turns out it is not difficult at all! You don’t even need to know how to write native mobile apps – your phone has a fully capable web browser with support for web sockets, which opens a lot of possibilities. In this short tutorial, we are going to use Node.js and Socket.io to remotely control a presentation that is running on your laptop with your phone.

There are a lot of cool HTML5 presentation libraries out there and it would be a waste of effort to create this functionality from scratch. This is why we will be using Reveal.js – it will handle animations and transitions between slides, as well as support for keyboard and touch events.

We won’t be making a dedicated remote control interface. Instead, we will synchronize the presentation that is opened on your phone with that on your computer using websockets. This will allow you not only to control the presentation, but see its mobile version on your phone, which will help you keep track of the current slide.

The Idea

The technique we are going to use is very simple. Reveal.js puts the current slide number in the URL as a hash (e.g. http://example.com/#/1). We will send this hash to all other connected devices, which will transition them to the new slide automatically. This will make it possible for people to just open the URL in a browser and sabotaging your presentation, so we will require all devices to enter a pass code before connecting.

It is worth mentioning that Reveal.js already has an API, so we could have used that to synchronize the two presentations. But the hash change technique is simpler and will work with any presentation library, so we chose to go with it instead.

Our slideshow

Our slideshow

Running our Example

You can run this example locally, or by deploying it to a hosting provider with node.js support like Heroku. Running it locally is easier, but you must have node.js and npm installed. Running it on Heroku requires you to have the heroku toolbelt installed and signing up for an account.

To run our code locally:

  1. Download the code from the button near the beginning of the article.
  2. Make sure you have node.js installed. If needed, install it.
  3. Unzip the archive you downloaded to a folder.
  4. Open a terminal and cd to the folder.
  5. Run npm install to install the required libraries
  6. Run node app.js to start the presentation
  7. Open http://localhost:8080 on your computer and enter your pass key (by default it is “kittens“).
  8. Open http://<your computer’s local ip address> on your phone and enter the same pass key.
  9. Have fun!

To run the code on Heroku:

  1. Download the code from the button near the beginning of the article.
  2. Unzip it to a folder.
  3. Open a terminal and cd to the folder.
  4. Create a git repository, and commit.
  5. Create a new Heroku App
  6. Run git push heroku master.
  7. Visit the URL of the app on every device you want to connect. The default pass key is “kittens”.

Read more about deploying node.js apps to heroku here. If you use a different cloud hosting provider, the last three steps will be different.

The Code

But enough talk, let’s see the code! There are only two JavaScript files – app.js for the server side, and script.js for the browser. You can run the app in Node.js 0.10+ or in io.js.

For the backend, we use express and socket.io. It’s main responsibility is listening for and responding to socket.io events. With express.static, we serve the files in the public folder to the world. We have a public/index.html file which contains the code for the presentation. It is served automatically by express.static, so we don’t need a “/” route.

app.js

// This is the server-side file of our mobile remote controller app.
// It initializes socket.io and a new express instance.
// Start it by running 'node app.js' from your terminal.


// Creating an express server

var express = require('express'),
	app = express();

// This is needed if the app is run on heroku and other cloud providers:

var port = process.env.PORT || 8080;

// Initialize a new socket.io object. It is bound to 
// the express app, which allows them to coexist.

var io = require('socket.io').listen(app.listen(port));


// App Configuration

// Make the files in the public folder available to the world
app.use(express.static(__dirname + '/public'));


// This is a secret key that prevents others from opening your presentation
// and controlling it. Change it to something that only you know.

var secret = 'kittens';

// Initialize a new socket.io application

var presentation = io.on('connection', function (socket) {

	// A new client has come online. Check the secret key and 
	// emit a "granted" or "denied" message.

	socket.on('load', function(data){

		socket.emit('access', {
			access: (data.key === secret ? "granted" : "denied")
		});

	});

	// Clients send the 'slide-changed' message whenever they navigate to a new slide.

	socket.on('slide-changed', function(data){

		// Check the secret key again

		if(data.key === secret) {

			// Tell all connected clients to navigate to the new slide
			
			presentation.emit('navigate', {
				hash: data.hash
			});
		}
	});
});

console.log('Your presentation is running on http://localhost:' + port);

And here is our JavaScript for the front-end, which listens for hashchange events and sends socket.io messages to the server.

public/assets/js/script.js

$(function() {

	// Apply a CSS filter with our blur class (see our assets/css/styles.css)
	
	var blurredElements = $('.homebanner, div.reveal').addClass('blur');

	// Initialize the Reveal.js library with the default config options
	// See more here https://github.com/hakimel/reveal.js#configuration

	Reveal.initialize({
		history: true		// Every slide will change the URL
	});

	// Connect to the socket

	var socket = io();

	// Variable initialization

	var form = $('form.login'),
		secretTextBox = form.find('input[type=text]');

	var key = "", animationTimeout;

	// When the page is loaded it asks you for a key and sends it to the server

	form.submit(function(e){

		e.preventDefault();

		key = secretTextBox.val().trim();

		// If there is a key, send it to the server-side
		// through the socket.io channel with a 'load' event.

		if(key.length) {
			socket.emit('load', {
				key: key
			});
		}

	});

	// The server will either grant or deny access, depending on the secret key

	socket.on('access', function(data){

		// Check if we have "granted" access.
		// If we do, we can continue with the presentation.

		if(data.access === "granted") {

			// Unblur everything
			blurredElements.removeClass('blurred');

			form.hide();

			var ignore = false;

			$(window).on('hashchange', function(){

				// Notify other clients that we have navigated to a new slide
				// by sending the "slide-changed" message to socket.io

				if(ignore){
					// You will learn more about "ignore" in a bit
					return;
				}

				var hash = window.location.hash;

				socket.emit('slide-changed', {
					hash: hash,
					key: key
				});
			});

			socket.on('navigate', function(data){
	
				// Another device has changed its slide. Change it in this browser, too:

				window.location.hash = data.hash;

				// The "ignore" variable stops the hash change from
				// triggering our hashchange handler above and sending
				// us into a never-ending cycle.

				ignore = true;

				setInterval(function () {
					ignore = false;
				},100);

			});

		}
		else {

			// Wrong secret key

			clearTimeout(animationTimeout);

			// Addding the "animation" class triggers the CSS keyframe
			// animation that shakes the text input.

			secretTextBox.addClass('denied animation');
			
			animationTimeout = setTimeout(function(){
				secretTextBox.removeClass('animation');
			}, 1000);

			form.show();
		}

	});

});

It’s Slideshow Time!

With this our smartphone remote control is ready! We hope that you find our experiment useful and have fun playing with it.

Quick Tip: Add Keyboard Shortcuts To Your Web App

quick-tip-add-keyboard-shortcuts-to-your-web-app

We power users love our keyboard shortcuts. We use them everywhere – in our code editor, in Photoshop, in gmail. And we hate it when we hit Ctrl+S in a web app, only to see our browser offering to download it. Adding shortcuts to your application is not hard at all. In this quick tip, we will show you how to do it, by using Mousetrap.js.

1. Single Keys

Single keys are easy. You can do it with a simple event listener for keypress on the document object. But with Mousetrap, it is even better.

After hitting the run button, focus the editor by clicking on it. Otherwise, it won’t register your key presses and the demo won’t work.

(Play with our code editor on Tutorialzine.com)

2. Alternative Symbols

Mousetrap shines when listening for more complex key combinations like capital letters and special symbols.

(Play with our code editor on Tutorialzine.com)

3. Key combinations

Combinations that involve the Control key are equally easy (see the next example for how to listen for both Control and the OS X Command key).

(Play with our code editor on Tutorialzine.com)

4. Multiple Combinations

Passing an array instead of a string lets you listen for multiple key combinations at once. This is useful when you have to listen for combinations which involve the Control (for Windows and Linux) and Command (for Mac) keys.

(Play with our code editor on Tutorialzine.com)

5. Sequences

This type of shortcuts are very powerful, and are used in apps like gmail. Works with array keys as well!

(Play with our code editor on Tutorialzine.com)

Conclusion

This was our quick tip on keyboard shortcuts. If you’ve used keyboard hotkeys before, or are brave enough to experiment with them in your next project, do share the results with us in the comments below.

Making a Single Page App Without a Framework

making-a-simple-page-app-without-a-framework

The idea behind single page applications (SPA) is to create a smooth browsing experience like the one found in native desktop apps. All of the necessary code for the page is loaded only once and its content gets changed dynamically through JavaScript. If everything is done right the page shouldn’t ever reload, unless the user refreshes it manually.

There are many frameworks for single page applications out there. First we had Backbone, then Angular, now React. It takes a lot of work to constantly learn and re-learn things (not to mention having to support old code you’ve written in a long forgotten framework). In some situations, like when your app idea isn’t too complex, it is actually not that hard to create a single page app without using any external frameworks. Here is how to do it.

Note: To run this example after downloading it, you need a locally running webserver like Apache. Our demo uses AJAX so it will not work if you simply double-click index.html for security reasons.

The Idea

We will not be using a framework, but we will be using two libraries – jQuery for DOM manipulation and event handling, and Handlebars for templates. You can easily omit these if you wish to be even more minimal, but we will use them for the productivity gains they provide. They will be here long after the hip client-side framework of the day is forgotten.

The app that we will be building fetches product data from a JSON file, and displays it by rendering a grid of products with Handlebars. After the initial load, our app will stay on the same URL and listen for changes to the hash part with the hashchange event. To navigate around the app, we will simply change the hash. This has the added benefit that browser history will just work without extra effort on our part.

The Setup

Our project's folder

Our project’s folder

As you can see there isn’t much in our project folder. We have the regular web app setup – HTML, JavaScript and CSS files, accompanied by a products.json containing data about the products in our shop and a folder with images of the products.

The Products JSON

The .json file is used to store data about each product for our SPA. This file can easily be replaced by a server-side script to fetch data from a real database.

products.json

[
  {
    "id": 1,
    "name": "Sony Xperia Z3",
    "price": 899,
    "specs": {
      "manufacturer": "Sony",
      "storage": 16,
      "os": "Android",
      "camera": 15
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/sony-xperia-z3.jpg",
      "large": "/images/sony-xperia-z3-large.jpg"
    }
  },
  {
    "id": 2,
    "name": "Iphone 6",
    "price": 899,
    "specs": {
      "manufacturer": "Apple",
      "storage": 16,
      "os": "iOS",
      "camera": 8
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/iphone6.jpg",
      "large": "/images/iphone6-large.jpg"
    }
  }
]

The HTML

In our html file we have several divs sharing the same class “page”. Those are the different pages (or as they are called in SPA – states) our app can show. However, on page load all of these are hidden via CSS and need the JavaScript to show them. The idea is that only one page can be visible at a time and our script is the one to decide which one it is.

index.html

<div class="main-content">

	<div class="all-products page">

		<h3>Our products</h3>

		<div class="filters">
			<form>
				Checkboxes here
			</form>
		</div>

    <ul class="products-list">
      <script id="products-template" type="x-handlebars-template">​
        {{#each this}}
          <li data-index="{{id}}">
            <a href="#" class="product-photo"><img src="{{image.small}}" height="130" alt="{{name}}"/></a>
            <h2><a href="#"> {{name}} </a></h2>
            <ul class="product-description">
              <li><span>Manufacturer: </span>{{specs.manufacturer}}</li>
              <li><span>Storage: </span>{{specs.storage}} GB</li>
              <li><span>OS: </span>{{specs.os}}</li>
              <li><span>Camera: </span>{{specs.camera}} Mpx</li>
            </ul>
            <button>Buy Now!</button>
            <p class="product-price">{{price}}$</p>
            <div class="highlight"></div>
          </li>
        {{/each}}
      </script>

    </ul>

	</div>


	<div class="single-product page">

		<div class="overlay"></div>

		<div class="preview-large">
			<h3>Single product view</h3>
			<img src=""/>
			<p></p>

			<span class="close">&times;</span>
		</div>

	</div>

	<div class="error page">
		<h3>Sorry, something went wrong :(</h3>
	</div>

</div>

We have three pages: all-products (the product listing), single-product (the individual product page) and error.

The all-products page consists of a title, a form containing checkboxes for filtering and a <ul> tag with the class “products-list”. This list is generated with handlebars using the data stored in products.json, creating a <li> for each entry in the json. Here is the result:

The Products

The Products

Single-product is used to show information about only one product. It is empty and hidden on page load. When the appropriate hash address is reached, it is populated with product data and shown.

The error page consist of only an error message to let you know when you’ve reached a faulty address.

The JavaScript Code

First, lets make a quick preview of the functions and what they do.

script.js

$(function () {

	checkboxes.click(function () {
		// The checkboxes in our app serve the purpose of filters.
		// Here on every click we add or remove filtering criteria from a filters object.

		// Then we call this function which writes the filtering criteria in the url hash.
		createQueryHash(filters);
	});



	$.getJSON( "products.json", function( data ) {
		// Get data about our products from products.json.

		// Call a function that will turn that data into HTML.
		generateAllProductsHTML(data);

		// Manually trigger a hashchange to start the app.
		$(window).trigger('hashchange');
	});


	$(window).on('hashchange', function(){
		// On every hash change the render function is called with the new hash.
		// This is how the navigation of our app happens.
		render(window.location.hash);
	});


	function render(url) {
		// This function decides what type of page to show 
		// depending on the current url hash value.
	}


	function generateAllProductsHTML(data){
		// Uses Handlebars to create a list of products using the provided data.
		// This function is called only once on page load.
	}


	function renderProductsPage(data){
		// Hides and shows products in the All Products Page depending on the data it recieves.
	}


	function renderSingleProductPage(index, data){
		// Shows the Single Product Page with appropriate data.
	}

	function renderFilterResults(filters, products){
		// Crates an object with filtered products and passes it to renderProductsPage.
		renderProductsPage(results);
	}

	function renderErrorPage(){
		// Shows the error page.
	}

	
	function createQueryHash(filters){
		// Get the filters object, turn it into a string and write it into the hash.
	}

});

Remember that the concept of SPA is to not have any loads going on while the app is running. That’s why after the initial page load we want to stay on the same page, where everything we need has already been fetched by the server.

However, we still want to be able to go somewhere in the app and, for example, copy the url and send it to a friend. If we never change the app’s address they will just get the app the way it looks in the beginning, not what you wanted to share with them. To solve this problem we write information about the state of the app in the url as #hash. Hashes don’t cause the page to reload and are easily accessible and manipulated.

On every hashchange we call this:

function render(url) {

		// Get the keyword from the url.
		var temp = url.split('/')[0];

		// Hide whatever page is currently shown.
		$('.main-content .page').removeClass('visible');


		var map = {

			// The Homepage.
			'': function() {

				// Clear the filters object, uncheck all checkboxes, show all the products
				filters = {};
				checkboxes.prop('checked',false);

				renderProductsPage(products);
			},

			// Single Products page.
			'#product': function() {

				// Get the index of which product we want to show and call the appropriate function.
				var index = url.split('#product/')[1].trim();

				renderSingleProductPage(index, products);
			},

			// Page with filtered products
			'#filter': function() {

				// Grab the string after the '#filter/' keyword. Call the filtering function.
				url = url.split('#filter/')[1].trim();

				// Try and parse the filters object from the query string.
				try {
					filters = JSON.parse(url);
				}
				// If it isn't a valid json, go back to homepage ( the rest of the code won't be executed ).
				catch(err) {
					window.location.hash = '#';
				}

				renderFilterResults(filters, products);
			}

		};

		// Execute the needed function depending on the url keyword (stored in temp).
		if(map[temp]){
			map[temp]();
		}
		// If the keyword isn't listed in the above - render the error page.
		else {
			renderErrorPage();
		}

	}

This function takes into consideration the beginning string of our hash, decides what page needs to be shown and calls the according functions.

For example if the hash is ‘#filter/{“storage”:[“16″],”camera”:[“5″]}’, our codeword is ‘#filter’. Now the render function knows we want to see a page with the filtered products list and will navigate us to it. The rest of the hash will be parsed into an object and a page with the filtered products will be shown, changing the state of the app.

This is called only once on start up and turns our JSON into actual HTML5 content via handlebars.

function generateAllProductsHTML(data){

    var list = $('.all-products .products-list');

    var theTemplateScript = $("#products-template").html();
    //Compile the template​
    var theTemplate = Handlebars.compile (theTemplateScript);
    list.append (theTemplate(data));


    // Each products has a data-index attribute.
    // On click change the url hash to open up a preview for this product only.
    // Remember: every hashchange triggers the render function.
    list.find('li').on('click', function (e) {
      e.preventDefault();

      var productIndex = $(this).data('index');

      window.location.hash = 'product/' + productIndex;
    })
  }

This function receives an object containing only those products we want to show and displays them.

function renderProductsPage(data){

    var page = $('.all-products'),
      allProducts = $('.all-products .products-list > li');

    // Hide all the products in the products list.
    allProducts.addClass('hidden');

    // Iterate over all of the products.
    // If their ID is somewhere in the data object remove the hidden class to reveal them.
    allProducts.each(function () {

      var that = $(this);

      data.forEach(function (item) {
        if(that.data('index') == item.id){
          that.removeClass('hidden');
        }
      });
    });

    // Show the page itself.
    // (the render function hides all pages so we need to show the one we want).
    page.addClass('visible');

  }

Shows the single product preview page:

function renderSingleProductPage(index, data){

    var page = $('.single-product'),
      container = $('.preview-large');

    // Find the wanted product by iterating the data object and searching for the chosen index.
    if(data.length){
      data.forEach(function (item) {
        if(item.id == index){
          // Populate '.preview-large' with the chosen product's data.
          container.find('h3').text(item.name);
          container.find('img').attr('src', item.image.large);
          container.find('p').text(item.description);
        }
      });
    }

    // Show the page.
    page.addClass('visible');

  }

Takes all the products, filters them based on our query and returns an object with the results.

function renderFilterResults(filters, products){

      // This array contains all the possible filter criteria.
    var criteria = ['manufacturer','storage','os','camera'],
      results = [],
      isFiltered = false;

    // Uncheck all the checkboxes.
    // We will be checking them again one by one.
    checkboxes.prop('checked', false);


    criteria.forEach(function (c) {

      // Check if each of the possible filter criteria is actually in the filters object.
      if(filters[c] && filters[c].length){


        // After we've filtered the products once, we want to keep filtering them.
        // That's why we make the object we search in (products) to equal the one with the results.
        // Then the results array is cleared, so it can be filled with the newly filtered data.
        if(isFiltered){
          products = results;
          results = [];
        }


        // In these nested 'for loops' we will iterate over the filters and the products
        // and check if they contain the same values (the ones we are filtering by).

        // Iterate over the entries inside filters.criteria (remember each criteria contains an array).
        filters[c].forEach(function (filter) {

          // Iterate over the products.
          products.forEach(function (item){

            // If the product has the same specification value as the one in the filter
            // push it inside the results array and mark the isFiltered flag true.

            if(typeof item.specs[c] == 'number'){
              if(item.specs[c] == filter){
                results.push(item);
                isFiltered = true;
              }
            }

            if(typeof item.specs[c] == 'string'){
              if(item.specs[c].toLowerCase().indexOf(filter) != -1){
                results.push(item);
                isFiltered = true;
              }
            }

          });

          // Here we can make the checkboxes representing the filters true,
          // keeping the app up to date.
          if(c && filter){
            $('input[name='+c+'][value='+filter+']').prop('checked',true);
          }
        });
      }

    });

    // Call the renderProductsPage.
    // As it's argument give the object with filtered products.
    renderProductsPage(results);
  }

Shows the error state:

function renderErrorPage(){
    var page = $('.error');
    page.addClass('visible');
  }

Stringifies the filters object and writes it into the hash.

function createQueryHash(filters){

    // Here we check if filters isn't empty.
    if(!$.isEmptyObject(filters)){
      // Stringify the object via JSON.stringify and write it after the '#filter' keyword.
      window.location.hash = '#filter/' + JSON.stringify(filters);
    }
    else{
      // If it's empty change the hash to '#' (the homepage).
      window.location.hash = '#';
    }

  }

Conclusion

Single page applications are perfect when you want give your project a more dynamic and fluid feel, and with the help of some clever design choices you can offer your visitors a polished, pleasant experience.

Tutorial: Making a Shoutbox with PHP and jQuery

shoutbox-php-jquery

In this tutorial, we are going to build a shout box with PHP and jQuery, which allows visitors of your website to leave short comments to one another. Shouts will be stored on the server as files, no database like MySQL will be required. We are going to use two PHP libraries to make things easier – Flywheel for storing the shouts as json files and RelativeTime for creating human readable relative time stamps. We will be using Composer to install these libraries.

On the client side, we are using plain jQuery code, and the Emoji One library, which is a free project and library for adding pretty emojis to web apps. Let’s begin!

Running the shoutbox

You can grab the source code from the download button above. It has plenty of comments and is easy to follow. To run it, simply upload it to your web hosting space or add it to the apache htdocs folder if you run something like XAMPP or MAMP. Then, open http://localhost in your browser (or your website, if you uploaded it to your hosting space). Here are a few things to look for:

  • The zip files already contains the dependencies, so you don’t need to install Composer. This makes it easy to get started with the code – simply upload it and use it!
  • Make sure that the data/shouts directory exists and is writable. Otherwise you will see errors in your log file and no shouts will be stored. You might need to chmod it to 777 if you keep seeing errors.

The HTML

Let’s start with index.html. It is a regular HTML5 document, which includes our JavaScript libraries, scripts and stylesheets. Here are the parts relevant to the shoutbox:

index.html

<div class="shoutbox">
    
    <h1>Shout box <img src='./assets/img/refresh.png'/></h1>
    
    <ul class="shoutbox-content"></ul>
    
    <div class="shoutbox-form">
        <h2>Write a message <span>×</span></h2>
        
        <form action="./publish.php" method="post">
            <label for="shoutbox-name">nickname </label> <input type="text" id="shoutbox-name" name="name"/>
            <label class="shoutbox-comment-label" for="shoutbox-comment">message </label> <textarea id="shoutbox-comment" name="comment" maxlength='240'></textarea>
            <input type="submit" value="Shout!"/>
        </form>
    </div>
    
</div>

With JavaScript we will insert the published shouts into the <ul> element. The form is hidden by default, and only revealed when the “Write a message” header is clicked.

Shoutbox with PHP and jQuery

Shoutbox with PHP and jQuery

The JavaScript Code

And here is our script.js, which makes the above HTML work:

assets/js/script.js

$(function(){

    // Storing some elements in variables for a cleaner code base

    var refreshButton = $('h1 img'),
        shoutboxForm = $('.shoutbox-form'),
        form = shoutboxForm.find('form'),
        closeForm = shoutboxForm.find('h2 span'),
        nameElement = form.find('#shoutbox-name'),
        commentElement = form.find('#shoutbox-comment'),
        ul = $('ul.shoutbox-content');

    // Replace :) with emoji icons:
    emojione.ascii = true;

    // Load the comments.
    load();
    
    // On form submit, if everything is filled in, publish the shout to the database
    
    var canPostComment = true;

    form.submit(function(e){
        e.preventDefault();

        if(!canPostComment) return;
        
        var name = nameElement.val().trim();
        var comment = commentElement.val().trim();

        if(name.length && comment.length && comment.length < 240) {
        
            publish(name, comment);

            // Prevent new shouts from being published

            canPostComment = false;

            // Allow a new comment to be posted after 5 seconds

            setTimeout(function(){
                canPostComment = true;
            }, 5000);

        }

    });
    
    // Toggle the visibility of the form.
    
    shoutboxForm.on('click', 'h2', function(e){
        
        if(form.is(':visible')) {
            formClose();
        }
        else {
            formOpen();
        }
        
    });
    
    // Clicking on the REPLY button writes the name of the person you want to reply to into the textbox.
    
    ul.on('click', '.shoutbox-comment-reply', function(e){
        
        var replyName = $(this).data('name');
        
        formOpen();
        commentElement.val('@'+replyName+' ').focus();

    });
    
    // Clicking the refresh button will force the load function
    
    var canReload = true;

    refreshButton.click(function(){

        if(!canReload) return false;
        
        load();
        canReload = false;

        // Allow additional reloads after 2 seconds
        setTimeout(function(){
            canReload = true;
        }, 2000);
    });

    // Automatically refresh the shouts every 20 seconds
    setInterval(load,20000);


    function formOpen(){
        
        if(form.is(':visible')) return;

        form.slideDown();
        closeForm.fadeIn();
    }

    function formClose(){

        if(!form.is(':visible')) return;

        form.slideUp();
        closeForm.fadeOut();
    }

    // Store the shout in the database
    
    function publish(name,comment){
    
        $.post('publish.php', {name: name, comment: comment}, function(){
            nameElement.val("");
            commentElement.val("");
            load();
        });

    }
    
    // Fetch the latest shouts
    
    function load(){
        $.getJSON('./load.php', function(data) {
            appendComments(data);
        });
    }
    
    // Render an array of shouts as HTML
    
    function appendComments(data) {

        ul.empty();

        data.forEach(function(d){
            ul.append('<li>'+
                '<span class="shoutbox-username">' + d.name + '</span>'+
                '<p class="shoutbox-comment">' + emojione.toImage(d.text) + '</p>'+
                '<div class="shoutbox-comment-details"><span class="shoutbox-comment-reply" data-name="' + d.name + '">REPLY</span>'+
                '<span class="shoutbox-comment-ago">' + d.timeAgo + '</span></div>'+
            '</li>');
        });

    }

});

The Emoji One library has version for both JavaScript and PHP. In the appendComments method, we use the emojione.toImage() function to convert all typed-out smileys into emoji. See all functions that are supported, and check out this handy emoji code website. Now that the frontend is ready, let’s move on to the backend.

The PHP Code

We have two files – publish.php and load.php. The first accepts a POST request for storing shouts in the data store, and the second returns the 20 latest shouts. These files are not opened directly by visitors – they only handle AJAX requests.

publish.php

<?php

// Include our composer libraries
require 'vendor/autoload.php';

// Configure the data store

$dir = __DIR__.'/data';

$config = new JamesMossFlywheelConfig($dir, array(
    'formatter' => new JamesMossFlywheelFormatterJSON,
));

$repo = new JamesMossFlywheelRepository('shouts', $config);
    
// Store the posted shout data to the data store

if(isset($_POST["name"]) && isset($_POST["comment"])) {
    
    $name = htmlspecialchars($_POST["name"]);
    $name = str_replace(array("n", "r"), '', $name);

    $comment = htmlspecialchars($_POST["comment"]);
    $comment = str_replace(array("n", "r"), '', $comment);
    
    // Storing a new shout

    $shout = new JamesMossFlywheelDocument(array(
        'text' => $comment,
        'name' => $name,
        'createdAt' => time()
    ));
    
    $repo->store($shout);
    
}

Here we directly use the Flywheel library we mentioned in the beginning. Once it is configured, you can store any type of data, which will be written as a JSON file in the data/shouts folder. Reading these shouts is done in load.php:

load.php

<?php

require 'vendor/autoload.php';

// If you want to delete old comments, make this true. We use it to clean up the demo.
$deleteOldComments = false;

// Setting up the data store

$dir = __DIR__.'/data';

$config = new JamesMossFlywheelConfig($dir, array(
    'formatter' => new JamesMossFlywheelFormatterJSON,
));

$repo = new JamesMossFlywheelRepository('shouts', $config);

// Delete comments which are more than 1 hour old if the variable is set to be true.

if($deleteOldComments) {
    
    $oldShouts = $repo->query()
                ->where('createdAt', '<', strtotime('-1 hour'))
                ->execute();

    foreach($oldShouts as $old) {
        $repo->delete($old->id);
    }
    
}

// Send the 20 latest shouts as json

$shouts = $repo->query()
        ->orderBy('createdAt ASC')
        ->limit(20,0)
        ->execute();

$results = array();

$config = array(
    'language' => 'RelativeTimeLanguagesEnglish',
    'separator' => ', ',
    'suffix' => true,
    'truncate' => 1,
);

$relativeTime = new RelativeTimeRelativeTime($config);
        
foreach($shouts as $shout) {
    $shout->timeAgo = $relativeTime->timeAgo($shout->createdAt);
    $results[] = $shout;
}

header('Content-type: application/json');
echo json_encode($results);

We’ve included code which deletes shouts older than an hour. We use this feature to keep the demo clean. You can enable it if you wish. After selecting the shouts, we are also calculating the human-readable relative time stamp with the RelativeTime library.

With this our shoutbox is ready! You can embed it in your website, customize it and change the code any way you please. We hope you like it!

Powered by Gewgley