Friday, December 19, 2014

Stop trying to add class structure to JavaScript

Recently, I read an interesting article titled The Two Pillars of Javascript, and one of the points in the article that I found interesting was the part about trying to create classes in JavaScript. Stop trying to create class structure in JavaScript? But, I thought classes and OOP are how we should be programming, plus classes are everywhere in .NET, Java and Ruby.

The article talks about how JavaScript is not like other languages and how we should embrace the "objects without classes" and "anonymous functions (Lambdas)" structure that it provides.

I only bring this up because after reading that article, I realized, that in Angular I'm unconsciously doing this already. With AngularJS you never use the "prototype" or "new" keywords, because your only registering objects or constructor functions.

Ok, lets step back, what are we talking about?
Here is how we create an object using class like functionality as shown in a previous post:
var Car = function(color) {
    this._wheels = 4;
    this._condition = 'good';
    this.initialize(color);
}
Car.prototype.initialize = function(color) {
    this._color = color;
    ...
};
Car.prototype.drive = function(speed) {};

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

//and then extend Car and create a Truck
var Truck = function(color){
    this._type = 'pickup';
    this.initialize(color);
}
var tmp = Truck.prototype = new Car();
Truck.prototype.Car_initialize = tmp.initialize;
Truck.prototype.initialize = function(color) {
    this.Car_initialize(color);
    ...
};
Truck.prototype.tow = function(weight){};

And this works fine, but how can we do this using just objects and factory methods? The key method you will need is the extend method which we can borrow from jquery $.extend() or any other JavaScript frameworks. The extend method basically copies all the properties and methods from one object to another. You've probably used this to create copies of data objects already.

With this method we can do the same operation as above but with factory methods instead of fake classes. For the Truck object, we can just 'extend' and internal car object using the extend method.
function createCar (color) {
    var _color = color,
        _wheels = 4,
        _condition = 'good';
        
    return {
        getColor : function () { return _color; },
        setColor : function (color) {
            _color = color;
        },
        drive : function (speed) {}
    }
}
//and then extend Car and create a Truck
function createTruck (color) {
    var _car = createCar(color),
        _type = 'pickup';
    
    //here we can use our extend method to add new methods to truck
    return $.extend(_car, {
        getType : function () { return _type; },
        tow : function (weight) {}
    });
}

//now we can create a truck and drive
var myTruck = createTruck(#bada55);
console.log(myTruck.getType());
myTruck.drive(90);


One of the great things about this way is you can now have real private variables. There is no way to access _color and _wheels, unless you create getters and setters which is generally a good practice. We can even have private functions inside our factory methods. Here is a Wiki link to learn more about the Factory Method Pattern. This article only covers a small part of it.

How is this used in Angular? Every time you call a app.controller(), app.directive(), app.factory(), etc. you are supplying a factory method to create this object. Now, thinking this way, I wonder if controllers, directives, or services could be extended? Hmm, not sure, I'll have to do some testing.

Friday, November 7, 2014

AngularJS custom data table example

This is probably the first of many post on AngularJS since the project I'm on uses AngularJS for the front-end with a Java (Play framework) back-end. I don't know anything about Play (sorry) but I have picked up quite a lot of Angular in the last few months.

Feeling like I might be finally getting the hang of AngularJS, I wanted to do a post about the custom data table I just finished. Before You read more, if you want a pre-made component then I'd recommend ngTable. We actually used it for some of the work on this project. In some situation we ended up running into issues and just rolled our own. It seems that, with AngularJS, I often start using a pre-built solution and find myself in a hole with some functionality that the client requested and that wasn't originally part of the component, ugh.

So, this is just a place for you to start from in your project. This table directive will have 2 attributes, "ng-model" which will be the rows of data and "columns" which will be how you configure columns headers and what data to display.

Let start with a simple directive definition:
// customAngularTable.js
mainApp.directive('customAngularTable', function () {
    return {
        restrict : 'E', //this can only be used as a tag 
        replace  : true, //replace the html tag with out template
        require  : 'ngModel', //this directive has to have the attr ng-modal=""
        scope    : {
            ngModel : '=', //variable name will be the same on our scope object
            columns : '='
        },
        templateUrl : 'customAngularTable.tpl.html',
        controller : 'customAngularTableCtrl'
    };
});
The basic template will look something like this:
// customAngularTable.tpl.html
<div>
    <table>
        <thead>
            <tr>
                <th ng-repeat="col in columns" 
                    ng-click="applySort(col)">
                    {{col.label}}
                </th>
            </tr>
            <tr>
                <th ng-repeat="col in columns">
                    <input type="text" 
                           ng-model="filters[col.property]" 
                           ng-change="applyFilters(col)" />
                </th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="row in tableRows">
                <td ng-repeat="col in columns">
                    {{row[col.property]}}
                </td>
            </tr>
        </tbody>
    </table>
    
    <hr />
    <!-- We can just use a pre-built component to page since that 
            is beyond the scope of this tutorial -->
    <pagination ng-model="currentPage" 
                items-per-page="pageSize" 
                total-items="rowsTotal" 
                previous-text="&lsaquo;" 
                next-text="&rsaquo;">
    </pagination>
</div>
Which brings us the the fun part, the controller:
// CustomAngularTableCtrl.js
mainApp.controller('customAngularTableCtrl', function ($scope) {
    'use strict';

    /**
     * View Variables
     */
    $scope.tableRows = [];
    //- sorting
    $scope.currentSortColumn = null;
    $scope.currentSortOrder = '';
    //- filtering
    $scope.filters = {};
    // - paging
    $scope.currentPage = 1;
    $scope.pageSize = 10;
    $scope.rowsTotal = 1;

    /**
     * View Methods
     */
    $scope.applySort = function (columnToSortOn) {
        
        if($scope.currentSortColumn === columnToSortOn) {
            //user has clicked the same column, so we need to change sort order
            //we have 3 sorting states here to step through (asc,desc,'')
            $scope.currentSortOrder = ($scope.currentSortOrder === '') ? 
                'asc' : ($scope.currentSortOrder === 'asc' ? 'desc' : '');
            
            //if the previous operation set currentSortOder to blank, 
            //remove currentSortColumn
            if($scope.currentSortOrder === '') {
                $scope.currentSortColumn = null;
            }
        } else {
            //user has clicked a new column
            $scope.currentSortColumn = columnToSortOn;
            //step to first sort state
            $scope.currentSortOrder = 'asc';
        }
        
        //update view
        updateTableData();
        
    };
    
    $scope.applyFilters = function () {
        //reset current page to 1, se we don't filter out all the rows 
        //and display an invalid page
        $scope.currentPage = 1;
        
        //since, updateTableData will apply all the filters for us, 
        //we just need to re-call that method here.
        updateTableData();
    };

    /**
     * Watches 
     */
    //we'll need to setup a watch for ngModel so if it changes we update the view
    $scope.$watch('ngModel', function (newValue, oldValue) {
        //check if the value is defined and not null
        if(angular.isDefined(newValue) && newValue !== null) {
            //since we have a new array, update view
            updateTableData();
        }
    });
    
    //We need to watch currentPage so we can update the view with the current page
    $scope.$watch("currentPage", function (newValue, oldValue) {
        //check if the page really did change
        if(newValue !== oldValue) {
            updateTableData();
        }
    });

    /**
     * Private Methods
     */   

    function updateTableData () {
        //we will create a new array that we will fill with 
        //all the rows that should still bee in the view.
        var viewArray;

        //step 1: apply filtering on all the rows
        viewArray = $scope.ngModel.filter(applyFilters);
        
        //step 2: if the user has clicked a column, apply sorting
        if($scope.currentSortColumn !== null) {
            // using a getSorter function here allows you to use custom 
            // sorting if you want
            viewArray = viewArray.sort(getSorter());
        }
        
        //step 3: update pagination and apply
        $scope.rowsTotal = viewArray.length;
        // - current page is 1, based but our array is 0 based so subtract 1
        var pageStartIndex = ($scope.currentPage-1) * $scope.pageSize;
        // - page end index is either page size or whatever is left in the array
        var pageEndIndex = pageStartIndex + Math.min(viewArray.length, $scope.pageSize);
        // - splice view array to page start and end index's, and return the 
        //   page we want to view
        viewArray = viewArray.splice(pageStartIndex, pageEndIndex);
        
        //pass the ref to the viewArray to $scope and let angular refresh the html table
        $scope.tableRows = viewArray;
    }
    
    function applyFilters (row) {
        var allowed = true;
        
        //since we set ng-model on each input in the 2nd header row to filters[col.property] 
        //in the view, angular will auto-create a matching property key on the filters object 
        //and set it to whatever the user types into that input.
        
        //So, here we can loop through each property on the $scope.filters property and check
        //if the row should still be displayed.
        Object.keys($scope.filters).forEach(function (key) {
            var rowValue = row[key],
                filterValue = (angular.isDefined($scope.filters[key]) 
                                 ? $scope.filters[key] : "");
            
            //if this value is still allowed by other columns, 
            //test it with this filter value
            if(allowed && filterValue !== null) {
                //here is a good place to add custom filters based on this column. 
                //Ex. var column = lookupColumnFormKey(key);
                //    if(column.type === 'number')  
                //        allowed = numberFilter(rowValue, filterValue); 
    
                allowed = stringSearchFilter(rowValue, filterValue);
            }
        });
        return allowed;
    }
    
    function getSorter () {
        //Here you can return different sort functions based on $scope.currentSortColumn
        //Ex. if($scope.currentSortColumn.type === 'number) 
        //         return numberSorter($scope.currentSortColumn, $scope.currentSortOrder);

        return stringSorter($scope.currentSortColumn, $scope.currentSortOrder);
    }
    
    /**
     * Checks if value contains the chars that are in filterValue.
     */
    function stringSearchFilter (value, filterValue) {
        value = value.toString().toLowerCase(); //toString in case it's a number
        filterValue.toString().trim().toLowerCase();
        return (value.indexOf(filterValue) !== -1);
    }
    
    /**
     * Compares 2 rows as strings based on sortColumn.property.
     */
    function stringSorter (sortColumn, sortOrder) {
        return function (rowA, rowB) {
            var valueA = rowA[sortColumn.property],
                valueB = rowB[sortColumn.property],
                result = valueA.localeCompare(valueB);
            if(sortOrder === 'desc') {
                result *= -1;
            }
            return result;
        };
    }
});
I tried to add as many comments as I could so you can see whats going on here. Next would be how to implement this directive. You'll need to setup the columns array based on the data you want to display in the table.

Here is an example of how this is done:
mainApp.controller('AngularTableTestCtrl', function($scope) {
    $scope.rows = [
        { first : 'Sue', last : 'Davis', title : 'Web Developer', company : 'Infusion' },
        { first : 'David', last : 'Marks', title : 'Sales Rep', company : 'Walmart' },
        { first : 'Jake', last : 'Richards', title : 'Customer Service', company : 'Target' }
    ];
    $scope.columns = [
        { label : 'First Name', property : 'first' },
        { label : 'Last Name', property : 'last' },
        { label : 'Occupation', property : 'title' },
        { label : 'Company', property : 'company' }
    ];
});
And the actual directive html tag would be:
<custom-angular-table columns="columns" ng-model="rows"></custom-angular-table>
This will setup a very basic angular data table directive with filtering, sorting and paging that you can add to as needed. For paging I used Angular UI Bootstrap which works really nice. In the app I'm working on, I had to add custom filters, sorters and input fields into the table body with validation. Basicly, Excel in the browser :).

These files can be found on my GitHub repo https://github.com/jasonsavage/simple-angular-table.

Wednesday, June 4, 2014

Moving to Raleigh to work for Infusion

The time has come for me to pack my bags and move south. I've been in Pittsburgh, PA since I graduated from Edinboro University in 2003, and even a few years before college. Living and working in the burgh, I've gained a lot of experience and had the pleasure working with many really talented people. But, alas, the weather has finally gotten to my wife and me. Plus, my son is starting 2nd grade in the fall! and I fear that if we don't move soon, we'll never leave.

I was offered an Interactive Developer role with a big New York company called Infusion. Infusion is opening an office in Raleigh, NC and I'll be part of the many new hires for that team.

It saddens me to leave my current role with Moxie and move out of the city I've grown to love (and hate sometimes), but this is a great opportunity and I have to give it a try. Raleigh is a really pretty city and the area is booming with a lot of tech jobs, so we'll see how it goes... and hey, you can always move back, right?

Thursday, May 8, 2014

Date Strings from Twitter & Facebook Invaild? (IE & Safari)

Just learned about this yesterday. It seems that if you pass the date string that is returned from Twitter and/or Facebook to the javascript Date() constructor, in IE (and Safari), it shows it as an invalid date.

Twitter returns the "created_time" as something like this: Thu May 01 13:57:04 +0000 2014, which is shown as "Invalid Date" in IE.
    
    //created_time = Thu May 01 13:57:04 +0000 2014
    var date = new Date( jsonData.created_time );
    
    console.log( date.toString() );
    // = IE 9 & 10: 'Invalid Date' 
    // = Chrome:    'Mon May 05 2014 14:50:00 GMT-0400 (Eastern Daylight Time)'
    // = FireFox:   'Mon May 05 2014 14:50:00 GMT-0400 (Eastern Standard Time)'
    // = Safari:    'Mon May 05 2014 14:50:00 GMT-0400 (Eastern Daylight Time)'


For Facebook, the 'created_time' value is something like: 2014-04-17T12:59:04+0000, which is shown as "Invalid Date" in IE and Safari.
    
    //created_time = 2014-04-17T12:59:04+0000
    var date = new Date( jsonData.created_time );
    
    console.log( date.toString() );
    // = IE 9 & 10: 'Invalid Date' 
    // = Chrome:    'Thu Apr 17 2014 08:59:04 GMT-0400 (Eastern Daylight Time)'
    // = FireFox:   'Thu Apr 17 2014 08:59:04 GMT-0400 (Eastern Standard Time)'
    // = Safari:    'Invalid Date'


So, what fixed the problem, for now, was to do a little manipulation on the date string before it is passed it to the Date() constructor.
    
    //twitter = 'Thu May 01 13:57:04 +0000 2014'
    //facebook = '2014-04-17T12:59:04+0000'
    
    var created = facebook.created_time;

    if( isFacebook )
    {
        //this fixes the issue in IE and Safari, and still works in Firefox and Chrome even though they don't need it.
        created = created.replace(/-/g, '/').replace(/T/, ' ').replace(/\+/, ' +');
    }
    else if( isTwitter )
    {
        //this is only an issue in IE, so we can just do a quick test and fix the issue.
        if( navigator.userAgent.match(/MSIE\s([^;]*)/) )
            created = created .replace(/( \+)/, ' UTC$1');
    }

    var date = new Date( created );

Monday, May 5, 2014

Moving my SVG Animation to Canvas with EaselJS (follow-up post)

This is a follow-up to the previous post titled "SVG Animation With Clip Paths and Linear Gradients".

I was able you port the SVG animation over to the <canvas /> using EaselJS. It wasn't too bad, I was having a lot of trouble converting the SVG path data that was exported from illustrator, but that all changed when my co-worker found this site: http://www.professorcloud.com/svg-to-canvas/.

Here is the code I ended up with that re-created the animation from before:
function initCanvasHeart(canvasId)
{
    var stage       = new createjs.Stage(canvasId),
        goldRect    = new createjs.Shape(),
        greenRect   = new createjs.Shape(),
        blueRect    = new createjs.Shape(),
        goldMsk     = new createjs.Shape(),
        greenMsk    = new createjs.Shape(),
        blueMsk     = new createjs.Shape(),
        aa          = new createjs.Shape(),
        width       = stage.canvas.width,
        height      = stage.canvas.height,
        rectWidth   = width / 0.4;
    
    //create gold rect
    goldRect.graphics
        .beginLinearGradientFill(
            ["#FFC423", "#ffe191", "#FFC423"], 
            [0.4, 0.5, 0.6], 0, 0, rectWidth, 0)
        .drawRect(0, 0, rectWidth, height);

    goldRect.x = width-rectWidth;
    goldRect.cache(0,0,rectWidth, height);
    
    //add it to the canvas
    stage.addChild(goldRect);
    
    //create gold rect mask
    drawGoldStrip( goldMsk.graphics.beginFill("#000000") );
    goldRect.mask = goldMsk;
    
    //create green rect
    greenRect.graphics
        .beginLinearGradientFill(
            ["#6EB43F", "#b6d99f", "#6EB43F"], 
            [0.4, 0.5, 0.6], 0, 0, rectWidth, 0)
        .drawRect(0, 0, rectWidth, height);

    greenRect.x = width-rectWidth;
    greenRect.cache(0,0,rectWidth, height);
    
    //add it to the canvas
    stage.addChild(greenRect);
    
    //create green rect mask
    drawGreenStrip( greenMsk.graphics.beginFill("#000000") );
    greenRect.mask = greenMsk;
    
    //create blue rect
    blueRect.graphics
        .beginLinearGradientFill(
            ["#0083C8", "#7fc1e3", "#0083C8"], 
            [0.4, 0.5, 0.6], 0, 0, rectWidth, 0)
        .drawRect(0, 0, rectWidth, height);

    blueRect.x = width-rectWidth;
    blueRect.cache(0,0,rectWidth, height);
    
    //add it to the canvas
    stage.addChild(blueRect);
    
    //create blue rect mask
    drawBlueStrip( blueMsk.graphics.beginFill("#000000") );
    blueRect.mask = blueMsk;
    
    //add a layer to fix anti-aliasing in Chrome
    //- the mask in chrome was really pixelated compared to Firefox
    aa.graphics.setStrokeStyle(1, 1, 1, 2);
    drawGreenStrip( aa.graphics.beginStroke("#6EB43F") ).endStroke();
    drawGoldStrip( aa.graphics.beginStroke("#FFC423") ).endStroke();
    drawBlueStrip( aa.graphics.beginStroke("#0083C8") ).endStroke();
    aa.cache(0, 0, width, height);
    stage.addChild(aa);

    //add animations
    //using TweenJS that is part of EaselJS, we can create a simple repeating animation
    createjs.Tween.get(goldRect, {loop:true}).to({x: 0 }, 8000);
    createjs.Tween.get(greenRect, {loop:true}).to({x: 0 }, 8000);
    createjs.Tween.get(blueRect, {loop:true}).to({x: 0 }, 8000);
    
    //add an event listener to the internal ticker object to update the stage, 
    //otherwise we won't see the animation
    createjs.Ticker.addEventListener("tick", function( evt ) 
    { 
        stage.update();
    });
}

//these method were each generated by:
//http://www.professorcloud.com/svg-to-canvas/ 
function drawGreenStrip(g)
{
    g.moveTo(72.603,317.736);
    g.bezierCurveTo(71.87,316.727,74.997,320.537,72.603,317.736);
    g.bezierCurveTo(63.166,306.699,37.833999999999996,281.177,25.986999999999995,258.153);
    g.bezierCurveTo(-0.2700000000000067,207.12500000000003,-12.807000000000002,128.69500000000002,18.386999999999993,68.50900000000001);
    g.bezierCurveTo(38.157,30.357,72.942,6.779,118.972,0.328);
    g.bezierCurveTo(122.661,0.096,126.004,0,129.237,0);
    g.bezierCurveTo(180.874,0,240.743,26.183,251.61399999999998,145.724);
    g.lineTo(251.61399999999998,145.724);    g.bezierCurveTo(224.38899999999998,88.70599999999999,175.11599999999999,64.73599999999999,124.02599999999998,79.07799999999999);
    g.bezierCurveTo(96.53399999999998,86.79299999999999,72.37499999999997,114.35999999999999,60.29699999999998,146.654);
    g.bezierCurveTo(42.154,195.165,42.749,259.684,72.603,317.736);
    g.lineTo(72.603,317.736);
    g.closePath();
    return g;
}
   
function drawGoldStrip(g)
{
    g.moveTo(446.57,41.168);
    g.bezierCurveTo(423.02,17.616,391.671,4.649,358.286,4.649);
    g.bezierCurveTo(310.154,4.649,265.962,31.224,249.12099999999998,69.678);
    g.bezierCurveTo(253.90999999999997,82.536,257.52799999999996,97.475,259.68199999999996,114.699);
    g.bezierCurveTo(279.698,91.652,307.98499999999996,77.94200000000001,335.94599999999997,77.94200000000001);
    g.bezierCurveTo(358.849,77.94200000000001,379.186,87.218,391.736,103.39500000000001);
    g.bezierCurveTo(407.001,123.06200000000001,410.514,151.238,401.897,184.871);
    g.bezierCurveTo(389.59499999999997,232.871,326.669,325.246,243.46599999999998,359.457);
    g.bezierCurveTo(244.652,359.401,276.452,359.675,295.70399999999995,354.659);
    g.bezierCurveTo(372.371,334.659,441.4409999999999,277.092,459.236,242.862);
    g.bezierCurveTo(490.488,182.738,503.576,98.174,446.57,41.168);
    g.closePath();
    return g;
}
   
function drawBlueStrip(g)
{
    g.moveTo(239.213,444.16);
    g.bezierCurveTo(218.941,444.16,205.017,435.036,187.38,423.485);
    g.bezierCurveTo(183.989,421.265,180.448,418.94100000000003,176.668,416.56);
    g.bezierCurveTo(152.79000000000002,401.536,106.22800000000001,355.632,96.757,342.354);
    g.bezierCurveTo(67.397,301.205,44.056000000000004,186.868,72.796,146.81599999999997);
    g.lineTo(79.569,137.36399999999998);
    g.lineTo(80.349,148.96899999999997);
    g.bezierCurveTo(83.259,192.36699999999996,93.43100000000001,212.12199999999996,111.904,247.97699999999998);
    g.bezierCurveTo(137.74599999999998,298.144,195.71699999999998,360.70899999999995,248.362,371.56399999999996);
    g.bezierCurveTo(307.151,369.43199999999996,356.589,351.84,428.28700000000003,299.106);
    g.lineTo(428.28700000000003,299.106);
    g.bezierCurveTo(401.58700000000005,335.235,329.206,400.438,291.47900000000004,424.78499999999997);
    g.bezierCurveTo(277.495,433.81,258.921,444.16,239.213,444.16);
    g.closePath();
    return g;
}

SVG Animation With Clip Paths and Linear Gradients

One of our clients decided to update their logo from a circle to more of a heart. Currently, on the about us page of their website, they have a large version of their logo that spins slowly and when you rollover (or swipe/tap) each color in the logo information appears describing their products. One of our designers and I, sat down to discuss what we could do with a heart shaped logo because having it spin looked weird. We finally came up with a subtle shine effect on the colors, which at first, I thought would be easy since I've done it before with flash. I came to find out that there was a little more to it then I originally thought.

I decided to try the animation with SVG. After reading up on SVG graphics and animations, came up with the following which turned out great:




Now, if you can see the animation (more on that in a sec), what is happening is that I have 3 wide <rect /> tags each with a <linearGradient /> set as there fill. Each rectangle has an <animation /> tag that slowly animates it from left to right and then repeats. I also have 3 <clipPaths /> tags that each contain a <path /> tag whose "d" attribute is a bunch of draw commands I exported from illustrator. This is one of my first times working with SVG graphics and I really enjoyed it. I can't wait until it is more widely supported because it reminds me of Flex and MXML.

That last sentence about it being more widely supported is where I am now. I have a backup image setup as a fallback for users whose browsers don't support SVG, ClipPath, and SMIL animations, but the account managers are a little concerned that the client is going to be upset since we made a big deal about this.

So, today I'm going to revisit one of the ideas of using the canvas to draw this animation. It's a bit more supported right now, but before, I was having trouble getting the vector drawing correct since each part of the logo is an irregular shape. I'm thinking either using EaselJS (since I've used it before) or maybe FabricJS (since I think it might be more popular).

Wednesday, April 16, 2014

Combining PHP template files with associative array models

I've been using Codeigniter for the past 2 years now and I really like it. It might just be the MVC (like) pattern, but it's got a lot of nice built in classes that make building website in PHP a lot quicker, easier and cleaner. So, with my recent exposure to Wordpress, I came to a spot where I was mixing a lot of html with php code, which is something I really hate doing.

Thinking of how, with Codeigniter (and other frameworks), you can combine a *.php template with a data array (or a view model) and render a page I was curious. How does Codeigniter load a php page into a scope with the variables from the supplied data array and spit out the resulting page? After doing a bit of digging I found this nifty function that I ported to my Wordpress theme and now my code looks much cleaner.
function load_template($filename, $data=array()) 
{
    $filename = '/inc/templates/' . $filename . '.php';
    
    if (is_file($filename)) 
    {
        extract($data);
        ob_start();
        
        include( $filename );
        
        $buffer = ob_get_contents();
        @ob_end_clean();
        return $buffer;
    }
    return false;
}
Now with this function you can write a template like...
//inc/templates/biocard.php
<div class="card">
    <div class="photo">
    <?php if( ! empty($image) ) : ?>
        <img src="<?= $image; ?>" alt="<?= $name; ?>" />
    <?php else : ?>
        <img src="/img/missing-user-image.jpg" alt="<?= $name; ?>" />
    <?php endif; ?>
    </div>
    <div class="detail">
        <h4><?= $name; ?></h4>
        <h5><?= $title; ?></h5>
        <?php if( ! empty($email) ) : ?>
            <a class="email" href="mailto:<?= strtolower($email); ?>">Email</a>
        <?php endif; ?>
    </div>
</div>
... and populate it with an associative array:
$model = array(
    'name' => 'Jason',
    'title' => 'Web Developer',
    'email' => 'jason@home.com',
    'image' => 'path/to/profile/image.jpg'
);

return load_template('biocard', $model);

Wednesday, March 12, 2014

How to get the Twitter tweets for a screen name using Application-only OAuth (PHP & cURL)

For a recent project, the designers had a sidebar that displayed the 5 most recent tweets from the client's twitter account. I was surprised to find out it's not as easy as it used to be. Twitter now requires you to use OAuth to display tweets from a certain account or hashtag.

If you ever come across a project like this, hopefully these steps below will help you out.

Step 1: You'll need to create a Twitter application to get an API key and secret which will allow you to make API request.
  • Go to: https://dev.twitter.com/ (make sure your logged in to twitter)
  • In the upper right side of the header you should see your avatar pict and a dropdown menu arrow.
  • Select "My Applications" from that dropdown and then click the "Create New App" button.
  • Give Twitter all the details about what your app does and then click "Create your Twitter Application".
  • Once your app is created, select it (if it's not already open) and select the tab at the top named "API Keys". This should show you your API key and API secret. Copy these two values, you will need them in the next steps.

Step 2: We'll start with the back end PHP code to make this work. These code examples are from an ajax callback in Wordpress, but you should be able to fit it into your project as needed. WARN: your API key and secret should not be visible on your site so don't put them in your javascript files.

First, we take the key and secret and create an access token out of them, then we can use the access token to get an application bearer token from Twitter. The bearer token is what we will use to make all our API requests. The Twitter documentation for this can be found here: https://dev.twitter.com/docs/auth/application-only-auth
add_action( 'wp_ajax_nopriv_twitter_tweets', 'twitter_tweets_callback' );

function twitter_tweets_callback() 
{
    //settings - example values
    $api_key = 'xvz1evFS4wEEPTGEFPHBog';
    $api_secret = 'L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg';
    $access_token = base64_encode(urlencode($api_key) . ':' . urlencode($api_secret));

    //connect to the twitter api and get bearer token
    $bearer_token = get_bearer_token( $access_token );

    //---

    //search for recent tweets for the client (method below)
    $tweets_json = get_user_timeline($bearer_token, '[client's screen name here]');
    
    //echo resulting json
    echo $tweets_json;
    
    die(); // this is required to return a proper result
}

function get_bearer_token( $access_token )
{
    $ch = curl_init( 'https://api.twitter.com/oauth2/token' );
    
    $post_fields  = array(
        'grant_type'    => 'client_credentials'
    );
    
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Basic ' . $access_token,
        'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
    ));
    
    //send request
    $response = json_decode( curl_exec($ch) );
    
    curl_close($ch);
    
    return $response->access_token;
}

Step 3: Hopefully, success! You now have a bearer token which allows you to make application-only API requests to many of the different REST resources that Twitter offers: https://dev.twitter.com/docs/api/1.1.

If we continue with the code above, we can now access the /statuses/user_timeline.json resource to pull tweets for a specific screen name.
//make a GET request using the Bearer token to access tweets for a screen name
function get_user_timeline( $bearer_token, $screen_name )
{
    $url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
    $fields  = array(
        'screen_name'   => $screen_name,
        'count'         => 5,
        'exclude_replies'=> 1
    );
    
    $ch = curl_init( $url . '?' . http_build_query($fields) );
    
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 0);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Bearer ' . $bearer_token
    ));
    
    //send request
    $response = curl_exec($ch);
    
    curl_close($ch);
    
    return $response;
}


Step 4: On the front-end, we can use javascript via ajax to grab the json data from PHP and generate a list of tweets to be displayed on the webpage.
var itemTmpl = [
    '<li>',
        '<span class="name"><strong>${name}</strong> @NPCpgh</span>',
        '<span class="desc">${text}</span>',
        '<span class="date">${date}</span>',
        '</li>'
    ].join('\n');

$.post('/wp-admin/admin-ajax.php', { action: 'twitter_tweets' }, function(response)
{
    //turn json array of object into html array and join
    var html = $(json).map(function(index, item) 
               {
                   return itemTmpl.replace('${name}', item.user.name)
                                  .replace('${text}', item.text);
                                  .replace('${date}', item.created_at );
               }).toArray()
                 .join('\n');

    //update display list
    $(".results").html( html );

}, 'json');


Friday, March 7, 2014

Introducing grunt-namespacer Task

Grunt has caught my eye, again, as a nice way to "build" all my javascript into a small, clean file that I can include in the websites I work on.

With the Grunt setup complete on the Wordpress site/theme I am working on, I started looking for a way to concat, closure wrap, and compress/obfuscate all the JavaScript in my source folder so I can separate out my scripts into different files (kinda like how a bunch of node.js projects work). I figured, at the most, this would be nice for utility functions that I could copy from site to site with minimal code editing.

My searches on GitHub lead me to the following tasks: grunt-contrib-concat, grunt-wrap, grunt-contrib-uglify. But, after setting up these tasks and doing some test builds, I started thinking that it would be nice if there was a task that would create namespaces automatically to avoid method name collisions. The only task I could find that was close was grunt-autowrap but it didn't do exactly what I wanted. This lead me into looking at how to build your own grunt tasks.

This is what I ended up with: grunt-namespacer.

//Gruntfile.js
grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    
    //grunt-namespacer task config
    namespacer : {
        basic : {
            src: 'js/src/app/',
            dest: 'js/build/'
        }
    }
    ...
  });

  // Load the plugins for all tasks.
  grunt.loadNpmTasks('grunt-namespacer');

  // Default task(s).
  grunt.registerTask('default', ['namespacer']);

This task will take your javascript files and concat and closure wrap them based on your folder structure.

For example, if your folder structure in your js folder is like this:
+ src/
    + app/
        init.js
        + controls/
            checkbox.js
            tabs.js
            forms.js
        + models/
            person.js
            cart.js
        + utils/
            empty.js
            json.js
            ajax.js

Then after you run the namespacer task you will get a file named 'app.js' (base folder name), and be able to access everything like this:
app.init.run();

var person = new app.models.Person('Dave');
app.utils.ajax('/register', person, function(response)
{
    person.id = response.data.id;
});

Also, scope is based on folder so any function in tabs.js can access methods in forms.js without having to use the namespace.

This task is free to use and I welcome any comments to help improve it.

later,
-Jason

Wednesday, February 12, 2014

How to change the tab size for SASS/SCSS files in Aptana [HACK]

I found this today and it was really helpful:

I'm currently using Aptana to do all my php development and the default tab size for SASS files is set to 2. For myself, prefer a tab size of 4 for all source files I'm working on (html, php, js, css, sass, etc.) so I went up to Window > Preferences >Editors > SASS and changed to size setting to 4. Which worked nicely but to my surprise this setting does not save when you exit Aptana.

Currently, this is a small bug in Aptana issue tracker that they are working on, so for now we have this little hack that a user named Drew responded with in the thread.

Here is the post:

Although it’s a hack, you can solve the problem by going to your Workbench directory > .metadata > .plugins > org.eclipse.core.runtime > .settings > com.aptana.editor.sass.prefs (or any other file in this directory) and add a line at the end of the document with any text editor:
tabWidth=4
Also, make sure this line is at the top, and set to “false” (if it’s not there, add it): 1
 
com.aptana.editor.common.useGlobalDefaults=false
Save and close the file, and Aptana will remember the tab preference. However, this doesn’t resolve the issue that Aptana isn’t writing to the preference file in the first place.

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);