Monthly Archives: January 2015

Socket.IO AngularJS Wrapper

I’ve been playing around with Socket.IO and AngularJS. To make things easier I created this service which can be used for transferring data between the client (which could be a web page) and a NodeJS server.

The app is served by NodeJS so the Socket.IO server is listening on same location.

'use strict';

angular
.module('APP')
.factory('$WS', function($rootScope) {
    var self = this;

    self.ready = false;

    self.socket = io.connect(location.host, {
        'connect timeout':       100,
        'reconnection delay':    100
    });

    self.socket.on('init', function() {
        console.log('init');
    });

    self.socket.on('disconnect', function() {
        console.log('socket disconnected');
    });

    self.socket.on('connect', function() {
        console.log('socket connected');
    });

    return {
        ready: function(callback) {
            if(self.ready) {
                callback();
                return;
            }

            self.socket.on('connect', function() {
                $rootScope.$apply(function() {
                    self.ready = true;
                    callback.apply(self.socket);
                });
            });
        },
        on: function(eventName, callback) {
            self.socket.on(eventName, function() {
                var args = arguments;

                $rootScope.$apply(function() {
                    callback.apply(self.socket, args);
                });
            });
        },
        emit: function(eventName, data, callback) {
            self.socket.emit(eventName, data, function() {
                var args = arguments;
                $rootScope.$apply(function() {
                    if(callback) {
                        callback.apply(self.socket, args);
                    }
                });
            });
        }
    };
});

Usage:

angular
.module('APP')
.controller('TestController', ['$scope', '$WS', function($scope, $WS) {

    $scope.messages = [];

    // Connect websocket
    $WS.ready(function() {
    
        // Receive message
        $WS.on('chat/message', function(data) {
            
            // Validate input data
            if(data.message_id) {
                console.log(data);
                $scope.messages.push(data);
            }
        });
    });

    // You can add another ready state listener for the same connection
    // Let's consider we have a method which sends messages
    $scope.send_message = function(msg_title, msg_value) {
        
        $WS.ready(function() {
            // Emit message
            $WS.emit('chat/message', {
                title:    msg_title,
                message:  mgs_value
            });
        });
    };

}]);

 

This is just basic usage.  Will be back with NodeJS & Socket.IO server example.

Google Maps JavaScript Wrapper

Some of the projects I’ve implemented required some dynamic or static maps.

Of course most powerful maps library at that time was Google Maps.

The wrapper allows the insertion of markers with custom icon, circles, polylines and setting the center and zoom of the map.

Here’s one version of the JavaScript Google Maps Wrapper that I’ve created:

function GMaps(opt) {
    this.map          = null;

    this.circles      = [];
    this.markers      = [];
    this.polylines    = [];

    this.m_icon_path  = opt.icon_path || null;
    this.m_icon       = null;
    
    this.lat          = opt.lat || 45.8805435;
    this.lng          = opt.lng || 24.9582169;
    this.zoom         = opt.zoom || 7;

    this._init(opt);
}

GMaps.prototype._init = function(opt) { 
    var self = this;

    if(opt == null) {
        opt = {};
    }

    var map_options = {
        zoom:               self.zoom,
        center:             new google.maps.LatLng(self.lat, self.lng),
        mapTypeId:          google.maps.MapTypeId.ROADMAP,
        streetViewControl:  false,
        scaleControl:       false,
        mapTypeControl:     false,
        panControl:         false,
        zoomControlOptions: {
            style: google.maps.ZoomControlStyle.SMALL
        }
    };

    self.map = new google.maps.Map(document.getElementById(opt.element), map_options);

    if(self.icon_path) {
        self.icon = new google.maps.MarkerImage(self.icon_path, null, null, null, new google.maps.Size(20, 26));
    }
};

GMaps.prototype.check_resize = function() {
    var self = this; 
    
    google.maps.event.addListenerOnce(self.map, 'idle', function() {
        google.maps.event.trigger(self.map, 'resize');
        self.map.setCenter(new google.maps.LatLng(self.lat, self.lng));
    });
};

GMaps.prototype.clear_markers = function() {
    for(var i=0; i<this.markers.length; ++i) {
        if(this.markers[i].circle != null) {
            this.markers[i].circle.setMap(null);
        }

        this.markers[i].setMap(null);
    }

    this.markers = [];
};

GMaps.prototype.clear_polylines = function() {
    for(var i=0; i<this.polylines.length; ++i) {
        if(this.polylines.markers != null) {
            for(var y=0; y<this.polylines.markers.length; ++y) {
                this.polylines.markers[y].setMap(null);
            }
        }

        this.polylines[i].setMap(null);
    }

    this.polylines = [];
};

GMaps.prototype.clear_circles = function() {
    for(var i=0; i<this.circles.length; ++i) {
        this.circles[i].setMap(null);
    }

    this.circles = [];
};

GMaps.prototype.reset = function() {
    this.clear_markers();
    this.clear_polylines();
    this.clear_circles();
};

GMaps.prototype.set_icon_path = function(path) {
    this.icon_path = path;

    if(this.icon_path) {
        this.icon = new google.maps.MarkerImage(this.icon_path, null, null, null, new google.maps.Size(71, 71));
    }
};

GMaps.prototype.set_zoom = function(zoom) {
    this.map.setZoom(zoom);
};

GMaps.prototype.set_center = function(lat, lng, opt) {
    this.map.setCenter(new google.maps.LatLng(lat, lng));

    if(opt && opt.zoom) {
        this.map.setZoom(opt.zoom);
    }
};

GMaps.prototype.add_marker = function(lat, lng, opt) {
    if(opt == null) {
        opt = {};
    }

    var m = new google.maps.Marker({
        position: new google.maps.LatLng(lat, lng),
        map:      this.map,
        title:    opt.title
    });

    if(this.icon) {
        m.setIcon(this.icon);
    }

    if(opt.circle) {
        m.circle = new google.maps.Circle({
            center:       new google.maps.LatLng(lat, lng),
            map:          this.map,
            strokeWeight: 0,
            fillColor:    opt.circle_fill || '#F00',
            radius:       opt.circle_radius || 50
        });
    }

    this.markers.push(m);
};

GMaps.prototype.add_polyline = function(items, show_origin) {
    var path = [];

    for(var i=0; i<items.length; ++i) {
        path.push(new google.maps.LatLng(items[i].lat, items[i].lng));
    }

    var p = new google.maps.Polyline({
        path: path,
        map:  this.map
    });
    
    if(show_origin) {
        p.markers = [];
        p.markers.push(this.add_marker(path[0].lat(), path[0].lng(), {}));
        p.markers.push(this.add_marker(path[path.length-1].lat(), path[path.length-1].lng(), {}));
    }

    this.polylines.push(p);
};

GMaps.prototype.add_circle = function(lat, lng, opt) {
    if(opt == null) {
        opt = {};
    }

    this.circles.push(new google.maps.Circle({
        center:       new google.maps.LatLng(lat, lng),
        map:          this.map,
        strokeWeight: 0,
        fillColor:    opt.fill || '#F00',
        radius:       opt.radius || 50
    }));
};

Usage:

Create element:

<div id="map-wrapper"></div>

Set element style:

<style type="text/css">
    #map-wrapper {
        width: 100%;
        height: 100%;
    }
</style>

Create instance of Google Maps Wrapper:

var map_instance = new GMaps({
    element:    'map-wrapper',
    zoom:       7,
    lat:        45.8805435,
    lng:        24.9582169,
    icon_path:  'http://www.catalinmunteanu.com/wp-content/uploads/2015/01/gmaps_marker.png'
});

// Setting map center
map_instance.set_center(45.8805435, 24.9582169, {zoom: 7});

// Setting map zoom
map_instance.set_zoom(7);

// Change/Set marker icon path (or use default icon)
map_instance.set_icon_path('http://www.catalinmunteanu.com/wp-content/uploads/2015/01/GMaps_marker.png');

// Create marker (circle is optional)
map_instance.add_marker(45.8805435, 24.9582169, {
    circle:         true,
    circle_radius:  20000
});

// Create polyline
map_instance.add_polyline([
    {lat: 45.6725422, lng: 23.567376},
    {lat: 45.0239766, lng: 24.887982},
    {lat: 45.6725422, lng: 26.2150817}
], true);

// Create circle
map_instance.add_circle(46.5037588, 24.2036717, {radius: 20000, fill: '#336699'});
map_instance.add_circle(46.5037588, 25.6674779, {radius: 20000, fill: '#336699'});

// To clear the map use
// map_instance.clear_markers();
// map_instance.clear_polylines();
// map_instance.clear_circles();

// Or with just one call
// map_instance.reset();

 

If  you like this article don’t forget to share it.

Senior JavaScript Interview Test

A few weeks ago a team that I collaborated with asked me to create a JavaScript Interview Test for seniors.

The third part of the interview test also contains answers.

My opinion is that if you ever use this test and the candidate can answer correctly to  1/3 of the questions, well you should definitely hire him.

Go ahead and test your skills:

 

Part I. Questions. [30 minutes, general JavaScript knowledge]

#1. Can you name some Javascript frameworks? Why would you recommend them?

#2. Do you use any Javascript code validators? Can you name one?

#3. Do you unit test your Javascript code? If yes, what frameworks are you using?

#4. Can you name the Javascript interpreter used by Mozilla Firefox? Or any other of the browsers?

#5. What is JSON? What is it used for?

#6. What is JSONP? Can you explain how it works?

#7. Can you write an example of a Javascript generator?

#8. What frameworks have you used for creating 2D, 3D animations?

#9. Describe how would you implement an algorithm to generate random numbers.

 

Part II. Option I. Classic Snake Game. [2 hours, medium developer]

Requirements:

  • The game must be implemented in a typical 2-dimensional layout.
  • The snake should be able to grow significantly within the bounds of the map (the grid size should be at least 20×20)
  • The user may move the snake using the arrow keys, however, the snake cannot double back on itself (e.g. if it is going West it cannot go East without first going North or South). A snake should be able to travel in all 4 directions: up, down, left, right (North, South, West, East).
  • Snake starts off as length 1, each time it eats a “food” object it grows +1 in length
  • Food objects are randomly placed in locations other than those occupied by the snake
  • Only one Food object is visible at any time
  • If the Snake hits itself or a wall the game is ended
  • When the game ends, the score(number of eaten “food”) is displayed followed by a button “Restart” which restarts the game.
  • The task must be completed with HTML5 features: you can choose from Canvas or SVG

 

Part II. Option II. Brick Breaker Game. [2 hours, senior developer]

Requirements:

  • The game must be implemented in a typical 2-dimensional layout
  • The objective is to smash a wall of bricks by deflecting a bouncing ball with a paddle.
  • The user may move the paddle horizontally using the left/right arrow keys.
  • When all the bricks have been destroyed the game is won.
  • If the ball hits the bottom border 3 times the game is lost.
  • The task must be completed with HTML5 features: you can choose from Canvas or SVG

 

Part III. Solve and explain the exercises. [30 minutes, senior developer]

#1. What does alert(x) and alert(y) output?

(function() {
    var x = y = 1;
}) ();

alert(x);
alert(y);

#2. What is the output of alert(window)?

(function() {
    alert(window);
    var window = window;
}) ();

#3. What does foo() call return?

function foo()
{
    return
    {
        haha: "ha"
    };
}
foo();

#4. What is the output for each of the following calls?

// a
function Dude(name) {
    this.name = name;
    return {name: 'Doug'};
}
new Dude('Bob');

// b
function Dude(name) {
    this.name = name;
    return [1, 2, 3];
}
new Dude('Bob');

// c
function Dude(name) {
    this.name = name;
    return 3;
}
new Dude('Bob');

#5. What is the output for the following sequences?

// a
parseInt('foo');

// b
parseInt('foo', 16);

#6. Output for following console.log calls:

var a = 1
    b = 1;

(function() {
    var a = 2
        b = 2;
} ())

console.log(a);
console.log(b);

#7. Output for following line:

[,,,].join()

#8. What do the anchors alert when clicked?

<a href="#">link1</a>
<a href="#">link2</a>
<a href="#">link3</a>

<script>
    var el = document.getElementsByTagName('a');
    for(var i = el.length; i--;) {
        el[i].onclick = function() {
            alert(i);
            return false;
        }
    }
</script>

#9. What’s the content of the following array after the evaluation?

[typeof 'hi' === 'string', typeof new String('hi') === 'string']

#10. What’s the output for this code:

function Point() {
    this.x = 20;
    this.getX = function() {
        return this.x;
    };
}

var a = new Point();
var f = a.getX;
alert(f());

#11. What are the values of x and y after the execution of the following code?

var y = 1, x = y = typeof x;

#12. What does this code sequence output:

var x = [typeof x, typeof y][1];

typeof typeof x;

#13. What does the following code sequence output:

(function f(){
    function f(){ return 1; }
    return f();
    function f(){ return 2; }
})();

 

Answers to Part III

#1. It’s treated like: var x = (y = 1); thus, ‘y=1’ creates an auto-global since there’s no binding ‘var’ statement for it. then that value gets copied into properly defined local var ‘x’.

alert(x); // undefined

alert(y); // 1

#2. Because of ‘hoisting’, all variable declarations will be executed immediately at the top of a function scope. However, the variable initializations are not hoisted. So, local variable ‘window’ is declared but uninitialized/’undefined’.

alert(window); // undefined

#3. returns ‘undefined’. Semicolons in javascript are optional but the interpreter just inserts them for you at certain newline characters if it thinks they are missing. A return statement followed by a new line tells the JS interpreter that a semicolon should be inserted after that return.

#4. Returning a primitive type from a constructor(number, string, date) will ignore the return value and return the originally initialized object, but otherwise, the returned value overrides.

  1. a) {name: ‘Doug’}
  2. b) [1, 2, 3]
  3. c) {name: ‘Bob’}

#5. parseInt should always have the radix parameter supplied

  1. a) NaN
  2. b) 15

#6. Since JavaScript has ‘automatic semicolon insertion’ for some statements like ‘var’, a semicolon gets inserted after ‘a = 2’, and variable ‘b’ gets declared as a global!

console.log(a); // prints 1

console.log(b); // prints 2

#7.  Trailing commas are allowed in javascript and are automatically removed.

The above array is evaluated as [undefined, undefined, undefined]

[,,,].join() // outputs “,,”

#8. All anchors will output -1 when clicked

#9. [true, false]

#10. undefined

#11. x and y are undefined

#12. “string”

#13. 2

 

If  you like this article don’t forget to share it.

JavaScript Prototypal Inheritance Pattern

This is pretty basic stuff but I’ll just place it here in case someone searches for a way to achieve inheritance in JavaScript.

 

// JavaScript doesn't have classes but same behavior can be easily obtained using functions
// Let's consider a class Person
function Person() {
    this.name   = '';
    this.birth  = {
        'year':  -1,
        'month': -1,
        'day':   -1
    };
}

// Basic setters and getters for the class attributes
// I'll skip the validation since we're not focusing on that now
Person.prototype.set_name = function(name) {
    this.name = name;
};

Person.prototype.set_birth = function(year, month, day) {
    this.birth.year  = year;
    this.birth.month = month;
    this.birth.day   = day;
};

Person.prototype.get_name = function() {
    return this.name;
};

Person.prototype.get_birth = function() {
    return this.birth.day + '/' + this.birth.month + '/' + this.birth.year;
};

// Student Class
function Student() {
    this.school = '';
}

// We inherit all properties from the Person Class
Student.prototype = new Person();

// We need to set the constructor otherwise Student instances would have Person constructor
Student.prototype.constructor = Student;

// Student setters and getters
Student.prototype.set_school = function(school) {
    this.school = school;
};

Student.prototype.get_school = function() {
    return this.school;
};

Usage of above described classes:

// New Student instance
var student1 = new Student();

student1.set_name('Catalin');
student1.set_birth(1900, 1, 1);
student1.set_school('A.I.Cuza');

console.log(student1.get_name());
console.log(student1.get_birth());
console.log(student1.get_school());

 

AngularJS Input Max Length Directive

In case you need to set a max length to a model binded input, well check this directive:

'use strict';

angular
.module('APP')
.directive('uiMaxlength', function() {
    return {
        require: 'ngModel',
        link: function(scope, el, attrs, model) {
            var max_length = parseInt(attrs.uiMaxlength, 10);

            var input_value_parser = function(value) {
                if(value.length > max_length) {
                    value = value.substring(0, max_length);
                    model.$setViewValue(value);
                    model.$render();
                }

                return value;
            };

            model.$parsers.push(input_value_parser);
        }
    };
})

;

Usage:

<input ui-maxlength="10"
       type="text"
       name="input_name"
       ng-model="model_name" />

With some minor changes this directive can also be used for setting Min Length.

AngularJS Ng-Repeat In Range

AngularJS does not provide a way to create multiple copies of an element.

Still this behavior is easy to achieve. Check the bellow filter which solves this problem:

'use strict';

angular
.module('APP')
.filter('range', function() {
    return function(input, total) {
        total = parseInt(total, 10);

        for(var i=0; i<total; ++i) {
            input.push(i);
        }
    
        return input;
    };
})

;

Usage:

<div ng-repeat="n in [] | range:10">element copy #{{$index}}/10</div>

 

 

NodeJS AES256 Encryption/Decryption

In one of my projects I needed to encrypt some data. I decided to use AES256 symmetrical encryption algorithm.

So this is what I’ve come to (aes_encryption.js):

var crypto	= require('crypto'),
    cipher_seed = 'some_random_characters';
 
var encrypt = function(text) {
	var cipher	= crypto.createCipher('aes-256-cbc', cipher_seed),
	    crypted	= cipher.update(text, 'utf8', 'hex');

	crypted += cipher.final('hex');

	return crypted;
};
 
var decrypt = function(text) {
	var decipher  = crypto.createDecipher('aes-256-cbc', cipher_seed),
	    decrypted = decipher.update(text, 'hex', 'utf8');

	decrypted += decipher.final('utf8');

	return decrypted;
};
 
module.exports.encrypt = encrypt;
module.exports.decrypt = decrypt;

Usage:

var AES = require('path_to_aes_encryption.js');

var data = {
    name:    'Catalin',
    surname: 'Munteanu',
    address: 'Romania'
};

// Encryption
var encrypted_data = AES.encrypt(JSON.stringify(data));

// Decryption
var decrypted_data = JSON.parse(AES.decrypt(encrypted_data));

Don’t forget that if the cipher seed is changed or lost all the encrypted data is also lost.

Easy, right?

 

 

Finalize – JavaScript Asynchronous Requests Queue

Most web apps send lots of asynchronous http requests. Sometimes you need to know when some selected requests are finished so that you can pass to the next step.

The following function can be used for creating a queue with asynchronous requests using JavaScript and be notified when the queue is empty again (all selected requests are finished):

function Finalize(_callback) {
	var count = 0,
	    exec  = false;

	if((typeof _callback !== 'undefined') && (_callback !== null)) {
		this.exec = function() {
			exec = true;
			if(count === 0) {
				_callback();
			}
		}
		this.queue = function() {
			count++;
			return function() {
				count--;
				if(exec && (count === 0)) {
					_callback();
				}
			}
		}
	} else {
		this.exec  = function() {};
		this.queue = function() {};
	}
}

Usage:

var request_set = new Finalize(function() {
    // Code inside this function will be executed at the end
});

// To add an item to the queue
var first_item = request_set.queue();

// Let's add another item
var second_item = request_set.queue();

// The code inside the final callback will only be called after
// all the items in queue are completed.
// Let's consider we have two async requests:
jQuery.get('http://example.com', function(data) {
    // We now have the response from the first request
    // Remove one item from queue by calling the item's name
    first_item();
});

// The items can also be removed by passing them directly as callback
jQuery.get('http://example.com', second_item);

// You can even skip declaring items and create them on the fly as the callback function
jQuery.get('http://example.com', request_set.queue());

// After you've added all items to the queue we need to execute it
request_set.exec();

The function can also be used in NodeJS.