July 27, 2010

Android browser bug: pinch/zoom kills setTimeout()

I'm working on some cross-platform/mobile touch/mouse code for a fancy html/js UI, and everything's been working great, but when I pinch/zoom the web page in an Android browser, my setTimeout() calls stop running. To be safe, I recommend using setInterval() instead.
// before:
setTimeout( function() { runTimer(); } , 1000/30 );
function runTimer() {
    // update graphics here
    setTimeout( function() { runTimer(); } , 1000/30 );
}

// after:
setInterval( function(){ runTimer(); }, 1000/30 );
function runTimer() {
    // update graphics here
}
I initially thought that my touch events (touchstart, touchmove, touchend) were randomly failing after zooming, because my custom motion code would completely break after running at a solid 30+ fps. It appears that this is a known bug in pre-2.2 (Froyo) Android web browsers: http://code.google.com/p/android/issues/detail?id=8566

July 25, 2010

PHPFlicker: Get images by user's tag

I started using phpFlickr (http://phpflickr.com/), so I could use Flickr as a query-able backend for some of my images. I'm tagging images that I want to show up on certain pages of my site, but there wasn't an example for using tags on the phpFlickr examples page. So here's an example:
<?php
 $api_key = "YOUR_API_KEY";
 $user_id = "YOUR_USER_ID";
 $secret = "YOUR_SECRET";
 
 require_once("./php/flickr/phpFlickr.php");
 $f = new phpFlickr($api_key, $secret);

 $photos = $f->photos_search(array( "api_key"=>$api_key, "user_id"=>$user_id, "tags"=>"promo", "tag_mode"=>"any", "extras"=>"original_format,tags,url_o,description") );
 
 // Loop through the photos and output the html
 foreach( (array)$photos['photo'] as $photo ) 
 {
  // get original, or large if no original
  if( isset( $photo['url_o'] ) ) 
   echo '<a rel="lightbox[flickr]" title="'. $photo['title'].' - '. $photo['description'].'" href="'. $photo['url_o'] .'">';
  else
   echo '<a rel="lightbox[flickr]" title="'. $photo['title'].' - '. $photo['description'].'" href="'. $f->buildPhotoURL($photo, "large") .'">';

  echo '<img border="0" alt="'.$photo[title].' - '. $photo['description'].'" title="'.$photo[title].' - '. $photo['description'].'" src="' . $f->buildPhotoURL($photo, "square") . '" />';

  echo "</a>";
 }
?>

Just replace YOUR_API_KEY, YOUR_USER_ID (something like: 38845956@N05), and YOUR_SECRET (something like 7fc67607bd5abc59). This type of search is "secure", meaning that you have to acquire a secret key via the Flickr API.

This script will display square thumbnails that link out to the largest available image size. This can easily by styled, augmented with a lightbox javascript, or customized via the phpFlickr search options.

Enjoy.

July 5, 2010

Javascript: iPad orientation class with Prototype.js

I'm writing an iPad app in "HTML5", and I wanted to keep track of the device orientation. I wrote this little class to send notifications when it changes, and to always have simple access to the current state. This requires Prototype.js, but could easily be ported to another OOP style.
var AppState = Class.create({
 PORTRAIT: 0,
 LANDSCAPE: 1,
 orientation: -1,    
 initialize: function() {
  this.orientation = this.PORTRAIT; // default for desktop browser
  this.setUpOrientationListener();
 },
 setUpOrientationListener : function() {
  // add listener to window if it's orientation-capable
  if( window.orientation !== undefined )
  {
   var self = this; // handles scope
   window.onorientationchange = function (event)
   {
    if ( Math.abs( window.orientation ) % 180 == 90 )
    {
     self.orientation = self.LANDSCAPE;
    }
    else
    {
     self.orientation = self.PORTRAIT;
    }
    // send out custom event
    var containerNode = $$('body');
    containerNode[0].fire("app:orientationchange", { orientation: self.orientation });
   }
   // make sure local flag is set right away
   window.onorientationchange(null);
  }
 }
});


/*
// example code for listening to custom event that fires on orientation change
document.observe("app:orientationchange", function(event) {
 console.log( "Tag " + event.target.tagName + " with id of " + event.target.id + " says the orientation is now " + event.memo.orientation + ".");
});
*/

/*
// class initialization
var app_state = new AppState(),
*/