Tuesday, January 28, 2014

Ruby Wrapper for Google Closure Compiler

I've been using Google's Closure Compiler to compress all the custom JavaScript I've been writing in my projects at work for some time now. Recently, I created a small wrapper script in ruby that will collect all the scripts in a folder and write them into one compressed application.min.js file.

With the script below, just set the #config params and point it at your JavaScript source folder. If you create a file called application.js in your source folder, it will be the first file that is written in the final file.

puts 'Google Closure Compiler';
puts 'Concatenate and minify all JavaScript in src directory';

# config 
closure  = 'C:/closure-compiler/compiler.jar'
src_dir  = '../assets/js/src'
src_dest = '../assets/js/application.min.js'


# load all files from src_dir
files_arr = Dir.glob(File.join(src_dir, "*.js"));

# apply sort order so application is first
files_arr.sort! do |file_a, file_b|
  
  if File.basename(file_a) == "application.js" then
    -1
  else
    0
  end
end

# compile all application src code
cmd = "java -jar #{closure} --js " + files_arr.join(" ") + " --js_output_file #{src_dest}"

puts "running: #{cmd}"

IO.popen(cmd);

Along with this script, here is a version for all the vendor scripts in your project. Point this at your 'vendor' or 'libs' folder and it will compress and create a *.min.js file for each script in that folder.

puts 'Google Closure Compiler';
puts 'Minifying all vendor src code';

# config 
closure  = 'C:/closure-compiler/compiler.jar'
src_dir  = '../assets/js/vendor'
src_dest = '../assets/js/vendor'

# compile all vendor src code
Dir.glob(File.join(src_dir, "*.js")).each do |f|
  
  unless f.include? '.min.js'
    
    name = File.basename f
    new_name = name.gsub(/.js/, '.min.js');
  
    cmd = "java -jar #{closure} --js #{src_dir}/#{name} --js_output_file #{src_dest}/#{new_name}"
 puts "running: #{cmd}"
    IO.popen(cmd);
    
  end
  
end;

Thursday, January 2, 2014

Using Flash SharedObject to Store User Info Similar to Cookies

Browser cookies are great, but can be limiting and easily lost (or cleared). Some browsers will auto clear cookies when they are closed based on user settings. Recently, while building a Facebook page tab application I ran into an issue where Safari wouldn't keep any cookie data because of iframe security. This ended up being painful and while banging my head on my desk I thought of something. Why not use at tiny flash swf to save cookie data? I know flash is not very popular but it's still installed on most people's browser even if they don't know it.

Here is the ActionScript 3 code to get your very own 10px by 10px flash swf to save cookie data with.
//create a file named FlashCookies.as as add the following code to it:
package
{
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.TimerEvent;
 import flash.external.ExternalInterface;
 import flash.net.SharedObject;
 import flash.utils.Timer;
 
 [SWF(width = "10", height = 10, fps = 10)]
 
 public class FlashCookies extends Sprite 
 {
  
  private var cookieName:String;
  private var readyTimer:Timer;
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   
   cookieName = loaderInfo.parameters['c'];
   
   
   if (ExternalInterface.available 
     && cookieName != null 
     && cookieName.length > 0)
   {
    try 
    { 
     // This calls the isContainerReady() method, which in turn calls 
     // the container to see if Flash Player has loaded and the container 
     // is ready to receive calls from the SWF. 
     var containerReady:Boolean = isContainerReady(); 
     if (containerReady) 
     { 
      // If the container is ready, register the SWF's functions. 
      setupCallbacks(); 
     } 
     else 
     { 
      // If the container is not ready, set up a Timer to call the 
      // container at 100ms intervals. Once the container responds that 
      // it's ready, the timer will be stopped. 
      readyTimer = new Timer(100); 
      readyTimer.addEventListener(TimerEvent.TIMER, timerHandler); 
      readyTimer.start(); 
     } 
    } 
    catch(e:Error) { }
    
   }
   else 
   { 
    trace("External interface is not available for this container."); 
   } 
   
  }
  
  private function setupCallbacks():void
  {
   //add callbacks
   ExternalInterface.addCallback('getCookie', getSOCookieData);
   ExternalInterface.addCallback('setCookie', setSOCookieData);
   ExternalInterface.addCallback('clearCookie', clearSOcookieData);
   
   //notify javascript that swf is ready
   ExternalInterface.call('flash_setSWFIsReady');
  }
  
  private function isContainerReady():Boolean 
  { 
   var result:Boolean = ExternalInterface.call("flash_isJSReady"); 
   return result; 
  }
    
  public function getSOCookieData(key:String):String
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   if ( so.data.hasOwnProperty(key) )
    return so.data[key];
   return null;
  }
  
  public function setSOCookieData(key:String, value:String):void
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   so.data[key] = value;
   so.flush();
  }
  
  public function clearSOcookieData():void
  {
   var so:SharedObject = SharedObject.getLocal( cookieName, "/" );
   so.clear();
  }
  
  //Event handlers
  private function timerHandler(event:TimerEvent):void 
  { 
   // Check if the container is now ready. 
   var isReady:Boolean = isContainerReady(); 
   if (isReady) 
   { 
    // If the container has become ready, we don't need to check anymore, 
    // so stop the timer. 
    readyTimer.stop(); 
    readyTimer.removeEventListener(TimerEvent.TIMER, timerHandler);
    readyTimer = null;
    
    // Set up the ActionScript methods that will be available to be 
    // called by the container. 
    setupCallbacks(); 
   } 
  }
 }
 
}

Now you will need to compile this it into a swf with either Adobe Flash or Flash Develop. Along with this generated swf, you'll also need a bit of JavaScript, which isn't much if you use swfobject.
//flash communication methods
var hasFlash = false;
var jsReady = false;
var swfReady = false;

window.flash_isJSReady = function()
{
    return jsReady;
};

window.flash_setSWFIsReady = function()
{
    swfReady = true;
};

function getSwf()
{
    if (navigator.appName.indexOf("Microsoft") !== -1) 
        return window["FlashCookies"]; 
    return document["FlashCookies"]; 
} 

$(document).on('ready', function()
{ 
    //embed flash on page
    var flashvars = {
        //add a custom name for this cookie
        c : 'my_so_cookie'
    };
    var params = {
        allowScriptAccess: "always",
        wmode: "transparent"
    };
    var attributes = {
        id:"FlashCookies"
    };
    swfobject.embedSWF("FlashCookies.swf", "altContent", 10, 10, "9.0.0", null, flashvars, params, attributes);

    // Record that JavaScript is ready to go. 
    jsReady = true; 
} 

And the html would just be a div with the id "altContent" (or whatever you want, just make sure to update the swfobject.embedSWF() call with the matching value.

Once all this is setup, all you need to do is call getSwf().setCookie("email","john@home.net"); and the flash cookie is saved and much harder to clear. The next time that user comes to your site, you can check the cookie getSwf().getCookie("email");.

Nice huh? Since I tried this, I've noticed this technique on a few major website like ebay and Amazon (I think). Also a lot of banner ads do this, which makes me a bit mad since I think they are one of the many reason Flash died so fast, but that's for another post.

How to Extend a Javascript Pseudo Class

I recently built an HTML 5 Canvas project using CreateJS which I ended up really enjoying since I spent a few years of my career building Adobe Flash apps. I remember thinking "Why doesn't Adobe take Flash/ActionScript 3 and write a version that works with HTML5 Canvas/Javascript?" we'll they did (sort of). In a future post, I hope to write more about that project, but this post is more on how to extend JavaScript classes since that's what you do with the CreateJS framework.

We all use jQuery and that's awesome, so you don't really need to worry about JavaScript pseudo classes, but once in a while you may find yourself writing plain JavaScript (or at least using a different framework than jQuery). Since JavaScript's biggest downfall is that there isn't a built in class structure we are left with pseudo classes.

This is all explained very well in David Shariff's blog that can be found here:
http://davidshariff.com/blog/javascript-inheritance-patterns/
So, I'm just going to show how to used the Pseudoclassical pattern like I did with CreateJS.

In CreateJS, you have a bunch of core JavaScript pseudo classes already available to you, and all you need to do is extend the correct ones for you app's needs. This functionality can be used without CreateJS since it's all basic javascript, for example:
(function() {
    var Car = function(color)
    {
        this._wheels = 4;
        this._condition = 'good';

        //this will serve as our 'super()' method
        this.initialize(color);
    }
    Car.prototype.initialize = function(color)
    {
        this._color = color;
        ...
    }
    Car.prototype.drive = function(speed)
    {
        ...
    }
    //setup a namespace if you want
    window.Car = Car;
}());

//create a car and drive
var myCar = new Car(#bada55);
myCar.drive(90);


Now we end up with the class 'Car' that we can re-use in our JavaScript application. Now, we have a 'Truck' object that is a lot like car so we should extend the 'Car' class to reduce code. How do we do this with Javascript since you can't just say:
function Truck() extends Car()
{
    //call car constructor
    super(color);

    //set variables for truck
    this._type = 'pickup';
} 

This is where we use the prototype attribute, but we can't just say Truck.prototype = Car.prototype because now any new methods on Truck will be passed back to Car. The trick is to pass a unique reference for a Car to a temp object and then add to it. So, now the 'Truck' class ends up looking like this:
(function() {
    var Truck = function(color)
    {
        this._type = 'pickup';

        this.initialize(color);
    }
    //here we are creating a Car and passing a unique reference 
    //to the var 'tmp'
    var tmp = Truck.prototype = new Car();

    //now we need to save a reference to the parent 
    //initialize method (like calling 'super()' in other languages)
    Truck.prototype.Car_initialize = tmp.initialize;
    
    //now create a initialize() method for truck that calls 
    //Cars initialize() method
    Truck.prototype.initialize = function(color) 
    {
        this.Car_initialize(color);
        ...
    };
    
    //add a new method only to the 'Truck' class
    Truck.prototype.tow = function(weight)
    {
        ...
    }
    //setup a namespace if you want
    window.Truck = Truck;
}());

I thought this was slick, and you can even move this into a method like so:
var classExtend = function(childClass, parentClass)
{
    var tmpObj = function() { };
    tmpObj.prototype = new parentClass();
    childClass.prototype = new tmpObj();
    childClass.prototype.constructor = childClass;
};

var Car = function()
{
    this._type = 'car';
    ...
}
var Truck = function()
{
    this._type = 'truck';
    ...
}
Truck.prototype.tow = function(weight) { }

//extend the car class
classExtend(Truck, Car);