Diff of /iipzoom/src/IIPZoom.as [000000] .. [r71] Maximize Restore

  Switch to side-by-side view

--- a
+++ b/iipzoom/src/IIPZoom.as
@@ -0,0 +1,1102 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  IIPZoom High Resolution Image Viewer for IIPImage
+//
+//  Version: MPL 1.1/GPL 3/LGPL 3
+//
+//  The contents of this file are subject to the Mozilla Public License Version
+//  1.1 (the "License"); you may not use this file except in compliance with
+//  the License. You may obtain a copy of the License at
+//  http://www.mozilla.org/MPL/
+//
+//  Software distributed under the License is distributed on an "AS IS" basis,
+//  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+//  for the specific language governing rights and limitations under the
+//  License.
+//
+//  The Original Code is the OpenZoom SDK.
+//
+//  The Initial Developer of the Original Code is Daniel Gasienica.
+//  Portions created by the Initial Developer are Copyright (c) 2007-2009
+//  the Initial Developer. All Rights Reserved.
+//
+//  IIPZoom Extensions are Copyright (c) 2010 Ruven Pillay
+//
+//  Contributor(s):
+//    Daniel Gasienica <daniel@gasienica.ch>
+//    Ruven Pillay <ruven@users.sourceforge.net>
+//
+//  Alternatively, the contents of this file may be used under the terms of
+//  either the GNU General Public License Version 3 or later (the "GPL"), or
+//  the GNU Lesser General Public License Version 3 or later (the "LGPL"),
+//  in which case the provisions of the GPL or the LGPL are applicable instead
+//  of those above. If you wish to allow use of your version of this file only
+//  under the terms of either the GPL or the LGPL, and not to allow others to
+//  use your version of this file under the terms of the MPL, indicate your
+//  decision by deleting the provisions above and replace them with the notice
+//  and other provisions required by the GPL or the LGPL. If you do not delete
+//  the provisions above, a recipient may use your version of this file under
+//  the terms of any one of the MPL, the GPL or the LGPL.
+//
+////////////////////////////////////////////////////////////////////////////////
+package
+{
+
+import caurina.transitions.Tweener;
+import caurina.transitions.properties.DisplayShortcuts;
+
+import flash.display.SimpleButton;
+import flash.display.Sprite;
+import flash.display.StageAlign;
+import flash.display.StageScaleMode;
+import flash.display.Bitmap;
+import flash.display.BitmapData;
+import flash.display.Loader;
+import flash.events.ContextMenuEvent;
+import flash.events.Event;
+import flash.events.IOErrorEvent;
+import flash.events.KeyboardEvent;
+import flash.events.MouseEvent;
+import flash.events.SecurityErrorEvent;
+import flash.events.TimerEvent;
+import flash.filters.GlowFilter;
+import flash.filters.DropShadowFilter;
+import flash.filters.ColorMatrixFilter;
+import flash.geom.Rectangle;
+import flash.net.FileReference;
+import flash.net.URLRequest;
+import flash.net.URLLoader;
+import flash.events.Event;
+import flash.net.navigateToURL;
+import flash.ui.ContextMenu;
+import flash.ui.ContextMenuItem;
+import flash.ui.Mouse;
+import flash.utils.Dictionary;
+import flash.utils.Timer;
+import flash.utils.setTimeout;
+import flash.text.TextField;
+import flash.text.TextFieldAutoSize;
+import flash.text.TextFormat;
+import flash.text.AntiAliasType;
+
+
+import org.openzoom.flash.descriptors.iip.IIPImageDescriptor;
+import org.openzoom.flash.components.MemoryMonitor;
+import org.openzoom.flash.components.MultiScaleImage;
+import org.openzoom.flash.components.Spinner;
+import org.openzoom.flash.descriptors.IImageSourceDescriptor;
+import org.openzoom.flash.descriptors.IMultiScaleImageDescriptor;
+import org.openzoom.flash.utils.ExternalMouseWheel;
+import org.openzoom.flash.utils.math.clamp;
+import org.openzoom.flash.viewport.constraints.CenterConstraint;
+import org.openzoom.flash.viewport.constraints.CompositeConstraint;
+import org.openzoom.flash.viewport.constraints.ScaleConstraint;
+import org.openzoom.flash.viewport.constraints.VisibilityConstraint;
+import org.openzoom.flash.viewport.constraints.ZoomConstraint;
+import org.openzoom.flash.viewport.controllers.ContextMenuController;
+import org.openzoom.flash.viewport.controllers.KeyboardController;
+import org.openzoom.flash.viewport.controllers.MouseController;
+import org.openzoom.flash.viewport.transformers.TweenerTransformer;
+import org.openzoom.nano.ui.Sad;
+import org.openzoom.nano.ui.FullScreenButton;
+import org.openzoom.nano.ui.ShowAllButton;
+import org.openzoom.nano.ui.ZoomInButton;
+import org.openzoom.nano.ui.ZoomOutButton;
+
+
+
+/**
+ * IIPZoom IIPImage flash viewer based on the open source OpenZoom SDK.
+ */
+[SWF(width="960", height="600", frameRate="30", backgroundColor="#000000")]
+public class IIPZoom extends Sprite
+{
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+
+    private static const DEFAULT_LOAD_TIMEOUT:uint = 100
+    private static const DEFAULT_MAX_SCALE_FACTOR:Number = 1.1
+    private static const DEFAULT_VISIBILITY_RATIO:Number = 0.5
+    private static const DEFAULT_CHROME_HIDE_DELAY:Number = 1500 // milliseconds
+    private static const DEFAULT_MEMORY_MONITOR_KEY:uint = 77 // M
+
+    private static const DEFAULT_BUTTON_SIZE:Number = 24
+    private static const DEFAULT_BUTTON_SPACING:Number = 2
+    private static const DEFAULT_BUTTON_MARGIN:Number = 5
+    private static const DEFAULT_BUTTON_ALPHA:Number = 0.8
+
+    private static const ABOUT_MENU_CAPTION:String = "About IIPImage..."
+    private static const ABOUT_MENU_URL:String = "http://iipimage.sourceforge.net/"
+
+    private static const VERSION_MAJOR:uint = 0
+    private static const VERSION_MINOR:uint = 9
+    private static const VERSION_BUGFIX:uint = 1
+    private static const VERSION_BUILD:uint = 0
+    private static const VERSION:String = [VERSION_MAJOR,
+                                           VERSION_MINOR,
+                                           VERSION_BUGFIX,
+                                           VERSION_BUILD].join(".")
+    private static const VERSION_MENU_CAPTION:String = "High Resolution Scientific Visualization" // (" + VERSION + ")"
+
+    private static const DEFAULT_SERVER:Object = "/fcgi-bin/iipsrv.fcgi"
+    private static const DEFAULT_IMAGE:Object = ""
+    private static const DEFAULT_VIEWPORT_BOUNDS:String = "0, 0, 1, 1"
+
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * Constructor.
+     */
+    public function IIPZoom()
+    {
+        addEventListener(Event.ADDED_TO_STAGE,
+                         addedToStageHandler,
+                         false, 0, true)
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //--------------------------------------------------------------------------
+
+    // UI
+    private var image:MultiScaleImage
+
+    private var sad:Sprite
+    private var spinner:Spinner
+    private var zoomInButton:SimpleButton
+    private var zoomOutButton:SimpleButton
+    private var showAllButton:SimpleButton
+    private var fullScreenButton:FullScreenButton
+
+    private var memoryMonitor:MemoryMonitor
+
+    [Embed(source="iip.png")]
+    private var Picture:Class
+
+    private var iipLogo:Bitmap
+    private var iipCredit:TextField = null
+
+    private var idleTimer:Timer
+    private var idle:Boolean = false
+
+    // Context menu
+    private var aboutMenu:ContextMenuItem
+    private var versionMenu:ContextMenuItem
+
+    private var viewImageMenuDescriptors:Dictionary = new Dictionary()
+    private var saveAsMenuDescriptors:Dictionary = new Dictionary()
+
+    private var activated:Boolean = false
+    private var imageFile:FileReference
+
+    private var server:String
+    private var im:String
+    private static var loader:URLLoader
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Initialization
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function initializeStage():void
+    {
+        if (stage)
+        {
+            // Enable mouse wheel support for browsers
+            // on Mac OS as well as Safari on Windows
+            ExternalMouseWheel.initialize(stage)
+
+            // Configure stage
+            stage.align = StageAlign.TOP_LEFT
+            stage.scaleMode = StageScaleMode.NO_SCALE
+            stage.addEventListener(Event.RESIZE,
+                                   stage_resizeHandler,
+                                   false, 0, true)
+            stage.addEventListener(KeyboardEvent.KEY_DOWN,
+                                   stage_keyDownHandler,
+                                   false, 0, true)
+            stage.addEventListener(MouseEvent.MOUSE_MOVE,
+                                   stage_mouseMoveHandler,
+                                   false, 0, true)
+            stage.addEventListener(Event.MOUSE_LEAVE,
+                                   stage_mouseLeaveHandler,
+                                   false, 0, true)
+//            stage.addEventListener(Event.ACTIVATE,
+//                                   stage_activateHandler,
+//                                   false, 0, true)
+//            stage.addEventListener(Event.DEACTIVATE,
+//                                   stage_deactivateHandler,
+//                                   false, 0, true)
+        }
+    }
+
+    private function initializeChromeTimer():void
+    {
+        idleTimer = new Timer(DEFAULT_CHROME_HIDE_DELAY, 1)
+        idleTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
+                                   chromeTimer_completeHandler,
+                                   false, 0, true)
+        idleTimer.start()
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function loadSource():void
+    {
+        try
+        {
+            // Image source
+	    server = getParameter(IIPZoomParameters.SERVER,DEFAULT_SERVER)
+	    im = getParameter(IIPZoomParameters.IMAGE,DEFAULT_IMAGE)
+	    var credit:String = getParameter(IIPZoomParameters.CREDIT,null)
+
+	    if( credit ) createMessage( credit )
+
+	    var request:URLRequest = new URLRequest( IIPImageDescriptor.getMetaDataURL(im,server) )
+	    loader = new URLLoader();
+
+            try {
+		loader.load(request);
+	    }
+	    catch (error:SecurityError)
+            {
+		trace("A SecurityError has occurred.");
+	    }
+
+	    loader.addEventListener(Event.COMPLETE, metaDataLoaded)
+	    loader.addEventListener(IOErrorEvent.IO_ERROR, showSad)
+
+        }
+        catch (error:Error) // Security error
+        {
+            showSad()
+        }
+    }
+
+    private function metaDataLoaded(event:Event):void
+    {
+	image.source = IIPImageDescriptor.fromBasicInfo( loader.data, im, server )
+
+	if( !image.source ){
+		showSad()
+		return
+	}
+
+    }
+
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Children
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function createChildren():void
+    {
+        if (!sad)
+            createSad()
+
+        if (!image)
+            createImage()
+
+        if (!spinner)
+            createSpinner()
+
+        if (!zoomInButton)
+            createZoomInButton()
+
+        if (!zoomOutButton)
+            createZoomOutButton()
+
+        if (!showAllButton)
+            createShowAllButton()
+
+        if (!fullScreenButton)
+            createFullScreenButton()
+
+	createLogo()
+    }
+
+    /**
+     * @private
+     */
+    private function createSad():void
+    {
+        sad = new Sad()
+        sad.visible = false
+        addChild(sad)
+    }
+
+    /**
+     * @private
+     */
+    private function createImage():void
+    {
+        image = new MultiScaleImage()
+
+        configureTransformer(image)
+        configureControllers(image)
+        configureListeners(image)
+
+        addChild(image)
+    }
+
+    /**
+     * @private
+     */
+    private function createSpinner():void
+    {
+        spinner = new Spinner(12, 4, 0xEEEEEE)
+        spinner.visible = false
+        spinner.alpha = 0
+        spinner.filters = [new GlowFilter(0xFFFFFF, 0.25, 2, 2)]
+        addChild(spinner)
+    }
+
+    /**
+     * @private
+     */
+    private function createZoomInButton():void
+    {
+        zoomInButton = new ZoomInButton()
+        zoomInButton.alpha = DEFAULT_BUTTON_ALPHA
+        zoomInButton.width = zoomInButton.height = DEFAULT_BUTTON_SIZE
+        zoomInButton.addEventListener(MouseEvent.MOUSE_DOWN,
+                                      zoomInButton_mouseDownHandler,
+                                      false, 0, true)
+        addChild(zoomInButton)
+    }
+
+    /**
+     * @private
+     */
+    private function createZoomOutButton():void
+    {
+        zoomOutButton = new ZoomOutButton()
+        zoomOutButton.alpha = DEFAULT_BUTTON_ALPHA
+        zoomOutButton.width = zoomOutButton.height = DEFAULT_BUTTON_SIZE
+        zoomOutButton.addEventListener(MouseEvent.MOUSE_DOWN,
+                                       zoomOutButton_mouseDownHandler,
+                                       false, 0, true)
+        addChild(zoomOutButton)
+    }
+
+    /**
+     * @private
+     */
+    private function createShowAllButton():void
+    {
+        showAllButton = new ShowAllButton()
+        showAllButton.alpha = DEFAULT_BUTTON_ALPHA
+        showAllButton.width = showAllButton.height = DEFAULT_BUTTON_SIZE
+        showAllButton.addEventListener(MouseEvent.MOUSE_DOWN,
+                                       showAllButton_mouseDownHandler,
+                                       false, 0, true)
+        addChild(showAllButton)
+    }
+
+    /**
+     * @private
+     */
+    private function createFullScreenButton():void
+    {
+        fullScreenButton = new FullScreenButton()
+        fullScreenButton.alpha = DEFAULT_BUTTON_ALPHA
+        fullScreenButton.width = fullScreenButton.height = DEFAULT_BUTTON_SIZE
+        addChild(fullScreenButton)
+    }
+
+    /**
+     * @private
+     */ 
+    private function createLogo():void
+    {
+	iipLogo = new Picture()
+	iipLogo.alpha = 0.7
+	iipLogo.filters = [new DropShadowFilter(5,45,0x000000,0.5)]
+
+	var sp:Sprite = new Sprite()
+	sp.addChild(iipLogo)
+	addChild(sp)
+
+	sp.addEventListener(MouseEvent.MOUSE_OVER, logoOver, false, 0, true)
+	sp.addEventListener(MouseEvent.MOUSE_OUT, logoOut, false, 0, true)
+	sp.addEventListener(MouseEvent.CLICK, gotoIIPImage, false, 0, true)
+
+	
+    }
+
+    // Logo event functions
+    private function logoOver(event:MouseEvent):void
+    {
+	Tweener.addTween(iipLogo, {_autoAlpha: 1, time: 1})
+    }
+    private function logoOut(event:MouseEvent):void
+    {
+	Tweener.addTween(iipLogo, {_autoAlpha: 0.7, time: 1})
+    }
+    private function gotoIIPImage(event:MouseEvent):void
+    {
+	var url:URLRequest = new URLRequest("http://iipimage.sourceforge.net")
+	navigateToURL( url, "_blank" )
+    }
+
+    // Create a Message box
+    private function createMessage( message:String ):void
+    {
+	iipCredit = new TextField()
+	iipCredit.autoSize = TextFieldAutoSize.LEFT
+	iipCredit.background = true
+	iipCredit.backgroundColor = 0x000000
+	iipCredit.alpha = 0.75
+	iipCredit.antiAliasType = AntiAliasType.ADVANCED
+
+        var format:TextFormat = new TextFormat( "Verdana", 14, 0xBBBBBB )
+	format.letterSpacing = 0.5
+	format.leftMargin = 5
+	format.rightMargin = 5
+        iipCredit.defaultTextFormat = format;
+
+//        iipCredit.filters = [new DropShadowFilter(5,45,0x000000,0.5),new GlowFilter(0xEEEEEE, 0.25, 2, 2)]
+	iipCredit.filters = [new DropShadowFilter(5,45,0x000000,0.5)]
+	iipCredit.htmlText = message
+
+	addChild(iipCredit)
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Image configuration
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function configureTransformer(image:MultiScaleImage):void
+    {
+        image.transformer = new TweenerTransformer()
+    }
+
+    /**
+     * @private
+     */
+    private function configureControllers(image:MultiScaleImage):void
+    {
+        var keyboardController:KeyboardController = new KeyboardController()
+        var mouseController:MouseController = new MouseController()
+
+        var contextMenuController:ContextMenuController
+        contextMenuController = new ContextMenuController()
+
+        image.controllers = [mouseController,
+                             keyboardController,
+                             contextMenuController]
+    }
+
+    /**
+     * @private
+     */
+    private function configureConstraints(image:MultiScaleImage):void
+    {
+        // Prevent image from zooming out
+        var zoomConstraint:ZoomConstraint = new ZoomConstraint()
+            zoomConstraint.minZoom = 1
+
+        // Center at minimum zoom level
+        var centerConstraint:CenterConstraint = new CenterConstraint()
+
+        // Prevent from zooming in more than the original size of the image
+        var scaleConstraint:ScaleConstraint = new ScaleConstraint()
+
+        var imageWidth:Number
+        var imageHeight:Number
+        var defaultDimension:Number = image.sceneWidth
+
+        if (image.source && image.source is IMultiScaleImageDescriptor)
+        {
+            var descriptor:IMultiScaleImageDescriptor
+            descriptor = IMultiScaleImageDescriptor(image.source)
+            imageWidth = descriptor.width
+            imageHeight = descriptor.height
+            var maxScale:Number = Math.max(imageWidth / defaultDimension,
+                                           imageHeight / defaultDimension)
+            scaleConstraint.maxScale = DEFAULT_MAX_SCALE_FACTOR * maxScale
+        }
+
+        // Prevent image from disappearing from the viewport
+        var visibilityConstraint:VisibilityConstraint = new VisibilityConstraint()
+            visibilityConstraint.visibilityRatio = DEFAULT_VISIBILITY_RATIO
+
+        // Chain all constraints together
+        var compositeContraint:CompositeConstraint = new CompositeConstraint()
+            compositeContraint.constraints = [centerConstraint,
+                                              visibilityConstraint,
+                                              zoomConstraint,
+                                              scaleConstraint]
+        // Apply constraints
+        image.constraint = compositeContraint
+    }
+
+    /**
+     * @private
+     */
+    private function configureListeners(image:MultiScaleImage):void
+    {
+        image.loader.addEventListener(Event.INIT,
+                                      loader_initHandler,
+                                      false, 0, true)
+        image.loader.addEventListener(Event.COMPLETE,
+                                      loader_completeHandler,
+                                      false, 0, true)
+
+        image.addEventListener(Event.COMPLETE,
+                               image_completeHandler,
+                               false, 0, true)
+        image.addEventListener(IOErrorEvent.IO_ERROR,
+                               image_ioErrorHandler,
+                               false, 0, true)
+        image.addEventListener(SecurityErrorEvent.SECURITY_ERROR,
+                               image_securityErrorHandler,
+                               false, 0, true)
+    }
+
+    /**
+     * @private
+     */
+    private function configureContextMenu(image:MultiScaleImage):void
+    {
+        var menu:ContextMenu
+
+        if (image.contextMenu && image.contextMenu.customItems)
+            menu = image.contextMenu
+        else
+            menu = new ContextMenu()
+
+        menu.hideBuiltInItems()
+
+	// Add these to the beginning
+
+	menu.customItems.unshift( new ContextMenuItem("...",true,false) );
+
+        aboutMenu = new ContextMenuItem(ABOUT_MENU_CAPTION,
+                                        false /* separator */)
+        aboutMenu.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+                                   aboutMenu_menuItemSelectHandler,
+                                   false, 0, true)
+        menu.customItems.unshift(aboutMenu)
+
+        versionMenu = new ContextMenuItem(VERSION_MENU_CAPTION,
+                                          false, /* separator */
+                                          false /* enabled */)
+        menu.customItems.unshift(versionMenu)
+
+
+
+        image.contextMenu = menu
+    }
+
+    /**
+     * @private
+     */
+    private function addSourcesContextMenus(image:MultiScaleImage):void
+    {
+        var menu:ContextMenu
+
+        if (image.contextMenu && image.contextMenu.customItems)
+            menu = image.contextMenu
+        else
+            menu = new ContextMenu()
+
+        menu.hideBuiltInItems()
+
+        var separator:Boolean = true
+        var sources:Array = IMultiScaleImageDescriptor(image.source).sources
+
+        var descriptor:IImageSourceDescriptor
+        var name:String
+        var caption:String
+
+        for each (descriptor in sources)
+        {
+            name = getDescriptorName(descriptor)
+            caption = ["View Image ", name].join("")
+
+            var viewImageMenu:ContextMenuItem
+            viewImageMenu = new ContextMenuItem(caption, separator /* separator */)
+            viewImageMenu.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+                                           viewImageMenu_menuItemSelectHandler,
+                                           false, 0, true)
+            menu.customItems.push(viewImageMenu)
+            viewImageMenuDescriptors[caption] = descriptor
+
+            if (separator)
+                separator = false
+        }
+
+        separator = true
+
+        for each (descriptor in sources)
+        {
+            var validDownload:Boolean = descriptor.url.indexOf("http://") == 0 ||
+                                        descriptor.url.indexOf("https://") == 0
+
+            if (!validDownload)
+                continue
+
+            name = getDescriptorName(descriptor)
+            caption = ["Save Image As... ", name].join("")
+
+            var saveAsMenu:ContextMenuItem
+            saveAsMenu = new ContextMenuItem(caption, separator /* Separator */)
+            saveAsMenu.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+                                        saveAsMenu_menuItemSelectHandler,
+                                        false, 0, true)
+            menu.customItems.push(saveAsMenu)
+            saveAsMenuDescriptors[caption] = descriptor
+
+            if (separator)
+                separator = false
+        }
+    }
+
+    private function getDescriptorName(descriptor:IImageSourceDescriptor):String
+    {
+        return ["(", descriptor.width, "x", descriptor.height, ")"].join("")
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Layout
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function layout():void
+    {
+        if (sad)
+        {
+            sad.width = stage.stageWidth / 2
+            sad.height = stage.stageHeight / 2
+
+            var scale:Number = Math.min(sad.scaleX, sad.scaleY)
+            sad.scaleX = sad.scaleY = scale
+
+            sad.x = (stage.stageWidth - sad.width)  / 2
+            sad.y = (stage.stageHeight - sad.height) / 2
+        }
+
+        if (spinner)
+        {
+            // top right
+            spinner.x = stage.stageWidth - spinner.width - 2
+            spinner.y = spinner.height + 2
+
+            // bottom left
+//            spinner.x = spinner.width + 2
+//            spinner.y = stage.stageHeight - spinner.height - 2
+        }
+
+
+	iipLogo.x = 5
+	iipLogo.y = stage.stageHeight - iipLogo.height - 5
+
+
+        if (fullScreenButton)
+        {
+//            fullScreenButton.x = stage.stageWidth - fullScreenButton.width - DEFAULT_BUTTON_MARGIN
+	    fullScreenButton.x = DEFAULT_BUTTON_MARGIN*3 + fullScreenButton.width*3 + iipLogo.width + 4
+            fullScreenButton.y = stage.stageHeight - fullScreenButton.height - DEFAULT_BUTTON_MARGIN - 1
+        }
+
+        if (showAllButton)
+        {
+            showAllButton.x = fullScreenButton.x - showAllButton.width - DEFAULT_BUTTON_SPACING
+            showAllButton.y = fullScreenButton.y
+        }
+
+        if (zoomOutButton)
+        {
+            zoomOutButton.x = showAllButton.x - zoomOutButton.width - DEFAULT_BUTTON_SPACING
+            zoomOutButton.y = fullScreenButton.y
+        }
+
+        if (zoomInButton)
+        {
+            zoomInButton.x = zoomOutButton.x - zoomInButton.width - DEFAULT_BUTTON_SPACING
+            zoomInButton.y = fullScreenButton.y
+        }
+
+        if (memoryMonitor)
+        {
+            memoryMonitor.x = 0//stage.stageWidth - memoryMonitor.width
+            memoryMonitor.y = 0//stage.stageHeight - memoryMonitor.height
+        }
+
+
+	if( iipCredit )
+	{
+		iipCredit.x = stage.stageWidth - iipCredit.width - 20
+		iipCredit.y = stage.stageHeight - iipCredit.height - 10
+	}
+
+        if (image)
+            image.setActualSize(stage.stageWidth, stage.stageHeight)
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Internal
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function showSad():void
+    {
+        sad.visible = true
+    }
+
+    /**
+     * @private
+     */
+    private function toggleMemoryMonitor():void
+    {
+        if (!memoryMonitor)
+        {
+            memoryMonitor = new MemoryMonitor()
+            addChild(memoryMonitor)
+        }
+        else
+        {
+            removeChild(memoryMonitor)
+            memoryMonitor = null
+        }
+
+        layout()
+    }
+
+    /**
+     * @private
+     */
+    private function hideChrome():void
+    {
+        idle = true
+        Mouse.hide()
+    }
+
+    /**
+     * @private
+     */
+    private function showChrome():void
+    {
+        idle = false
+        Mouse.show()
+
+        idleTimer.reset()
+        idleTimer.start()
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function addedToStageHandler(event:Event):void
+    {
+        // Tweener _autoAlpha property
+        DisplayShortcuts.init()
+
+        initializeStage()
+        initializeChromeTimer()
+
+        createChildren()
+        layout()
+
+        if (loaderInfo.url.indexOf("file://") == 0)
+            setTimeout(loadSource, DEFAULT_LOAD_TIMEOUT) // Workaround for FF on Mac OS X
+        else
+            loadSource()
+    }
+
+//    /**
+//     * @private
+//     */
+//    private function stage_activateHandler(event:Event):void
+//    {
+//        if (!activated)
+//        {
+//            activated = true
+//            loadSource()
+//        }
+//    }
+//
+//    /**
+//     * @private
+//     */
+//    private function stage_deactivateHandler(event:Event):void
+//    {
+//        if (activated)
+//            activated = false
+//    }
+
+    /**
+     * @private
+     */
+    private function stage_resizeHandler(event:Event):void
+    {
+        layout()
+    }
+
+    /**
+     * @private
+     */
+    private function stage_keyDownHandler(event:KeyboardEvent):void
+    {
+        if (event.keyCode == DEFAULT_MEMORY_MONITOR_KEY)
+           toggleMemoryMonitor()
+    }
+
+    /**
+     * @private
+     */
+    private function stage_mouseMoveHandler(event:MouseEvent):void
+    {
+        if (idle)
+           showChrome()
+    }
+
+    /**
+     * @private
+     */
+    private function stage_mouseLeaveHandler(event:Event):void
+    {
+        hideChrome()
+    }
+
+    /**
+     * @private
+     */
+    private function chromeTimer_completeHandler(event:TimerEvent):void
+    {
+        hideChrome()
+    }
+
+    /**
+     * @private
+     */
+    private function zoomInButton_mouseDownHandler(event:MouseEvent):void
+    {
+        if (image)
+           image.viewport.zoom *= 1.6
+    }
+
+    /**
+     * @private
+     */
+    private function zoomOutButton_mouseDownHandler(event:MouseEvent):void
+    {
+        if (image)
+           image.viewport.zoom *= 0.3
+    }
+
+    /**
+     * @private
+     */
+    private function showAllButton_mouseDownHandler(event:MouseEvent):void
+    {
+        if (image)
+           image.viewport.showAll()
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers: Loader
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function loader_initHandler(event:Event):void
+    {
+        Tweener.addTween(spinner, {_autoAlpha: 1, time: 1})
+    }
+
+    /**
+     * @private
+     */
+    private function loader_completeHandler(event:Event):void
+    {
+
+        Tweener.addTween(spinner, {_autoAlpha: 0, time: 1})
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers: Image
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function image_completeHandler(event:Event):void
+    {
+        // Viewport bounds
+        var bounds:Rectangle = getViewportBoundsParameter()
+        image.viewport.fitToBounds(bounds, 1.0, true)
+
+        configureConstraints(image)
+        addSourcesContextMenus(image)
+
+        // Important that this happens after attachment
+        configureContextMenu(image)
+        layout()
+    }
+
+    /**
+     * @private
+     */
+    private function image_ioErrorHandler(event:IOErrorEvent):void
+    {
+        showSad()
+        layout()
+    }
+
+    /**
+     * @private
+     */
+    private function image_securityErrorHandler(event:SecurityErrorEvent):void
+    {
+        showSad()
+        layout()
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Event handlers: Context menu
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function aboutMenu_menuItemSelectHandler(event:ContextMenuEvent):void
+    {
+        navigateToURL(new URLRequest(ABOUT_MENU_URL), "_blank")
+    }
+
+    /**
+     * @private
+     */
+    private function viewImageMenu_menuItemSelectHandler(event:ContextMenuEvent):void
+    {
+        var descriptor:IImageSourceDescriptor
+        var key:String = (event.currentTarget as ContextMenuItem).caption
+        descriptor = viewImageMenuDescriptors[key] as IImageSourceDescriptor
+
+        if (descriptor)
+        {
+            var request:URLRequest = new URLRequest(descriptor.url)
+            navigateToURL(request, "_blank")
+        }
+    }
+
+    /**
+     * @private
+     */
+    private function saveAsMenu_menuItemSelectHandler(event:ContextMenuEvent):void
+    {
+        var descriptor:IImageSourceDescriptor
+        var key:String = (event.currentTarget as ContextMenuItem).caption
+        descriptor = saveAsMenuDescriptors[key] as IImageSourceDescriptor
+
+        if (descriptor)
+        {
+            try
+            {
+                var request:URLRequest = new URLRequest(descriptor.url)
+                imageFile = new FileReference()
+                var defaultFileName:String = descriptor.url.substring(descriptor.url.lastIndexOf("/") + 1)
+                imageFile.download(request, defaultFileName)
+            }
+            catch(error:Error)
+            {
+                // Do nothing
+            }
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Methods: Parameters
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     * @private
+     */
+    private function getParameter(name:String, defaultValue:*):*
+    {
+        if (loaderInfo.parameters.hasOwnProperty(name))
+        {
+            var value:* = loaderInfo.parameters[name]
+            return value
+        }
+
+        return defaultValue
+    }
+
+    /**
+     * @private
+     */
+    private function getViewportBoundsParameter():Rectangle
+    {
+        var boundsParameterString:String =
+            getParameter( IIPZoomParameters.VIEWPORT_BOUNDS,
+                DEFAULT_VIEWPORT_BOUNDS) as String
+
+        var boundsParameter:Array = boundsParameterString.split(",")
+
+        var bounds:Rectangle = new Rectangle()
+        bounds.x = clamp(parseFloat(boundsParameter[0]), 0, 1)
+        bounds.y = clamp(parseFloat(boundsParameter[1]), 0, 1)
+        bounds.width = clamp(parseFloat(boundsParameter[2]), 0, 1)
+        bounds.height = clamp(parseFloat(boundsParameter[3]), 0, 1)
+
+        return bounds
+    }
+}
+
+}