Archive for November, 2006

Suggested Firefox add-on

Tuesday, November 14th, 2006

I’ve found this little Firefox add-on: Tamper Data:

Use Tamper Data to view and modify HTTP/HTTPS headers and post parameters.

It is useful to see the headers sent, the response time and to alter form data, but the best feature is the connections graph.
Worth a try.

Scroll browser window inside Flash

Tuesday, November 14th, 2006

Updated: fixed some typos

I've found that resizing a flash movie to fit his content has a little drawback: the mouse wheel doesn't scroll the browser window when the movie has focus, and in my case the movie has width: 100%.
Luckily I've found a nice javascript to scroll the page.

Add this javascript to the page:

JAVASCRIPT:
  1. function getScrollXY() {
  2.   var scrOfX = 0, scrOfY = 0;
  3.   if( typeof( window.pageYOffset ) == 'number' ) {
  4.     //Netscape compliant
  5.     scrOfY = window.pageYOffset;
  6.     scrOfX = window.pageXOffset;
  7.   } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
  8.     //DOM compliant
  9.     scrOfY = document.body.scrollTop;
  10.     scrOfX = document.body.scrollLeft;
  11.   } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
  12.     //IE6 standards compliant mode
  13.     scrOfY = document.documentElement.scrollTop;
  14.     scrOfX = document.documentElement.scrollLeft;
  15.   }
  16.   return [ scrOfX, scrOfY ];
  17. }

and in your movie:

Actionscript:
  1. var mouseListener:Object = new Object();
  2. mouseListener.onMouseWheel = function(delta:Number) {
  3.   getURL("javascript:window.scroll(getScrollXY()[0], getScrollXY()[1] + "+(0-delta*8)+");");
  4. };
  5. Mouse.addListener(mouseListener);

I multiply to 8 the delta to adjust the mouse sensitivity. Sadly there isn't a way to find the user's system sensitivity.
Maybe a better way can be to scroll directly to a specific point in the page:

Actionscript:
  1. mouseListener.onMouseWheel = function(delta:Number) {
  2.   if(delta> 0) {
  3.     getURL("javascript:window.scroll(getScrollXY()[0], "+this.bottom+");");
  4.   }
  5.   else {
  6.     getURL("javascript:window.scroll(getScrollXY()[0], "+this.top+");");
  7.   }
  8. };

Resize Flash at runtime

Saturday, November 11th, 2006

If your content is bigger than the average screen size, using a flash scrollbar for the entire document is really slow and not user-friendly, so its better to enlarge flash to fit the content and scroll the entire page with the browser's scroll bar.

Put the flash object in a div:

HTML:
  1. ...
  2. <SCRIPT LANGUAGE="JavaScript">
  3. <!--
  4. function newSize(width,height) {
  5.     if(document.all && !document.getElementById) {
  6.       document.all['myflash'].style.pixelWidth = width;
  7.       document.all['myflash'].style.pixelHeight = height;
  8.     }else{
  9.         document.getElementById('myflash').style.width = width;
  10.         document.getElementById('myflash').style.height = height;
  11.     }
  12. }
  13. //-->
  14. </script>
  15. </head>
  16. <div id="myflash" style="position:relative; width:100%; height:100%; z-index:1; min-width:1000px;">
  17.  ...
  18. </object>
  19. </div>

and in the movie do:

Actionscript:
  1. getURL("javascript:newSize('1000px', '100%')");

Other info here

Flash max connections

Saturday, November 11th, 2006

Recently I'm making a Flash photo gallery for my dad and I'm trying to optimize the loading of many pictures at once.
The question is: how many concurrent download can Flash make?
Google seems to have no answer, so I decided to make a small test to find it out by myself.

So I've made an swf that load 4 pictures simultaneously, and the result is in line with the HTTP specifications of max 2 connections per server.
More exactly, Flash use the browser configuration, in Firefox is Network.http.max-connections-per-server in about:config.
Can it be improved?
It is possible to use the old trick of loading images through multiples domain: for example static1.foo.com, static2.foo.com. This increase the maximum connection limit until network.http.max-connections

Default values in Firefox 1.5 are 8 for max-connections-per-server and 24 for max-connections.

You can try it by yourself:
Load from a single domain
Load from two domain

Sources:
Single domain
Two domain

Feed problem

Wednesday, November 8th, 2006

It seems that various aggregators have had problems with my last isp change, Feedburner now seems to work, bloglines, are you still there?

Simple CakeAMFPhp How To

Wednesday, November 8th, 2006

It's been a while since I last wrote, sorry! I've been busy with work, thesis and some other suffs (which I'll be posting about in the next days).
One of those is AMFPhp: a method to connect Flash with Php.
Why AMF instead of JSON or XML? Basically because AMF is more mature than JSON, offer a ready-made and really complete set of classes, many utilities like the connection debugger component, a great Php implementation, and, above all, CakeAMFPhp. (AMF is also a binary protocol on top of HTTP, so performance should be better than XML)
CakeAMFPhp is the integration of AMFPhp and Cake, a Rails-inspired php framework. With Cake I've quickly build a prototype with the Bake command-line utility. Then I've started implementing the methods for the AMF calls. Thanks to the great database abstraction, every method is only few lines long.

Since CakeAMFPhp is a beta, I've found some problems, for which I've found a quick fix that I'll explain shortly.

To start a CakeAMFPhp project, simply download the required libraries as explained in this tutorial. (If you are new to Cake you should also see this)
Then in your controller, for example GalleriesController, put this:

PHP:
  1. vendor("cakeamfphp/amf-core/util/MethodTable");
  2. vendor('cakeamfphp/amf-core/adapters/lib/Arrayft');
  3. vendor('cakeamfphp/amf-core/adapters/lib/Arrayf');
  4.  
  5. class GalleriesController extends AppController
  6. {
  7. ...
  8. function GalleriesController()
  9. {
  10.   //AMF:
  11.   $this->methodTable = MethodTable::create(__FILE__);
  12.   parent::__construct();
  13. }

In the line 11 we call the automatic method table constructor of AMFPhp: it read the javadoc-like comments in the file and do all the magic:

PHP:
  1. /**
  2. * @desc Return the list of public galleries
  3. * @access remote
  4. * @pagesize 25
  5. */
  6. function getGalleries($offset = 0, $limit = 25) {
  7.   $data = $this->Gallery->findAll(null, null, 'created DESC', $limit, $offset, 0);
  8.   return $this->_arrayft($data, 'Gallery');
  9. }
  10.  
  11. function getGalleries_count() {
  12.   return $this->Gallery->findCount();
  13. }

The @access remote tag specify that the getGalleries method is callable from flash:

Actionscript:
  1. var ser = new Service("http://localhost/cake_gateway.php", null, 'GalleriesController', null , null);
  2. var pc:PendingCall = ser.getGalleries();
  3. pc.responder = new RelayResponder(this, "handleResult", "handleError");

The getGalleries_count() method is needed for the pageable recordset:

Actionscript:
  1. function handleResult (re:ResultEvent) {
  2.   var rs = RecordSet(re.result);
  3.   rs.addEventListener('modelChanged', this); //Listen for updateItems
  4.   this.totalItems = rs.getLength();
  5.  
  6.   for(var i:Number=startFrom; i <l; i++) {
  7.     item = rs.getItemAt(i);
  8.     if(i <rs.getNumberAvailable()) { //It is already fetched
  9.     ... //Process a row
  10.   }
  11. }

getGalleries return only the first 25 row from the database. When the data is loaded in flash, rs.getLength() method return the value from getGalleries_count(). getItemAt return an item or a future, we check if the item is loaded in the line 8. If the item isn't from the ones loaded, it is automatically requested from the server when we call getItemAt. When it arrives, the modelChanged method is called:

Actionscript:
  1. function modelChanged (info:Object) {
  2.   var rs = this.rs;
  3.   var item:Object;
  4.   if(info.eventName == "updateItems") {
  5.     for(var i:Number=info.firstItem; i <= info.lastItem; i++) {
  6.       item = rs.getItemAt(i);
  7.       target.addItem(item);
  8.     }
  9.   }
  10. }

in the info object there are the rows received, you can configure Flash to get only the item required or the page containing it.
That's all, simply add the @access remote tag, a *methodname*_count method and it works.
You can find more information on pageable recordsets here.

The _arrayft is required for the issue I've talk about before: CakeAMFPhp 0.6 does not support sending recorset to flash, so this method is required to transform the data returned by the Cake database abstraction layer into a AMF-compatible recordset:

PHP:
  1. function _arrayft($data, $table) {
  2.   $r = array();
  3.   if(!empty($data[$table])) {
  4.     //Multi table query:
  5.     if(is_array($data[$table][0])) {
  6.       //Nested table:
  7.       foreach($data[$table] as $t) {
  8.         $r[] = $t;
  9.       }
  10.     }
  11.     else {
  12.     //Sigle record:
  13.       foreach($data[$table] as $t) {
  14.         $r[] = $t;
  15.       }
  16.     }
  17.   }
  18.   else {
  19.     foreach($data as $t) {
  20.       array_push($r, $t[$table]);
  21.     }
  22.   }
  23.   return new Arrayf($r, array_keys($r[0])  );
  24. }

For performance reason, it is suggested to use a 0 'recursive' value in the find(..) call.