Tuesday 22 July 2014

Using PEGJS parser as client side parser

The topic of this article is to present to you how to use PEGJS for creating and using a parser in an application. The aim is to get a quick look on how to implement a grammar with PEGJS and integrate it in a simple application. In order to get the display more user-friendly, Bootstrap CSS and JQuery will be used.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: A presentation of PEGJS
  • PART 2: Overview of the application
  • PART 3: Defining the grammar
  • PART 4: Integrating the parser
  • PART 5: Parsing a text input
  • PART 6: Getting a working example


PART 1: A presentation of PEGJS

PEGJS is a parser generator for javascript that allows developers to build interpreters or compilers with good error reporting and to create their own Domain Specific Language (DSL) for instance. PEG stands for Parsing Expression Grammar. PEGJS has been developed by David Majda.


PART 2: Overview of the application

The application will implement a calculator for simple arithmetic operations such like multiplications and additions with integer and float numbers. When initializing the applictaion will load the grammar file and then display two panels: one for typing the arithmetic expressions and the other for displaying the result.


The arithmetic operations entered by the user support parenthesis and blank spaces between operators and operands.


PART 3: Defining the grammar

In the application, the grammar has been defined in a file which extension is '.pegjs', but it could have been defined in any other files with a different extension (a '.txt' file for instance). Here is the grammar describes in the 'myGrammar.pegjs' file:

start = additive

additive = left:multiplicative space* "+" space* right:additive { return left + right; } / multiplicative

multiplicative = left:primary space* "*" space* right:multiplicative { return left * right; } / primary

primary = number / "(" space* additive:additive space* ")" { return additive; }

number = float / integer

float "a float" = digits1:[0-9]+ "." digits2:[0-9]+ {
  return parseFloat(digits1.join("") + "." + digits2.join(""));
}

integer "an integer" = digits:[0-9]+ { return parseInt(digits.join(""), 10); }

space = [ \t]


The grammar defines some rules, which most of the time consist of an identifier, a parsing expression and some javascript code that is executed when the pattern of the parsing expression matches successfully. The parsing starts with the rule which identifier is 'start'.

It is possible to make a reference to a rule in another rule. For instance, the 'additive' rule has a reference to the 'multiplicative' rule. It is also possible to give an expression a label in the rule. Then this label is used to reference in the javascript code. For instance the label 'left' is a reference to the 'primary' expression in the 'additive' rule and is used in the javascript code as 'return left + right;'. The label is declared before the expression and separated from the expression by a column ':'.

It is also possible to give a rule a human-readable name. This is the case for the 'float' rule where the name 'a float' will be for instance displayed in case of error in the error message (see PART 4 for more details). The human-readable name should be declared between the rule identifier and the '=' sign.

The expressions separated by a slash character '/' are interpreted as follow: if the first expression does not match successfully, then the parser tries to match the second expression. If the parser matches none of the expressions, then the match fails.

For more information about how to defined rules in PEGJS grammar, you can have a look here.


PART 4: Integrating the parser

When the grammar is defined, then you need in to upload the file that describes the grammar and build the parser. You can upload the file with an XMLHttpRequest:

var req = new XMLHttpRequest();
req.open("GET", 'grammar/myGrammar.pegjs', true);
req.onload = function(e) {
  var grammarInput =  req.responseText;
  if ((grammarInput != null) && (PEG != null)) {
    window.pegjsmain.parser = PEG.buildParser(grammarInput);
  }
};
req.send();

When the request is sent and the response is loaded, you can get the file text thanks to the 'req.responseText' instruction.
You are able to create a PEGJS parser with the grammar text by calling the 'buildParser' function on the 'PEG' object.


PART 5: Parsing a text input

The parser object returned by the 'PEG.buildParser' instruction provides an API for parsing some text input with the 'parse' function. The 'parse' function returns the output of the parsing if the parsing is successfull or throws a structured error as a json object if the parsing fails.

try {
  var output = this.parser.parse(content);
  this.displayResult(output, true);
}
catch(error) {
  this.displayResult(error, false);
}

displayResult : function(output, success) {
  var errorDisplay = document.getElementById('errorDisplay');
  var resultDisplay = document.getElementById('resultDisplay');
  if ((resultDisplay != null) && (errorDisplay != null)) {
    if (success) {
      errorDisplay.innerHTML = " ";
      errorDisplay.classList.add("hidden");
      resultDisplay.innerHTML = output;
      resultDisplay.classList.remove("hidden");
    }
    else {
      resultDisplay.innerHTML = " ";
      resultDisplay.classList.add("hidden");
      errorDisplay.innerHTML = "ERROR: line " + output.line + ", column " + output.column + " : " + output.message;
      errorDisplay.classList.remove("hidden");
    }
  }
}

The json object thrown as an error when the parsing fails contains the following properties: message which is the error message, the name which is the error name, line and column which are respectively the line and the column where the parser failed to match an expression, expected which is an array of the expected patterns. Unfortunately it seems that there is no documentation about the error object properties, so if you want to get more information about them, you will need to run an application with PEGJS and define some break points in your favorite brower tool.


PART 6: Getting a working example

You can find a working example in my github repository (the pegjsapp folder).

Saturday 26 April 2014

Implementing a login with Firebase and Angularjs

The topic of this article is to present to you how to use Firebase and Angularjs in an application. The aim is to get a quick look on how to implement a login with Firebase and Angularjs in a simple application. In order to get the display more user-friendly, Bootstrap CSS and JQuery will be used.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: A presentation of Firebase
  • PART 2: A presentation of Angularfire
  • PART 3: Creating and setting an application in Firebase
  • PART 4: Overview of the application with login
  • PART 5: Defining the service for connecting to Firebase
  • PART 6: Defining the login controller
  • PART 7: Make it work all together
  • PART 8: Getting a working example


PART 1: A presentation of Firebase

Firebase is a backend in the cloud that provides an API for storing and synchronizing data. Developers can focus on implementing the client-side of their application and all the server-side part is handled by Firebase. The developers only need to include a Firebase library in their application; there are libraries for the following support : Web, Nodejs, Objective-C and Java.
If the synchronization of data gives Firebase the advantage of building real-time application easily, you can use Firebase as soon as you do not want to write any single line of code on server side. However Firebase has also some limitations: it is not possible to perform some actions specific to servers such as sending emails. Heavy data processing is not recommended either.
Another key feature of Firebase is security. A large bunch of authentication is provided for securing your applications. The access to an application can be configured through a set of rules that can be defined in the Firebase online interface.
In the application described in PART 4, I will use the web library (javascript) provided by Firebase. I will implement a login form for accessing the application in order to introduce the security mechanism put in place in Firebase.


PART 2: A presentation of Angularfire

As I will develop my application using Angularjs, I will use an Angularjs binding for Firebase which is called AngularFire. This binding is a javascript library that helps you synchronizing and fetching Firebase data by linking Firebase URLs with Angularjs models. AngularFire just exposes the same API as Firebase with an Angularjs function name style.


PART 3: Creating and setting an application in Firebase

The first step for getting a Firebase application (after signing up Firebase) is to create a Firebase application. In order to create an application when you are logged in Firebase, you just need to type the name of your application in the top-left corner of your dashboard and click on the "CREATE NEW APP" button.


When the application has been created, you can select it from your dashboard. As soon as you have selected the application, you are redirected to the "Data" tab of the application.


The "Data" tab is where you can view the data that are currently stored in your application. As you can see on the screenshot, you are able to export or import the application data as Json format.
Then you need to go to the "Security Rules" tab in order to secure you application and define who have access to the application data and to which data they have access to. The rules are defined as Json format. For more information about the security rules, you can have a look to the security rules section. In the application example, the access to the data in write and read mode are only restricted to authenticated users.


Then you need to define the authentication providers when the users want to log in. In the application example, the "Email & Password" provider has been enabled. You are also able to specify for which domain you want to allow the simple login service (localhost and 127.0.0.1 in the example).


If you have selected the "Email & Password" authentication provider, you can add some users by entering their email and password.


When your application has been created and the settings are correctly set for the simple login service, it is time to code... But first, let's have a look to the application we would like to implement as an example.


PART 4: Overview of the application with login

The application will only implement a login form, and then (when the user is logged in) a single page with a toolbar and a welcome message to the user (with its email displayed).


When the user is logged in and he is on the dashboard, he is able to log out when clicking the icon displayed in the left part of the toolbar.


The index.html page of the application should at least include the angular, the firebase, the firebasesimplelogin and the angularfire javascript files. These files can be dowloaded from the official webdite of angularjs and firebase or included from an online repository (like cdnjs for instance).


PART 5: Defining the service for connecting to Firebase

The connection to Firebase services will be defined in an angular service defined in a specific module to keep all the logic in one place. The service will provide 3 functions: one for getting a reference to firebase data, one for login and one for logout. As login and logout functions are asynchronous functions, they will raise an event when user has been logged in (loginsuccessed or loginfailed) and when the user has logged out (logoutsucceded).
The login and logout functions will rely on the FirebaseSimpleLogin API provided by Firebase.

var base = new Firebase(FIREBASE_URL);
var auth = new FirebaseSimpleLogin(base, function(error, user) {
  if (error) {
    // an error occurred while attempting login
    // Perform logic when login failed
  } else if (user) {
    // user authenticated with Firebase
    // Fire event when logged in
  } else {
    // user is logged out
    // Fire event when logged out
  }
});

var service = {};
service.login = function(email, password) {
  auth.login('password', {
    email : email,
    password : password
  });
};
service.logout = function() {
  auth.logout();
};
service.getBase = function() {
  return base;
};

All this logic will be implemented in an angular service returned by an angular 'factory'. The constant 'FIREBASE_URL' is the URL of your firebase application. Firing event is performed by '$rootScope.$broadcast' function.


PART 6: Defining the login controller

The login firebase service implemented above will be used in an angular controller dedicated for managing the login and the switching between the login form and the application home page.

You first need to specify the dependency on the angular module of the firebase service when declaring the application module:

var firebaseangularapp = angular.module('firebaseangularapp', ['ngRoute', 'firebase', 'firebasemodule']);

Then you can declare your login controller in the application module as follow:

firebaseangularapp.controller('LoginCtrl', ['$scope', '$firebase', 'firebaseservice', function LoginCtrl($scope, $firebase, firebaseservice) {
  // TODO
}]);

In the login controller, let's declare the user email and the user password data in order to use them in the login form page. Let's also declare the login and logout function. All these data and functions should be declared in the '$scope' variable in order to be accessible from the html. The logged boolean will be useful for switching from the login form to the home page.

firebaseangularapp.controller('LoginCtrl', ['$scope', '$firebase', 'firebaseservice', function LoginCtrl($scope, $firebase, firebaseservice) {
  $scope.logged = false;
  $scope.useremail = "";
  $scope.userpassword = "";
  $scope.login = function() {
    if ((this.useremail !== "") && (this.userpassword !== "")) {
      firebaseservice.login(this.useremail, this.userpassword);
    }
  };
  $scope.logout = function() {
    firebaseservice.logout();
  };
}]);

When the user will click on the Login button of the login form, the login function of the login controller will be called. This function will call the login function of the firebase service. When the user will be logged in in Firebase and when the service will fire the loginsucceded event through the '$rootScope', this event needs to be intercepted in the login controller to know if the user is logged in or if the login failed. This is done by listening to the event with the '$on' function of the '$scope' object.

$scope.$on('loginsucceeded', function(e, args) {
  $scope.logged = true;
  $scope.apply();
};

The '$scope.apply()' instruction forces the refresh of the scope to keep it up to date with the latest changes.


PART 7: Make it work all together

Now let's have a look at the html. The index.html page will link the logic described above to the displayed page.

<html ng-app="firebaseangularapp">
  <head>
    <!-- Include all the js and css files -->
  </head>
  <body ng-controller="LoginCtrl">
    <div ng-if="!logged">
      <!-- the html for the login form -->
    </div>
    <div id="output" ng-if="logged">
      <header>
        <!-- the html for the navbar -->
      </header>
      <div ng-controller="MainCtrl">
        <!-- the html for the home page main content -->
      </div>
    </div>
  </body>
</html>

The 'MainCtrl' is the controller for the home page main content that will be displayed under the navigation bar after the user logs in.


PART 8: Getting a working example

You can find a working example in my github repository (the firebaseangularapp folder). Please do not forget to declare your own firebase application URL in the firebasemodule.js file in constant 'FIREBASE_URL'.

Sunday 9 March 2014

Generating PDF from client side with jsPDF

The topic of this article is to present to you jsPDF which is a javascript library for generating PDF from client side. The aim is to get a quick look on the main functions provided by the library and put them into pratice with a simple application. In order to get the display more user-friendly, Bootstrap CSS and JQuery will be used.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: A short presentation of jsPDF
  • PART 2: A simple application
  • PART 3: Handling PDF document in javascript
  • PART 4: Preview of the PDF document
  • PART 5: Getting a working example


PART 1: A short presentation of jsPDF

jsPDF is a javascript library for handling PDF documents developed by James Hall who currently works for Parallax. The source code of jsPDF library is available in Github. You can get a live demo on Parallax website and you can read the API documentation online here.



PART 2: A simple application

The pdfapp application allows the user to type a title and a text in two fields. Then the user is able to click a preview button to display the generated PDF in a section below.




PART 3: Handling PDF document in javascript

When you have retrieved the values of the two fields (title and text), you can create a new PDF document with the jsPDF object:

var doc = new jsPDF('p', 'mm', 'letter');

jsPDF object takes 3 parameters. The first parameter is the orientation (portrait 'p' or landscape 'l'). The second parameter is the unit for measurement when specifying some coordinates (pt, cm, in or mm as default). The third parameter is the format of the PDF document (a3, a4, letter, legal or a5 as default).

Then you can set the title in the PDF document. If the title is too long, it should be written on multiple lines:

var titleLines = doc.setFont('Courier','Bold')
  .setFontSize(20)
  .splitTextToSize(title, 160);
doc.text(50, 20, titleLines);

You can set the font with setFont which takes font name and font type as parameters. You can set the font size with setFontSize where the unit of the parameter is the one declared in the instantiation of jsPDF object.
The function splitTextToSize will split the text into lines according to the size provided as second parameter. The returned object is an array containing the lines. The unit of the size is the one declared in the instantiation of jsPDF object.
You can add some text to the document by using the function text on the jsPDF object.

Then you can set the content of the text exactly the same way as for title, by using the same functions and objects.



PART 4: Preview of the PDF document

When you have finished to edit the document, you are able to preview the document. In order to visualize the generated document we decided to use an iframe element and to transform the document as a data uri string.

var pdfOutput = doc.output('datauristring');
preview.src = pdfOutput;

When the document is generated as a data uri string, then this string is displayed in the iframe by setting it in the src property of the iframe.



PART 5: Getting a working example

You can find a working example in my github repository (the pdfapp folder).

Sunday 26 January 2014

Introduction to d3.js

The topic of this article is to present to you d3.js. D3.js is the framework on which relies the toolkit introduced in the previous article: Rickshaw. The aim is to get a quick look on the concepts introduced by d3.js and put them into pratice with a simple application. In order to get the display more user-friendly, Bootstrap CSS and JQuery will be used.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: A short presentation of d3.js
  • PART 2: The link between data and DOM
  • PART 3: Utilitary classes and visualization components
  • PART 4: Use d3.js in an application
  • PART 5: Displaying the axis
  • PART 6: Displaying the circles
  • PART 7: Getting a working example


PART 1: A short presentation of d3.js

D3.js is a javascript library that allows the developers to manipulate documents based on data. This library provides a data-driven approach to DOM manipulation and also some visualization components. D3.js can support large datasets and dynamic behavior for interaction and animation. It is very usefull for displaying data through charts and graphs.

D3.js is well documented, you can find the documentation page here. You can get access to API reference, tutorials, examples, google groups...



PART 2: The link between data and DOM

In order to illustrate the way d3.js links data to DOM elements, we will take as an example a SVG element with some circles to add according to the data. D3.js dynamically handles the changes in data (removing and adding) to reflect them in the DOM elements.

You need first to retrieve the DOM elements with d3 and then bind them to a dataset. Then you need to specify what you want to do with the new data that have no associated DOM elements and what you want to do with the DOM elements that have no piece of data anymore. Finally you need to specify what to do with the DOM elements that are linked to data.

var myData = [{r : 5, x : 10, y : 10, color : "red"}, 
                {r : 25, x : 100, y : 100, color : "yellow"}];
var linkedData = d3.select("svg").selectAll("circle").data(myData);
linkedData.exit().remove();
linkedData.enter().append("svg:circle");
linkedData.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; })
            .attr("r", function(d) { return d.r; })
            .attr("fill", function(d) { return d.color; });

As you can see in this code snippet, the 3rd line binds the circle SVG elements with the data "myData". Then when the SVG elements and the data are linked, the 4th and the 5th lines respectively describe what to do with SVG elements that have no data anymore and with pieces of data that have no corresponding SVG elements yet.
The last line explain what to do with SVG elements that have linked data (after updating). D3.js provides some usefull function to directly modify the attributes of the elements. In this case the "attr" function takes as arguments the name of the attribute and a function that will compute the value for this attribute.

The concept of data and elements binding under d3.js can be summarized with this schema:



PART 3: Utilitary classes and visualization components

D3.js provides many utilitary classes and visualization components. The utilitary classes allow the developer to work on DOM element selections, transitions, arrays, strings, mathematical functions, colors, scales, time and geographic features. The visualization components are SVG shapes, axis, geometric shapes and main graphs and charts. A complete list of the API references is available here.



PART 4: Use d3.js in an application

Let's put into practice the d3.js concepts described above with a simple application. Here is a screenshot of the application graphic we are going to implement:


The graph will be displayed in a DIV element which id is "graph". The javascript implementation of this graph is devided in 2 parts: one for adding the SVG elements with the horizontal and vertical axis and one for displaying the circles in the graph.

The SVG element is added to the "graph" DIV element thanks to d3.js utilities (space, height and width are 3 constants):
this.circles = [{r : 5, x : 10, y : 10, color : "red"}, 
                {r : 25, x : 100, y : 100, color : "yellow"},
                {r : 10, x : 400, y : 200, color : "green"},
                {r : 5, x : 50, y : 25, color : "purple"},
                {r : 50, x : 250, y : 300, color : "blue"}];
this.svgcontainer = d3.select("#graph").append("svg:svg")
            .attr("width", this.width + 2*this.space)
            .attr("height", this.height + 2*this.space);




PART 5: Displaying the axis

The axis creation is implemented in 3 steps. First you need to scale the axis (for instance from 0 to 500) to the axis length on the screen (in pixels for instance). Second you need to create the axis according to these scales and orient the vertical scale to the left part of the screen. Third you need to attach the axis to the SVG container. The piece of code in charge of the steps described above is:
//Create the Scale we will use for the Axis
var xAxisScale = d3.scale.linear().domain([0, this.width]).range([0, this.width]);
var yAxisScale = d3.scale.linear().domain([0, this.height]).range([this.height, 0]);
//Create the Axis
var xAxis = d3.svg.axis().scale(xAxisScale);
var yAxis = d3.svg.axis().scale(yAxisScale).orient("left");
//Create an SVG group Element for the Axis elements and call the xAxis function
var xAxisGroup = this.svgcontainer.append("svg:g")
    .attr("class", "x axis")
    .attr("transform", "translate(" + this.space + "," + (this.height + this.space) + ")")
    .call(xAxis);
var yAxisGroup = this.svgcontainer.append("svg:g")
    .attr("class", "y axis")
    .attr("transform", "translate(" + this.space + ","  + this.space + ")")
    .call(yAxis);

Here are few comments on the code above:
The scales are created by specifying the domain and the range.
The axis are created and scaled thanks to the scale function.
The Y axis is set vertically thanks to the orient function.
The axis are added to the SVG element by creating two G elements with a "transform" attribute for setting the axis at the correct position (in our case, the graph origin - bottom, left - is not the screen origin - top, left -) and applying the call function on these G elements with the axis as parameter.




PART 6: Displaying the circles

The concept used for displaying the circles is the same as the one described in PART 2. The code for displaying the circles is:
this.suite = this.svgcontainer.selectAll("circle");
this.bindCircles(this.suite.data(this.circles));

with the following functions:
    bindCircles : function(displayedData) {
        this.removeCircle(displayedData);
        this.appendCircle(displayedData);
        this.displayCircle(displayedData);
    },
    
    appendCircle : function(displayedData) {
        displayedData.enter().append("svg:circle");
    },
    
    removeCircle : function(displayedData) {
        displayedData.exit().remove();
    },
    
    displayCircle : function(displayedData) {
        displayedData.attr("cx", this.getX.bind(this))
            .attr("cy", this.getY.bind(this))
            .attr("r", function(d) { return d.r; })
            .attr("fill", function(d) { return d.color; });
    },

When you modify the data (for instance you want to add an element to the array this.circles), you need to call the 2 lines described in the first code section of this part for binding the data and the DOM elements and specify how to display the new circle, update the existing circle and remove the circle that do not exist in the dataset anymore.



PART 7: Getting a working example

You can find a working example in my github repository (the d3introduction folder).

Wednesday 1 January 2014

Rickshaw toolkit in an Angularjs application

The topic of this article is to show how to integrate smoothly the Rickshaw toolkit in an Angularjs application. The aim is to take advantage of this graphing toolkit within the Angularjs concepts. In order to get the display more user-friendly, Bootstrap CSS and JQuerywill be used.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: A short presentation of Rickshaw toolkit
  • PART 2: The Angularjs application
  • PART 3: The strategy
  • PART 4: Defining Angularjs directives
  • PART 5: Integrating the directives in the application
  • PART 6: Getting a working example


PART 1: A short presentation of Rickshaw toolkit

Rickshaw is an opensource Javascript toolkit based on d3.js for creating interactive graphs. It is developed by Shutterstock and as it is opensource the code is available on Github.

The first and simplest example given by Shutterstock for creating a graph with Rickshaw is to define an empty div element in an HTML page (after loading d3js and Rickshaw scripts) and render a graph built with this div element through rickshaw graph class.
<script src="/vendor/d3.min.js"></script> 
<script src="/vendor/d3.layout.min.js"></script> 
<script src="/rickshaw.min.js"></script>
 
<div id="chart"></div>

<script> 
var graph = new Rickshaw.Graph( {
    element: document.querySelector("#chart"), 
    width: 300, 
    height: 200, 
    series: [{
        color: 'steelblue',
        data: [ 
            { x: 0, y: 40 }, 
            { x: 1, y: 49 }, 
            { x: 2, y: 38 }, 
            { x: 3, y: 30 }, 
            { x: 4, y: 32 } ]
    }]
});
 
graph.render();
</script>
You can find a working example of this code here.

As you can see in the code snippet above, Rickshaw is based on DOM manipulation (with jquery for instance). Rickshaw will populate the empty div element with some SVG tags and will style it with some CSS.


PART 2: The Angularjs application

The Angularjs application will display a simple graph with 4 forms below for changing some properties (the colour, the width and the height) of the graph and add new points in the graph.
The application is based on a single html page. At the first load of the page, the graph will be empty (only one point at 0,0). In order to display something in the graph, you need to first add a point: add a value in the field dedicated to adding a data point and then click the button "Add a data point".


PART 3: The strategy

In an Angularjs application, it is not recommended to directly manipulate the DOM elements as it is explained in the Rickshaw example. The best strategy for manipulating DOM elements in Angularjs application is to use directives which are HTML tags or HTML attributes that are interpreted by the framework for rendering or behaving the way they have been designed. For more information on directives you can read directly the dedicated page in Angularjs Developer Guide here.

The strategy for displaying a graph with Rickshaw in an Angularjs application will be to create a custom directive and use it in our HTML page. The directive will be an HTML tag <rickshawgraph></rickshawgraph> with the following attributes: color, width, height and graphicaldata.


PART 4: Defining Angularjs directives

The custom directive will be defined in an angularjs module dedicated to implementing custom directives for integrating Rickshaw toolkit. The directive will replace the rickshawgraph tag by an empty div element. When the framework will attach the behaviour of the directive to the div element (calling the link function defined in the directive), a new rickshaw graph will be created based on the div element thanks to the Graph class provided by the Rickshaw toolkit and the graph render function will be called to display the graph.

In order to bind the attribute values (color, graphicaldata, width and height) to the data scope of the application and update the view when the data change, some callbacks will be attached to the attributes thanks to the $watch and $watchCollection functions of the scope object. These callbacks will update the Rickshaw graph object and call its update function.
var angularrickshawmodule = angular.module('angularrickshaw', ['ngRoute', 'angularrickshaw.tpl']);
angularrickshawmodule.directive('rickshawgraph', function() {
  return {
    restrict: 'E',
    templateUrl: 'rickshawgraphDirective.html',
    replace: true,
    link: function(scope, element, attrs) {
      var graph = null;
      var series = [{
        color: 'black',
        data: [{x:0,y:0}]
      }];
      
      scope.$watch(attrs.color, function(value){
        if (graph != null) {
          graph.series[0].color = value;
          graph.update();
        }
      });
      
      scope.$watch(attrs.height, function(value){
        if (graph != null) {
          graph.setSize({height: value, width: graph.width});
          graph.update();
        }
      });
      
      scope.$watch(attrs.width, function(value){
        if (graph != null) {
          graph.setSize({height: graph.height, width: value});
          graph.update();
        }
      });
      
      scope.$watchCollection(attrs.graphicaldata, function(value){
        if (graph != null) {
          graph.series[0].data = value;
          graph.update();
        }
      });
      
      graph = new Rickshaw.Graph( {
          element: element[0],
          width: 200,
          height: 100,
          series: series
      });
      graph.render();
    }
  };
});
Here are few comments on the code snippet displayed above:

  • The restrict property is set for using the directive as an attribute ('A' value) or an element ('E' value) or both ('AE' value).
  • The templateUrl property set the url of the HTML template to use. In our case, rickshawgraphDirective.html is only an empty div element.
  • The replace property set whether the element(s) defined in the template should replace the directive element (the rickshawgraph tag in our case).
  • The link property is a function with 3 parameters which are the scope of the directives, the DOM element retrieved by jqlite or jquery and the attributes represented by an object.
  • The function used for listening to changes of graphicaldata attributes is $watchCollection because the value that would be passed to this attribute should be a function that returns an array or a reference to an array.


PART 5: Integrating the directives in the application

The first step for integrating the custom directives in the application is to use them in the HTML makup. You can see below in the code snippet of the index.html page that a <rickshawgraph> tag is used:
<div class="graphic-content">
    <rickshawgraph color="graphcolor" graphicaldata="graphdata" height="graphheight" width="graphwidth"></rickshawgraph>
    <form class="form-inline" role="form">
        <!-- The form for modifying one attribute of the custom directive -->
    </form>
    <form class="form-inline" role="form">
        <!-- The form for modifying one attribute of the custom directive -->
    </form>
    ...
</div>
The second step is to inject the dependency of the angularrickshaw module (the module in which the custom directives are defined) into the rickshawapp module which is the application main module. The scope used for defining the data injected in the attributes of the custom HTML tag (color, graphicaldata, width and height) will also be defined in a controller attached to the rickshawapp module.
var rickshawapp = angular.module('rickshawapp', ['ngRoute', 'angularrickshaw']);
rickshawapp.controller('MainCtrl', function MainCtrl($scope) {
  $scope.text = "The aim of this code example is to show how to implement some angular directives for integrating Rickshaw framework in Angularjs.";
  $scope.title = "Test Angular Rickshaw";
  $scope.graphcolor = "steelblue";
  $scope.graphheight = 100;
  $scope.graphwidth = 200;
  $scope.graphdata = [{x:0,y:0}];
  ...
});


PART 6: Getting a working example

You can find a working example in my github repository (the angularrickshaw folder).

Saturday 28 December 2013

Sliding Panel Menu

The topic of this article is to show you how to implement a sliding panel menu for a mobile device (smartphone or tablet) using HTML and Javascript. The idea is to find a way to implement the same behaviour as the one used by the SlidingPanelLayout in Android with HTML and Javascript for using it in a web based mobile application or an hybrid mobile application (such as the one developed with Phonegap). In order to get the display more user-friendly, Bootstrap CSS will be used only for its CSS classes.
Explanations will be completed with code snippets and a link to a Github repository will be provided for testing a real example.

Here is the plan of this article:
  • PART 1: Emulate a mobile device with your desktop browser
  • PART 2: The strategy
  • PART 3: HTML markup
  • PART 4: Handling the touch events
  • PART 5: Displaying and hiding the menu
  • PART 6: Getting a working example


PART 1: Emulate a mobile device with your desktop browser


A convenient way of developing mobile application based on HTML and Javascript is to test it directly in your desktop browser in order to get access to your favourite development tools. Chrome provides some tools for emulating mobile devices; you can read how to enable them in this page.


PART 2: The strategy


The strategy for implementing the sliding panel menu is to get two panels in the DOM, one for the main display and one for the menu. The menu panel should slide from the browser view to outside the browser view when we want to hide the menu and it should slide from outside the browser view to the browser view when we want to display the menu.
The user should be able to make the menu slide by touching the screen and the horizontal sliding of the menu should be exactly the sliding of the user finger on the screen. Of course, the user will not be able to make the menu slide more than the menu width, in order to make the menu stick to the browser view left border when it is displayed.


The slide of the menu panel should be handled thanks to touch events and CSS animations. The three following sections will respectively explain how to define the HTML markups, the touch event handlers and the sliding animation.


PART 3: HTML markup


As we want to implement a mobile application, it could be nice to get a bar at the top of the application for displaying the icon and the name of the application. Then of course the main display zone below the application bar should contain two panels, one for the menu, and one for the main content.
<body onload="main.initialize()">
  <div id="output">
    <header id="headercontainer">
      <!-- the header with the icon and the name of the application -->
    </header>
    <div id="swipingcontainer" class="maincontainer">
      <div id="menu" class="leftcontainer">
        <!-- menu panel -->
      </div>
      <div id="main" class="rightcontainer">
        <!-- main content panel -->
      </div>
    </div>
  <div>
</body>
The “main.initialize” function called on the onload of the body is defined in a separate javascript file and will mainly handle the size definition of the div elements (panels) and will attach the event listener to the correct div elements.


PART 4: Handling the touch events


In the “initialize” function, the div elements are resized as described above, and the width of the menu is store in a variable (this.menuWidth). Some listeners are also attached for touchstart, touchmove and touchend events on the swipingcontainer div element which is the parent of the menu and the main content div elements.

The idea is to detect the beginning of the swipe and then compute the horizontal distance of the swipe. During the swipe, the menu should be moved horizontally according to the horizontal distance between the finger position at the beginning of the swipe and the current finger position. As we previously decided to let the menu stick to the left border of the application, it is not possible to make the menu slide more than the menu width. Then when the swipe ends, we need to decide if the user makes the menu slide as much as necessary for hiding or displaying it. We decide to consider that the menu should be hidden (if it was previously displayed) when the swipe is from the right to the left and the swipe distance is higher than half of the menu width. We also decide to consider that the menu should be displayed (if it was previously hidden) when the swipe is from the left to the right and the swipe distance is higher than half of the menu width.

The callback function that is attached to the listener of the touchstart event on the swiping container needs to store the finger horizontal position which the finger position at the beginning of the swipe. The left position of the menu at the beginning of the swipe also needs to be stored in order to be used when computing the menu left position during the swipe.
touchStartHandler : function(evt) {
  evt.preventDefault(true);
  var touch = evt.changedTouches;
  if ((touch != null) && (touch.length > 0)) {
    this.startX = touch[0].screenX;
    if (this.display.menuVisible) {
      this.menuLeftPosition = 0;
    }
    else {
      this.menuLeftPosition = - this.menuWidth;
    }
  }
}
The callback function that is attached to the listener of the touchmove event on the swiping container needs to get the current horizontal position of the finger and then compute the new left position of the menu with the constraint of sticking to the left border of the application when totally displayed.
touchMoveHandler : function(evt) {
  evt.preventDefault(true);
  var touch = evt.changedTouches;
  if ((this.startX != null) && (touch != null) && (touch.length > 0)) {
    var moveX = touch[0].screenX;
    var diff = moveX - this.startX;
    var menuCurrentPosition = this.menuLeftPosition + diff;
    if ((- this.menuWidth <= menuCurrentPosition) &&
        (menuCurrentPosition <= 0)) {
      var menu = document.getElementById('menu');
      menu.style.left = menuCurrentPosition + "px";
    }
  }
}
The callback function that is attached to the listener of the touchend event on the swiping container needs to get the horizontal position of the finger when the swipe ends for computing the distance between the horizontal finger position at the beginning of the swipe and the one at the end of the swipe. Thanks to this distance, we will be able to decide if the menu should be displayed or hidden according to the criteria described above.
touchEndHandler : function(evt) {
  evt.preventDefault(true);
  var touch = evt.changedTouches;
  if ((this.startX != null) && (touch != null) && (touch.length > 0)) {
    var moveX = touch[0].screenX;
    var diff = moveX - this.startX;
    var menuCurrentPosition = this.menuLeftPosition + diff;
    var menu = document.getElementById('menu');
    if (menuCurrentPosition < (- this.menuWidth / 2)) {
      this.display.menuVisible = false;
      this.displayMenu(menu, false);
    }
    else {
      this.display.menuVisible = true;
      this.displayMenu(menu, true);
    }
  }
}


PART 5: Displaying and hiding the menu


The displayMenu function called in the code snippet above in the callback function of the touchevent on the swiping container is in charge of displaying or hiding the menu (depending on the value of its second parameter) and trigger the animation for sliding the menu from its current position to its final position which is totally displayed or totally hidden.

The animation is declared in the CSS on the left property, as you can see this is a fake animation (the values of the property at the beginning and at the end are the same). The idea is that this animation exists in the CSS, even if it does nothing.
@-webkit-keyframes displayMenuAccount {
  from {left : 0px;}
  to {left : 0px;}
}
In the javascript file the displayMenu function is:
displayMenu : function(menu, display) {
  var rule = this.getMenuDisplayAnimation(display);
  this.addAnimationToMenu(menu, rule.animation,
  rule.duration, display);
}
The getMenuDisplayAnimation function returns an object that contains the definition of the animation as a string and the duration of the animation:
getMenuDisplayAnimation : function(display) {
  var animation = '@-webkit-keyframes displayMenuAccount {\n' + 
      'from {left : ' + menu.style.left + ';}\n' + 
      'to {left : ' + (display ? '0px' : ((-this.menuWidth) + 'px')) + 
      ';}\n' +
    '}';
  var distance = display ? (-this.getMenuLeftPosition(menu)) : 
    (this.getMenuLeftPosition(menu) + this.menuWidth);
  var duration = (distance * 500) / this.menuWidth ;
  return {'animation' : animation, 'duration' : duration};
}
Then the animation definition and the animation duration are used for adding the animation to the menu in the addAnimationToMenu function. This function is in charge of finding the animation defined in the CSS (see above) and replacing it by the new definition computed in the getMenuDefinitionDisplay function. When the animation definition has been replaced in the CSS, this animation is attached to the menu element.
Finally a callback is attached to the menu on the webkitAnimationEnd event. This function is menuDisplayed and is in charge of setting the correct left property to the menu div element when the animation ends (otherwise the left property of the menu will be set again to the value at the beginning of the animation).
addAnimationToMenu : function(menu, rule, time, display) {
  if( document.styleSheets && document.styleSheets.length) {
    var i = 0;
    var j = 0;
    var found = false;
    var currentStyleSheet = null;
    while ((i < document.styleSheets.length) && !found) {
      currentStyleSheet = document.styleSheets[i];
      if (currentStyleSheet.rules && currentStyleSheet.rules.length) {
        j = 0;
        while ((j < currentStyleSheet.rules.length) && !found) {
          if (currentStyleSheet.rules[j].name === 
              "displayMenuAccount") {
            found = true;
          }
          j++;
        }
      }
      i++;
    }
    if (found) {
      currentStyleSheet.deleteRule(j-1);
      currentStyleSheet.insertRule(rule, j-1);
    }
    else {
      document.styleSheets[i-1].addRule(rule);
    }
  }
  else {
    var s = document.createElement( 'style' );
    document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
    s.innerHTML = rule;
  }
  menu.addEventListener("webkitAnimationEnd", 
                        this.menuDisplayed.bind(this, 
                             {'display' : display}), false);
  menu.style.webkitAnimation = 'displayMenuAccount ' + 
                                time + 'ms linear 1';
},

menuDisplayed : function(args, evt) {
  var menu = document.getElementById('menu');
  menu.style.left = (args.display ? 0 : -this.menuWidth) + 'px';
  menu.style.webkitAnimation = '';
}


PART 6: Getting a working example


You can find a working example in my github repository (the slidingpanelmenu folder).