Google Closure Tutorials

SlideShow tutorial

Posted in Animation, Closure Code, DOM, UI by googleclosuretutorials on November 19, 2009

LOOKING FOR SOURCE? Zip containing all the required files, and a copy of this tutorial.

For my next tutorial, I’m going to look into building a SlideShow. There is a Slide object in goog.fx.dom that we could use, but I really wanted to have a look at goog.fx.Animation so I’ve used that instead. If you’ve not read the InlinePopup tutorial, now would be a good time as I’ll be skipping over some of the more basic concepts that I explained last time.

First up, let’s define our namespace and requires:

goog.provide( ‘neame.ui.SlideShow’ );

goog.require( ‘goog.events’ );
goog.require( ‘goog.dom’ );
goog.require( ‘goog.dom.query’ );
goog.require( ‘goog.style’ );
goog.require( ‘goog.fx.Animation’ );
goog.require( ‘goog.array’ );

You should be able to tell what sort of things we’ll be working with from that (events, DOM, inline styles, animation and some array methods). Next up is the largest method of our SlideShow object – the constructor:

/**
* SlideShow constructor
* @args slideshow:String – ID of the slideshow container
* speed:int – how long in ms it should take to slide
* axis:char – ‘x’ or ‘y’
* automated:bool – if true, it will slide by itself until ‘stop’ method called
* pause_ms – how long it should pause when sliding automagically
* All arguments are optional except slideshow
*/
neame.ui.SlideShow = function( slideshow , speed , axis , automated , pause_ms ){

// set vars from arguments passed, or default
this.automated = automated || false;
this.pause_ms = !isNaN( pause_ms ) ? pause_ms : 8000;
this.speed = speed || 750;
this.axis = axis || ‘x’ ;

// get useful slide information – all based on first list, then first list item
this.slidecontainer = goog.dom.query( ‘#’ + slideshow + ‘ > ul’ )[ 0 ];
this.slides = goog.dom.query( ‘#’ + slideshow + ‘ > ul > li’ );

// get dimension and length of slides – making the assumption all are the same size
this.slideSize = goog.style.getBorderBoxSize( this.slides[ 0 ] );
this.numslides = this.slides.length;

// set float by mapping the array according to axis
if( axis == ‘x’ )
goog.array.map( this.slides , function( slide ){ goog.style.setFloat( slide , ‘left’ ); } );
else
goog.array.map( this.slides , function( slide ){ goog.style.setFloat( slide , ‘none’ ); } );

// make global reference
this.id = neame.ui.SlideShow.Instances.length;
neame.ui.SlideShow.Instances[ this.id ] = this;

// not currently sliding
this.sliding = false;
this.timer = null;

if( this.automated )
this.nextSlide();

};

We start off the constructor by defining our object variables. The first 4 lines should be pretty self-explanatory – all we’re doing here is looking for the arguments this instance was called with and if not present, falling back to some sensible default values.

The next 2 lines are a bit more interesting – here you can see 2 calls to goog.dom.query. If you’re looking for an equivalent to jQuery’s $() method, you’ve just found it. Since goog.dom.query returns an array of matched Elements, we can just pull off the first matching UL for the slidecontainer by adding [ 0 ] after the method call.

Now that we have references to the container and slides, we can get the dimensions of the slides with a call to goog.style.getBorderBoxSize. Note here again I’m just pulling the first Element from the slides array (I’m assuming all slides are of a regular width and height).

Ok so we’ve done plenty of gets so far… Time for a couple of sets. By calling goog.array.map, we can apply a function to every element in the array, which in this case is every slide. All we are doing here is explicitly setting the float of every slide to ‘left’ or ‘none’ depending on the axis passed when the object was instantiated.

Next, we add a global reference to the slide (required for setTimeout later), set some initial variables (not sliding, timer is null) and finally, if automated, call nextSlide. And what does nextSlide do, you ask? Well, let’s have a look…

/**
* nextSlide
* calculates the position of the next slide and calls slideFromTo
*/
neame.ui.SlideShow.prototype.nextSlide = function(){

// if we are already sliding then exit
if( this.sliding ) return;
// get container position
var containerPos = goog.style.getPosition( this.slidecontainer );
// calculate next slide position based on axis
if( this.axis == ‘x’ ){
// calculate target x position
var tx = ( Math.abs( containerPos.x – this.slideSize.width ) / this.numslides < this.slideSize.width ) ? ( containerPos.x – this.slideSize.width ) : 0;
// slide from current x to tx
this.slideFromTo( [ containerPos.x , 0 ] , [ tx , 0 ] );
}else{
// calculate target y position
var ty = ( Math.abs( containerPos.y – this.slideSize.height ) / this.numslides < this.slideSize.height ) ? ( containerPos.y – this.slideSize.height ) : 0;
// slide from current y to ty
this.slideFromTo( [ 0 , containerPos.y ] , [ 0 , ty ] );
}

};

nextSlide is pretty simple – first off, if we are already sliding, then just return straight away – don’t execute any of the other logic. If we get to the next line, then we aren’t sliding and so we need to calculate the position of the next slide and make a call to slideFromTo, which moves the slideshow from one position to another. I’m not going to go into the longer lines too much, but all they are saying is ‘if the next slide is off the end of the list of slides, then go back to 0. Otherwise the co-ordinates for the next slide are the same as the current position minus one slide’s width (or height)’.

prevSlide is exactly the same as nextSlide but it does it the other way around – plus one slide’s width (or height). I won’t include it here but it’s in the source file.

/**
* slideFromTo
* @args oc:Array(2) – origin co-ordinates
* tc:Array(2) – target co-ordinates
*/
neame.ui.SlideShow.prototype.slideFromTo = function( oc , tc ){

// create animation object
this.anim = new goog.fx.Animation( oc , tc , this.speed , this.animationAcceleration );

// array of animation events we want to capture
var animationevents = [ goog.fx.Animation.EventType.BEGIN,
goog.fx.Animation.EventType.ANIMATE,
goog.fx.Animation.EventType.END ];
// bind listeners
goog.events.listen( this.anim , animationevents , this.tick , false, this );
goog.events.listen( this.anim , goog.fx.Animation.EventType.END , this.animationDone , false , this );

// Start animation
this.anim.play( false );
this.sliding = true;

};

slideFromTo is one of the most interesting methods. The first thing we do is create an Animation object provided by goog.fx.Animation, passing it the origin co-ordinates, target co-ordinates, time that the animation should take and an easing function (defined later).

Next we create an array of animation events we would like to listen for (I have to say, I was pleasantly surprised when I found out you can pass arrays to goog.events.listen – you could probably live without the BEGIN and END listeners but for the sake of this tutorial I’ll leave them in). We then bind this.tick to these events, so that every time BEGIN, ANIMATE or END are dispatched our tick method will be called. We also bind this.animationDone to the END event so we can do some extra things once the animation is complete.

Then we just have to start the Animation object with this.anim.play and set this.sliding to true, so that any calls to nextSlide or prevSlide will be ignored until this flag is removed. When this.anim.play is called, the BEGIN event is dispatched and then ANIMATE is dispatched every time the Animation object starts a new ‘frame’.

/**
* tick
* @args e:Event – Animation Event
*/
neame.ui.SlideShow.prototype.tick = function( e ){
if( this.axis == ‘x’ )
goog.style.setPosition( this.slidecontainer , e.x );
else
goog.style.setPosition( this.slidecontainer , 0 , e.y );
};

tick, as mentioned above, is the method that is called on every ‘frame’ in the animation. It’s pretty simple but there’s one thing you should note here – we are setting the position of the slidecontainer with the event ordinates. Since the event being passed to tick is an Animation Event, the constants x and y refer to the position that the slidecontainer should be at, at that time. So it’s a simple check of axis and then a call to goog.style.setPosition. Next up, we need to think about what to do when the animation is complete:

/**
* animationDone
* @args e:Event – Animation Event
*/
neame.ui.SlideShow.prototype.animationDone = function( e ){
// ok to slide
this.sliding = false;
if( this.automated ) this.timer = setTimeout( ‘SlideShow_Instances[‘ + this.id + ‘].nextSlide()’ , this.pause_ms );
};

This method is pretty simple and a little bit ugly… We set this.sliding to false so that we can move the slideshow again and then – if the SlideShow is automated – make a call to setTimeout to fire nextSlide() on the current object in this.pause_ms ms time. I’m sure there’s a neater way to do this but I’ve relied on a global variable SlideShow_Instances (see constructor) to make the call. We now have every method we need to automate the slideshow, except a method to make it start and to make it stop:

/***
* stop
* stops the slideshow if automated
*/
neame.ui.SlideShow.prototype.stop = function(){
this.automated = false;
clearTimeout( this.timer );
};

/***
* go
* starts automated slideshow
*/
neame.ui.SlideShow.prototype.go = function(){
this.automated = true;
clearTimeout( this.timer );
this.nextSlide();
};

These two methods are pretty simple – stop sets automated to false and halts the timer, whereas go sets automated to true, halts the timer and then starts sliding. There’s only one method we haven’t covered now, which is animationAcceleration. I have to admit I hit a bit of a snag here – there’s an easeOut method in goog.fx.easing, but for some reason I couldn’t get it to work with my script. I could require() the package with no errors, but then I would get errors telling me the method was not available. So in the end I just looked up the source and copied it into my own method, seeing as it’s so short anyway:

/***
* animationAccelleration
* ease out function
*/
neame.ui.SlideShow.prototype.animationAcceleration = function( t ) {
return 1 – Math.pow( 1 – t , 3 );
};

And last but not least, we need to instantiate our global array (to hold references to each slide in, required for automated animation) and export symbols to the document. Since there is a good chance our method names will be changed when the code is compiled, exportSymbol ensures that we always have a reference to them, even if their names change.

/***
* make Instances array and export symbols
*/
neame.ui.SlideShow.Instances = new Array();
// export symbols
goog.exportSymbol( ‘SlideShow_Instances’ , neame.ui.SlideShow.Instances );
goog.exportSymbol( ‘SlideShow’ , neame.ui.SlideShow );

Now that we have exportSymbol‘d our constructor and instances array, neame.ui.SlideShow will be available as ‘SlideShow’. This means we can make new slideshows by calling new SlideShow( id ) rather than new neame.ui.SlideShow( id ). Note that in animationDone we refer to neame.ui.SlideShow.Instances as SlideShow_Instances.

And that’s it! Make sure you grab a copy of the source and have a look at how the XHTML is structured. The SlideShow object requires an unordered list of list items, wrapped in a div element. The CSS in the .html file is also required, though SlideShow *will* overwrite any float declarations on the list items.

JSON Tutorial – Displaying Friendfeed items

Posted in DOM, JSON by googleclosuretutorials on November 14, 2009

There’s an interesting tutorial at lahosken.san-francisco.ca.us for retrieving and displaying Friendfeed items using Google Closure’s Jsonp class. It’s great for seeing how to handle JSON requests with Closure, as well as covering some useful methods in goog.array and a number of DOM manipulation methods in goog.dom.
Well worth running through.

Lightbox / Shadowbox / Whateverbox built on Closure

Posted in Images, UI by googleclosuretutorials on November 13, 2009

LOOKING FOR SOURCE? Zip containing all the required files (you still need the Closure library though, naturally).

For my first few forays into Google’s Closure library I’ll be looking at building some common UI components.

First up is the inline / modal popup. Most of the time these things are used for images, so for this little app I’m going to make it work for that purpose.

Now, what do we want out of this?

  1. It must be portable and easy to implement.
  2. The popup itself should shrink or expand to match the image inside it.
  3. When the popup is launched, the rest of the page should fade out.
  4. The popup must have a close button, but clicking on the page / background should also close it.

Right, let’s get started… I’m going to assume you’ve run through the Hello World tutorial provided by Google. If not, there is a great walkthrough of it on moretechtips.net.

To begin with, make a new .JS file and give it a meaningful name (being the imaginative person I am, I chose ‘inlinepopup.js’). The first thing we need to do is define a namespace for our InlinePopup Class. Therefore, our first declaration should therefore make use of goog.provide():

goog.provide( 'neame.ui.InlinePopup' );

Now that we’ve done that, we need to identify which classes in the Closure library we’ll be using for our InlinePopup. A quick glance through the google.ui package shows a few potential candidates but Dialog seems to be the most appropriate.  Frankly, it looks like all we’re going to need to do is tweak a few properties and Bob’s your uncle.

As for preloading the image – Google kindly provides us with the ImageLoader class, in the goog.net package. We’re also going to need to handle some events, so we’ll need goog.events for that, and we’re going to be working with the DOM so a DomHelper would be handy too. The lines to include these classes are as follows:

goog.require( 'goog.events' );
goog.require( 'goog.ui.Dialog' );
goog.require( 'goog.dom.DomHelper' );
goog.require( 'goog.net.ImageLoader' );

Ok, so we have our namespace, we’ve included the required classes… We know we’re going to need at least a constructor, a method to launch the popup, a method to close the popup and a method to handle the image load, so let’s define those now:

// constructor – takes no arguments
neame.ui.InlinePopup = function(){ };// open popup – takes a click event as an argument
neame.ui.InlinePopup.prototype.goPopup = function( e ){ };

// close popup – takes a click event as an argument
neame.ui.InlinePopup.prototype.closePopup = function( e ){ };

// image loaded – takes a load event as an argument
neame.ui.InlinePopup.prototype.imageLoaded = function( e ){ };

// bind to class – adds a listener to any links with the specified class ‘c’
neame.ui.InlinePopup.prototype.bindToClass = function( c ){ };

Note that none of these methods will return anything (or if you like AS3 or Java, they return void).

So we have our skeletons set up but no logic yet. The constructor is the logical place to start. I’ll write out the code here and we can discuss it afterwards:

neame.ui.InlinePopup = function(){// init objects
this.dia = new goog.ui.Dialog( 'inlinepopup' , true );
this.loader = new goog.net.ImageLoader();
this.domh = new goog.dom.DomHelper();

// setup objects
this.dia.setVisible( false );
this.dia.setBackgroundElementOpacity( 0.9 );
this.dia.getButtonElement().parentNode.removeChild( this.dia.getButtonElement() );
this.dia.getTitleCloseElement().innerHTML = 'X';

// event listeners
goog.events.listen( this.dia.getBackgroundElement() ,  goog.events.EventType.CLICK , this.closePopup , false , this );
goog.events.listen( this.loader , goog.events.EventType.LOAD , this.imageLoaded , false , this );

};

First off, we instantiate our objects. We create a new Dialog with the class name ‘inlinepopup’, using an iframe mask for the background. Next up, we create our ImageLoader and DomHelper. Nothing too taxing so far.

Now for some more interesting stuff – let’s set up our Dialog. The syntax is pretty much self-commenting but all I’m doing here is making sure it’s not visible at first, setting the background to 90% opacity, removing the OK and Cancel buttons, and adding a simple ‘X’ character to the close button (you might want to use an image background via CSS instead). If you change setVisible to true right now, you’ll be able to see the Dialog on your page if you create a new InlinePopup… But it won’t look like much until we apply the CSS later.

Next we set up our event listeners. One to listen for a CLICK on the background and call closePopup. Another to listen for a LOAD event on our imageloader and call imageLoaded.

So our constructor is now in place! On to the open and close methods:

neame.ui.InlinePopup.prototype.goPopup = function( e ){
// dont follow the link
e.preventDefault();// add link href as image to load
this.loader.addImage( 'previewimg' , e.currentTarget.href );
this.loader.start();

// set title
if( e.currentTarget.title )
this.dia.setTitle( e.currentTarget.title ); // use title if the anchor has one
else
this.dia.setTitle( e.currentTarget.href ); // otherwise show href as title

};

neame.ui.InlinePopup.prototype.closePopup = function( e ){
this.dia.setVisible( false );
};

closePopup shouldn’t really need any explanation – all it does it setVisible to false on the Dialog (i.e.: it hides it).

openPopup is a little more involved. Since this is going to be launched from anchor elements on the page (<a>), we need to make sure the link isn’t followed or the browser will navigate off the page. To prevent this we make a call to preventDefault on the event. This means the default action of the click event will not fire – the link will not be followed and the user will stay on the page. Next, we pass the link href to the ImageLoader and tell it to start loading. Since the anchor element is the currentTarget of the CLICK event, we can access this URL via e.currentTarget.href. ‘previewimg’ in this call is just the ID we want to give the image. If you want to load a bunch of these you’ll need a unique ID for each image, but in this case we’re only dealing with 1 image at a time so it’s safe to use the same ID each time.  Finally, we set the Dialog title. If the anchor element has a title attribute, we use that. Otherwise we just default to the href value.

So, we have a constructor, a way to open the Dialog and close it, but what we are missing is logic to handle the image load:

neame.ui.InlinePopup.prototype.imageLoaded = function( e ){
// set width, then content , then show it
this.dia.getDialogElement().style.width = ( e.target.width + 30 ) + 'px';
this.dia.setContent( '<img src="' + e.target.src + '" width="' + e.target.width + '" height="' + e.target.height + '" />' );
// show it
this.dia.setVisible( true );
};

The first statement matches the Dialog width to the image width. This is done fairly simply by accessing the target of the load event (the image) and just reading off it’s width property. I’m adding 30 pixels here to allow for 15 pixels padding either side of the image. Then we just need to set the content of the dialog and show it.

Last up is the bindToClass method:

neame.ui.InlinePopup.prototype.bindToClass = function( c ){var es = this.domh.getElementsByTagNameAndClass( '*' , c );
// listen for CLICK Event on every object in the NodeList
for( i in es ) if( typeof es[ i ] == 'object' )
goog.events.listen( es[ i ] , goog.events.EventType.CLICK , this.goPopup , false , this );

};

This is pretty self-explanatory but all we’re doing here is selecting every element on the page with the class given and listening for a click event on it. Then the event is passed to our goPopup method to open the Dialog.

And that’s it for the InlinePopup class!

Here’s the CSS I used. Feel free to play around with these but you need to keep the position declarations, or the dialog will render below the background iframe rather than on top of it. I pretty much just ripped this off from the Dialog example page and then modified it to suit a style that I’d like:

.inlinepopup-bg {
position: absolute;
background-color: #000;
top: 0;
left: 0;
}
.inlinepopup {
position: absolute;
background-color: #000;
width: 1000px; /* this gets replaced anyway via Closure JS */
overflow: hidden;
}
.inlinepopup-title {
position: relative;
background-color: #000;
color: #fff;
padding: 10px 15px;
font-size: 16px;
font-weight: bold;
vertical-align: middle;
cursor: hand;
}.inlinepopup-title-close {
position: absolute;
top: 10px;
right: 15px;
width: 15px;
height: 16px;
cursor: default;
}

.inlinepopup-content {   padding: 15px; }

Finally, we pull it all together in our HTML document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<title>Google Closure Shadowbox!</title>

<script src="/path/to/closure/goog/base.js"></script>

<script src="inlinepopup.js"></script>

<link href=”style.css” />

</head>

<body>

<div><a href="http://arkandis.com/images/blog/google.gif" class="inlinepopup-trigger" title="Google logo in Lego"><img src="pic.jpg" width="200" height="125" alt="Pic" /></a></div>

<div><a href="http://images.businessweek.com/ss/06/07/top_brands/image/google.jpg" class="inlinepopup-trigger" title="Google logo on some dude's hand"><img src="pic.jpg" width="200" height="125" alt="Pic" /></a></div>

<script>

var popup = new neame.ui.InlinePopup();

popup.bindToClass( 'inlinepopup-trigger' );

</script>

</body>

</html>

Note: If you’re going to push this to a live site, don’t forget to compile your code. This stuff is just the source!

Anyway, hope you found the tutorial useful. Comments are very welcome. Next up is a Slideshow!

Dan

‘allo ‘allo

Posted in Uncategorized by googleclosuretutorials on November 12, 2009

There’s nothing here right now but if you check back on Friday the 13th November you will find a spiffing tutorial on how to build a Shadowbox / Lightbox with Google Closure.