////////////////////////////////////////////////////////////////////////////////
//
//  tandem. explore your world.
//  Copyright (c) 2007–2008 Daniel Gasienica (daniel@gasienica.ch)
//
//  tandem is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  tandem is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with tandem. If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
package tandem.core
{

import caurina.transitions.Tweener;
import caurina.transitions.properties.ColorShortcuts;

import com.adobe.webapis.flickr.FlickrService;
import com.adobe.webapis.flickr.PagedPhotoList;
import com.adobe.webapis.flickr.User;
import com.adobe.webapis.flickr.events.FlickrResultEvent;

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.Security;

import tandem.events.TandemEvent;
import tandem.model.ApplicationModel;
import tandem.ui.GlobalNavigation;
import tandem.ui.GlobalNavigationComponent;
import tandem.ui.KeyboardNavigationButton;
import tandem.ui.KeyboardNavigationButtonComponent;
import tandem.ui.MemoryIndicator;
import tandem.ui.MemoryIndicatorComponent;
import tandem.ui.NotificationOverlay;
import tandem.ui.NotificationOverlayComponent;
import tandem.ui.Timeline;
import tandem.ui.ZoomNavigator;
import tandem.ui.ZoomViewport;
import tandem.ui.views.StreamView;

public class Application extends Sprite
{
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    /**
    * Constructor.
    */
	public function Application()
	{
		addEventListener( Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true )
    }

    //--------------------------------------------------------------------------
    //
    //  Class Constants
    //
    //--------------------------------------------------------------------------

    private const DEFAULT_USER_ADDRESS : String = "gasi"
    private const DEFAULT_USER_ID : String = "72389028@N00"
    private const DEFAULT_MINIMUM_ZOOM : Number = 0.25//0.5
    private const DEFAULT_MAXIMUM_ZOOM : Number = 180

    //--------------------------------------------------------------------------
    //
    //  Children
    //
    //--------------------------------------------------------------------------

    private var globalNavigation : GlobalNavigation
    private var memoryIndicator : MemoryIndicator
    private var notificationOverlay : NotificationOverlay
    private var keyboardNavigationButton : KeyboardNavigationButton
    private var timeline : Timeline

    private var viewport : ZoomViewport
    private var navigator : ZoomNavigator

    private var view : DisplayObject
    private var initialized : Boolean = false

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    private var model : ApplicationModel = ApplicationModel.getInstance()
    private var completed : Boolean = false

    private var numPhotos : int = 363
    private var page : int = 1
    private var pageSize : int = 500
    private var extras : String = "date_taken, original_format"

    //--------------------------------------------------------------------------
    //
    //  Event Handler: Stage
    //
    //--------------------------------------------------------------------------

    private function addedToStageHandler( event : Event ) : void
    {
        // Code in constructor is interpreted by
        // AVM, therefore move processing-intensive code
        // outside, so it can be processed by the JIT compiler
        initialize()
    }

    //--------------------------------------------------------------------------
    //
    //  Methods: Initialization
    //
    //--------------------------------------------------------------------------

    private function initialize() : void
    {
        stage.align = StageAlign.TOP_LEFT
        stage.scaleMode = StageScaleMode.NO_SCALE

        initializeLibraries()
        initializeSecurity()
        registerListeners()
        createChildren()
        updateDisplayList()

        model.service = new FlickrService( model.API_KEY )
        model.service.addEventListener( FlickrResultEvent.PEOPLE_GET_PUBLIC_PHOTOS,
                                        getPublicPhotosHandler )
    }

    private function initializeSecurity() : void
    {
        // TODO: Smells bad… =(
        Security.loadPolicyFile( "http://static.flickr.com/crossdomain.xml" )

        Security.loadPolicyFile( "http://farm1.static.flickr.com/crossdomain.xml" )
        Security.loadPolicyFile( "http://farm2.static.flickr.com/crossdomain.xml" )
        Security.loadPolicyFile( "http://farm3.static.flickr.com/crossdomain.xml" )
        Security.loadPolicyFile( "http://farm4.static.flickr.com/crossdomain.xml" )

        Security.loadPolicyFile( "http://l.yimg.com/crossdomain.xml" )
    }

    private function initializeLibraries() : void
    {
        // Tweener
        ColorShortcuts.init()
    }

    private function registerListeners() : void
    {
        stage.addEventListener( Event.RESIZE, resizeHandler )
        addEventListener( TandemEvent.APPLICATION_COMPLETE, applicationCompleteHandler )

        SWFAddress.addEventListener( SWFAddressEvent.CHANGE, swfAddressChangeHandler )
    }

    //--------------------------------------------------------------------------
    //
    //  User Interface
    //
    //--------------------------------------------------------------------------

    private function createChildren() : void
    {
        createTimeline()
        createGlobalNavigation()
        createMemoryIndicator()
        createNotificationOverlay()
        createKeyboardNavigationButton()
    }

	private function createViewport() : void
	{
		completed = false
		model.photos = []

		if( view )
		    removeChild( view )

		if( navigator )
		    removeChild( navigator )

		// view
		initialized = false
        view = new StreamView()
        view.alpha = 0

		// container
		viewport = new ZoomViewport()
		viewport.minZoom = DEFAULT_MINIMUM_ZOOM
		viewport.maxZoom = DEFAULT_MAXIMUM_ZOOM
        viewport.content = view

        // navigator
        navigator = new ZoomNavigator()
        navigator.model = viewport
        navigator.alpha = 0

        // add children to display list
        addChildAt( viewport, 0 )
        addChildAt( view, 1 )
        addChildAt( navigator, 2 )

        // fade in notification overlay
        Tweener.addTween(
                            notificationOverlay,
                            {
                                alpha: 1,
                                time: 1
                            }
                        )

        // call service
        model.service.people.getPublicPhotos( model.user.nsid, extras,
                                              Math.min( pageSize, numPhotos ),
                                              page )

        // initial layout
        updateDisplayList()
    }

	private function createTimeline() : void
	{
		//timeline = new TimelineComponent()
		//addChild( timeline )
	}

	private function createGlobalNavigation() : void
	{
		globalNavigation = new GlobalNavigationComponent()
		addChild( globalNavigation )
	}

    private function createMemoryIndicator() : void
    {
        memoryIndicator = new MemoryIndicatorComponent()
        addChild( memoryIndicator )
    }

    private function createNotificationOverlay() : void
    {
        notificationOverlay = new NotificationOverlayComponent()
        notificationOverlay.alpha = 0
        addChild( notificationOverlay )

        // fade in notification overlay
        Tweener.addTween(
                            notificationOverlay,
                            {
                                alpha: 1,
                                time: 1.5,
                                delay: 2
                            }
                        )
    }

    private function createKeyboardNavigationButton() : void
    {
        keyboardNavigationButton = new KeyboardNavigationButtonComponent()
        keyboardNavigationButton.alpha = 0
        addChild( keyboardNavigationButton )

        Tweener.addTween(
                            keyboardNavigationButton,
                            {
                                alpha: 1,
                                time: 1.5,
                                delay: 1
                            }
                        )
    }

    //--------------------------------------------------------------------------
    //
    //  SWFAdress
    //
    //--------------------------------------------------------------------------

    private function swfAddressChangeHandler( event : SWFAddressEvent ) : void
    {
        var user : String
        var action : String = SWFAddress.getPathNames()[ 0 ]
        var flickrNSIDPattern : RegExp = /[0-9]*@N[0-9]*/

        if( action == "photos" )
        {
	        // Long URL, e.g. http://flickr.com/photos/gasi
	        // or http://flickr.com/photos/72389028@N00
        	user = SWFAddress.getPathNames()[ 1 ]
        }
        else
        {
	        // Short URL, e.g. http://flickr.com/gasi
        	user = SWFAddress.getPathNames()[ 0 ]
        }

        if( user == null )
        {
			// Default user
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
        else if( flickrNSIDPattern.test( user ) )
        {
        	// Look up Flickr NSID, e.g. 72389028@N00
            model.service.people.getInfo( user )
            model.service.addEventListener( FlickrResultEvent.PEOPLE_GET_INFO, getInfoHandler )
        }
        else
        {
	        // Look up Flickr URL
    		model.service.urls.lookupUser( "http://flickr.com/photos/" + user + "/" )
    		model.service.addEventListener( FlickrResultEvent.URLS_LOOKUP_USER, lookupUserHandler )
        }
	}

    //--------------------------------------------------------------------------
    //
    //  Event Handlers: Application
    //
    //--------------------------------------------------------------------------

    private function resizeHandler( event : Event ) : void
    {
        updateDisplayList()
    }

    private function applicationCompleteHandler( event : Event ) : void
    {
    	// fade in view
        Tweener.addTween(
                             view,
                             {
                                alpha: 1,
                                time: 5
                             }
                        )

        // fade in navigator
        Tweener.addTween(
                           navigator,
                           {
                               alpha: 1,
                               time: 2,
                               delay: 2
                           }
                        )

        // fade out notification
        Tweener.addTween(
                           notificationOverlay,
                           {
                               alpha: 0,
                               time: 0.8,
                               delay: 1.8
                           }
                        )

       updateDisplayList()
    }

    //--------------------------------------------------------------------------
    //
    //  Event Handlers: Service
    //
    //--------------------------------------------------------------------------

    private function getPublicPhotosHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            var result : PagedPhotoList = PagedPhotoList( event.data.photos )

            // add result data to view
            if( view is StreamView )
            {
            	result.photos.forEach(
                                            function( item : *,
                                                     ...ignored ) : void
                                            {
                                            	model.photos.push( item )
                                                StreamView( view ).addItem( item )
                                            }
                                     )

            }

            // we're done
            if( model.photos.length >= Math.min( numPhotos, result.total ) && !completed )
            {
                completed = true
            }

            // fetch more data
            if( result.page < result.pages && !completed )
            {
                page++
                model.service.people.getPublicPhotos( model.user.nsid, extras, pageSize, page )
            }

            if( !initialized )
            {
                initialized = true
                dispatchEvent( new TandemEvent( TandemEvent.APPLICATION_COMPLETE ) )
            }
        }
    }

    private function lookupUserHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            model.user = User( event.data.user )
            createViewport()
            SWFAddress.setTitle( "tandem — " + model.user.username + " (" + model.user.nsid + ")" )
        }
        else
        {
            // Default
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
    }

    private function getInfoHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            model.user = User( event.data.user )
            createViewport()
            SWFAddress.setTitle( "tandem — " + model.user.username + " (" + model.user.nsid + ")" )
        }
        else
        {
            // Default
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Methods: Layout
    //
    //--------------------------------------------------------------------------

	private function updateDisplayList() : void
	{
        // Navigation
        if( globalNavigation )
        {
            globalNavigation.x = 0
            globalNavigation.y = 0
        }

        // Viewport
        if( viewport )
        {
            viewport.x = 0
            viewport.y = 0
            viewport.width = stage.stageWidth
            viewport.height = stage.stageHeight
        }

		// Navigator
        if( navigator )
        {
            navigator.x = stage.stageWidth - navigator.width - 12
            navigator.y = globalNavigation ? globalNavigation.height + 12 : 12
        }

		// Timeline
		if( timeline )
		{
			timeline.width = stage.stageWidth
			timeline.y = stage.stageHeight - timeline.height
		}

        // Memory Indicator
        if( memoryIndicator )
        {
            memoryIndicator.x = stage.stageWidth - memoryIndicator.width - 10
            memoryIndicator.y = stage.stageHeight - memoryIndicator.height - 10
        }

        // Notification Overlay
        if( notificationOverlay )
        {
            notificationOverlay.x = ( stage.stageWidth - notificationOverlay.width ) / 2
            notificationOverlay.y = ( stage.stageHeight - notificationOverlay.height ) / 2
        }

        // Keyboard Navigation Button
        if( keyboardNavigationButton )
        {
            keyboardNavigationButton.x = ( stage.stageWidth - keyboardNavigationButton.width ) / 2
            keyboardNavigationButton.y = stage.stageHeight - keyboardNavigationButton.height - 10
        }
	}
}

}

////////////////////////////////////////////////////////////////////////////////
//
//  tandem. explore your world.
//  Copyright (c) 2007–2008 Daniel Gasienica (daniel@gasienica.ch)
//
//  tandem is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  tandem is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with tandem. If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////
package tandem.core
{

import caurina.transitions.Tweener;
import caurina.transitions.properties.ColorShortcuts;

import com.adobe.webapis.flickr.FlickrService;
import com.adobe.webapis.flickr.PagedPhotoList;
import com.adobe.webapis.flickr.User;
import com.adobe.webapis.flickr.events.FlickrResultEvent;

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.system.Security;

import tandem.events.TandemEvent;
import tandem.model.ApplicationModel;
import tandem.ui.GlobalNavigation;
import tandem.ui.GlobalNavigationComponent;
import tandem.ui.KeyboardNavigationButton;
import tandem.ui.KeyboardNavigationButtonComponent;
import tandem.ui.MemoryIndicator;
import tandem.ui.MemoryIndicatorComponent;
import tandem.ui.NotificationOverlay;
import tandem.ui.NotificationOverlayComponent;
import tandem.ui.Timeline;
import tandem.ui.ZoomNavigator;
import tandem.ui.ZoomViewport;
import tandem.ui.views.StreamView;

public class Application extends Sprite
{
    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------
    /**
    * Constructor.
    */
	public function Application()
	{
		addEventListener( Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true )
    }

    //--------------------------------------------------------------------------
    //
    //  Class Constants
    //
    //--------------------------------------------------------------------------

    private const DEFAULT_USER_ADDRESS : String = "gasi"
    private const DEFAULT_USER_ID : String = "72389028@N00"
    private const DEFAULT_MINIMUM_ZOOM : Number = 0.25//0.5
    private const DEFAULT_MAXIMUM_ZOOM : Number = 180
    private const NUM_FARMS = 10

    //--------------------------------------------------------------------------
    //
    //  Children
    //
    //--------------------------------------------------------------------------

    private var globalNavigation : GlobalNavigation
    private var memoryIndicator : MemoryIndicator
    private var notificationOverlay : NotificationOverlay
    private var keyboardNavigationButton : KeyboardNavigationButton
    private var timeline : Timeline

    private var viewport : ZoomViewport
    private var navigator : ZoomNavigator

    private var view : DisplayObject
    private var initialized : Boolean = false

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    private var model : ApplicationModel = ApplicationModel.getInstance()
    private var completed : Boolean = false

    private var numPhotos : int = 363
    private var page : int = 1
    private var pageSize : int = 500
    private var extras : String = "date_taken, original_format"

    //--------------------------------------------------------------------------
    //
    //  Event Handler: Stage
    //
    //--------------------------------------------------------------------------

    private function addedToStageHandler( event : Event ) : void
    {
        // Code in constructor is interpreted by
        // AVM, therefore move processing-intensive code
        // outside, so it can be processed by the JIT compiler
        initialize()
    }

    //--------------------------------------------------------------------------
    //
    //  Methods: Initialization
    //
    //--------------------------------------------------------------------------

    private function initialize() : void
    {
        stage.align = StageAlign.TOP_LEFT
        stage.scaleMode = StageScaleMode.NO_SCALE

        initializeLibraries()
        initializeSecurity()
        registerListeners()
        createChildren()
        updateDisplayList()

        model.service = new FlickrService( model.API_KEY )
        model.service.addEventListener( FlickrResultEvent.PEOPLE_GET_PUBLIC_PHOTOS,
                                        getPublicPhotosHandler )
    }

    private function initializeSecurity() : void
    {
        // TODO: Smells bad… =(
        Security.loadPolicyFile( "http://static.flickr.com/crossdomain.xml" )

        for (var i:int = 0; i < NUM_FARMS; i++)
        {
            Security.loadPolicyFile("http://farm" + i + ".static.flickr.com/crossdomain.xml")
        }

        Security.loadPolicyFile( "http://l.yimg.com/crossdomain.xml" )
    }

    private function initializeLibraries() : void
    {
        // Tweener
        ColorShortcuts.init()
    }

    private function registerListeners() : void
    {
        stage.addEventListener( Event.RESIZE, resizeHandler )
        addEventListener( TandemEvent.APPLICATION_COMPLETE, applicationCompleteHandler )

        SWFAddress.addEventListener( SWFAddressEvent.CHANGE, swfAddressChangeHandler )
    }

    //--------------------------------------------------------------------------
    //
    //  User Interface
    //
    //--------------------------------------------------------------------------

    private function createChildren() : void
    {
        createTimeline()
        createGlobalNavigation()
        createMemoryIndicator()
        createNotificationOverlay()
        createKeyboardNavigationButton()
    }

	private function createViewport() : void
	{
		completed = false
		model.photos = []

		if( view )
		    removeChild( view )

		if( navigator )
		    removeChild( navigator )

		// view
		initialized = false
        view = new StreamView()
        view.alpha = 0

		// container
		viewport = new ZoomViewport()
		viewport.minZoom = DEFAULT_MINIMUM_ZOOM
		viewport.maxZoom = DEFAULT_MAXIMUM_ZOOM
        viewport.content = view

        // navigator
        navigator = new ZoomNavigator()
        navigator.model = viewport
        navigator.alpha = 0

        // add children to display list
        addChildAt( viewport, 0 )
        addChildAt( view, 1 )
        addChildAt( navigator, 2 )

        // fade in notification overlay
        Tweener.addTween(
                            notificationOverlay,
                            {
                                alpha: 1,
                                time: 1
                            }
                        )

        // call service
        model.service.people.getPublicPhotos( model.user.nsid, extras,
                                              Math.min( pageSize, numPhotos ),
                                              page )

        // initial layout
        updateDisplayList()
    }

	private function createTimeline() : void
	{
		//timeline = new TimelineComponent()
		//addChild( timeline )
	}

	private function createGlobalNavigation() : void
	{
		globalNavigation = new GlobalNavigationComponent()
		addChild( globalNavigation )
	}

    private function createMemoryIndicator() : void
    {
        memoryIndicator = new MemoryIndicatorComponent()
        addChild( memoryIndicator )
    }

    private function createNotificationOverlay() : void
    {
        notificationOverlay = new NotificationOverlayComponent()
        notificationOverlay.alpha = 0
        addChild( notificationOverlay )

        // fade in notification overlay
        Tweener.addTween(
                            notificationOverlay,
                            {
                                alpha: 1,
                                time: 1.5,
                                delay: 2
                            }
                        )
    }

    private function createKeyboardNavigationButton() : void
    {
        keyboardNavigationButton = new KeyboardNavigationButtonComponent()
        keyboardNavigationButton.alpha = 0
        addChild( keyboardNavigationButton )

        Tweener.addTween(
                            keyboardNavigationButton,
                            {
                                alpha: 1,
                                time: 1.5,
                                delay: 1
                            }
                        )
    }

    //--------------------------------------------------------------------------
    //
    //  SWFAdress
    //
    //--------------------------------------------------------------------------

    private function swfAddressChangeHandler( event : SWFAddressEvent ) : void
    {
        var user : String
        var action : String = SWFAddress.getPathNames()[ 0 ]
        var flickrNSIDPattern : RegExp = /[0-9]*@N[0-9]*/

        if( action == "photos" )
        {
	        // Long URL, e.g. http://flickr.com/photos/gasi
	        // or http://flickr.com/photos/72389028@N00
        	user = SWFAddress.getPathNames()[ 1 ]
        }
        else
        {
	        // Short URL, e.g. http://flickr.com/gasi
        	user = SWFAddress.getPathNames()[ 0 ]
        }

        if( user == null )
        {
			// Default user
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
        else if( flickrNSIDPattern.test( user ) )
        {
        	// Look up Flickr NSID, e.g. 72389028@N00
            model.service.people.getInfo( user )
            model.service.addEventListener( FlickrResultEvent.PEOPLE_GET_INFO, getInfoHandler )
        }
        else
        {
	        // Look up Flickr URL
    		model.service.urls.lookupUser( "http://flickr.com/photos/" + user + "/" )
    		model.service.addEventListener( FlickrResultEvent.URLS_LOOKUP_USER, lookupUserHandler )
        }
	}

    //--------------------------------------------------------------------------
    //
    //  Event Handlers: Application
    //
    //--------------------------------------------------------------------------

    private function resizeHandler( event : Event ) : void
    {
        updateDisplayList()
    }

    private function applicationCompleteHandler( event : Event ) : void
    {
    	// fade in view
        Tweener.addTween(
                             view,
                             {
                                alpha: 1,
                                time: 5
                             }
                        )

        // fade in navigator
        Tweener.addTween(
                           navigator,
                           {
                               alpha: 1,
                               time: 2,
                               delay: 2
                           }
                        )

        // fade out notification
        Tweener.addTween(
                           notificationOverlay,
                           {
                               alpha: 0,
                               time: 0.8,
                               delay: 1.8
                           }
                        )

       updateDisplayList()
    }

    //--------------------------------------------------------------------------
    //
    //  Event Handlers: Service
    //
    //--------------------------------------------------------------------------

    private function getPublicPhotosHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            var result : PagedPhotoList = PagedPhotoList( event.data.photos )

            // add result data to view
            if( view is StreamView )
            {
            	result.photos.forEach(
                                            function( item : *,
                                                     ...ignored ) : void
                                            {
                                            	model.photos.push( item )
                                                StreamView( view ).addItem( item )
                                            }
                                     )

            }

            // we're done
            if( model.photos.length >= Math.min( numPhotos, result.total ) && !completed )
            {
                completed = true
            }

            // fetch more data
            if( result.page < result.pages && !completed )
            {
                page++
                model.service.people.getPublicPhotos( model.user.nsid, extras, pageSize, page )
            }

            if( !initialized )
            {
                initialized = true
                dispatchEvent( new TandemEvent( TandemEvent.APPLICATION_COMPLETE ) )
            }
        }
    }

    private function lookupUserHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            model.user = User( event.data.user )
            createViewport()
            SWFAddress.setTitle( "tandem — " + model.user.username + " (" + model.user.nsid + ")" )
        }
        else
        {
            // Default
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
    }

    private function getInfoHandler( event : FlickrResultEvent ) : void
    {
        if( event.success )
        {
            model.user = User( event.data.user )
            createViewport()
            SWFAddress.setTitle( "tandem — " + model.user.username + " (" + model.user.nsid + ")" )
        }
        else
        {
            // Default
            SWFAddress.setValue( "photos/" + DEFAULT_USER_ADDRESS + "/" )
        }
    }

    //--------------------------------------------------------------------------
    //
    //  Methods: Layout
    //
    //--------------------------------------------------------------------------

	private function updateDisplayList() : void
	{
        // Navigation
        if( globalNavigation )
        {
            globalNavigation.x = 0
            globalNavigation.y = 0
        }

        // Viewport
        if( viewport )
        {
            viewport.x = 0
            viewport.y = 0
            viewport.width = stage.stageWidth
            viewport.height = stage.stageHeight
        }

		// Navigator
        if( navigator )
        {
            navigator.x = stage.stageWidth - navigator.width - 12
            navigator.y = globalNavigation ? globalNavigation.height + 12 : 12
        }

		// Timeline
		if( timeline )
		{
			timeline.width = stage.stageWidth
			timeline.y = stage.stageHeight - timeline.height
		}

        // Memory Indicator
        if( memoryIndicator )
        {
            memoryIndicator.x = stage.stageWidth - memoryIndicator.width - 10
            memoryIndicator.y = stage.stageHeight - memoryIndicator.height - 10
        }

        // Notification Overlay
        if( notificationOverlay )
        {
            notificationOverlay.x = ( stage.stageWidth - notificationOverlay.width ) / 2
            notificationOverlay.y = ( stage.stageHeight - notificationOverlay.height ) / 2
        }

        // Keyboard Navigation Button
        if( keyboardNavigationButton )
        {
            keyboardNavigationButton.x = ( stage.stageWidth - keyboardNavigationButton.width ) / 2
            keyboardNavigationButton.y = stage.stageHeight - keyboardNavigationButton.height - 10
        }
	}
}

}

Add additional URLs for crossdomain policy files.


#if __SWF_VERSION__ == 5
// create a _global object, since it doesn't have one, these are ver 6 values
_global = new_empty_object ();
_global.ASSetNative = ASSetNative;
_global.ASSetNativeAccessor = ASSetNativeAccessor;
_global.ASSetPropFlags = ASSetPropFlags;
_global.ASconstructor = ASconstructor;
_global.ASnative = ASnative;
_global.Accessibility = Accessibility;
_global.Array = Array;
_global.AsBroadcaster = AsBroadcaster;
_global.AsSetupError = AsSetupError;
_global.Boolean = Boolean;
_global.Button = Button;
_global.Camera = Camera;
_global.Color = Color;
_global.ContextMenu = ContextMenu;
_global.ContextMenuItem = ContextMenuItem;
_global.Date = Date;
_global.Error = Error;
_global.Function = Function;
_global.Infinity = Infinity;
_global.Key = Key;
_global.LoadVars = LoadVars;
_global.LocalConnection = LocalConnection;
_global.Math = Math;
_global.Microphone = Microphone;
_global.Mouse = Mouse;
_global.MovieClip = MovieClip;
_global.MovieClipLoader = MovieClipLoader;
_global.NaN = NaN;
_global.NetConnection = NetConnection;
_global.NetStream = NetStream;
_global.Number = Number;
_global.Object = Object;
_global.PrintJob = PrintJob;
_global.RemoteLSOUsage = RemoteLSOUsage;
_global.Selection = Selection;
_global.SharedObject = SharedObject;
_global.Sound = Sound;
_global.Stage = Stage;
_global.String = String;
_global.System = System;
_global.TextField = TextField;
_global.TextFormat = TextFormat;
_global.TextSnapshot = TextSnapshot;
_global.Video = Video;
_global.XML = XML;
_global.XMLNode = XMLNode;
_global.XMLSocket = XMLSocket;
_global.clearInterval = clearInterval;
_global.clearTimeout = clearTimeout;
_global.enableDebugConsole = enableDebugConsole;
_global.escape = escape;
_global.flash = flash;
_global.isFinite = isFinite;
_global.isNaN = isNaN;
_global.o = o;
_global.parseFloat = parseFloat;
_global.parseInt = parseInt;
_global.setInterval = setInterval;
_global.setTimeout = setTimeout;
_global.showRedrawRegions = showRedrawRegions;
_global.textRenderer = textRenderer;
_global.trace = trace;
_global.unescape = unescape;
_global.updateAfterEvent = updateAfterEvent;
#endif

function new_empty_object () {
  var hash = new Object ();
  ASSetPropFlags (hash, null, 0, 7);
  for (var prop in hash) {
    hash[prop] = "to-be-deleted";
    delete hash[prop];
  }
  return hash;
}

#if __SWF_VERSION__ >= 6
function hasOwnProperty (o, prop)
{
  if (o.hasOwnProperty != undefined)
    return o.hasOwnProperty (prop);

  o.hasOwnProperty = _global.Object.prototype.hasOwnProperty;
  var result = o.hasOwnProperty (prop);
  delete o.hasOwnProperty;
  return result;
}
#else
// this gets the same result as the above, with following limitations:
// - if there is a child __proto__[prop] with value that can't be changed, no
//   test can be done and false is returned
// - native properties that have value undefined by default get overwritten by
//   __proto__[prop]'s value (atleast in version 6 and 7) so their existance
//   won't be detected by this function
function hasOwnProperty (o, prop)
{
  if (o.__proto__ == undefined)
  {
    o.__proto__ = new_empty_object ();

    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    o.__proto__ = "to-be-deleted";
    delete o.__proto__;
    if (o.__proto__ != undefined) {
      trace ("ERROR: Couldn't delete temporary __proto__");
      o.__proto__ = undefined;
    }

    return result;
  }

  if (hasOwnProperty (o.__proto__, prop))
  {
    var constant = false;
    var old = o.__proto__[prop];

    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
    if (o.__proto__[prop] != "safdlojasfljsaiofhiwjhefa") {
      constant = true;
      ASSetPropFlags (o.__proto__, prop, 0, 4);
      o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
      if (o.__proto__[prop] != "safdlojasfljsaiofhiwjhefa") {
	if (o[prop] != o.__proto__[prop]) {
	  return true;
	} else {
	  trace ("ERROR: can't test property '" + prop +
	      "', __proto__ has superconstant version");
	  return false;
	}
      }
    }

    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    o.__proto__[prop] = old;
    if (o.__proto__[prop] != old)
      trace ("Error: Couldn't set __proto__[\"" + prop +
	  "\"] back to old value");
    if (constant)
      ASSetPropFlags (o.__proto__, prop, 4);

    return result;
  }
  else
  {
    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";

    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    ASSetPropFlags (o, prop, 0, 4);
    o.__proto__[prop] = "to-be-deleted";
    delete o.__proto__[prop];
    if (o.__proto__[prop] != undefined)
      trace ("ERROR: Couldn't delete temporary __proto__[\"" + prop + "\"]");

    return result;
  }
}
#endif

function new_info () {
  return new_empty_object ();
}

function set_info (info, prop, id, value) {
  info[prop + "_-_" + id] = value;
}

function get_info (info, prop, id) {
  return info[prop + "_-_" + id];
}

function is_blaclisted (o, prop)
{
  if (prop == "mySecretId" || prop == "globalSecretId")
    return true;

  if (o == _global.Camera && prop == "names")
    return true;

  if (o == _global.Microphone && prop == "names")
    return true;

#if __SWF_VERSION__ < 6
  if (prop == "__proto__" && o[prop] == undefined)
    return true;
#endif

  return false;
}

function trace_properties_recurse (o, prefix, identifier, level)
{
  // to collect info about different properties
  var info = new_info ();

  // calculate indentation
  var indentation = "";
  for (var j = 0; j < level; j++) {
    indentation = indentation + "  ";
  }

  // mark the ones that are not hidden
  for (var prop in o)
  {
    // only get the ones that are not only in the __proto__
    if (is_blaclisted (o, prop) == false) {
      if (hasOwnProperty (o, prop) == true)
	set_info (info, prop, "hidden", false);
    }
  }

  // unhide everything
  ASSetPropFlags (o, null, 0, 1);

  var all = new Array ();
  var hidden = new Array ();
  for (var prop in o)
  {
    // only get the ones that are not only in the __proto__
    if (is_blaclisted (o, prop) == false) {
      if (hasOwnProperty (o, prop) == true) {
	all.push (prop);
	if (get_info (info, prop, "hidden") != false) {
	  set_info (info, prop, "hidden", true);
	  hidden.push (prop);
	}
      }
    }
  }
  all.sort ();

  // hide the ones that were already hidden
  ASSetPropFlags (o, hidden, 1, 0);

  if (all.length == 0) {
    trace (indentation + "no children");
    return nextSecretId;
  }

  for (var i = 0; i < all.length; i++)
  {
    var prop = all[i];

    // try changing value
    var old = o[prop];
    var val = "hello " + o[prop];
    o[prop] = val;
    if (o[prop] != val)
    {
      set_info (info, prop, "constant", true);

      // try changing value after removing constant propflag
      ASSetPropFlags (o, prop, 0, 4);
      o[prop] = val;
      if (o[prop] != val) {
	set_info (info, prop, "superconstant", true);
      } else {
	set_info (info, prop, "superconstant", false);
	o[prop] = old;
      }
      ASSetPropFlags (o, prop, 4);
    }
    else
    {
      set_info (info, prop, "constant", false);
      set_info (info, prop, "superconstant", false);
      o[prop] = old;
    }
  }

  for (var i = 0; i < all.length; i++)
  {
    var prop = all[i];

    // remove constant flag
    ASSetPropFlags (o, prop, 0, 4);

    // try deleting
    var old = o[prop];
    delete o[prop];
    if (hasOwnProperty (o, prop))
    {
      set_info (info, prop, "permanent", true);
    }
    else
    {
      set_info (info, prop, "permanent", false);
      o[prop] = old;
    }

    // put constant flag back, if it was set
    if (get_info (info, prop, "constant") == true)
      ASSetPropFlags (o, prop, 4);
  }

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    // format propflags
    var flags = "";
    if (get_info (info, prop, "hidden") == true) {
      flags += "h";
    }
    if (get_info (info, prop, "superpermanent") == true) {
      flags += "P";
    } else if (get_info (info, prop, "permanent") == true) {
      flags += "p";
    }
    if (get_info (info, prop, "superconstant") == true) {
      flags += "C";
    } else if (get_info (info, prop, "constant") == true) {
      flags += "c";
    }
    if (flags != "")
      flags = " (" + flags + ")";

    var value = "";
    // add value depending on the type
    if (typeof (o[prop]) == "number" || typeof (o[prop]) == "boolean") {
      value += " : " + o[prop];
    } else if (typeof (o[prop]) == "string") {
      value += " : \"" + o[prop] + "\"";
    }

    // recurse if it's object or function and this is the place it has been
    // named after
    if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function")
    {
      if (prefix + (prefix != "" ? "." : "") + identifier + "." + prop ==
	  o[prop]["mySecretId"])
      {
	trace (indentation + prop + flags + " = " + typeof (o[prop]));
	trace_properties_recurse (o[prop], prefix + (prefix != "" ? "." : "") +
	    identifier, prop, level + 1);
      }
      else
      {
	trace (indentation + prop + flags + " = " + o[prop]["mySecretId"]);
      }
    }
    else
    {
      trace (indentation + prop + flags + " = " + typeof (o[prop]) + value);
    }
  }
}

function generate_names (o, prefix, identifier)
{
  // mark the ones that are not hidden
  var nothidden = new Array ();
  for (var prop in o) {
    nothidden.push (prop);
  }

  // unhide everything
  ASSetPropFlags (o, null, 0, 1);

  var all = new Array ();
  for (var prop in o)
  {
    if (is_blaclisted (o, prop) == false) {
      // only get the ones that are not only in the __proto__
      if (hasOwnProperty (o, prop) == true) {
	all.push (prop);
      }
    }
  }
  all.sort ();

  // hide the ones that were already hidden
  ASSetPropFlags (o, null, 1, 0);
  ASSetPropFlags (o, nothidden, 0, 1);

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function") {
      if (hasOwnProperty (o[prop], "mySecretId")) {
	all[i] = null; // don't recurse to it again
      } else {
	o[prop]["mySecretId"] = prefix + (prefix != "" ? "." : "") +
	  identifier + "." + prop;
      }
    }
  }

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    if (prop != null) {
      if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function")
	generate_names (o[prop], prefix + (prefix != "" ? "." : "") +
	    identifier, prop);
    }
  }
}

function trace_properties (o, prefix, identifier)
{
  _global["mySecretId"] = "_global";
  _global.Object["mySecretId"] = "_global.Object";
  generate_names (_global.Object, "_global", "Object");
  generate_names (_global, "", "_global");

  generate_names (o, prefix, identifier);

  if (typeof (o) == "object" || typeof (o) == "function")
  {
    if (prefix + (prefix != "" ? "." : "") + identifier == o["mySecretId"])
    {
      trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	  typeof (o));
    }
    else
    {
      trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	  o["mySecretId"]);
    }
    trace_properties_recurse (o, prefix, identifier, 1);
  }
  else
  {
    trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	typeof (o[prop]) + value);
  }
}

#if __SWF_VERSION__ == 5
// create a _global object, since it doesn't have one, these are ver 6 values
_global = new_empty_object ();
_global.ASSetNative = ASSetNative;
_global.ASSetNativeAccessor = ASSetNativeAccessor;
_global.ASSetPropFlags = ASSetPropFlags;
_global.ASconstructor = ASconstructor;
_global.ASnative = ASnative;
_global.Accessibility = Accessibility;
_global.Array = Array;
_global.AsBroadcaster = AsBroadcaster;
_global.AsSetupError = AsSetupError;
_global.Boolean = Boolean;
_global.Button = Button;
_global.Camera = Camera;
_global.Color = Color;
_global.ContextMenu = ContextMenu;
_global.ContextMenuItem = ContextMenuItem;
_global.Date = Date;
_global.Error = Error;
_global.Function = Function;
_global.Infinity = Infinity;
_global.Key = Key;
_global.LoadVars = LoadVars;
_global.LocalConnection = LocalConnection;
_global.Math = Math;
_global.Microphone = Microphone;
_global.Mouse = Mouse;
_global.MovieClip = MovieClip;
_global.MovieClipLoader = MovieClipLoader;
_global.NaN = NaN;
_global.NetConnection = NetConnection;
_global.NetStream = NetStream;
_global.Number = Number;
_global.Object = Object;
_global.PrintJob = PrintJob;
_global.RemoteLSOUsage = RemoteLSOUsage;
_global.Selection = Selection;
_global.SharedObject = SharedObject;
_global.Sound = Sound;
_global.Stage = Stage;
_global.String = String;
_global.System = System;
_global.TextField = TextField;
_global.TextFormat = TextFormat;
_global.TextSnapshot = TextSnapshot;
_global.Video = Video;
_global.XML = XML;
_global.XMLNode = XMLNode;
_global.XMLSocket = XMLSocket;
_global.clearInterval = clearInterval;
_global.clearTimeout = clearTimeout;
_global.enableDebugConsole = enableDebugConsole;
_global.escape = escape;
_global.flash = flash;
_global.isFinite = isFinite;
_global.isNaN = isNaN;
_global.o = o;
_global.parseFloat = parseFloat;
_global.parseInt = parseInt;
_global.setInterval = setInterval;
_global.setTimeout = setTimeout;
_global.showRedrawRegions = showRedrawRegions;
_global.textRenderer = textRenderer;
_global.trace = trace;
_global.unescape = unescape;
_global.updateAfterEvent = updateAfterEvent;
#endif

function new_empty_object () {
  var hash = new Object ();
  ASSetPropFlags (hash, null, 0, 7);
  for (var prop in hash) {
    hash[prop] = "to-be-deleted";
    delete hash[prop];
  }
  return hash;
}

#if __SWF_VERSION__ >= 6
function hasOwnProperty (o, prop)
{
  if (o.hasOwnProperty != undefined)
    return o.hasOwnProperty (prop);

  o.hasOwnProperty = _global.Object.prototype.hasOwnProperty;
  var result = o.hasOwnProperty (prop);
  delete o.hasOwnProperty;
  return result;
}
#else
// this gets the same result as the above, with following limitations:
// - if there is a child __proto__[prop] with value that can't be changed, no
//   test can be done and false is returned
// - native properties that have value undefined by default get overwritten by
//   __proto__[prop]'s value (atleast in version 6 and 7) so their existance
//   won't be detected by this function
function hasOwnProperty (o, prop)
{
  if (o.__proto__ == undefined)
  {
    o.__proto__ = new_empty_object ();

    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    o.__proto__ = "to-be-deleted";
    delete o.__proto__;
    if (o.__proto__ != undefined) {
      trace ("ERROR: Couldn't delete temporary __proto__");
      o.__proto__ = undefined;
    }

    return result;
  }

  if (hasOwnProperty (o.__proto__, prop))
  {
    var constant = false;
    var old = o.__proto__[prop];

    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
    if (o.__proto__[prop] != "safdlojasfljsaiofhiwjhefa") {
      constant = true;
      ASSetPropFlags (o.__proto__, prop, 0, 4);
      o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";
      if (o.__proto__[prop] != "safdlojasfljsaiofhiwjhefa") {
	if (o[prop] != o.__proto__[prop]) {
	  return true;
	} else {
	  trace ("ERROR: can't test property '" + prop +
	      "', __proto__ has superconstant version");
	  return false;
	}
      }
    }

    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    o.__proto__[prop] = old;
    if (o.__proto__[prop] != old)
      trace ("Error: Couldn't set __proto__[\"" + prop +
	  "\"] back to old value");
    if (constant)
      ASSetPropFlags (o.__proto__, prop, 4);

    return result;
  }
  else
  {
    o.__proto__[prop] = "safdlojasfljsaiofhiwjhefa";

    if (o[prop] != o.__proto__[prop]) {
      result = true;
    } else {
      result = false;
    }

    ASSetPropFlags (o, prop, 0, 4);
    o.__proto__[prop] = "to-be-deleted";
    delete o.__proto__[prop];
    if (o.__proto__[prop] != undefined)
      trace ("ERROR: Couldn't delete temporary __proto__[\"" + prop + "\"]");

    return result;
  }
}
#endif

function new_info () {
  return new_empty_object ();
}

function set_info (info, prop, id, value) {
  info[prop + "_-_" + id] = value;
}

function get_info (info, prop, id) {
  return info[prop + "_-_" + id];
}

function is_blaclisted (o, prop)
{
  if (prop == "mySecretId" || prop == "globalSecretId")
    return true;

  if (o == _global.Camera && prop == "names")
    return true;

  if (o == _global.Microphone && prop == "names")
    return true;

#if __SWF_VERSION__ < 6
  if (prop == "__proto__" && o[prop] == undefined)
    return true;
#endif

  return false;
}

function trace_properties_recurse (o, prefix, identifier, level)
{
  // to collect info about different properties
  var info = new_info ();

  // calculate indentation
  var indentation = "";
  for (var j = 0; j < level; j++) {
    indentation = indentation + "  ";
  }

  // mark the ones that are not hidden
  for (var prop in o)
  {
    // only get the ones that are not only in the __proto__
    if (is_blaclisted (o, prop) == false) {
      if (hasOwnProperty (o, prop) == true)
	set_info (info, prop, "hidden", false);
    }
  }

  // unhide everything
  ASSetPropFlags (o, null, 0, 1);

  var all = new Array ();
  var hidden = new Array ();
  for (var prop in o)
  {
    // only get the ones that are not only in the __proto__
    if (is_blaclisted (o, prop) == false) {
      if (hasOwnProperty (o, prop) == true) {
	all.push (prop);
	if (get_info (info, prop, "hidden") != false) {
	  set_info (info, prop, "hidden", true);
	  hidden.push (prop);
	}
      }
    }
  }
  all.sort ();

  // hide the ones that were already hidden
  ASSetPropFlags (o, hidden, 1, 0);

  if (all.length == 0) {
    trace (indentation + "no children");
    return nextSecretId;
  }

  for (var i = 0; i < all.length; i++)
  {
    var prop = all[i];

    // try changing value
    var old = o[prop];
    var val = "hello " + o[prop];
    o[prop] = val;
    if (o[prop] != val)
    {
      set_info (info, prop, "constant", true);

      // try changing value after removing constant propflag
      ASSetPropFlags (o, prop, 0, 4);
      o[prop] = val;
      if (o[prop] != val) {
	set_info (info, prop, "superconstant", true);
      } else {
	set_info (info, prop, "superconstant", false);
	o[prop] = old;
      }
      ASSetPropFlags (o, prop, 4);
    }
    else
    {
      set_info (info, prop, "constant", false);
      set_info (info, prop, "superconstant", false);
      o[prop] = old;
    }
  }

  for (var i = 0; i < all.length; i++)
  {
    var prop = all[i];

    // remove constant flag
    ASSetPropFlags (o, prop, 0, 4);

    // try deleting
    var old = o[prop];
    delete o[prop];
    if (hasOwnProperty (o, prop))
    {
      set_info (info, prop, "permanent", true);
    }
    else
    {
      set_info (info, prop, "permanent", false);
      o[prop] = old;
    }

    // put constant flag back, if it was set
    if (get_info (info, prop, "constant") == true)
      ASSetPropFlags (o, prop, 4);
  }

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    // format propflags
    var flags = "";
    if (get_info (info, prop, "hidden") == true) {
      flags += "h";
    }
    if (get_info (info, prop, "superpermanent") == true) {
      flags += "P";
    } else if (get_info (info, prop, "permanent") == true) {
      flags += "p";
    }
    if (get_info (info, prop, "superconstant") == true) {
      flags += "C";
    } else if (get_info (info, prop, "constant") == true) {
      flags += "c";
    }
    if (flags != "")
      flags = " (" + flags + ")";

    var value = "";
    // add value depending on the type
    if (typeof (o[prop]) == "number" || typeof (o[prop]) == "boolean") {
      value += " : " + o[prop];
    } else if (typeof (o[prop]) == "string") {
      value += " : \"" + o[prop] + "\"";
    }

    // recurse if it's object or function and this is the place it has been
    // named after
    if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function")
    {
      if (prefix + (prefix != "" ? "." : "") + identifier + "." + prop ==
	  o[prop]["mySecretId"])
      {
	trace (indentation + prop + flags + " = " + typeof (o[prop]));
	trace_properties_recurse (o[prop], prefix + (prefix != "" ? "." : "") +
	    identifier, prop, level + 1);
      }
      else
      {
	trace (indentation + prop + flags + " = " + o[prop]["mySecretId"]);
      }
    }
    else
    {
      trace (indentation + prop + flags + " = " + typeof (o[prop]) + value);
    }
  }
}

function generate_names (o, prefix, identifier)
{
  // mark the ones that are not hidden
  var nothidden = new Array ();
  for (var prop in o) {
    nothidden.push (prop);
  }

  // unhide everything
  ASSetPropFlags (o, null, 0, 1);

  var all = new Array ();
  for (var prop in o)
  {
    if (is_blaclisted (o, prop) == false) {
      // only get the ones that are not only in the __proto__
      if (hasOwnProperty (o, prop) == true) {
	all.push (prop);
      }
    }
  }
  all.sort ();

  // hide the ones that were already hidden
  ASSetPropFlags (o, null, 1, 0);
  ASSetPropFlags (o, nothidden, 0, 1);

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function") {
      if (hasOwnProperty (o[prop], "mySecretId")) {
	all[i] = null; // don't recurse to it again
      } else {
	o[prop]["mySecretId"] = prefix + (prefix != "" ? "." : "") +
	  identifier + "." + prop;
      }
    }
  }

  for (var i = 0; i < all.length; i++) {
    var prop = all[i];

    if (prop != null) {
      if (typeof (o[prop]) == "object" || typeof (o[prop]) == "function")
	generate_names (o[prop], prefix + (prefix != "" ? "." : "") +
	    identifier, prop);
    }
  }
}

function trace_properties (o, prefix, identifier)
{
  _global["mySecretId"] = "_global";
  _global.Object["mySecretId"] = "_global.Object";
  generate_names (_global.Object, "_global", "Object");
  generate_names (_global, "", "_global");

  if (typeof (o) == "object" || typeof (o) == "function")
  {
    if (o["mySecretId"] == undefined) {
      o["mySecretId"] = prefix + (prefix != "" ? "." : "") + identifier;
      generate_names (o, prefix, identifier);
    }

    if (prefix + (prefix != "" ? "." : "") + identifier == o["mySecretId"])
    {
      trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	  typeof (o));
    }
    else
    {
      trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	  o["mySecretId"]);
    }
    trace_properties_recurse (o, prefix, identifier, 1);
  }
  else
  {
    var value = "";
    if (typeof (o) == "number" || typeof (o) == "boolean") {
      value += " : " + o;
    } else if (typeof (o) == "string") {
      value += " : \"" + o + "\"";
    }
    trace (prefix + (prefix != "" ? "." : "") + identifier + " = " +
	typeof (o) + value);
  }
}

Fix errors in trace_properties when called with something else than _global


package
{
  import flash.display.BlendMode;

  import org.flixel.*;

  // The main coordinator: part config list, part asset list.
  // Handles all of the character animations.

  public class PlayState extends FlxState implements IPlayerAnimationDelegate
  {
    // Tileset that works with AUTO mode (best for thin walls)
    [Embed(source='data/auto_tiles.png')]private static var ImgAutoTiles:Class;
    // Tileset that works with OFF mode (do what you want mode)
    [Embed(source='data/empty_tiles.png')]private static var ImgCustomTiles:Class;

    [Embed(source='data/player.png')]private static var ImgPlayer:Class;

    // From farthest to closest.
    [Embed(source='data/bg-1.png')]private static var ImgBg1:Class;
    [Embed(source='data/bg-2.png')]private static var ImgBg2:Class;
    [Embed(source='data/bg-3.png')]private static var ImgBg3:Class;
    [Embed(source='data/bg-4.png')]private static var ImgBg4:Class;
    [Embed(source='data/bg-5.png')]private static var ImgBg5:Class;
    [Embed(source='data/bg-6.png')]private static var ImgBg6:Class;
    [Embed(source='data/bg-7.png')]private static var ImgBg7:Class;
    [Embed(source='data/bg-8.png')]private static var ImgBg8:Class;
    [Embed(source='data/bg-9.png')]private static var ImgBg9:Class;
    [Embed(source='data/bg-10.png')]private static var ImgBg10:Class;
    [Embed(source='data/bg-11.png')]private static var ImgBg11:Class;
    [Embed(source='data/bg-12.png')]private static var ImgBg12:Class;

    // The dynamically generated and extended FlxTilemap.
    private var platform:Platform;
    // Ledge controls, in tiles.

    // The extend FlxSprite.
    private static const PLAYER_WIDTH:uint = 72;
    private static const PLAYER_HEIGHT:uint = 72;
    private var player:Player;

    // The background with parallax.
    private var bg:Background;

    // Some game switches.
    private var fallChecking:Boolean;

    // Flixel Methods
    // --------------
    override public function create():void
    {
      FlxG.mouse.hide();

      // Globals.
      FlxG.framerate = 30;
      FlxG.flashFramerate = 30;
      fallChecking = false;

      // Start our setup chain.
      setupPlatform();
      setupPlatformAfter();
      setupPlatformAndPlayerAfter();

      // For now, we add things in order to get correct layering.
      add(bg);
      add(platform);
      add(player);
    }
    override public function update():void
    {
      // Start our update chain.
      updatePlatform();
      updatePlatformAfter();
      updatePlatformAndPlayerAfter();

      super.update();
    }
    override public function draw():void
    {
      super.draw();
    }

    // Setup Routines
    // --------------
    // Since the observer pattern is too slow, we'll just name our functions to be like hooks.
    // The platform is the first thing that gets set up.
    private function setupPlatform():void
    {
      setupBg();

      // Creates a new tilemap with no arguments.
      platform = new Platform();

      // Customize our tile generation.
      platform.tileWidth = 32;
      platform.tileHeight = 32;
      platform.minLedgeSize = 3;
      platform.maxLedgeSize = 6;
      platform.minLedgeSpacing = new FlxPoint(4, 2);
      platform.maxLedgeSpacing = new FlxPoint(8, 4);
      platform.ledgeThickness = 2;

      // Set the bounds based on the background.
      platform.bounds = new FlxRect(bg.bounds.x, bg.bounds.y, bg.bounds.width, bg.bounds.height);

      // Make our platform.
      platform.makeMap(ImgAutoTiles);

      // Set points.
      var floorHeight:Number = PLAYER_HEIGHT;
      platform.startingPoint.x = (FlxG.width - PLAYER_HEIGHT) / 2;
      platform.startingPoint.y = platform.height - (PLAYER_HEIGHT + floorHeight);
      platform.endingPoint.y = (platform.maxLedgeSpacing.y + 1) * platform.tileHeight - PLAYER_HEIGHT
    }
    // Hooks.
    private function setupPlatformAfter():void
    {
      // Draw player at the bottom.
      setupPlayer(platform.startingPoint);
//      setupPlayer(platform.endingPoint);

      // Move until we don't overlap.
      while (platform.overlaps(player))
      {
        if (player.x <= 0)
        {
          player.x = FlxG.width;
        }
        player.x -= platform.tileWidth;
      }
    }
    private function setupPlatformAndPlayerAfter():void
    {
      setupCamera();
    }
    // Hooked routines.
    private function setupPlayer(start:FlxPoint):void
    {
      // Find start position for player.

      player = new Player(start.x, start.y);
      player.loadGraphic(ImgPlayer, true, true, 72);

      // Bounding box tweaks.
      player.height = player.frameWidth / 2;
      player.offset.y = player.frameWidth - player.height;
      player.tailOffset.x = 35;
      player.headOffset.x = 10;
      player.width = player.frameWidth - player.tailOffset.x;
      player.face(FlxObject.RIGHT);

      // Basic player physics.
      player.naturalForces.x = 1000; // friction
      player.naturalForces.y = 500; // gravity
      player.maxVelocity.x = 150;
      player.maxVelocity.y = 1500;

      // Player jump physics.
      player.jumpMaxVelocity.y = -250;
      player.jumpAccel.y = -0.3;

      // Animations.
      // Make sure to add end transitions, otherwise the last frame is skipped if framerate is low.
      player.addAnimation('still',[17], 12);
      player.addAnimation('idle', [], 12, false);
      player.addAnimation('run',  [0,1,2,3,4,5,6,7,8,9,10,11], 24);
      player.addAnimation('stop', [12,13,14,15,16,17], 24, false);
      player.addAnimation('start',[17,16,15,14,13,12], 24, false);
      player.addAnimation('jump', [18,19,20,21,22,23,24,25,26,27,28,29,30,31], 24, false);
      player.addAnimation('fall', [31]);
      player.addAnimation('land', [32,33,18,17], 12, false);
      player.animDelegate = this;

      // Process settings.
      player.init();
    }
    private function setupCamera():void
    {
      FlxG.camera.follow(player);
      platform.follow();
    }
    private function setupBg():void
    {
      // Load our scenery.
      bg = new Background();
      bg.bounds.x = 0;
      bg.bounds.y = 0;
      bg.parallax_factor = 1; // Our bg is part "foreground".
      bg.parallax_buffer = 1.7;

      // This is the lamest image loading ever.
      bg.addImage(ImgBg1);
      bg.addImage(ImgBg2);
      bg.addImage(ImgBg3);
      bg.addImage(ImgBg4);
      bg.addImage(ImgBg5);
      bg.addImage(ImgBg6);
      bg.addImage(ImgBg7);
      bg.addImage(ImgBg8);
      bg.addImage(ImgBg9);
      bg.addImage(ImgBg10);
      bg.addImage(ImgBg11);
      bg.addImage(ImgBg12);

      bg.layout();
    }

    // Update Routines
    // ---------------
    private function updatePlatform():void
    {
    }
    // Hooks.
    private function updatePlatformAfter():void
    {
      updatePlayer();
    }
    private function updatePlatformAndPlayerAfter():void
    {
      updateCamera(player.justFell());
      updateGameState();
    }
    // Hooked routines.
    private function updatePlayer():void
    {
      // Tilemaps can be collided just like any other FlxObject, and flixel
      // automatically collides each individual tile with the object.
      FlxG.collide(player, platform);
      var pPos:FlxPoint = new FlxPoint(player.x, player.y);
      wrapToStage(player);
      var pos:FlxPoint = new FlxPoint(player.x, player.y);
      if (FlxU.getDistance(pos, pPos) > 0)
      {
        player.nextAction = Player.STOP;
      }
    }
    private function updateCamera(playerJustFell:Boolean):void
    {
      if (fallChecking && playerJustFell)
      {
        FlxG.camera.shake(
          0.01,
          0.1, null, true,
          FlxCamera.SHAKE_VERTICAL_ONLY
        );
      }
    }
    private function updateGameState():void
    {
      // Check if player is on top of last platform, periodically.
      // Play the end screen for a while, on click, switch to start screen.
      if (player.isTouching(FlxObject.FLOOR) && platform.isAtEndingPoint(player))
      {
        if (player.controlled)
        {
          player.controlled = false;
          var title:Text, instructions:Text;
          title = new Text(0, Text.BASELINE * 2, FlxG.width,
            "The End", Text.H1);
          instructions = new Text(0, Text.BASELINE * 4, FlxG.width,
            "Click to play again");
          instructions.size = 16;
          // Stay on the screen.
          instructions.scrollFactor = title.scrollFactor = new FlxPoint();
          add(title);
          add(instructions);
        }
        else if (!player.controlled && FlxG.mouse.justPressed())
        {
          MorningStroll.endGame();
        }
      }
      else if (FlxG.keys.justPressed('Q'))
      {
        MorningStroll.endGame();
      }
    }

    // Delegate Methods
    // ----------------
    public function playerWillUpdateAnimation():void {}
    public function playerDidUpdateAnimation():void {}
    // Smooth, once.
    public function playerWillStart():void
    {
      if (!player.finished) return;
      if (player.currentAnimation().name == 'start') return;
      player.play('start');
    }
    // Interruptive, once.
    public function playerWillStop():void
    {
      if (player.currentAnimation().name == 'stop') return;
      player.play('stop');
    }
    // Interruptive, once.
    public function playerWillJump():void
    {
      if (player.currentAnimation().name == 'jump') return;
      player.play('jump');
    }
    // Smooth.
    public function playerIsStill():void
    {
      if (!player.finished) return;
      player.play('still');
    }
    // Smooth.
    public function playerIsRunning():void
    {
      if (!player.finished) return;
      player.play('run');
    }
    // Smooth, once.
    public function playerIsLanding():void
    {
      if (!player.finished) return;
      if (player.currentAnimation().name == 'land') return;
      player.play('land');
    }
    public function playerIsRising():void {}
    // Smooth.
    public function playerIsFalling():void
    {
      if (!player.finished) return;
      player.play('fall');
    }

    // Helpers
    // -------
    private function wrapToStage(obj:FlxSprite):void
    {
      obj.x = FlxU.bound(obj.x, 0, (platform.width - obj.width));
      obj.y = FlxU.bound(obj.y, 0, (platform.height - obj.height));
    }
  }
}

package
{
  import flash.display.BlendMode;

  import org.flixel.*;

  // The main coordinator: part config list, part asset list.
  // Handles all of the character animations.

  public class PlayState extends FlxState implements IPlayerAnimationDelegate
  {
    // Tileset that works with AUTO mode (best for thin walls)
    [Embed(source='data/auto_tiles.png')]private static var ImgAutoTiles:Class;
    // Tileset that works with OFF mode (do what you want mode)
    [Embed(source='data/empty_tiles.png')]private static var ImgCustomTiles:Class;

    [Embed(source='data/player.png')]private static var ImgPlayer:Class;

    // From farthest to closest.
    [Embed(source='data/bg-1.png')]private static var ImgBg1:Class;
    [Embed(source='data/bg-2.png')]private static var ImgBg2:Class;
    [Embed(source='data/bg-3.png')]private static var ImgBg3:Class;
    [Embed(source='data/bg-4.png')]private static var ImgBg4:Class;
    [Embed(source='data/bg-5.png')]private static var ImgBg5:Class;
    [Embed(source='data/bg-6.png')]private static var ImgBg6:Class;
    [Embed(source='data/bg-7.png')]private static var ImgBg7:Class;
    [Embed(source='data/bg-8.png')]private static var ImgBg8:Class;
    [Embed(source='data/bg-9.png')]private static var ImgBg9:Class;
    [Embed(source='data/bg-10.png')]private static var ImgBg10:Class;
    [Embed(source='data/bg-11.png')]private static var ImgBg11:Class;
    [Embed(source='data/bg-12.png')]private static var ImgBg12:Class;

    // The dynamically generated and extended FlxTilemap.
    private var platform:Platform;
    // Ledge controls, in tiles.

    // The extend FlxSprite.
    private static const PLAYER_WIDTH:uint = 72;
    private static const PLAYER_HEIGHT:uint = 72;
    private var player:Player;

    // The background with parallax.
    private var bg:Background;

    // Some game switches.
    private var fallChecking:Boolean;

    // Internal helpers.
    private var gameStatePollInterval:FlxTimer;

    // Flixel Methods
    // --------------
    override public function create():void
    {
      FlxG.mouse.hide();

      // Globals.
      FlxG.framerate = 30;
      FlxG.flashFramerate = 30;
      fallChecking = false;

      // Start our setup chain.
      setupPlatform();
      setupPlatformAfter();
      setupPlatformAndPlayerAfter();

      // For now, we add things in order to get correct layering.
      add(bg);
      add(platform);
      add(player);

      // Internals.
      // Don't do expensive operations too often, if possible.
      gameStatePollInterval = new FlxTimer();
      gameStatePollInterval.start(2, Number.POSITIVE_INFINITY,
        function(onTimer:FlxTimer):void {
          updateGameState(true);
        }
      );
    }
    override public function update():void
    {
      // Start our update chain.
      updatePlatform();
      updatePlatformAfter();
      updatePlatformAndPlayerAfter();

      super.update();
    }
    override public function draw():void
    {
      super.draw();
    }

    // Setup Routines
    // --------------
    // Since the observer pattern is too slow, we'll just name our functions to be like hooks.
    // The platform is the first thing that gets set up.
    private function setupPlatform():void
    {
      setupBg();

      // Creates a new tilemap with no arguments.
      platform = new Platform();

      // Customize our tile generation.
      platform.tileWidth = 32;
      platform.tileHeight = 32;
      platform.minLedgeSize = 3;
      platform.maxLedgeSize = 6;
      platform.minLedgeSpacing = new FlxPoint(4, 2);
      platform.maxLedgeSpacing = new FlxPoint(8, 4);
      platform.ledgeThickness = 2;

      // Set the bounds based on the background.
      platform.bounds = new FlxRect(bg.bounds.x, bg.bounds.y, bg.bounds.width, bg.bounds.height);

      // Make our platform.
      platform.makeMap(ImgAutoTiles);

      // Set points.
      var floorHeight:Number = PLAYER_HEIGHT;
      platform.startingPoint.x = (FlxG.width - PLAYER_HEIGHT) / 2;
      platform.startingPoint.y = platform.height - (PLAYER_HEIGHT + floorHeight);
      platform.endingPoint.y = (platform.maxLedgeSpacing.y + 1) * platform.tileHeight - PLAYER_HEIGHT
    }
    // Hooks.
    private function setupPlatformAfter():void
    {
      // Draw player at the bottom.
      setupPlayer(platform.startingPoint);
//      setupPlayer(platform.endingPoint);

      // Move until we don't overlap.
      while (platform.overlaps(player))
      {
        if (player.x <= 0)
        {
          player.x = FlxG.width;
        }
        player.x -= platform.tileWidth;
      }
    }
    private function setupPlatformAndPlayerAfter():void
    {
      setupCamera();
    }
    // Hooked routines.
    private function setupPlayer(start:FlxPoint):void
    {
      // Find start position for player.

      player = new Player(start.x, start.y);
      player.loadGraphic(ImgPlayer, true, true, 72);

      // Bounding box tweaks.
      player.height = player.frameWidth / 2;
      player.offset.y = player.frameWidth - player.height;
      player.tailOffset.x = 35;
      player.headOffset.x = 10;
      player.width = player.frameWidth - player.tailOffset.x;
      player.face(FlxObject.RIGHT);

      // Basic player physics.
      player.naturalForces.x = 1000; // friction
      player.naturalForces.y = 500; // gravity
      player.maxVelocity.x = 150;
      player.maxVelocity.y = 1500;

      // Player jump physics.
      player.jumpMaxVelocity.y = -250;
      player.jumpAccel.y = -0.3;

      // Animations.
      // Make sure to add end transitions, otherwise the last frame is skipped if framerate is low.
      player.addAnimation('still',[17], 12);
      player.addAnimation('idle', [], 12, false);
      player.addAnimation('run',  [0,1,2,3,4,5,6,7,8,9,10,11], 24);
      player.addAnimation('stop', [12,13,14,15,16,17], 24, false);
      player.addAnimation('start',[17,16,15,14,13,12], 24, false);
      player.addAnimation('jump', [18,19,20,21,22,23,24,25,26,27,28,29,30,31], 24, false);
      player.addAnimation('fall', [31]);
      player.addAnimation('land', [32,33,18,17], 12, false);
      player.animDelegate = this;

      // Process settings.
      player.init();
    }
    private function setupCamera():void
    {
      FlxG.camera.follow(player);
      platform.follow();
    }
    private function setupBg():void
    {
      // Load our scenery.
      bg = new Background();
      bg.bounds.x = 0;
      bg.bounds.y = 0;
      bg.parallax_factor = 1; // Our bg is part "foreground".
      bg.parallax_buffer = 1.7;

      // This is the lamest image loading ever.
      bg.addImage(ImgBg1);
      bg.addImage(ImgBg2);
      bg.addImage(ImgBg3);
      bg.addImage(ImgBg4);
      bg.addImage(ImgBg5);
      bg.addImage(ImgBg6);
      bg.addImage(ImgBg7);
      bg.addImage(ImgBg8);
      bg.addImage(ImgBg9);
      bg.addImage(ImgBg10);
      bg.addImage(ImgBg11);
      bg.addImage(ImgBg12);

      bg.layout();
    }

    // Update Routines
    // ---------------
    private function updatePlatform():void
    {
    }
    // Hooks.
    private function updatePlatformAfter():void
    {
      updatePlayer();
    }
    private function updatePlatformAndPlayerAfter():void
    {
      updateCamera(player.justFell());
      updateGameState();
    }
    // Hooked routines.
    private function updatePlayer():void
    {
      // Tilemaps can be collided just like any other FlxObject, and flixel
      // automatically collides each individual tile with the object.
      FlxG.collide(player, platform);
      // Wrap to stage.
      if (!player.inMidAir() && player.nextAction != Player.STOP)
      {
        var pPos:FlxPoint = new FlxPoint(player.x, player.y);
        wrapToStage(player);
        var pos:FlxPoint = new FlxPoint(player.x, player.y);
        if (FlxU.getDistance(pos, pPos) > 0)
        {
          player.nextAction = Player.STOP;
        }
      }
      else
      {
        wrapToStage(player);
      }
    }
    private function updateCamera(playerJustFell:Boolean):void
    {
      if (fallChecking && playerJustFell)
      {
        FlxG.camera.shake(
          0.01,
          0.1, null, true,
          FlxCamera.SHAKE_VERTICAL_ONLY
        );
      }
    }
    private function updateGameState(doChecks:Boolean=false):void
    {
      if (doChecks)
      {
        // Check if player is on top of last platform, periodically.
        // Play the end screen for a while, on click, switch to start screen.
        if (player.isTouching(FlxObject.FLOOR) && platform.isAtEndingPoint(player))
        {
          if (player.controlled)
          {
            player.controlled = false;
            var title:Text, instructions:Text;
            title = new Text(0, Text.BASELINE * 2, FlxG.width,
              "The End", Text.H1);
            instructions = new Text(0, Text.BASELINE * 4, FlxG.width,
              "Click to play again");
            instructions.size = 16;
            // Stay on the screen.
            instructions.scrollFactor = title.scrollFactor = new FlxPoint();
            add(title);
            add(instructions);
          }
          else if (!player.controlled && FlxG.mouse.justPressed())
          {
            MorningStroll.endGame();
          }
        }
      }
      if (FlxG.keys.justPressed('Q'))
      {
        MorningStroll.endGame();
      }
    }

    // Delegate Methods
    // ----------------
    public function playerWillUpdateAnimation():void {}
    public function playerDidUpdateAnimation():void {}
    // Smooth, once.
    public function playerWillStart():void
    {
      if (!player.finished) return;
      if (player.currentAnimation().name == 'start') return;
      player.play('start');
    }
    // Interruptive, once.
    public function playerWillStop():void
    {
      if (player.currentAnimation().name == 'stop') return;
      player.play('stop');
    }
    // Interruptive, once.
    public function playerWillJump():void
    {
      if (player.currentAnimation().name == 'jump') return;
      player.play('jump');
    }
    // Smooth.
    public function playerIsStill():void
    {
      if (!player.finished) return;
      player.play('still');
    }
    // Smooth.
    public function playerIsRunning():void
    {
      if (!player.finished) return;
      player.play('run');
    }
    // Smooth, once.
    public function playerIsLanding():void
    {
      if (!player.finished) return;
      if (player.currentAnimation().name == 'land') return;
      player.play('land');
    }
    public function playerIsRising():void {}
    // Smooth.
    public function playerIsFalling():void
    {
      if (!player.finished) return;
      player.play('fall');
    }

    // Helpers
    // -------
    private function wrapToStage(obj:FlxSprite):void
    {
      obj.x = FlxU.bound(obj.x, 0, (platform.width - obj.width));
      obj.y = FlxU.bound(obj.y, 0, (platform.height - obj.height));
    }
  }
}

Add throttled checking using timers.

Add throttled checking using timers.


package aerys.minko.type.bounding
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;
	import aerys.minko.type.math.Vector4;

	import flash.geom.Vector3D;

	/**
	 * The BoundingBox class represents a six-sided (8 vertices) box that bounds the
	 * maximum extent of an object (ie an IMeshFilter object).
	 * @author Jean-Marc Le Roux
	 * @see aerys.minko.mesh.filter.IMeshFilter
	 */
	public final class BoundingBox
	{
		use namespace minko_math;

		private static const TMP_VECTOR4_1	: Vector4	= new Vector4();
		private static const TMP_VECTOR4_2	: Vector4	= new Vector4();

		minko_math var _min			: Vector4			= new Vector4();
		minko_math var _max			: Vector4			= new Vector4();

		minko_math var _vertices	: Vector.<Number>	= new Vector.<Number>(24, true);

		/**
		 * The bottom-left corner of the bounding box.
		 */
		public function get min() : Vector4
		{
			return _min;
		}

		/**
		 * The top-right corner of the bounding box.
		 */
		public function get max() : Vector4
		{
			return _max;
		}

		/**
		 * Creates a new BoundingBox object.
		 * @param	min The position of the bottom-left vertex.
		 * @param	max The position of the top-right vertex.
		 */
		public function BoundingBox(min : Vector4, max : Vector4)
		{
			update(min, max);
		}

		public function testPoint(point : Vector4) : Boolean
		{
			return point.x >= _min.x && point.x <= _max.x
				   && point.y >= _min.y && point.y <= _max.y
				   && point.z >= _min.z && point.z <= _max.z;
		}

		/**
		 * http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm
		 * http://gitorious.org/dimension/dimension/blobs/master/libdimension/prtree.c#line490
		 *
		 * @param ray
		 * @param transform
		 * @return The distance to the collision point on the box if the ray hits it, -1 otherwise.
		 *
		 */
		public function testRay(ray 		: Ray,
								transform	: Matrix4x4	= null,
								maxDistance	: Number	= Number.POSITIVE_INFINITY) : Number
		{
			var localOrigin 	: Vector4	= ray.origin;
			var localDirection	: Vector4	= ray.direction;

			if (transform)
			{
				localOrigin = transform.transformVector(ray.origin, TMP_VECTOR4_1);

				localDirection = transform.deltaTransformVector(ray.direction, TMP_VECTOR4_2);
				localDirection.normalize();
			}

			var ox		: Number	= localOrigin.x;
			var oy		: Number	= localOrigin.y;
			var oz		: Number	= localOrigin.z;

			var dx		: Number	= 1.0 / localDirection.x;
			var dy		: Number	= 1.0 / localDirection.y;
			var dz		: Number	= 1.0 / localDirection.z;

			var minX	: Number	= _min.x;
			var minY	: Number	= _min.y;
			var minZ	: Number	= _min.z;
			var maxX	: Number	= _max.x;
			var maxY	: Number	= _max.y;
			var maxZ	: Number	= _max.z;

			var tx1		: Number	= (minX - ox) * dx;
			var tx2		: Number	= (maxX - ox) * dx;

			var min		: Number	= tx1 < tx2 ? tx1 : tx2;
			var max		: Number	= tx1 > tx2 ? tx1 : tx2;
			var tmin	: Number	= min;
			var tmax	: Number	= max;

			var ty1		: Number	= (minY - oy) * dy;
			var ty2		: Number	= (maxY - oy) * dy;

			min = ty1 < ty2 ? ty1 : ty2;
			max = ty1 > ty2 ? ty1 : ty2;
			tmin = tmin > min ? tmin : min;
			tmax = tmax < max ? tmax : max;

			var tz1		: Number	= (minZ - oz) * dz;
			var tz2		: Number	= (maxZ - oz) * dz;

			min = tz1 < tz2 ? tz1 : tz2;
			max = tz1 > tz2 ? tz1 : tz2;
			tmin = tmin > min ? tmin : min;
			tmax = tmax < max ? tmax : max;

			return tmax >= Math.max(0, tmin) && tmin < maxDistance
				? tmin
				: -1.0;
		}

		public function clone() : BoundingBox
		{
			return new BoundingBox(_min, _max);
		}

		public function merge(box : BoundingBox) : void
		{
			var min : Vector3D	= _min._vector;
			var max : Vector3D	= _max._vector;

			min.x = _vertices[0] = _vertices[9] = _vertices[15] = _vertices[18] = Math.min(min.x, box._min.x);
			min.y = _vertices[1] = _vertices[4] = _vertices[7] = _vertices[10] = Math.min(min.y, box._min.y);
			min.z = _vertices[2] = _vertices[5] = _vertices[20] = _vertices[23] = Math.min(min.z, box._min.z);

			max.x = _vertices[3] = _vertices[6] = _vertices[12] = _vertices[21] = Math.max(max.x, box._max.x);
			max.y = _vertices[13] = _vertices[16] = _vertices[19] = _vertices[22] = Math.max(max.y, box._max.y);
			max.z = _vertices[8] = _vertices[11] = _vertices[14] = _vertices[17] = Math.max(max.z, box._max.z);
		}

		public static function merge(box1 : BoundingBox, box2 : BoundingBox) : BoundingBox
		{
			return new BoundingBox(
				new Vector4(
					Math.min(box1._min.x, box2._min.x),
					Math.min(box1._min.y, box2._min.y),
					Math.min(box1._min.z, box2._min.z)
				),
				new Vector4(
					Math.max(box1._max.x, box2._max.x),
					Math.max(box1._max.y, box2._max.y),
					Math.max(box1._max.z, box2._max.z)
				)
			);
		}

		public function update(min : Vector4 	= null,
							   max : Vector4	= null) : void
		{
			var vmin : Vector3D	= _min._vector;
			var vmax : Vector3D	= _max._vector;

			vmin.x = _vertices[0] = _vertices[9] = _vertices[15] = _vertices[18] = min.x;
			vmin.y = _vertices[1] = _vertices[4] = _vertices[7] = _vertices[10] = min.y;
			vmin.z = _vertices[2] = _vertices[5] = _vertices[20] = _vertices[23] = min.z;

			vmax.x = _vertices[3] = _vertices[6] = _vertices[12] = _vertices[21] = max.x;
			vmax.y = _vertices[13] = _vertices[16] = _vertices[19] = _vertices[22] = max.y;
			vmax.z = _vertices[8] = _vertices[11] = _vertices[14] = _vertices[17] = max.z;
		}
	}
}

package aerys.minko.type.bounding
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.type.math.Matrix4x4;
	import aerys.minko.type.math.Ray;
	import aerys.minko.type.math.Vector4;

	import flash.geom.Vector3D;

	/**
	 * The BoundingBox class represents a six-sided (8 vertices) box that bounds the
	 * maximum extent of an object (ie an IMeshFilter object).
	 * @author Jean-Marc Le Roux
	 * @see aerys.minko.mesh.filter.IMeshFilter
	 */
	public final class BoundingBox
	{
		use namespace minko_math;

		private static const TMP_VECTOR4_1	: Vector4	= new Vector4();
		private static const TMP_VECTOR4_2	: Vector4	= new Vector4();

		minko_math var _min			: Vector4			= new Vector4();
		minko_math var _max			: Vector4			= new Vector4();

		minko_math var _vertices	: Vector.<Number>	= new Vector.<Number>(24, true);

		/**
		 * The bottom-left corner of the bounding box.
		 */
		public function get min() : Vector4
		{
			return _min;
		}

		/**
		 * The top-right corner of the bounding box.
		 */
		public function get max() : Vector4
		{
			return _max;
		}

		/**
		 * Creates a new BoundingBox object.
		 * @param	min The position of the bottom-left vertex.
		 * @param	max The position of the top-right vertex.
		 */
		public function BoundingBox(min : Vector4, max : Vector4)
		{
			update(min, max);
		}

		public function testPoint(point : Vector4) : Boolean
		{
			return point.x >= _min.x && point.x <= _max.x
				   && point.y >= _min.y && point.y <= _max.y
				   && point.z >= _min.z && point.z <= _max.z;
		}

		/**
		 * http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm
		 * http://gitorious.org/dimension/dimension/blobs/master/libdimension/prtree.c#line490
		 *
		 * @param ray
		 * @param transform
		 * @return The distance to the collision point on the box if the ray hits it, -1 otherwise.
		 *
		 */
		public function testRay(ray 		: Ray,
								transform	: Matrix4x4	= null,
								maxDistance	: Number	= Number.POSITIVE_INFINITY) : Number
		{
			var localOrigin 	: Vector4	= ray.origin;
			var localDirection	: Vector4	= ray.direction;

			if (transform)
			{
				localOrigin = transform.transformVector(ray.origin, TMP_VECTOR4_1);

				localDirection = transform.deltaTransformVector(ray.direction, TMP_VECTOR4_2);
				localDirection.normalize();
			}

			var ox		: Number	= localOrigin.x;
			var oy		: Number	= localOrigin.y;
			var oz		: Number	= localOrigin.z;

			var dx		: Number	= 1.0 / localDirection.x;
			var dy		: Number	= 1.0 / localDirection.y;
			var dz		: Number	= 1.0 / localDirection.z;

			var minX	: Number	= _min.x;
			var minY	: Number	= _min.y;
			var minZ	: Number	= _min.z;
			var maxX	: Number	= _max.x;
			var maxY	: Number	= _max.y;
			var maxZ	: Number	= _max.z;

			var tx1		: Number	= (minX - ox) * dx;
			var tx2		: Number	= (maxX - ox) * dx;

			var min		: Number	= tx1 < tx2 ? tx1 : tx2;
			var max		: Number	= tx1 > tx2 ? tx1 : tx2;
			var tmin	: Number	= min;
			var tmax	: Number	= max;

			var ty1		: Number	= (minY - oy) * dy;
			var ty2		: Number	= (maxY - oy) * dy;

			min = ty1 < ty2 ? ty1 : ty2;
			max = ty1 > ty2 ? ty1 : ty2;
			tmin = tmin > min ? tmin : min;
			tmax = tmax < max ? tmax : max;

			var tz1		: Number	= (minZ - oz) * dz;
			var tz2		: Number	= (maxZ - oz) * dz;

			min = tz1 < tz2 ? tz1 : tz2;
			max = tz1 > tz2 ? tz1 : tz2;
			tmin = tmin > min ? tmin : min;
			tmax = tmax < max ? tmax : max;

			return tmax >= Math.max(0, tmin) && tmin < maxDistance
				? tmin
				: -1.0;
		}

		public function clone() : BoundingBox
		{
			return new BoundingBox(_min, _max);
		}

		public function merge(box : BoundingBox) : void
		{
			var min : Vector3D	= _min._vector;
			var max : Vector3D	= _max._vector;

			min.x = _vertices[0] = _vertices[9] = _vertices[15] = _vertices[18] = Math.min(min.x, box._min.x);
			min.y = _vertices[1] = _vertices[4] = _vertices[7] = _vertices[10] = Math.min(min.y, box._min.y);
			min.z = _vertices[2] = _vertices[5] = _vertices[20] = _vertices[23] = Math.min(min.z, box._min.z);

			max.x = _vertices[3] = _vertices[6] = _vertices[12] = _vertices[21] = Math.max(max.x, box._max.x);
			max.y = _vertices[13] = _vertices[16] = _vertices[19] = _vertices[22] = Math.max(max.y, box._max.y);
			max.z = _vertices[8] = _vertices[11] = _vertices[14] = _vertices[17] = Math.max(max.z, box._max.z);
		}

		public static function merge(box1 : BoundingBox, box2 : BoundingBox) : BoundingBox
		{
			return new BoundingBox(
				new Vector4(
					Math.min(box1._min.x, box2._min.x),
					Math.min(box1._min.y, box2._min.y),
					Math.min(box1._min.z, box2._min.z)
				),
				new Vector4(
					Math.max(box1._max.x, box2._max.x),
					Math.max(box1._max.y, box2._max.y),
					Math.max(box1._max.z, box2._max.z)
				)
			);
		}

		public function update(min : Vector4 	= null,
							   max : Vector4	= null) : void
		{
			var vmin : Vector3D	= _min._vector;
			var vmax : Vector3D	= _max._vector;

			vmin.x = _vertices[0] = _vertices[9] = _vertices[15] = _vertices[18] = min.x;
			vmin.y = _vertices[1] = _vertices[4] = _vertices[7] = _vertices[10] = min.y;
			vmin.z = _vertices[2] = _vertices[5] = _vertices[20] = _vertices[23] = min.z;

			vmax.x = _vertices[3] = _vertices[6] = _vertices[12] = _vertices[21] = max.x;
			vmax.y = _vertices[13] = _vertices[16] = _vertices[19] = _vertices[22] = max.y;
			vmax.z = _vertices[8] = _vertices[11] = _vertices[14] = _vertices[17] = max.z;
		}

		public function toString() : String
		{
			return '[BoundingBox=(' + _min.toString() + '), (' + _max.toString() + ')]';
		}

	}
}

Add a .toString() method on BoundingBox class.


package org.voisen.crayon
{
  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.display.Graphics;
  import flash.display.StageScaleMode;
  import flash.display.StageAlign;

  import flash.events.Event;

  import flash.geom.Point;

  import org.voisen.crayon.color.Colors;

  public class CrayonProgram extends Sprite
  {
    protected var canvas:Object;

    public function CrayonProgram()
    {
      setupCanvas();
      stage.addEventListener( Event.RESIZE, handleStageResize );
    }

    protected function clear():void
    {
      graphics.clear();
    }

    protected function draw(params:Object):void
    {
      setupParams( "shape", params );

      var g:Graphics = graphics;
      g.beginFill( (params.color is String ? Colors.getColorValue( params.color ) : params.color) || 0x000000 );
      switch( params.shape )
      {
        case "circle":
          g.drawCircle( params.center[0] || 0, params.center[1] || 0, params.radius || 0 );
          break;

        case "ellipse":
          g.drawEllipse( params.center[0] || 0, params.center[1] || 0, params.width || 0, params.height || 0 );
          break;

        case "rectangle":
          g.drawRect( params.corner[0] || 0, params.corner[1] || 0, params.width || 0, params.height || 0 );
          break;

        case "line":
          break;

        default:
          throw new Error( params.shape + " is not a valid shape" );
          break;
      }

      g.endFill();
    }

    protected function print(params:Object):void
    {
      setupParams( "output", params );
      trace( params.output );
    }

    protected function random(params:Object):Number
    {
      setupParams( "max", params );
      return Math.round( Math.random()*(params.max - params.min) + params.min );
    }

    protected function sin(params:Object):Number
    {
      setupParams( "value", params );
      return Math.sin( params.value );
    }

    protected function cos(params:Object):Number
    {
      setupParams( "value", params );
      return Math.cos( params.value );
    }

    private function setupParams( defaultParamName:String, params:Object ):Object
    {
      if( params.__default )
      {
        params[defaultParamName] = params.__default;
      }

      return params;
    }

    private function setupCanvas():void
    {
      canvas = {width:stage.stageWidth,height:stage.stageHeight};
    }

    private function handleStageResize( event:Event ):void
    {
      setupCanvas();
    }
  }
}

package org.voisen.crayon
{
  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.display.Graphics;
  import flash.display.StageScaleMode;
  import flash.display.StageAlign;

  import flash.events.Event;

  import flash.geom.Point;

  import org.voisen.crayon.color.Colors;

  public class CrayonProgram extends Sprite
  {
    protected var canvas:Object;

    public function CrayonProgram()
    {
      setupCanvas();
      stage.addEventListener( Event.RESIZE, handleStageResize );
    }

    protected function clear():void
    {
      graphics.clear();
    }

    protected function draw(params:Object):void
    {
      setupParams( "shape", params );

      var g:Graphics = graphics;
      g.beginFill( (params.color is String ? Colors.getColorValue( params.color ) : params.color) || 0x000000 );
      switch( params.shape )
      {
        case "circle":
          g.drawCircle( params.center[0] || 0, params.center[1] || 0, params.radius || 0 );
          break;

        case "ellipse":
          g.drawEllipse( params.center[0] || 0, params.center[1] || 0, params.width || 0, params.height || 0 );
          break;

        case "rectangle":
          g.drawRect( params.corner[0] || 0, params.corner[1] || 0, params.width || 0, params.height || 0 );
          break;

        case "line":
          break;

        default:
          throw new Error( params.shape + " is not a valid shape" );
          break;
      }

      g.endFill();
    }

    protected function print(params:Object):void
    {
      setupParams( "output", params );
      trace( params.output );
    }

    protected function random(params:Object):Number
    {
      setupParams( "max", params );
      return Math.round( Math.random()*(params.max - params.min) + params.min );
    }

    protected function sin(params:Object):Number
    {
      setupParams( "value", params );
      return Math.sin( params.value );
    }

    protected function cos(params:Object):Number
    {
      setupParams( "value", params );
      return Math.cos( params.value );
    }

    protected function get pi():Number
    {
      return Math.PI;
    }

    private function setupParams( defaultParamName:String, params:Object ):Object
    {
      if( params.__default )
      {
        params[defaultParamName] = params.__default;
      }

      return params;
    }

    private function setupCanvas():void
    {
      canvas = {width:stage.stageWidth,height:stage.stageHeight};
    }

    private function handleStageResize( event:Event ):void
    {
      setupCanvas();
    }
  }
}

Add pi to base AS3 class

Add pi to base AS3 class