I have been reading a few tutorials on HTML5′s canvas tag, and decided to give it a whirl. Rather than bunch everything together in a huge javascript file, like most of the examples I read, I wanted to split files into sensible chunks of code. The RequireJS project has crossed my path on more than one occasion, so I figured it might be a good thing to try. I figured correctly, as it was super easy to set up, and works swimmingly.
There are plenty of greatresourcesoutthere to learn all about modular javascript, as well as an interestingdebate on the proper method of defining modules, but the RequireJS site itself has enough info to get a basic project off the ground. I had to do a bit of experimenting on my own to clear things up in my head, and thought I would post the results here.
The project basically draws a canvas that takes up the entire browser window (and redraws itself when the window is resized). A white dot is drawn on the screen and floats towards the bottom-right corner until the user swipes a finger (using a mobile device) or the mouse pointer (on a lap/desktop). The dot will switch directions to match the swipe. Try it out!. I had intended to get the dot to do more interesting things, but time gets away from me.
The folder structure holds three files in the root, and the rest of the folder structure looks like this:
Notice that the only file in the root is index.html. Also notice that the only javascript file referenced in index.html is src/require.js, which has a data-main attribute stuck in there, as well. That data-main attribute references the entry point to the application. RequireJS will automatically append ‘.js’ to the filename, and will run whatever is in that file.
This bit of code tells RequireJS what’s what. The baseUrl is a relative filepath that will act as the root for all other file paths referenced in the config block. The paths object is a list of aliases (shortcuts) to other subdirectories that contain code. The key (lib) is the alias name, and the value (‘../assets/lib’) is the file path relative to the baseUrl.
The shim object allows you to define modules that originally weren’t intended to be used with RequireJS. In this case, the Hammer library is referenced, and will export a ‘Hammer’ module that can be used in the project.
Underneath the config block is a function that acts as the main starting point of the application. The first two lines are the most important, in terms of RequireJS.
This is basically a function call that says, “Hey, RequireJS!, do this! And don’t forget to load these three modules before you do it!”
The three modules in question are Stage, Mover, and Vector2D. The first parameter of the requirejs() function is an array of modules that are needed in the function that is passed in as the second parameter. RequireJS will look up the modules, and pass them into the anonymous function, using whatever names you define.
Each of the modules used here are set up to be specifically compatible with RequireJS using a function called define(). The first line of each module is very similar to that of catToy.js:
define(function(){
...// code goes here });
Yeah, that’s it. Just wrap your code in the define() function and it can be used as a RequireJS module. If the module needs to use code from another module, then pass in an array of module names, just like before.
define(['lib/Hammer'],function(Hammer){
...// code goes here });
Well here it is, folks, the moment I’ve been waiting for :) Real-time updates that actually work! Socket.IO has been integrated to work its magic alongside CodeIgniter. Now when a user is logged in, updates will tumble in like an avalanche with absolutely no refreshes or any user intervention at all! To catch up on what this is all about, be sure to check out A Sample CodeIgniter Application with Login And Session and Use Redis instead of MySQL for CodeIgniter Session Data before reading this.
Take a look at the video above to see the live updates in action. There are three different browser windows open (Chrome, Firefox, and IE9), each logged in with a different user. The top left browser is the Admin, which will get updated when any user posts a message. The Admin is also a member of team one, so when the admin posts a message, members of team one (bottom left) will see it. The browser on the right is team two, so she will not see anyone else’s posts, but the Admin will see what she posts.
* The demo may or may not be working depending on the state of my VPS. Most of my sample projects so far have been hosted on my shared hosting account, but due to the Redis and Node requirement I had to deploy this project to a VPS that I normally use for development and testing. If I am doing some development or testing, then Apache or Node or Redis might not be working properly – hence the video. Your best option really, is to download the code and try it yourself!
The Socket.IO library makes it painfully easy to work with NodeJS (aka, Node). Prior to this project I knew almost nothing about Node other than what had read in a few shortbooks. I still don’t know much about Node, but I know that I like it and will continue to keep investigating it for future projects. One thing in particular that I think pretty cool in this project, is that all of the Node specific functionality (the real-time message updates) runs mostly parallel to the PHP application. So if Node decides to blow up, the application will still work, only without live updates.
Anyway, enough jibber jabber. Here’s the rundown on the changes to the application, and the highlights of the Socket.IO and Node code. Again, this is not meant to be a tutorial, but rather a show and tell and perhaps a nice piece of code for others to experiment with. Use at your own risk.
Installing Socket.IO
First things first: Install Socket.IO. I already had Node and NPM installed, but I needed a spot to create the node ‘server’ within my project. I created a folder in the project root called ‘nodejs’. Through the wonders of Node Package Management (NPM), I installed the socket.io package, as well as the node_redis package. This was done by simply navigating to the nodejs folder and running:
npm install socket.io
npm install redis
Yeah, that’s it. NPM will create a folder called ‘node_modules’ and download all the dependencies necessary to run the packages. After that I created the cisockServer.js file, and I was off to the races.
To get things working right away, I added code similar to the following, just to get things rolling. The first line instantiates Socket.IO and gets a Node server up and running on port 8080. An event handler is created for the ‘connection’ event, which fires whenever a Socket.IO client connects to the server. When this happens, Socket.IO will emit an event with an identifier of ‘startup’ and attach an object with a message. If the client is listening for the ‘startup’ event, it will receive the message.
var io = require('socket.io').listen(8080);
io.sockets.on('connection',function(socket){ // Let everyone know it's working
socket.emit('startup',{ message:'I Am Working!!'}); });
To actually get the Node server fired up, it’s as easy as node --debug cisocketServer.js to get it going. I added the –debug option because I was using node-inspector for debugging and tracing. There is also an interesting project called Forever available from NodeJitsu that manages one or more Node processes. I use it on my VPS. It’s nice.
The Socket.IO Client
A server is all fine-and-dandy, but it won’t do much good without clients. I created a new javascript file in my CodeIgniter assets folder called ‘socket.js’ to hold all of my Socket.IO related client code. I wrapped most of the code in the MY_Socket namespace so it is more easily accessed by the javascript in main.js. The minimum amount of code needed to work with the server code above is what follows. It simply creates a socket connection, then listens for the ‘startup’ event from the socket connection. When the ‘startup’ event occurs, the attached message will be displayed on the console.
$(function(){
window.MY_Socket={
// Instantiate the Socket.IO client and connect to the server
socket : io.connect('http://localhost:8080'),
// Set up the initial event handlers for the Socket.IO client
bindEvents :function(){ this.socket.on('startup',MY_Socket.startupMessage); },
// This just indicates that a Socket.IO connection has begun.
startupMessage :function(data){
console.log(data.message); } }// end window.MY_Socket
// Start it up!
MY_Socket.bindEvents(); });
To get this working, all that is needed are a couple more lines in header.php:
Now upon visiting the login screen (or any screen, really), the words, “I am working!” will appear in the console.
Joining A Room
Socket.IO has a concept called rooms, which is a nice way to segment broadcasted messages from a server so only a subset of users receive the message. In this application, users will join a room based on their team numbers. Team 1 will join room 1, and so on. The exception here are admins. Admin users can be on a team, but will receive messages from all users regardless of their team. To handle this, I created another room called ‘admin’.
The room joining process starts when a user is logged in. I added a bit of jQuery code to check the current page for team badges, and if any are found, run the joinRoom function. Another way to do this would be to just put a call to MY_Socket.joinRoom() at the top of /application/views/main.php so it runs whenever the main view is loaded.
So the joinRoom function does some interesting things. First it grabs the cookie named, “ci_session” and reads the value. This value is the session ID set by CodeIgniter. This ID will be used to look up some of the other session information stored in Redis by the application’s MY_Session class. When the session ID is obtained, a ‘joinRoom’ event is emitted with the ID attached. If no session ID is found, then nothing happens. The code below is part of the client code in the socket.js file.
joinRoom :function(){ // get the CodeIgniter sessionID from the cookie var sessionId = readCookie('ci_session');
if(sessionId){ // Send the sessionID to the Node server in an effort to join a 'room'
MY_Socket.socket.emit('joinRoom',sessionId); }else{ // If no sessionID exists, don't try to join a room.
console.log('No session id found. Broadcast disabled.'); //forward to logout url? } }
Socket.IO will be listening for the ‘joinRoom’ event on the server. When it hears the event, it will grab the session ID, use it to construct a string that matches the corresponding session key in the Redis database, and get the data associated with that key. The results returned from Redis will contain the rest of the user’s session information, including teamId and isAdmin (indicating if the user is or is not an admin). The result is parsed into a JSON, and the teamId and isAdmin values are used to join the appropriate ‘rooms’.
For any of this to work, a Redis client must be set up to execute Redis commands. The following code is in cisockServer.js.
// Start up a Node server with Socket.IO var io = require('socket.io').listen(8080);
// Let Node know that you want to use Redis var redis = require('redis');
// Listen for the client connection event
io.sockets.on('connection',function(socket){ // Instantiate a Redis client that can issue Redis commands. var rClient = redis.createClient();
// Handle a request to join a room from the client // sessionId should match the Session ID assigned by CodeIgniter
socket.on('joinRoom',function(sessionId){ var parsedRes, team, isAdmin;
// Use the redis client to get all session data for the user
rClient.get('sessions:'+sessionId,function(err,res){
console.log(res);
parsedRes = JSON.parse(res);
team = parsedRes.teamId;
isAdmin = parsedRes.isAdmin;
// Join a room that matches the user's teamId
console.log('Joining room '+ team.toString());
socket.join(team.toString());
// Join the 'admin' room if user is an admin if(isAdmin){
console.log('Joining room for Admins');
socket.join('admin'); } });
}); });
Excellent.
Send and Receive Messages
When a user posts a new message, the data is sent to the web server using jQuery’s Ajax function. This happens in the App.postMessage function in the main.js file. If the post is successful, a callback function – App.successfulPost – is executed. In order for the post to be successful, it needs to be processed by the CodeIgniter controller method responsible for saving posts to the database. This method – main.post_message – had to be refactored so that it would not only save the message, but also respond to jQuery’s ajax request with the message wrapped up in the HTML template so it can be sent out to other users.
The HTML template responsible for rendering each individual message was separated out into its own view and saved as /application/views/single_posts.php. It was basically just cut and pasted from the main view.
In order to populate that template, CodeIgniter’s Loader.view method was used with the third parameter set to ‘true’ so it will return data instead of immediately rendering the view in the browser. The view is then loaded into the response data as a string, along with the user’s teamId value, and the HTML string that will be prepended to the user’s own message list. The following code is from /application/controller/main.php (the Main controller).
function post_message() {
... save message to db code ...
if ( isset($saved) && $saved ) {
// Gather up data to fill the message template
$post_data = array();
$post_data = $this->user_m->fill_session_data($post_data);
$post_data['body'] = $saved['body'];
$post_data['createdDate'] = $saved['createdDate'];
// Create a message html partial from the 'single_post' template and $post_data
$broadcastMessage = $this->load->view('single_post',$post_data,true);
// Create an html snipped for the user's message table.
$myMessage = "<tr><td>". $saved['body'] ."</td><td>". $saved['createdDate'] ."</td></tr>";
// Create some data to return to the client.
$output = array('myMessage'=>$myMessage,
'broadcastMessage'=>$broadcastMessage,
'team'=>$post_data['teamId']);
// Encode the data into JSON
$this->output->set_content_type('application/json');
$output = json_encode($output);
// Send the data back to the client
$this->output->set_output($output);
}
}
The response object is sent back to the jQuery callback function, and it begins the process of broadcasting the message out to all the appropriate users. This really only takes one extra line of code in App.successfulPost.
All this does is send two pieces of information to the MY_Socket.sendNewPost function. The MY_Socket.sendNewPost function will simply take the message and teamId value and send it to the Node server by emitting a Socket.IO event.
When the ‘newPost’ event is handled on the server, it will relay the message to the appropriate team room, and also to the ‘admin’ room.
socket.on('newPost',function(post,team,sessionId){
console.log('Broadcasting a post to team: '+ team.toString());
// Broadcast the message to the sender's team var broadcastData ={message: post, team: team};
socket.broadcast.to(team.toString()).emit('broadcastNewPost',broadcastData);
// Broadcast the message to all admins
broadcastData.team='admin';
socket.broadcast.to('admin').emit('broadcastNewPost',broadcastData); });
The ‘broadcastNewPost’ event is emitted twice, and will therefore be handled twice by the client. This is not normally a problem, unless there is an admin with the same teamId as the sender. Then the admin will receive the message twice, and duplicate messages will be displayed on the screen. To correct this, a little logic prevents the message from being displayed twice. The message attached to the ‘broadcastData’ object is forwarded to the App.showBroadcastedMessage function.
// on 'broadcastNewPost' update the message list from other users
updateMessages :function(data){ // Because the message is broadcasted twice (once for the team, again for the admins) // we need to make sure it is only displayed once if the Admin is also on the same // team as the sender. if((!userIsAnAdmin()&& data.team!='admin')|| ( userIsAnAdmin()&& data.team==='admin')){ // Send the html partial with the new message over to the jQuery function that will display it.
App.showBroadcastedMessage(data.message); } }
When the App.showBroadcastedMessage function receives the message, it appends it to the top of the list of messages from other users using simple jQuery.
In my effort to add awesome real-time live updates to a plain ol’ CodeIgniter application, I decided to move the session information usually stored in a database table to a key-value store – namely, Redis. Not only does this alleviate some load from the MySQL database, but it also provides an easy way to expose user session data to a NodeJS server. This will be important in the future when adding Socket.IO.
The process for converting my existing application to use Redis rather than MySQL was painfully simple. It was pretty much just a handful of step-by-step instructions via the command line, and voila! PHP and Redis! BFFs 4Ever!
Here were the basic steps I followed:
1. Install Redis
Simply follow the instructions on in the Redis quickstart guide. These instructions were written for linux, and should work on most distributions. It might work on a Mac, but I’m not sure. Windows… you’re on your own. Below is the tl;dr version with just the commands.
wget http://download.redis.io/redis-stable.tar.gz tar xvzf redis-stable.tar.gz cd redis-stable make
cd src sudocp redis-server /usr/local/bin/ sudocp redis-cli /usr/local/bin/
Edit the configuration file, making sure to perform the following changes:
Set daemonize to yes (by default it is set to no).
Set the pidfile to /var/run/redis_6379.pid (modify the port if needed).
Change the port accordingly. In our example it is not needed as the default port is already 6379.
Set your preferred loglevel.
Set the logfile to /var/log/redis_6379.log
Set the dir to /var/redis/6379 (very important step!)
PHP needs an extension to talk to Redis. There are a few options for this listed on the Redis site. I went with phpredis by Nicolas Favre-Felix mainly because it is required for the My_Session class used below. It’s also a great project that is still updated frequently.
To install, follow the directions in the README for your system. The default directions worked just fine for me.
Then add extension=redis.so to /etc/php5/apache2/php.ini and /etc/php5/cli/php.ini (if you are using Apache on linux). Finally, restart Apache.
Done, son.
3. Configure CodeIgniter to use Redis for session data storage
Finally, we just drop one class into the application’s library folder, and add a couple of lines to the config file to get everything working.
Really. That’s it. I’m not joking. It’s that easy.
Grab the MY_Session.php class from Ming Zhou’s gist: https://gist.github.com/zhouming/3672207 and drop it into /application/libraries. CodeIgniter will automatically use it as a subclass of Session. Most of the essential functions are overridden by My_Session so that Redis is used to store session data that will be used to authenticate user sessions while the application is running. The only thing left is to add the following two lines to /application/config/config.php:
Obvously you will need to change ‘localhost’ to the IP address of your Redis server if it is on a different machine from your application.
And that’s it! The application is Redis ready!
Note: I’ve added the MY_Session class and some installation notes to the ci_sock project in my GitHub repo, just in case the links become broken or you want an easy reference.
I was assigned to work on a PHP project a few weeks back that utilized the CodeIgniter framework. I’ve used MVC frameworks in the past in other languages, and because CodeIgniter does not deviate too far from common MVC patterns, it was pretty easy to pick up. To get myself up to speed, I put together a sample project based of what I learned from the documentation and a few tutorials. It’s a small microblogging app with very basic user auth and CRUD functionality. The basics of it go like this…
User login screen with basic password authentication.
Each user is assigned a ‘team’ number. Users can only view posts from their teammates.
An admin can view all posts from all users.
Each user has a ‘tagline’.
The tagline can be edited by clicking on it and typing something new (modern browsers only, as it uses ‘contenteditable’)
Hovering over a user’s avatar reveals the username and tagline for that user.
Admins can create new user accounts.
A user’s own messages appear below their profile. This is limited by 5 posts, currently.
Careful, there is not much form validation going on at the moment.
Building it gave me a good feel for the basic mechanics of CodeIgniter and allowed me to brush up on some PHP. The real motivation behind this project, however, is to eventually work in a Socket.IO implementation to allow real time updates for the user. I’ve been eyeing NodeJS for quite some time now, but never really had cause to use it. Fortunately, the project I was working on needed a more robust and scalable ‘real time’ framework to replace a rudimentary long-polling system. Socket.IO would have been a pretty good solution, in my opinion. Unfortunately, the project was cancelled before I could get started. But since I’ve already got the ball rolling on this sample application, I figure I might as well finish, and learn a few things in case a situation like this arises again. You never know…
The Setup
The front end of the application uses Twitter Bootstrap for styling and layout, jQuery for client-side interactivity, PHP and CodeIgniter for most of the functionality, and MySQL for data storage. This is a fairly common toolset that runs on most L/W/M/AMP-style environment stacks. The code available on GitHub has everything you need to run the app yourself, provided you have a web server, MySQL, and PHP 5.3 or greater.
You’ll need to create a database (preferably called ‘cisock’), and then import cisock.sql in the root of the ‘partOne’ folder in the repository. You can do this with the following command from the command line (be sure to change ‘yourusername’ and ‘/path/to/’ to match your local setup):
mysql -u yourusername -p-h localhost cisock </path/to/cisock.sql
Once you’ve gotten the code checked out into your web root and the SQL imported, you’ll need to do some slight configuration before getting started. In ‘part_one/application/config/config.php’ you will need to change line 17 to reflect your local URL. If you simply cloned the project directly into your ‘localhost’ web root, then no changes will likely be needed. A similar fix is necessary in ‘part_one/assets/js/main.js’ line 6. The context root of your application goes here. Again, if you cloned to your web root, the default value should be fine. Ideally, the context root should only have to be configured in one place, but it is what it is for now.
Secondly, the settings in ‘part_one/application/config/database.php’ must be set to reflect your local database configuration. The following entries are specific to your local environment:
$db['default']['hostname']='localhost'; $db['default']['username']='yourdatabaseusernamehere'; $db['default']['password']='yourdatabasepasswordhere'; $db['default']['database']='cisock';//or use the database name you chose, if it is different
Once that is done, you should be able to navigate your browser to the /part_one/ directory and see the login screen.
To recap:
Clone the repo
Set up the database and import the tables from cisock.sql
Edit database.php and also config.php and main.js if necessary
The Code
I tried to follow suggested CodeIgniter conventions as closely as possible. As such, the file and directory structure is pretty much as it is out of the box. I altered /application/config/routes.php to set the login controller as the entry point for the application like so:
$route['default_controller']="login";
The index method in /application/controllers/login.php checks to see if the user is logged in, and if not will redirect to the login screen by calling the show_login function. The code immediately below is login/index – which will run when application’s url is loaded in the browser.
function index(){ if($this->session->userdata('isLoggedIn')){
redirect('/main/show_main'); }else{ $this->show_login(false); } }
Because the ‘isLoggedIn’ session variable starts out as false, the show_login() function is called. The ‘false’ argument indicates that an error message is not to be shown.
function show_login($show_error=false){ $data['error']=$show_error;
That last line there: $this->load->view('login',$data); is what opens up the login view ( /application/views/login.php ). When the user types in credentials and clicks the ‘sign-in’ button, the login_user function is called through a normal form POST as indicated by this line of code in /application/views/login.php: <?phpecho form_open('login/login_user')?>. The form_open function is part of CodeIgniter’s Form Helper, which generates the form’s HTML for you.
The login/login_user function gives us our first taste of a CodeIgniter model. It loads up an instance of a user model and calls the validate_user method on that model, passing it the email and password typed in by the user. Take a look at the code below for the entire sequence.
function login_user(){ // Create an instance of the user model $this->load->model('user_m');
// Grab the email and password from the form POST $email=$this->input->post('email'); $pass=$this->input->post('password');
//Ensure values exist for email and pass, and validate the user's credentials if($email&&$pass&&$this->user_m->validate_user($email,$pass)){ // If the user is valid, redirect to the main view
redirect('/main/show_main'); }else{ // Otherwise show the login screen with an error message. $this->show_login(true); } }
In the ‘user’ model, a couple of interesting things happen. First, a query is built using CodeIgniter’s ActiveRecord implementation. The username and password entered by the user are compared to the user table in the database to see if the credentials exist. If so, the corresponding record in the database will be retrieved. If that happens, the data retrieved from the database will be used to set session variables using the set_session function of CodeIgniter’s Session class. All the code for this is in /application/model/user_m.php and can be seen below.
var$details;
function validate_user($email,$password){ // Build a query to retrieve the user's details // based on the received username and password $this->db->from('user'); $this->db->where('email',$email); $this->db->where('password',sha1($password)); $login=$this->db->get()->result();
// The results of the query are stored in $login. // If a value exists, then the user account exists and is validated if(is_array($login)&&count($login)==1){ // Set the users details into the $details property of this class $this->details=$login[0]; // Call set_session to set the user's session vars via CodeIgniter $this->set_session(); returntrue; }
returnfalse; }
function set_session(){ // session->set_userdata is a CodeIgniter function that // stores data in a cookie in the user's browser. Some of the values are built in // to CodeIgniter, others are added (like the IP address). See CodeIgniter's documentation for details. $this->session->set_userdata(array( 'id'=>$this->details->id, 'name'=>$this->details->firstName.' '.$this->details->lastName, 'email'=>$this->details->email, 'avatar'=>$this->details->avatar, 'tagline'=>$this->details->tagline, 'isAdmin'=>$this->details->isAdmin, 'teamId'=>$this->details->teamId, 'isLoggedIn'=>true ) ); }
So now that the user is authenticated and their session info is set in a cookie, CodeIgniter will take an extra step and store the user’s IP address, session ID, user agent string and last activity timestamp in the database. So when the user logs out, closes the browser, or is idle for too long, the session will expire and be cleared from the database. If someone with a cookie containing the same session ID then tries to connect to the application, it will be invalid because it won’t match any of the sessions stored in the database. Check out the Session class documentation for a more thorough explanation.
So now that the user is logged in, the ‘main’ controller can do its thing. The show_main function runs just before loading the main view, and does a number of things to prepare for displaying the view to the user. The user’s details are used to change certain parts of the view, such as which posts to display (team only, or everyone) and the admin controls.
The show_main function grabs some data from the user’s session, retrieves all the user’s posted messages, counts the total number of messages posted by the user, and retrieves the messages from other users. All of this info is placed into the $data object and passed to the ‘main’ view (/application/views/main.php). Much of the heavy lifting is taken care of by ActiveRecord commands in the Post model (/application/model/post_m).
That’s about it, as far as logging in goes. Now that everything is set up using conventional CodeIgniter practices, I can begin the process of converting the server side session data to be stored in Redis, rather than in a MySQL table…
The most prominent feature, by far, of AngularJS SignIt! is the signature pad. It’s a fixed-size canvas that can be drawn upon with a mouse pointer or finger. The code for this wonderful widget is provided as a jQuery plugin by Thomas Bradley. There are a few more features for the signature pad than I use in this app, and I encourage you to check out the full documentation if you get a chance. It’s pretty great.
In order to implement the signature pad into my form, It must be a required field, and have the data included with the other fields when the form is submitted. The sig pad works by creating objects of x/y coordinates that correspond to marks on the canvas. When drawing on the pad, all that data gets set into a hidden form field. The data from that field is what needs to get into the Angular scope, and get validated by Angular’s form validation routine. Oh, and Angular somehow needs to fire the custom validation function built into the signature pad.
Luckily, this is the type of thing directives were built for. Custom directives let you create custom html tags that get parsed and rendered by Angular. So to start out, I created a custom directive called sigpad that is used by placing <sigpad></sigpad> in my HTML. The sigpad tag then gets replaced by the directive’s template HTML. The template for the HTML5 signature pad is just the default snippet taken directly from the signature pad documentation. See below:
The logic for the directive is defined in a separate Angular module (see directives.js for full code). Take a look at the code below, and be sure to review Angular’s documentation on directives. The Angular docs give a great conceptual overview, but the examples are a bit lacking. I reviewed the docs many, many times, and still needed some help from the mailing list to get this working correctly (thanks P.B. Darwin!).
My biggest stumbling block was not using ngModelController, and instead trying to manipulate scope data directly. When working with a form, Angular provides all sorts of awesome code to work with data and do some validation. Basically, it all went down like this…
Now Angular knows to replace the sigpad element with the template defined earlier. After this happens, Angular runs the linking function which contains all the logic for the directive. The little snippet shown below is in the linking function. It uses jQuery to select the template element (passed into the linking function as ‘element’) and runs the signaturePad function from the signature pad API. This is what creates the actual, drawable canvas.
var sigPadAPI = $(element).signaturePad({
drawOnly:true,
lineColour:'#FFF' });
The ng-model=’user.signature’ bit is key. This is how data is shared between the signature pad and Angular. Also, go back up and look at the template. You will see ng-mouseup="updateModel() as an attribute of the canvas element. This tells Angular to run the updateModel() function when your mouse click ends on the signature pad. The updateModel() function is defined in the linking function of the sigpad directive.
When the updateModel() function is executed, it will wait for a split second so the signature pad can finish writing data to its hidden form field, then it will assign all that data to the Angular model value of the directive. Sounds confusing, and it is. The signature pad is off doing its own thing, completely oblivious to the fact that it is in an AngularJS app. It is Angular’s responsibility to grab data from the sigpad to get that data into its own scope. That is what $setViewValue is for. It hands the signature data over to Angular, so when the form is submitted, Angular has it available in scope.
Below is the entire directive for the drawable signature pad. You can see that it relies heavily on the signature pad API, but only after certain events handled by Angular have occurred.
.directive('sigpad',function($timeout){ return{
templateUrl:'views/sigPad.html',// Use a template in an external file
restrict:'E',// Must use <sigpad> element to invoke directive
scope :true,// Create a new scope for the directive
require:'ngModel',// Require the ngModel controller for the linking function
link:function(scope,element,attr,ctrl){
// Attach the Signature Pad plugin to the template and keep a reference to the signature pad as 'sigPadAPI' var sigPadAPI = $(element).signaturePad({
drawOnly:true,
lineColour:'#FFF' });
// Clear the canvas when the 'clear' button is clicked
$(attr.clearbtn).on('click',function(e){
sigPadAPI.clearCanvas(); });
// when the mouse is lifted from the canvas, set the signature pad data as the model value
scope.updateModel=function(){
$timeout(function(){
ctrl.$setViewValue(sigPadAPI.getSignature()); }); };
// Render the signature data when the model has data. Otherwise clear the canvas.
ctrl.$render =function(){ if( ctrl.$viewValue ){
sigPadAPI.regenerate(ctrl.$viewValue); }else{ // This occurs when signatureData is set to null in the main controller
sigPadAPI.clearCanvas(); } };
// Validate signature pad. // See http://docs.angularjs.org/guide/forms for more detail on how this works.
ctrl.$parsers.unshift(function(viewValue){ if( sigPadAPI.validateForm()){
ctrl.$setValidity('sigpad',true); return viewValue; }else{
ctrl.$setValidity('sigpad',false); return undefined; } }); } }; })
And what about the tiny signatures that show up in the signatories list? Also a custom directive. This one is smaller, but still tricky. The signature is displayed as an image on-screen, but a canvas element is still required to generate the signature from raw data before it can be converted to an image.
The directive is implemented with <regensigpad sigdata={{signed.get('signature')}}></regensigpad>. The ‘signed’ value is a single signature in the signature collection pulled from the back-end when the user picks a petition. the signature data from signed is passed into the directive scope using scope:{sigdata:'@'}.
When a list of signatures is retrieved, each signature record (including first & last name, email, and signature data) goes into a table row using ngRepeat. The regensigpad directive is executed for each row. The linking function will create a canvas element and make a displayOnly signature pad from it. The signature drawing is regenerated from the data, and then the canvas is converted to PNG format.
This PNG data is then used in the pic scope value, which is bound to the ng-src of an img tag. This img tag is the directive’s template, and will be inserted into the page. The full code for this directive is below.
.directive('regensigpad',function(){ return{
template:'<img ng-src="{{pic}}" />',
restrict:'E',
scope:{sigdata:'@'},
link:function(scope,element,attr,ctrl){ // When the sigdata attribute changes...
attr.$observe('sigdata',function(val){ // ... create a blank canvas template and attach the signature pad plugin var sigPadAPI = $('<div class="sig sigWrapper"><canvas class="pad" width="436" height="120"></canvas></div>').signaturePad({
displayOnly:true }); // regenerate the signature onto the canvas
sigPadAPI.regenerate(val); // convert the canvas to a PNG (Newer versions of Chrome, FF, and Safari only.)
scope.pic= sigPadAPI.getSignatureImage(); }); } }; });
But that’s not all! You might have noticed that the select box holding the names of each petition looks kinda fancy, and allows you to type stuff to filter the list. This fancy form control is the select2 widget which is based of the Chosen library.
I didn’t have to write my own directive for it though. The Angular-UI project has already done the honors. Angular-UI is an open-source companion suite for AngularJS. It provides a whole pile of custom directives, and even a few extra filters. Many of the directives are wrappers for other widgets and open source projects, like CodeMirror, Google Maps, Twitter Bootstrap modal windows, and many more. It’s definitely worth looking into for any AngularJS project.
The AngularJS SignIt! application basically has three different interactions with a web service – fetch petitions, save a signature, and fetch a list of signatures based on the selected petition. That’s it – a get, a save, and a query. Initially, I was only using Parse.com to store data, so it was possible to include Parse specific objects and methods in my controller to save and get data.
But then I remembered I have a StackMob account just sitting around doing nothing, and thought I should put it to good use. So now I have two (slightly) different options to store my signatures. Rather than jumbling up my controller with code specific to StackMob and Parse, I created a module to abstract the Parse and StackMob APIs into their own services. These services could then hide any code specific to Parse or StacMob behind a common interface used by the controller.
With the back-end(s) abstracted, all the controller needs to worry about is calling saveSignature, getPetitions, and getSignatures. Below is a severely truncated version of the Main Controller that shows the three methods in use. Notice there is no mention of Parse or StackMob.
var MainCtrl = ngSignItApp.controller('MainCtrl',function($scope,DataService){
// GET A LIST OF SIGNATURES FOR A PETITION
$scope.getSignatures=function getSignatures (petitionId){
DataService.getSignatures(petitionId,function(results){
$scope.$apply(function(){
$scope.signatureList= results; }); }); };
// SAVE A SIGNATURE
$scope.saveSignature=function saveSignature(){
DataService.saveSignature($scope.user,function(){//user is an object with firstName, lastName, email and signature attributes.
$scope.getSignatures($scope.select2);//select2 is the value from the petition dropdown }); };
// GET ALL PETITIONS
DataService.getPetitions(function(results){
$scope.$apply(function(){
$scope.petitionCollection= results;
$scope.petitions= results.models; }); });
});
If you look closely, you’ll see that each service method is prefixed with DataService. This is the injectable that provides either the StackMob service, or Parse service to the controller. Each of those services has an implementation of the getSignatures, saveSignature, and getPetitions. Take a look:
angular.module('DataServices',[]) // PARSE SERVICE
.factory('ParseService',function(){
Parse.initialize("<PLEASE USE YOUR OWN APP KEY>","<PLEASE USE YOUR OWN API KEY>"); var Signature = Parse.Object.extend("signature"); var SignatureCollection = Parse.Collection.extend({ model: Signature }); var Petition = Parse.Object.extend("petition"); var PetitionCollection = Parse.Collection.extend({ model: Petition });
var ParseService ={
// GET ALL PETITIONS
getPetitions :function getPetitions(callback){ var petitions =new PetitionCollection();
petitions.fetch({
success:function(results){
callback(petitions); } }); },
// SAVE A SIGNATURE
saveSignature :function saveSignature(data, callback){ var sig =new Signature();
sig.save( data,{
success:function(obj){callback(obj);} }); },
// GET A LIST OF SIGNATURES FOR A PETITION
getSignatures :function getSignatures(petitionId, callback){ var query =new Parse.Query(Signature);
query.equalTo("petitionId", petitionId);
query.find({
success:function(results){
callback(results); } }); }
};
return ParseService; }) // STACKMOB SERVICE
.factory('StackMobService',function(){ // Init the StackMob API. This information is provided by the StackMob app dashboard
StackMob.init({
appName:"ngsignit",
clientSubdomain:"<PLEASE USE YOUR OWN SUBDOMAIN>",
publicKey:"<PLEASE USE YOUR OWN PUBLICKEY>",
apiVersion:0 });
var Signature = StackMob.Model.extend({schemaName:"signature"}); var SignatureCollection = StackMob.Collection.extend({ model: Signature }); var Petition = StackMob.Model.extend({schemaName:"petition"}); var PetitionCollection = StackMob.Collection.extend({ model: Petition });
saveSignature :function saveSignature(data, callback){ var sigToSave =new Signature();
sigToSave.set({
firstname: data.firstName,
lastname: data.lastName,
petitionid: data.petitionId,
email: data.email,
signature: JSON.stringify(data.signature)//Also, StackMob does not allow arrays of objects, so we need to stringify the signature data and save it to a 'String' data field. });
// Then save, as usual.
sigToSave.save({},{
success:function(result){
callback(result); },
error:function(obj, error){ alert("Error: "+ error.message); } }); },
getSignatures :function getSignatures(petitionId, callback){ var signatures =new SignatureCollection(); var q =new StackMob.Collection.Query(); var signatureArray =[];
}; // The factory function returns StackMobService, which is injected into controllers. return StackMobService; })
This is an abridged version of the DataServices module. To see the full code, as well as many more comments explaining the code, head over to GitHub. The main point to observe here is that each service has slightly different code for getSignatures, getPetitions, and saveSignature. Also, each service has its own initialization code for its respective back-end service. The controller could care less though, because as long as the service methods accept and provide data in the right format, it’s happy.
But how does the controller know which service to use? Well, if you paid attention to the controller code, you’ll see that ‘DataService’ is injected, which is not defined yet. In the full code, there is a service defined all the way at the bottom of the file. It looks like this:
All the other services (ParseService, StackMobService, and BackboneService) are injected into this service. In case you are wondering, BackboneService is yet another back-end service that can be used in place of the others – see the full code for details. The code above simply examines the URL and decides which service get injected via DataService. If ‘parse’ appears as the url path (e.g. www.example.com/app/#/parse), then ParseService is returned. StackMob requires that HTML apps be hosted on their servers, so just check the domain name for ‘stackmob’ and return the StackMob service. If neither of these conditions occur, then the BackboneService is returned, and no data is saved.
In retrospect, I think what I’ve got here is the beginnings of an OO-like interface – where a set of functions are defined to form a contract with the client ensuring their existence. And the implementations of that interface are a set of adapters (or is it proxies?). If I had to do this over, I would use a proper inheritance pattern with DataService as the abstract parent, and the other services as the implementations or subclasses. One of these days I’ll get around to refactoring. One day…
After finishing my first experimental project in AngularJS, I wanted to try saving data, rather than just retrieving it. Angular has some great mechanisms for creating forms, so I put together a small application to test out more of what Angular has to offer. Oh, and I also wanted to build this using Yeoman. More on that in a minute.
The app I ended up with is a petition signer. A user chooses a petition from the select-box, enters a first name, last name and email into the form, and then signs the signature pad either by drawing with the mouse, or using a finger on a touchscreen. Hitting the Save button will capture the information, and add it to the list of signatories for the selected petition. Pretty simple, no?
The really fun part comes when the data is saved. Depending on the url, the application will choose a place to save the data. If ‘parse’ shows up in the url path, then Parse.com is used as the back-end. If the application is hosted on StackMob’s server, then data is stored with StackMob.com. If neither of these things occur, then data is just saved to a temporary Backbone collection and cleared out when you refresh the page.
Please note that a modern browser is needed. This will not work on IE8 or lower. I have only tested it in the latest Chrome, Firefox 15, and Safari on iOS 5. YMMV with other browsers. If I find the time, I’ll add in the fixes for IE later on.
Aside from AngularJS, Parse, and StackMob, I threw in plenty of other nifty libraries and snippets to get this thing working and looking (kinda) nice.
The select2 component, wrapped in a directive provided by Angular-UI. Angular-UI is a great add-on library for AngularJS that provides some neat widgets and additional functionality to directives.
A font-face generated from fontsquirrel.com using Estrya’s Handwriting font.
Stack of paper CSS3 snippet from CSS-Tricks.com
And, of course, Yeoman was involved. There is tons of functionality built into Yeoman for handling unit-tests, live compilation of SASS and CoffeeScript, dependency management, and other stuff. I basically just used it for it’s AngularJS scaffolding, and to download some libraries via Bower. It went something like this:
Then I went about building my application as usual. Upon completion, I ran
yeoman build
to package everything that is needed into the ‘dist’ folder, and plopped that on my web server. Done.
// TODO
In upcoming blog posts, I’ll go over some of the more interesting bits of code – namely the services module and custom directives. There are a fair number of comments in the code now, but I think a higher-level overview would add some value. Stay tuned.
I recently bought a Mini PC. It’s one of those ‘Android on a stick‘ devices from China, that cost anywhere from $50 – $90. This one in particular is a Rikomagic MK802 II. It’s got an Allwinner A10 System on a Chip (ARM Cortex-A8, Mali400 Gfx), 1GB DDR3, and a 4GB of onboard storage with Android 4.0 pre-installed. It came with some USB adapters and a nice box, all for $55 shipped. The best part is, you can slip in a micro SD card with an ARM compatible Linux image, and it will boot!
Linaro 12.07
Miniand.com has several SD card images ready to download for this particular device, and I settled on a custom build of Linaro. It’s an Ubuntu 12.04 derivative with support for ARM devices. The custom build on Miniand has Mali 400 gfx drivers, and apparently a bunch of ARM related kernel optimizations and drivers for the MK802.
It was super easy to install – just download, unzip, and write the image to the card using a single dd command.
File: linaro-alip-armhf-t4.7z
Commands:
With the MK802′s primary function being a web development sever, my first task after getting the OS up and running was installing Yeoman. Linaro includes apt-get, so the installation process was mostly identical to what I have outlined in this earlier post. There were a couple key differences, though, that nearly derailed the installation.
Because most default build configs are set up for x86 systems, I had to take an alternate route with Git, NodeJS and PhantomJS due to the ARM based architecture of the MK802. For Git and Phantom, I went with the pre-compiled packages available in the apt repository. I won’t get bleeding edge versions, but from what I can tell so far, the repo versions work just fine.
Except Node. The apt repository installs NodeJS 0.6, and does not make 0.8 available. Problem is, Yeoman requires 0.8, and the default NodeJS 0.8.11 build instructions fail on the MK802. Luckily, there’s a pretty solid community of very-tiny-pc enthusiasts, and I was able to find a solution on Google Groups. All that’s needed is adding a couple lines to a file before building.
Compiling NodeJS took about an hour on the MK802. I think there were some additional arguments for the make command, but unfortunately I didn’t write those down. They probably weren’t that important anyway. Sorry :P
Cloud9
Here’s where things got really hairy. Cloud9 just did not want to cooperate. In retrospect, it really only came down to one (maybe two) issues, but working through the entire installation took the better part of my Saturday.
It all started with the ‘sm’ module from npm. Apparently ‘sm’ is the smallmint package manager used by Cloud9 to keep its own node packages separate from those of npm. Installing sm via npm went OK, but downloading the Cloud9 source with sm failed. I had to use git instead.
Download and install Cloud9:
git clone https://github.com/ajaxorg/cloud9.git cloud9 cd cloud9
sm install
A ton of scripts and package installations executed, and I eventually ended up with an error like this:
Checking for program g++ or c++ : /usr/bin/g++
Checking for program cpp : /usr/bin/cpp
Checking for program ar : /usr/bin/ar
Checking for program ranlib : /usr/bin/ranlib
Checking for g++ : ok
Checking for node path : not found
Checking for node prefix : ok /usr/local
'configure' finished successfully (0.970s)
Waf: Entering directory `/home/root/o3/build'
[1/3] cxx: hosts/node-o3/sh_node.cc -> build/default/hosts/node-o3/sh_node_1.o
16:01:40 runner system command -> ['/usr/bin/g++', '-g', '-O3', '-msse2', '-ffast-math', '-fPIC', '-DPIC', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_GNU_SOURCE', '-DEV_MULTIPLICITY=0', '-Idefault/include', '-I../include', '-Idefault/hosts', '-I../hosts', '-Idefault/modules', '-I../modules', '-Idefault/deps', '-I../deps', '-I/usr/local/include/node', '../hosts/node-o3/sh_node.cc', '-c', '-o', 'default/hosts/node-o3/sh_node_1.o']
cc1plus: error: unrecognized command line option "-msse2"
Luckily, I discovered a related issue reported on GitHub. The libxml submodule is the offending culprit. The module depends on Ajax.org’s o3 project, which cannot compile on ARM as-is. The problem has something to do with ‘msse2′ not being present on ARM v7 CPUs. I’m not entirely sure what that means, but fortunately, someone has figured out how to modify o3 and libxml so they will build properly.
I checked out a customized fork of libxml into cloud9/node_modules/ and ran the build script (build.sh). Then I re-ran sm install for Cloud9 and everything went off without a hitch.
Thanks to Ian Corbitt’s blog and libxml fork. Below are the useful commands that got things going for me (start from the cloud9 directory):
cd node_modules git clone--recursive git://github.com/iancorbitt/node-libxml.git libxml cd libxml
./build.sh
After the installation was complete, I could start Cloud9 with ./bin/cloud9.sh -w/var/www/myProject. Then firing up a web browser and going to localhost:3131 brought me to the IDE.
Access Cloud9 Remotely
Running a web browser on the MK802 is painfully slow, and I don’t always have physical access to it anyway. I would much rather use my laptop or desktop to run the Cloud9 client. The first time I attempted to open Cloud9 from a remote computer, I was very dissappointed. All I got was a 503 error with no information. Turns out Ubuntu does not allow connections willy-nilly on random ports.
Enter Apache and reverse proxying. I never knew about this before, but I’m glad I ran across it. With a simple VirtualHost file entry, Apache can accept requests for specified domains & ports, and forward them elsewhere. So in my remote browser, I can call the IP address or domain alias of the Cloud9 server over port 80, and Apache will forward that requests to localhost:3131. Great!
All that’s needed are a couple Apache mods enabled
Then in the /etc/hosts file of my remote computer (where I will be using Cloud9), I add the entry: c9.mk802 192.168.1.xxx (where xxx is the last 3 digits of my mk802 IP address).
Opening up Chrome and tapping http://c9.mk802 into the address bar brought up the Cloud9 interface running on my tiny PC in another room. Excellent!
Except I couldn’t type anything or create any new files. Bummer :(
Cloud9 had started in Read-Only mode. After a bunch of trial and error, I finally found some advice stating that local installations of cloud9 would only be fully accessible to a single user via the host specified in cloud9/configs/default.js.
Open default.js and change all instances of “localhost” to “0.0.0.0″. Restart Cloud9, and all should be good and merry. Just make sure that Cloud9 is safely tucked away, as this really is not very secure.
It was pretty satisfying being able to create a fully functional development environment with two commands:
A few months ago I worked through the first 10 problems of Project Euler. I did OK up to problem #10. The question involved finding all the prime numbers up to 2,000,001. There were earlier questions that involved finding primes, and I cobbled together some javascript with the help of underscore.js that got the job done – albiet very slowly. My homespun solution was just too durn slow for problem #10. I turned to the internet, and it provided me a blazing fast solution. See below.
var ten =function sieve(max){ var D =[],
primes =[],
sum =0; for(var q=2; q < max; q++){ if(D[q]){ for(var i=0; i < D[q].length; i++){ var p = D[q][i];
So yeah, I cheated. At the time, I had no idea how this code worked, I just knew that it did. Since then, I’ve occasionally revisited this code to take another shot at figuring out what this mysterious D array is doing with its Ps and Qs. However, trace after trace and many breakpoints later I was still scratching my head.
But just yesterday I stumbled across Python Tutor. This site is simply amazing. These geniuses have developed an interactive online code editor that spits out pictures based on what your code is doing. You even get little playhead controls to step through each line! This wizardry finally allowed me to scratch the itch that’s been bugging me for a while.
I returned to the site where I ‘borrowed’ the JavaScript algorithm for the Sieve of E., and found the Python equivalent. The syntax is a bit different, but it essentially doing the same thing. The code looks like this:
def eratosthenes(maxnum):
D ={}# map composite integers to primes witnessing their compositeness
q =2# first integer to test for primality while q <= maxnum: if q notin D: yield q # not marked composite, must be prime
D[q*q]=[q]# first multiple of q not already marked else: for p in D[q]: # move each witness to its next multiple
D.setdefault(p+q,[]).append(p) del D[q]# no longer need D[q], free memory
q +=1
for p in eratosthenes(19): print p
The comments were helpful in determining that it was doing the same thing as the JS algorithm, but when I plugged this code into Python Tutor, WHAMMO! It hit me like a ton of bricks. D is a dictionary!!! D is where non-prime numbers are stored and created from prime factors.
So we iterate over all the numbers from 1 to whatever, with q as the iterator index. For each value of q, check the dictionary (D). If that value does not exist in D, then the number is prime. For each prime number, find the square and store it in the dictionary for later. So when q = 2, the first entry in D is 4. Then when q = 3, stick 9 in the dictionary. Also, for each dictionary entry created by squaring a prime, attach that prime as a child object.
When q is not prime, examine the dictionary entry. Take the child, add it to the dictionary value, and create a new entry in the dictionary for the sum. So when q = 4, its child value is 2. Take 2 + 4 and get 6. Create a dictionary entry for 6, and store the original prime factor (2) as its child. Then delete the previous dictionary entry (4). Keep this up until q is larger than your target value (2000001).
This part took me the longest to figure out, and it makes a lot more sense by skipping the deletion part. In the Python Tutor example, comment out the line del D[q]. When running the visualizer, the dictionary keeps growing and it is much easier to see how each entry is created, and why each entry is composite rather than prime. For each square value, its root is added over and over again until q reaches its limit. Deleting dictionary entries is not absolutely necessary with low values of q. It’s a memory saver that keeps D from getting out of hand (and crashing Chrome when trying this in JavaScript).
I know I did a sub-par job of explaining this, but seriously, that’s what Python Tutor is for. Try it, you’ll like it.
Yeoman is awesome, but holy jeez are there a lot of requirements to get it fired up for the first time. Listed below are all the commands I typed in to get every last Yeoman dependency installed. I did this on 64-bit Ubuntu. For 32-bit, you’ll need to download a different version of PhantomJS, but otherwise I’m pretty sure everything else works the same.