The TextMate Super Function (AS3/PHP)

Following my obsession with TextMate snippets and commands, I created a function snippet which extended the basic function and added a few new useful features. This snippet is mapped to Shift + Enter. Just type the name of your function...

myFunction

and hit Shift + Enter after it. The following snippet is inserted:

/**
 *
 *
 * @return
 */
public function myFunction()
{

}

The first tab stop is inside the parentheses so you can add some parameters, hit tab again to focus on the scope (public). We use a simple regular expression here to add "_" or "__" to the method name depending on if you type public|private|protected. The rest of the tabs go through the return statement, docblock and finally end inside the function declaration.

I've got both an AS3 and PHP version. Starting with the PHP version:

cat <<SNIPPET
/**
 * $4
 *
 * @return $3
 */
${2:public} function ${2/(private)|(protected)|(.+)/(?1:__)(?2:_)(?3:)/}${TM_SELECTED_TEXT:-$TM_CURRENT_WORD}($1)
{
	$0
}
SNIPPET

And the AS3 version is basically the same with just a return type defined:

cat <<SNIPPET
/**
 * $4
 *
 * @return ${5:$2}
 */
${3:public} function ${3/(private)|(protected)|(.+)/(?1:__)(?2:_)(?3:)/}${TM_SELECTED_TEXT:-$TM_CURRENT_WORD}($1):${2:void}
{
	$0${2/void$|(.+)/(?1:return null;)/}
}
SNIPPET

I'd also like to eventually add @param docs to the docblock as you type out parameters. Would love some suggestions there.

You can download both the PHP and AS3 Function Commands here. You can also download my TextMate theme Plum Dumb, or—if you're into PureMVC—you can download my PureMVC TextMate templates.

December 29th, 2010 | Permalink

List of Countries, Nationalities and ISO codes in SQL

Working on adding regionalization and localization to T1EOS, I needed a solid list of countries. Poking around on google led me to sabbour's awesome SQL list of Countries and their respective nationalities (e.g. United States – American). I added them to my database and then went through and updated each record with its ISO 3166-1-alpha-2 code. I've provided the resulting SQL dump below (simple table with index and unique on the country slug). Some of the nationalities and country names may be out of date, so take a glance before using in any official capacity.

→ Download countries-nationalities-iso.sql

December 22nd, 2010 | Permalink

Reducing CSS/Javascript Requests and File Size with Ant and YUI

With continuing reliance on front-end Javascript plugins and CSS, it's important to keep HTTP requests and file sizes smaller to reduce page load times. On this site, we use jQuery, jQuery Address, a custom application Javascript include, and other plugins. We also use a master CSS file as well as a reset file and other CSS additions for things like @media queries and the like. We can reduce all these file requests to one or two requests by combining them all into one file and minifying them.

We can use a compression tool such as YUI Compressor to handle the minifying, but actually doing the work to compile all those files together and run them through the compressor can be a time-consuming task to perform manually. It'd be great if we had a tool to do that for us. Enter Apache Ant.

Ant is a java-based build automation tool. It can be used to perform a variety of tasks and is ideally suited for building files for web applications. If you're on Mac OS X, Ant comes pre-installed (1.7.x on 10.5 and 1.8.x on 10.6). For other systems, check out the easy instructions here. YUI is a simple download and extraction from here.

All we need for a basic Ant task is a build file. Create a "build.xml" file in the root of your application. To start, you just need some basic markup. I added a property that contains the path to the YUI Compressor .jar file.

<?xml version="1.0" encoding="UTF-8"?>

<!-- project name, default target and base directory -->
<project name="typeoneerror" default="init" basedir=".">

    <!-- path to YUI Compressor -->
    <property name="yuic" location="/Users/typeoneerror/bin/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar"/>

    <!-- path to build resources -->
    <property name="css" location="public/css"/>
    <property name="js" location="public/js"/>
    <property name="ui_css" location="public/ui/css"/>
    <property name="ui_js" location="public/ui/js"/>

    <!-- default target -->
    <target name="init"/>
</project>

This defines our project build with a default target (one of the actions that will run when we build) called "init". Also, we've defined some properties (paths to YUI and to our CSS and Javascript folder) that we'll use later.

Next, create the first target:

<!--concatenate javascript-->
<target name="concat.js" description="Stick all the JS together">
     <concat destfile="build/application.js">
        <!-- first concat all the files in the /ui/js directory -->
        <filelist dir="${ui_js}" files="jquery.min.js,jquery.address.min.js"/>
        <!-- then add our application js file -->
        <filelist dir="${js}" files="application.js"/>
    </concat>
</target>

This target uses the concat process to concatenate a list of javascript files into a destfile (destination file) build/application.js. This combines everything we're using into one file. The next target will use that file and minify it:

<!--minify javascript-->
<target name="minify.js" depends="concat.js" description="Minify JavaScript using YUI Compressor">
    <apply executable="java" parallel="false">
        <!-- look for any javascript files in the build dir -->
        <fileset dir="build" includes="*.js"/>
        <!-- pass arguments to the yui program -->
        <arg line="-jar"/>
        <arg path="${yuic}"/>
        <srcfile/>
        <!-- output minified files to our js dir with .min.js extenstion -->
        <arg line="-o"/>
        <mapper type="glob" from="*.js" to="${js}/*.min.js"/>
        <targetfile/>
    </apply>
</target>

The depends property defines actions that need to be performed before running the target. Add the minify.js target to the depends property of our default action:

<target name="init" depends="minify.js"/>

When we run the build, init will be run and then minify.js. Of course, concat.js will be run before minify.js since minify.js depends on it.

We add another two targets to concat and minify our CSS. Add another target to the list of targets in the init target. Our final build file looks something like this:

<?xml version="1.0" encoding="UTF-8"?>

<!-- project name, default target and base directory -->
<project name="typeoneerror" default="init" basedir=".">

    <!-- path to YUI Compressor -->
    <property name="yuic" location="/Users/typeoneerror/bin/yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar"/>

    <!-- path to build resources -->
    <property name="css" location="public/css"/>
    <property name="js" location="public/js"/>
    <property name="ui_css" location="public/ui/css"/>
    <property name="ui_js" location="public/ui/js"/>

    <!-- default target -->
    <target name="init" depends="minify.js,minify.css"/>

    <!--concatenate javascript-->
    <target name="concat.js" description="Stick all the JS together">
         <concat destfile="build/application.js">
            <!-- first concat all the files in the /ui/js directory -->
            <filelist dir="${ui_js}" files="jquery.min.js,jquery.address.min.js"/>
            <!-- then add our application js file -->
            <filelist dir="${js}" files="application.js"/>
        </concat>
    </target>

    <!--minify javascript-->
    <target name="minify.js" depends="concat.js" description="Minify JavaScript using YUI Compressor">
        <apply executable="java" parallel="false">
            <!-- look for any javascript files in the build dir -->
            <fileset dir="build" includes="*.js"/>
            <!-- pass arguments to the yui program -->
            <arg line="-jar"/>
            <arg path="${yuic}"/>
            <srcfile/>
            <!-- output minified files to our js dir with .min.js extenstion -->
            <arg line="-o"/>
            <mapper type="glob" from="*.js" to="${js}/*.min.js"/>
            <targetfile/>
        </apply>
    </target>

    <!--concatenate css-->
    <target name="concat.css" description="Stick all the CSS together">
         <concat destfile="build/core.css">
            <filelist dir="${ui_css}" files="reset.min.css"/>
            <filelist dir="${css}" files="screen.css,additions.css"/>
        </concat>
    </target>

    <!--minify concatenated css-->
    <target name="minify.css" depends="concat.css" description="Minify CSS using YUI Compressor">
        <apply executable="java" parallel="false">
            <fileset dir="build" includes="*.css"/>
            <arg line="-jar"/>
            <arg path="${yuic}"/>
            <srcfile/>
            <arg line="-o"/>
            <mapper type="glob" from="*.css" to="${css}/*.min.css"/>
            <targetfile/>
        </apply>
    </target>
</project>

Running an Ant build file is as simple as shelling into the directory via your Terminal or command line interface and entering ant. It automatically looks for a file called "build.xml". You can also grab Simon Gregory's Ant TextMate Bundle if you want to just hit Cmd+R to build in your TextMate project (awesome).

December 20th, 2010 | Permalink

HTML5 & CSS3: Graceful Degradation

Webkit animations and transitions are a nifty way to add sexy effects and animations to browsers that use the webkit engine. As the list of browsers that implement these becomes longer (Safari, Chrome, Mobile Safari, Blackberry 6.0, Android, webOS, et al)—especially in the mobile space—the more you can rationalize using some of these features in production. The key words for things like this are "graceful degradation" i.e., use it, but provide a fallback solution for other browsers that will still engage the user. The idea is for the user to not think that something is missing or broken—they are still provided an engaging experience.

An example of this we created recently is a mobile-optimized HTML5 site for First Bank. At the moment it's a place to download free books in PDF form. We'll be adding more activities soon ;) On the books page, though, we use @webkit-keyframes to create a 3D transition effect when changing pages and a Mac-style "bouncing icon in the dock" effect when a book is clicked (try it in Chrome or Safari). In other browsers (Firefox, IE, etc), Javascript is used to create a simple tween animation to show the next page of books. So we give "fancy-ass" browsers some cool effects, but still give the average crowd a nice animation as well.

Here's how we created the Mac icon dock bounce. First, you define a series of keyframes that will define our animation. This is just in your CSS file—I know it looks kind of crazy when you're thinking CSS:

@-webkit-keyframes book-active {
    from {top:0px;}
    to {top:-10px;}
}

Pretty simply, we've defined an "animation name" called book-active. This animation would move whatever element it is applied to from 0 to -10. To run the animation, create a class with a similar name (I find it helps organize) and add the animation-name to it:

.book-active {
     -webkit-animation-name:book-active;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

You can see above we gave it an "animation-name" of "book-active". The animation will take 0.2 seconds to complete and it'll repeat the animation 10 times. The animation-direction parameter tells it to reverse the animation for each interaction. Hence, we bounce.

To apply the animation, you simply add the class to the element:

<article class="book-active"></article>

On the First Bank micro-site, we applied two separate bounce animations to the books. One to bounce the book element, and one to scale the shadow underneath the book on the same bounce to give it some depth. Here's the whole CSS section for the book bounce, shadow, and the 3D transform during the page change animation:

@-webkit-keyframes book-active {
    from {top:0px;}
    to {top:-10px;}
}

@-webkit-keyframes shadow-bounce {
    from {-webkit-transform:scale(1.0);}
    to {-webkit-transform:scale(1.05);}
}

@-webkit-keyframes book-transition {
    from {
        -webkit-transform:scale(1.0);
        -webkit-animation-timing-function:ease-out;
    }
    20% {
        -webkit-transform:scale(0.9);
        webkit-animation-timing-function:ease-in;
    }
    to {
        -webkit-transform:scale(1.0);
        -webkit-animation-timing-function:ease-out;
    }
}

.book-active {
     -webkit-animation-name:book-active;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

.book-transition {
    -webkit-animation-name:book-transition;
    -webkit-animation-duration:1s;
    -webkit-animation-iteration-count:1;
}

.shadow-bounce {
     -webkit-animation-name:shadow-bounce;
     -webkit-animation-duration:0.2s;
     -webkit-animation-iteration-count:10;
     -webkit-animation-direction:alternate;
}

One caveat for animation with webkit, is that repeating animations is tricky. Once the class has been added, you need to remove the animation-name from the element to restart the animation. We created this simple jQuery element function to help:

$.fn.startWebkitAnimation = function(name)
{
    var element = $(this);
    if (!element.hasClass(name))
    {
        element.addClass(name);
    }
    else
    {
        element.css("-webkit-animation-name", "none");
        setTimeout(function() {
            element.css("-webkit-animation-name", name);
        }, 0);
    }
    return $(this);
}

So to start an animation, simply call $("selector").startWebkitAnimation("animation-name"). The CSS class and the keyframes should have the same name. What this does is clear the animation-name property and then reset it. This is based on Apple's own recommendation for resetting animations.

On First Bank Free, we use Modernizr to check if CSS Animations are available. If they are, we start the bouncing animation when the book is clicked and delay the transition to the link. Otherwise, we go right to the link (graceful degradation!):

$("#book-list li a").click(function(event)
{
    if ($(this).data("was-clicked") != undefined)
    {
        // prevent multiple clicks
        event.preventDefault();
        return;
    }
    if (Modernizr.cssanimations)
    {
        event.preventDefault();
        // prevent multiple clicks
        $(this).data("was-clicked", 1);
        // start the book bouncing
        $(this).startWebkitAnimation("book-active");
        // start the shadow bouncing
        $(this).parent().find(".book-shadow").startWebkitAnimation("shadow-bounce");
        // delay the page direction for a tick
        setTimeout('window.location = "' + $(this).attr("href") + '"', 1000);
    }
});

So, you can see how HART (HTML5 and Related Technologies) can be used right now with a little extra effort. The keys are fallbacks and degradation. Also—and I find this to be critical—definitely let your clients know up front what to expect for different browser vendors. This might be defined in functional specification or proposal before development begins.

October 2nd, 2010 | Permalink

Game Center Achievement Notification

Game Center has a notification window that slides down and informs the GKLocalPlayer that they've been authenticated. There is no built-in way to display achievements that the user may have earned during use of your application. The GKAchievementNotification classes are a way to display achievements awarded to the player in the same manner as the authenticated message—similar to Xbox Live style achievement dialogs. The achievement dialogs are added to the UIWindow view of your application:


Achievement notifications in the upcoming Cee-lo 1.1 update.

Using It

You can grab the code from github. Add the folder (.h, .m, images) to your Xcode project. The GKAchievementHandler class handles the display of the notifications. You'll primarily use that (there'd usually be no reason to create a notification directly). When your player earns an achievement, you can notify them of this via GKAchievementHandler:

// grab an achievement description from where ever you saved them
GKAchievementDescription *achievement = [[GKAchievementDescription alloc] init];

// notify the user
[[GKAchievementHandler defaultHandler] notifyAchievement:achievement];

You can also use custom messages instead of a GKAchievementDescription object:

[[GKAchievementHandler defaultHandler] notifyAchievementTitle:@"High Roller" andMessage:@"Earned 100 points online."];

Customization

Apples Guidelines state that "it is up to you to do so in a way that fits the style of your game". and Allan Schaffer of Apple stated in the forums that "[the] best way to do that would be to present a custom dialog using the look and feel of your game" (emphasis, his). This to me means you may be rejected for using Apple's artwork in a custom application. If this worries you, use the setImage: methods to change the logo displayed in the dialog or change the gk-icon.png images in your images. You can also set the image to nil to not show any image:

[[GKAchievementHandler defaultHandler] setImage:nil];

You can also edit the gk-notification.png images to change the stretchable background.

October 1st, 2010 | Permalink
prev 1 2 3 4 5 6 7 8 9 10 next