Home  >  

Developing Mashup Air Apps: Consuming Flickr Web Services

Author photo
AddThis Social Bookmark Button

This is excerpted from Chapter 18 of the Adobe AIR 1.5 Cookbook by David Tucker, Marco Casario, Koen De Weggheleire, and Rich Tretola. The book includes hands-on recipes to help you solve a variety of tasks and scenarios often encountered when using Adobe AIR to build Rich Internet Applications for the desktop.

Get the Adobe AIR 1.5 Cookbook or download the PDF of this entire chapter.

Introduction

The term mashup has been used in the music industry for quite some time to define the result of producing a new sound by mixing two or more existing pieces together, so it’s easy to guess that in the world of web applications the term means the ability of an application to combine data from more than one source.

Web application mashups were born at the same time as the new approach of Web 2.0, which, among its many objectives, was aimed at reusing different data sources to create hybrid applications. So, mashups owe their success to the growth of user-generated content on the Web. For example, with the increase of blogs on the Internet, bloggers wanted to offer services and multimedia elements in addition to classical web content, which is how services such as YouTube, Flickr, and Google Maps started to be integrated in millions of posts, adding value to the simple text. But all this wouldn’t have been possible if it hadn’t been for the increase of web services and XML data sources that could be used and invoked by the applications. Another great catalyst of mashup applications was the success of Ajax and technologies such as Flex, Ruby on Rails, and now Adobe AIR.

Useful mashup applications are born on the Web every day. Here are a few examples that you can try straightaway to get a better idea of what it means to combine different data sources in one application:

Kayak

A mashup that consumes Expedia, Traveloprice, and other travel web services to help you choose your flight or hotel at your convenience. In the trip ideas section (http://www.kayak.com/h/buzz), it uses Google Maps.

Flash Earth

A zoomable mashup of satellite applications created with Flash. It consumes web services from many sources: NASA, Google Maps, Virtual Earth, Yahoo Maps, and OpenLayers—to name just a few.

Netvibes

Netvibes is a personalized start page similar to Pageflakes, My Yahoo, iGoogle, and Microsoft Live. It is written in Ajax and organized into tabs, with each tab containing user-defined modules. It integrates and consumes an RSS/Atom feed reader, local weather forecasts, a calendar supporting iCal, bookmarks, notes, to-do lists, and multiple searches. It also has support for POP3, IMAP4 email, and the webmail providers Gmail, Yahoo Mail, Hotmail, and AOL Mail. In addition, it supports Box.net web storage, del.icio.us, Meebo, Flickr photos, podcasts (via a built-in audio player), and more.

Note: You can find a more complete and updated list of available mashups at http://www.programmableweb.com/mashups.

Mashup applications are based on the possibility of consuming remote data sources, and to create one, you need a good understanding of the APIs available. (You can find an updated list of APIs at http://www.programmableweb.com/apis/directory.) AIR offers even greater possibilities for creating mashup applications and widgets. With AIR, you can go beyond all the sandbox security of the browser and add advanced features to the application to interact with the file system or local storage with SQLite. This chapter demonstrates how to integrate the Flickr, Yahoo Maps, and Twitter web services to create desktop mashup applications with AIR.

Consuming Flickr Web Services

Problem

You want to consume Flickr (http://www.flickr.com) web services.

Solution

Developing Mashup Air Apps: Consuming Flickr Web Services

Useful to amateur and professional photographers alike, Flickr is a portal that provides photographic material. You can use it as a simple private archive of password-protected photographs or as an online portfolio that can be freely accessed by potential clients or whoever else is interested. Flickr’s services, however, have limits. For example, because Flickr is a web application, it requires you use a web browser to access the service.

You can avoid this limitation with AIR.

Discussion

AIR applications allow you to access many functions that can’t normally be accessed by traditional web developers, such as reading and writing files on the user’s computer and creating multiwindow applications. By joining the simplicity of Flickr’s services with the power and flexibility of the AIR software development kit (SDK), you can create very interesting mashup applications. The only limits to the integrations are your imagination and needs.

To access Flickr services, you first must register and request a free application key (http://www.flickr.com/services/api/keys/apply/). The key consists of two codes: an access key and a shared secret key; the combined use of the two keys allows you to access all the services provided by the portal. To get the application key, you’ll be asked to fill out a simple online form (Figure 18.1, “Flickr wizard to obtain your free application key”), specifying the type of application you are creating, the technologies involved, and whether the final product will be commercial.

Figure 2
Figure 18.1. Flickr wizard to obtain your free application key

Once you obtain the application key to use the service, you can begin using Flickr. The documentation (Figure 18.2, “Flickr API online documentation”), which is very easy to understand, is available online at http://www.flickr.com/services/api.

Figure 1
Figure 18.2. Flickr API online documentation

The online documentation lists which functions are available to developers and how to use them. You will also find a list of the libraries to access existing Flickr services for the main web and desktop programming languages, such as PHP, .NET, Java, Delphi, Perl, and obviously ActionScript. The libraries that have been created to access the Flickr web service enable you to interact with the functions of the portal without having to worry about communication details between the web services and your development environment.

The recommended library on the Flickr portal, and the one used in this example, is as3flickrlib, developed by Mike Chambers and available as a Google Code project (http://code.google.com/p/as3flickrlib/downloads/list). In addition, as3flickrlib requires the as3corelib library in order to manage HTTP queries to the web services and to parse the returned XML data. You can download as3corelib from http://code.google.com/p/as3corelib/downloads/list.

Note: If the libraries don’t offer the specific functions that your application needs, you can either create a new library to communicate with Flickr services or extend one of the existing libraries.

The downloads are compressed archives that contain ActionScript 3 sources, documents in HTML format regarding the available functions, and compressed libraries in SWC format (Figure 18.3, “ActionScript 3 required libraries”). For AIR applications, you need the two libraries’ SWC files. They should automatically decompress to the lib folder in your project folder, which contains the AIR application project.

ActionScript/Flash CS4

Once you obtain the necessary data from Flickr to consume its web services, you can begin creating the ActionScript class to control the mashup of the remote data.

From ASDoc in as3flickrlib (http://as3flickrlib.googlecode.com/svn/trunk/docs/index.html), take a look at the classes you’ll be working with:

  • FlickrService: Abstracts the Flickr API found at http://www.flickr.com/services/api

  • FlickrError: Common errors that can happen during a call to a Flickr method

  • FlickrResultEvent: Event class that contains information about events broadcast in response to data events from the Flickr API

  • PagedPhotoList: A ValueObject for the Flickr API

  • Photo: A ValueObject for the Flickr API

  • PhotoSize: A ValueObject for the Flickr API

  • User: A ValueObject for the Flickr API

Figure 3
Figure 18.3. ActionScript 3 required libraries

Here is the complete code of the FlickrWS.as class to launch a search and display the response data using Flickr APIs:

package com.oreilly.aircookbook.bonus
{
     import com.adobe.webapis.flickr.FlickrService;
     import com.adobe.webapis.flickr.PagedPhotoList;
     import com.adobe.webapis.flickr.Photo;
     import com.adobe.webapis.flickr.PhotoSize;
     import com.adobe.webapis.flickr.User;
     import com.adobe.webapis.flickr.events.FlickrResultEvent;
 
     import flash.display.Bitmap;
     import flash.display.Loader;
     import flash.events.Event;
     import flash.events.IOErrorEvent;
     import flash.net.URLRequest;
     import flash.system.Security;
 
     import mx.collections.ArrayCollection;
 
     [Bindable]
     public class FlickrWS
     {
          private const CROSSDOMAIN_ADDRESS:String = "http://api.flickr.com/crossdomain.xml";
          private const SECRET_KEY:String = "YOURSECRETKEY";
          private const SHARED_SHARED_KEY:String = "YOURSHAREDKEY";
  
          private const THUMBHEIGHT:Number = 175;
          private const THUMBWIDTH:Number = 155;
  
          public var fs:FlickrService;
  
          private var imgList:PagedPhotoList;
  
          private var userVO:User;
  
          private var activePhotoVO:Photo;
  
          private var _currImage:Number = 0;
  
          public function get currImage():Number
          {
               return _currImage;
           }
  
          public function set currImage(_currImage:Number):void
          {
               this._currImage = _currImage;
           }
  
          public function get thumbsDP():ArrayCollection
          {
               return _thumbsDP;
           }
  
          public function FlickrWS()
          {
               Security.loadPolicyFile( CROSSDOMAIN_ ADDRESS );
               fs = new FlickrService(SHARED_KEY);
               fs.secret = SECRET_KEY;
   
               fs.addEventListener( FlickrResultEvent.PHOTOS_GET_SIZES, onPhotoSize );
               fs.addEventListener( FlickrResultEvent.PEOPLE_GET_INFO, onUserInfo );
               fs.addEventListener( FlickrResultEvent.PHOTOS_SEARCH , onPhotoList );
   
           }
  
          private function onPhotoList( evt:FlickrResultEvent ):void
          {
   
               imgList = evt.data.photos as PagedPhotoList;
   
               if (imgList && imgList.total > 0)
               {
                    loadNextImage();
    
                } else
               {
                    trace("No results");
                }
           }
  
          private function onUserInfo( evt:FlickrResultEvent ):void
          {
               userVO = evt.data.user as User;
   
               checkReadyStatus();
           }
  
          private function onPhotoSize( evt:FlickrResultEvent ):void
          {
               if ( evt.success )
               {
                    var availableSizeList:Array = evt.data.photoSizes;
    
                    var availableSize:PhotoSize;
    
                for(var i:int = 0; i < availableSizeList.length; i++)
                {
                     availableSize = availableSizeList[i];
     
                 if ( availableSize.width > THUMBWIDTH || 
                      availableSize.height > THUMBHEIGHT )
                 {
                      break;
                  }
             }
    
            if( availableSize.source != null || availableSize.source != "" )
            {
     
             if( imgLoader != null )
             {
                  imgLoader.contentLoaderInfo.removeEventListener( Event.COMPLETE, onImageReady );
                  imgLoader.contentLoaderInfo.removeEventListener( IOErrorEvent.IO_ERROR, 
                                                                   onImageError );
              }
     
             imgLoader = new Loader();
     
             imgLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, onImageReady );
             imgLoader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, onImageError );
     
             imgLoader.load( new URLRequest( availableSize.source ) );
     
             } else
            {
                 loadNextImage();
             }
    
            } else
           {
                loadNextImage();
            }
           }
  
  
          private function checkReadyStatus():void
          {
               if ( isImgReady )
               {
                    var photoDataRow:Object = new Object();
    
                    photoDataRow[ 'label' ] = imageTitle + " [" + imageAuthor + "]";
    
                    photoDataRow[ 'source' ] = thumbnailImage;
    
                    photoDataRow[ 'scaleContent' ] = true;
    
                    photoDataRow[ 'photoVO' ] = activePhotoVO;
    
                    _thumbsDP.addItem( photoDataRow );
    
                    loadNextImage();
    
                } else
               {
                    isImgReady = true;
                }
           }
  
  
          public function loadNextImage():void
          {
               if (currImage >= imgList.photos.length)
               {
                    return;
                }
   
                   activePhotoVO = imgList.photos[ currImage ];
   
                   isImgReady = false;
   
               if( activePhotoVO != null )
               {
    
                if( activePhotoVO.id != null )
                {
     
                 fs.photos.getSizes( activePhotoVO.id );
     
                 if( activePhotoVO.ownerId != null )
                 {
      
                      fs.people.getInfo( activePhotoVO.ownerId );
      
                  }else
                 {
      
                      isImgReady = true;
                  }
                 } else
                {
                     currImage++;
     
                     loadNextImage();
     
                     return;
                 }
                } else
               {
    
                }
   
                   currImage++;
               }
  
  
          private function onImageReady( evt:Event ):void
          {
               try
               {
                    var imageAlias:Bitmap = Bitmap( imgLoader.content );
    
                    checkReadyStatus();
    
                } catch (e:Error)
               {
                 trace("Returned image was not a proper bitmap: " + imgLoader.loaderInfo.url);
    
    
                loadNextImage();
            }
           }
  
  
          private function onImageError( evt:IOErrorEvent ):void
          {
               trace( "Error loading image: " + imgLoader.loaderInfo.url );
   
               loadNextImage();
           }
  
  
  
          public function get thumbnailImage():Bitmap
          {
            return imgLoader.content as Bitmap;
           }
  
  
          public function get imageTitle():String
          {
            return activePhotoVO.title;
           }
  
  
          public function get imageAuthor():String
          {
            return userVO.fullname;
           }
          }
}</code></pre>

<p>The ActionScript class uses the Flickr library and imports all the classes it needs with a series of imports that point to the <code>com.adobe.webapis.flickr</code> package. It defines private constants that contain your API keys and the URL of the cross-domain file:</p><a name="I_programlisting18_d1e23251"></a>

<div class="acode" style="overflow: auto; padding: 10px;" ><div style="overflow-x: visible;">
<mt:CodeBeautify language="actionscript">private const CROSSDOMAIN_ADDRESS:String = "http://api.flickr.com/crossdomain.xml";
private const SECRET_KEY:String = "YOURSECRETKEY";
private const SHARED_KEY:String = "YOURSHAREDKEY";</pre><p>The class constructor creates event listeners to control the response data of a search, which includes the information returned on the images and the users:</p><a name="I_programlisting18_d1e23255"></a><pre>fs.addEventListener( FlickrResultEvent.PHOTOS_GET_SIZES, onPhotoSize );
fs.addEventListener( FlickrResultEvent.PEOPLE_GET_INFO, onUserInfo );
fs.addEventListener( FlickrResultEvent.PHOTOS_SEARCH , onPhotoList );</pre><p>The data provider is populated in the <code>onPhotoSize</code> event listener, which is triggered by the <code>Result</code> event of the Flickr call, and then the data provider is passed into the <code>tileList</code> control to display the images:</p><a name="I_programlisting18_d1e23269"></a><pre>var photoDataRow:Object = new Object();

photoDataRow[ 'label' ] = imageTitle + " [" + imageAuthor + "]";

photoDataRow[ 'source' ] = thumbnailImage;

photoDataRow[ 'scaleContent' ] = true;

photoDataRow[ 'photoVO' ] = activePhotoVO;

_thumbsDP.addItem( photoDataRow );


loadNextImage();

A photoDataRow object is created, holding the returned image and metaproperties such as label, scaleContent, and photoVO. This is added to the _thumbsDP ArrayCollection using the addItem method.

Then the loadNextImage method is called, which repeats the actions for each element in the response data.

The ActionScript class can now easily be implemented in an AIR application. The following is the MXML code that uses the class you just created:

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

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 initialize="init()"
 showFlexChrome="false"
 showStatusBar="false">

<mx:DefaultTileListEffect id="TLEffect"
fadeOutDuration="350"
fadeInDuration="350"
moveDuration="1000" />
<mx:Script>
<![CDATA[

    import mx.controls.Alert;
    import flash.events.MouseEvent;
    import com.oreilly.aircookbook.bonus.FlickrWS;

    [Bindable]
    private var fsClass:FlickrWS;

    private function init():void
    {
         fsClass = new FlickrWS();
         searchBtn.addEventListener( MouseEvent.CLICK, startNewSearch );
     }

    private function startNewSearch( evt:MouseEvent ):void
    {
 
         if( searchTxt.text == "" )
         {
              return;
          }
 
         fsClass.thumbsDP.removeAll();
 
         fsClass.currImage = 0;
 
         var tagSearch:String;
         var generalSearch:String;
 
         if( searchCbx.selectedLabel == "tag" )
         {
              tagSearch = searchTxt.text;
              generalSearch = "";
  
          }else {
  
              tagSearch = "";
              generalSearch = searchTxt.text;
          }
 
         fsClass.fs.photos.search("", tagSearch, "any", generalSearch, null, null, null, null, 
                                  &#8722;1, "", 20, 1, "interestingness-desc");
 
     }
]]>
</mx:Script>


<mx:TextInput id="searchTxt" />

<mx:Button label="Search" id="searchBtn" />

<mx:TileList id="photoTL" dataProvider="{fsClass.thumbsDP}"
 columnCount="4" rowCount="3" rowHeight="155" columnWidth="175"
 itemRenderer="com.oreilly.aircookbook.bonus.FlickrThumbnail"
 itemsChangeEffect ="{TLEffect}"
 dragEnabled="true"
 dropEnabled="true"
 dragMoveEnabled="true"  />

<mx:ComboBox id="searchCbx">
<mx:dataProvider>
<mx:Object label="Tag Search" data="tag" />
<mx:Object label="Text Search" data="text" />
</mx:dataProvider>

</mx:ComboBox>
</mx:WindowedApplication>

The application has a ComboBox control to allow the user to launch the search by using the text or a tag (inserted by the user) as a parameter in a TextInput control. The search on the Flickr web services actually occurs when the startNewSearch method is invoked, triggered by the click event of the Button control. The search method of the Flickr SWC library is invoked, and the parameters it needs to obtain the images from the Flickr web service are sent to it:

fsClass.fs.photos.search("", tagSearch, "any", generalSearch, null, null, null, null, 
&#8722;1, "", 20, 1, "interestingness-desc");

The request data is controlled by the onPhotoList method of the ActionScript class, which populated the thumbsDP data provider typed as ArrayCollection. This value is passed onto the TileList control, creating a data binding:

<mx:TileList id="photoTL" dataProvider="{fsClass.thumbsDP}"
 columnCount="4" rowCount="3" rowHeight="155" columnWidth="175"
 itemRenderer="com.oreilly.aircookbook.bonus.FlickrThumbnail"
 itemsChangeEffect ="{TLEffect}"
 dragEnabled="true"
 dropEnabled="true"
 dragMoveEnabled="true"  />

This control uses an item renderer to display the image and text as an element of the list. The renderer is passed to it with the itemRenderer property, where an external component, called FlickrThumbnail, is specified in com.oreilly.aircookbook.bonus.

The TileList control also makes it possible to drag and drop items in it by applying the specified effect to the movements at the beginning of the MXML application:

<mx:DefaultTileListEffect id="TLEffect"
fadeOutDuration="350"
fadeInDuration="350"
moveDuration="1000" />

This is the code of the FlickrThumbnail.mxml component, which exploits the data property that is propagated by the TileList in the item renderer and contains the data provider and all its properties:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
    horizontalAlign="center"
    verticalGap="0"
    borderStyle="none" backgroundColor="white" >

    <mx:Image id="image" width="60" height="60" source="{data.source}"/>
    <mx:Label text="{data.label}" width="120" textAlign="center"/>

</mx:VBox>

This simple mashup implements and consumes only one method from the Flickr APIs. You could improve it by adding remote features and by exploiting AIR SDKs to allow users to save images locally or create a local database with SQLite to store their favorite images.

HTML/JavaScript

Get the Adobe AIR 1.5 Cookbook or download the PDF of this entire chapter.

You can also consume Flickr services by using HTML and JavaScript. Before you begin, it’s worth consulting the API explorer tool (http://www.flickr.com/services/api/explore), with which you can query any method of Flickr’s APIs, send parameters to it, and display the response. The syntax is http://www.flickr.com/services/api/explore/?method=METHODNAME. For example, the following URL invokes the search method of the APIs, and, in the page that opens, you can send the parameters by filling in the text inputs:

http://www.flickr.com/services/api/explore/?method=flickr.photos.search

What the API explorer actually does is build the web page to pass the parameters of the method using the reflection approach and build the REST URL of that method.

Once you get familiar with Flickr’s APIs, you can start interacting with them. By using a Flickr badge, for example, you can create a web page showing your photos by importing the badge from http://www.flickr.com/badge_new.gne. Then again, you can create your own Flickr badge to insert in your AIR application with a few lines of JavaScript code.

Flickr generates feeds in different formats and for different types of content, so your application can consume these feed links in various languages. You can, for example, consume the feeds to obtain recent public photos that have been uploaded on Flickr from http://api.flickr.com/services/feeds/photos_public.gne or your own public photos from http://api.flickr.com/services/feeds/photos_public.gne?id=USER-NSID.

Now all you have to do is choose the format to consume: RSS, ATOM, PHP, JSON, CSV, YAML, or SQL. The example uses the JavaScript Object Notation (JSON).

Note: According to Wikipedia, JSON is a lightweight computer data interchange format. It is a text-based, human-readable format to represent simple data structures and associative arrays (called objects). The JSON format is often used for transmitting structured data over a network connection in a process called serialization. Its main application is in Ajax web application programming, where it serves as an alternative to the XML format. Although JSON was based on a subset of JavaScript and is commonly used with that language, it is considered to be a language-independent data format. Code for parsing and generating JSON data is readily available for a large variety of programming languages. You can find a comprehensive listing of existing JSON bindings, organized by language, at http://www.json.org.

When you launch the URL http://api.flickr.com/services/feeds/photos_public.gne?id=44124469126@N01&format=json from your browser, the following JavaScript code is generated:

jsonFlickrFeed({
           "title": "Uploads from Marco Casario",
           "link": "http://www.flickr.com/photos/44124469126@N01/",
           "description": "",
           "modified": "2008-07-27T21:10:55Z",
           "generator": "http://www.flickr.com/",
           "items": [
        {
                 "title": "STA72737",
                 "link": "http://www.flickr.com/photos/44124469126@N01/2708076572/",
                 "media": {"m":"http://farm4.static.flickr.com/3115/2708076572_505dc798eb_m.jpg"},
                 "date_taken": "2008-07-27T23:10:55-08:00",
                 "description": "&amp;lt;p&amp;gt;&amp;lt;a 
  href=&amp;quot;http://www.flickr.com/people/44124469126@N01/&amp;quot;&amp;gt;Marco 
  Casario&amp;lt;/a&amp;gt; posted a photo:&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;&amp;lt;a 
  href=&amp;quot;http://www.flickr.com/photos/44124469126@N01/2708076572/&amp;quot; 
  title=&amp;quot;STA72737&amp;quot;&amp;gt;&amp;lt;img 
  src=&amp;quot;http://farm4.static.flickr.com/3115/2708076572_505dc798eb_m.jpg&amp;quot; 
  width=&amp;quot;240&amp;quot; height=&amp;quot;180&amp;quot; alt=&amp;quot;STA72737&amp;quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt; ",
                 "published": "2008-07-27T21:10:55Z",
                 "author": "nobody@flickr.com (Marco Casario)",
                 "author_id": "44124469126@N01",
                 "tags": "seychelles"
         },
        {
                 "title": "STA72447",
                 "link": "http://www.flickr.com/photos/44124469126@N01/2708076122/",
                 "media": {"m":"http://farm4.static.flickr.com/3118/2708076122_e04bf714d7_m.jpg"},
                 "date_taken": "2008-07-27T23:10:46-08:00",
                 "description": "&amp;lt;p&amp;gt;&amp;lt;a 
  href=&amp;quot;http://www.flickr.com/people/44124469126@N01/&amp;quot;&amp;gt;Marco 
  Casario&amp;lt;/a&amp;gt; posted a photo:&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;&amp;lt;a 
  href=&amp;quot;http://www.flickr.com/photos/44124469126@N01/2708076122/&amp;quot; 
  title=&amp;quot;STA72447&amp;quot;&amp;gt;&amp;lt;img 
  src=&amp;quot;http://farm4.static.flickr.com/3118/2708076122_e04bf714d7_m.jpg&amp;quot; 
  width=&amp;quot;240&amp;quot; height=&amp;quot;180&amp;quot; alt=&amp;quot;STA72447&amp;quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt; ",
                 "published": "2008-07-27T21:10:46Z",
                 "author": "nobody@flickr.com (Marco Casario)",
                 "author_id": "44124469126@N01",
                 "tags": "seychelles"
         },
 ....
}
]
})

You can use this feed to dynamically create an HTML page by using JavaScript only. The following is the JavaScript code, saved to a file called FlickrBadge.js, that creates the elements in an HTML page and displays the photos of a Flickr user:

function jsonFlickrFeed(feed)
{
 
   var flickrRegExp = /(http:\/\/farm4.static.flickr.com\/\d+\/\d+_[0-9a-z]+)_m\.jpg/;
   var htmlContent = "";
   var items = feed['items'];
 
   htmlContent += '<div id="flickr_badge_uber_wrapper">';
   htmlContent += '<p class="title"><nobr>Go to</nobr><a href="' + feed['link'] + '">' + 
                  feed['title'] + '</a></p>';
 
   htmlContent += '<ul id="flickr_badge_source">';
 
 
   for (var i = 0; i < items.length; i++)
   {
      var data = flickrRegExp.exec(items[i]['description']);
  
      if (data != null)
      {
         var image = data[1] + '_s.jpg';
   
         htmlContent += '<li class="flickr_badge_image"><a href="' + items[i]['link'] + '" 
                        id="flickr_www"><img src="'
             + image + '" /></a></li>';
       }
    }
   htmlContent += '</ul><p id="flickr_badge_source_txt"><nobr>Go to</nobr> <a href="' + 
                  feed['link'] + '">' + feed['title'] + '</a></p>'
 
   htmlContent += '</div>';
 
   document.writeln(htmlContent);
}

This script dynamically generates an HTML <ul> list of elements that repeats a for loop as many times as the number of items that are returned from the feed.

Most of the work is carried out by the regular expression, which returns the string that matches the URL of the Flickr image:

var flickrRegExp = /(http:\/\/farm4.static.flickr.com\/\d+\/\d+_[0-9a-z]+)_m\.jpg/;

The URL address http://farm4.static.flickr.com may change for your photos. Pay attention to the URL used by Flickr to store your public photos in the media property of the JSON feed:

"media": {"m":"http://farm4.static.flickr.com/3115/2708076572_505dc798eb_m.jpg"},

The response data of the method flickrRegExp.exec(items[i]['description']) actually creates the image list.

Now you can create the web page that calls the JSON feed and this simple JavaScript library:

<!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" />

<link rel="stylesheet" type="text/css" href="css/main.css"/>

<script type="text/javascript" src="FlickrBadge.js"></script>

<script
src='http://api.flickr.com/services/feeds/photos_public.gne?id=44124469126@N01&amp;format=json'
type='text/javascript'></script>

<title>Flickr Badge</title>
</head>

<body>
</body>
</html>

This HTML page doesn’t specify any content in its body. All the work is carried out by the JavaScript contained in the FlickrBadge.js file. The main.css file applies a style formation to the page with the following CSS syntax:

body {padding:0; font: 12px Arial, Helvetica, Sans serif; color:#666666;}

.flickr_badge_image {text-align:center !important;display: inline;list-style-type: none;}

.flickr_badge_image img {border: 1px solid black !important;}

#flickr_badge_uber_wrapper {width:120px;}
#flickr_www {display:block; text-align:center; padding:0 10px 0 10px !important; 
 font: 11px Arial, Helvetica, Sans serif !important; color:#3993ff !important;}

#flickr_badge_uber_wrapper a:hover,
#flickr_badge_uber_wrapper a:link,
#flickr_badge_uber_wrapper a:active,
#flickr_badge_uber_wrapper a:visited {text-decoration:none !important; background:inherit 
 !important;color:#3993ff;}
#flickr_badge_wrapper {background-color:#ffffff;border: solid 1px #000000}

#flickr_badge_source {padding:0 !important; font: 11px Arial, Helvetica, Sans serif 
 !important; color:#666666 !important;width: 190px; margin: 0; padding: 0;}

Go to the catalog page for the Adobe AIR 1.5 Cookbook. Download the PDF of this entire chapter. View the book's table of contents.

Read more from David Tucker. David Tucker's Atom feed

Read more from Rich Tretola. Rich Tretola's Atom feed richtretola on Twitter

Read more from Marco Casario. Marco Casario's Atom feed

Comments

10 Comments

Quite informative . Thanks for sharing.

Regards,
Software
http://www.sblsoftware.com/embedded-prog.aspx

ilja.panin said:

Great book.
Cool information about flickr.

Usefull information I will use it in my new project!

The application has a Combo Box control to allow the user to launch the search by using the text or a tag (inserted by the user) as a parameter in a Text Input control. The search on the Flick web services actually occurs when the star tNew Search method is invoked, triggered by the click event of the Button control his simple smashup implements and consumes only one method from the Flickr APIs. You could improve it by adding remote features and by exploiting AIR SDKs to allow users to save images locally or create a local database with Sq Lite to store their favorite images.

l. The search on the Flick web services actually occurs when the star tNew Search method is invoked, triggered by the click event of the Button control his simple smashup implements and consumes only one method from the Flickr APIs. You could improve it by adding remote features and by exploiting AIR SDKs to allow users to save images locally or create a local database with Sq Lite to store their favorite images...
http://www.roulette-download.com

I am also using Flickr on my blogs. The Flickr API exposes identifiers for users, photos, photosets and other uniquely identifiable objects. These IDs should always be treated as opaque strings, rather than integers of any specific type. The format of the IDs can change over time, so relying on the current format may cause you problems in the future.

fit flops said:

It is quite informative article. Adding data by two different sources are easy now with massup. Thanks for sharing it here.

Trish said:

The search on the Flick web services actually occurs when the star tNew Search method is invoked.Custom Research Paper

Well..according to Wikipedia, JSON is a lightweight computer data interchange format. It is a text-based, human-readable format to represent simple data structures and associative arrays (called objects). The JSON format is often used for transmitting structured data over a network connection in a process called serialization. Its main application is in Ajax web application programming, where it serves as an alternative to the XML format.
Thanks

sem said:

that's for sharing
all the best~

Bob de Haan said:

I've been working on an flickr extension for MediaWiki on LeningWiki. Currently I have a problem figuring out to what extent copyright free images can be imported from the Flickr Api. Is this automatically done by the Flickr API, or should I use a restriction for that? For a Community integration this option is vital (you can't keep control on all image licenses if Flickr is integrated.)

Plz. contact me by email if you an answer for my problem

Leave a comment


Tag Cloud

Question of the Week: Open Source Flex Projects

What would you say are the 5 most prominent open source projects in the Flex world?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.