    package {

        import away3d.cameras.HoverCamera3D;
        import away3d.containers.View3D;

        import com.techlabs.puzzle.ControlPanel;
        import com.techlabs.puzzle.PuzzleBoard;
        import com.techlabs.puzzle.events.PuzzleEvent;
        import com.techlabs.puzzle.helpers.ImageSlicer;

        import flash.display.Bitmap;
        import flash.display.Loader;
        import flash.display.Sprite;
        import flash.display.StageAlign;
        import flash.display.StageQuality;
        import flash.display.StageScaleMode;

        import flash.events.Event;
        import flash.net.URLRequest;

        /**
         * ...
         * @author Timo Virtanen
         */
        [SWF(width=800, height = 600, backgroundColor = "#333333", framerate = "25")]
        public class SlidingPuzzle extends Sprite {

            // Defines how the puzzle will be divided
            public static var subdivisions:int = 4;

            // Padding between the pieces
            public static var padding:int = 2;

            // The size of the puzzle board
            public static var size:int = 500;

            private var _image:Bitmap;

            private var _loader:Loader;
            private var _request:URLRequest;

            private var _slicer:ImageSlicer;
            private var _puzzleImages:Array;

            private var _gameBoard:PuzzleBoard;

            private var _view:View3D;
            private var _camera:HoverCamera3D;

            private var _controlPanel:ControlPanel;

            public function SlidingPuzzle() {
                _slicer = new ImageSlicer();

                _gameBoard = new PuzzleBoard();
                _puzzleImages = new Array();

                _controlPanel = new ControlPanel();
                _controlPanel.addEventListener(PuzzleEvent.CHANGE_IMAGE, imageChangeHandler);
                _controlPanel.addEventListener(PuzzleEvent.SHUFFLE, suffleHandler);
                addChild(_controlPanel);

                initStage();
                init3D();

                initPuzzle("assets/Puzzle_Flash.png");
            }

            private function initStage():void {
                stage.align = StageAlign.TOP_LEFT;
                stage.quality = StageQuality.MEDIUM;
                stage.scaleMode = StageScaleMode.NO_SCALE;
            }

            private function init3D():void {
                _camera = new HoverCamera3D();
                _camera.mintiltangle = 15;
                _camera.maxtiltangle = 65;

                _view = new View3D({width:800, height:600, x:400, y:300, camera:_camera});
                addChild(_view);

                addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            }

            private function initPuzzle(imageURL:String):void {
                loadPuzzleImage(imageURL);
            }

            // Loads the image
            private function loadPuzzleImage(imageURL:String):void {
                _loader = new Loader();
                _request = new URLRequest(imageURL);
                _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadComplete);
                _loader.load(_request);
            }

            // This will execute when load completes
            private function imageLoadComplete(e:Event):void {
                _image = _loader.content as Bitmap;

                // sliceImage() returns a two dimensional array containing the sliced bitmaps
                _puzzleImages = _slicer.sliceImage(_image);

                // creates the puzzle board
                _gameBoard.createPuzzle(_puzzleImages);
                _view.scene.addChild(_gameBoard);

                // sets the preview image in the control panel
                _controlPanel.setPreview(_image);
            }

            private function imageChangeHandler(e:PuzzleEvent):void {
                initPuzzle(e.imageURL);
            }

            private function suffleHandler(e:PuzzleEvent):void {
                _gameBoard.shuffle();
            }

            // EnterFrameHandler is responsible for
            // 1. camera controls
            // 2. rendering the 3D view
            private function enterFrameHandler(e:Event):void {
                _camera.targetpanangle = (stage.stageWidth * .5 - stage.mouseX) * .1 - 180;
                _camera.targettiltangle = (stage.stageHeight * .5 - stage.mouseY) * .5;
                _camera.hover();

                _view.render();
            }

        }

    }