diff --git a/README.markdown b/README.markdown
index 659b63a..af161a2 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,6 +1,6 @@
# FlexBook #
-- Current Version: 1.0.4
+- Current Version: 1.0.5
- Flex 3.5 / AS3 / Flash Player 9+
- Date: 4th July 2007
- Original Author: Ruben Swieringa
@@ -14,13 +14,14 @@ For demos / implementations, look at this repo:
- Fixed the 'sticky-page' bug;
- Added the jumptoPage method. It's similar to the gotoPage method, but it won't do a lot of flips to reach some page;
- Fixed the bug that, when adding a Page dynamically, the Page didn't show fold gradients;
-- Fixed the "RangeError #2006" bug, when a Book was instantiated without any children.
+- Fixed the "RangeError #2006" bug, when a Book was instantiated without any children;
+- Now jumptoPage is jumpToPage;
+- Fixed the jumpToPage bug. The changes on the jumpToPage method make it feel more natural.
## Known Issues / Bugs
- For Book instances, height values greater than the height of the content may slow down the application;
- ScrollPolicies for Page instances are disabled (the properties have been overridden and are idle in the Page class). When a Page instance is not being flipped, its fold-gradient is drawn upon a Shape instance stored within that Page its rawChildren. When scrollbars are displayed, the Shape instance will no longer be in place;
-- When using the jumptoPage method, the previous pages are instantly rendered before the flip. Example: You are on pages 2-3 and want to go to pages 8-9. When jumptoPage is called, the pages 6-7 will override pages 2-3, before flipping to pages 8-9.
## Credits
diff --git a/src/com/rubenswieringa/book/Book.as b/src/com/rubenswieringa/book/Book.as
index 4e26853..3dfe1c1 100755
--- a/src/com/rubenswieringa/book/Book.as
+++ b/src/com/rubenswieringa/book/Book.as
@@ -1,1589 +1,1608 @@
-package com.rubenswieringa.book {
- import com.foxaweb.pageflip.PageFlip;
- import com.rubenswieringa.geom.Geom;
- import com.rubenswieringa.managers.StateManager;
-
- import org.flashsandy.display.DistortImage;
-
- import flash.display.BitmapData;
- import flash.display.DisplayObject;
- import flash.events.Event;
- import flash.events.MouseEvent;
- import flash.geom.Point;
- import flash.geom.Rectangle;
- import flash.utils.*;
-
- use namespace limited;
-
-
- /**
- * Dispatched when the user picks up the corner of a page.
- * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_STARTED
- * @see BookEvent#PAGEFLIP_STARTED
- */
- [Event(name="pageflipStarted", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when the user releases the corner of a page. Note that this Event is dispatched just before the page starts falling in place.
- * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_ENDING
- * @see BookEvent#PAGEFLIP_FINISHED
- */
- [Event(name="pageflipEnding", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when a page falls in place after being flipped. This Event is dispatched regardless of whether or not the page has been turned, or has fallen back into its original position.
- * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_FINISHED
- * @see BookEvent#PAGE_TURNED
- */
- [Event(name="pageflipFinished", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when the corner of a page is rolled over with the mouse.
- * Only applicable if the hover property is set to true.
- * @eventType com.rubenswieringa.book.BookEvent.HOVER_STARTED
- * @see BookEvent#HOVER_STARTED
- * @see Book#hover
- */
- [Event(name="hoverStarted", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when the corner of a page is rolled out of with the mouse. Note that this Event is dispatched just before the page starts falling back in place.
- * Only applicable if the hover property is set to true.
- * @eventType com.rubenswieringa.book.BookEvent.HOVER_ENDING
- * @see BookEvent#PAGEFLIP_FINISHED
- * @see Book#hover
- */
- [Event(name="hoverEnding", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when a page falls back in place after being rolled over with the mouse.
- * Only applicable if the hover property is set to true.
- * @eventType com.rubenswieringa.book.BookEvent.HOVER_FINISHED
- * @see BookEvent#PAGE_TURNED
- * @see Book#hover
- */
- [Event(name="hoverFinished", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when a pageflip is successful.
- * @eventType com.rubenswieringa.book.BookEvent.PAGE_TURNED
- * @see BookEvent#PAGE_NOT_TURNED
- */
- [Event(name="pageTurned", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when a pageflip is not successful.
- * @eventType com.rubenswieringa.book.BookEvent.PAGE_NOT_TURNED
- * @see BookEvent#PAGE_TURNED
- */
- [Event(name="pageNotTurned", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched when a Page is torn out of its Book.
- * @eventType com.rubenswieringa.book.BookEvent.PAGE_TORN
- */
- [Event(name="pageTorn", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched at the same time as the page-turned Event, when the Book was previously closed, and the first or last Page was flipped successfully.
- * @eventType com.rubenswieringa.book.BookEvent.BOOK_OPENED
- * @see BookEvent#PAGE_TURNED
- */
- [Event(name="bookOpened", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched at the same time as the page-turned Event, when the Book was previously open, and the first or last Page was flipped successfully.
- * @eventType com.rubenswieringa.book.BookEvent.BOOK_CLOSED
- * @see BookEvent#PAGE_TURNED
- */
- [Event(name="bookClosed", type="com.rubenswieringa.book.BookEvent")]
- /**
- * Dispatched at the same time as the page-turned Event, when the Book was previously open, and the first or last Page was flipped successfully.
- * @eventType com.rubenswieringa.book.BookEvent.BOOK_CLOSED
- * @see BookEvent#STATUS_CHANGED
- * @see Book#status
- */
- [Event(name="statusChanged", type="com.rubenswieringa.book.BookEvent")]
-
-
- /**
- * Book is a container class that creates a rich animated and interactive book from its contents, through which the end-user can browse by flipping the pages over (pageflip-effect).
- * Its core functionality (for example storage of Page instances) is defined by the PageManager class, which the Book class extends.
- * Book itself describes the functionality for the management of pageflips. The actual pageflip class was written by Didier Brun (also mentioned in the credits).
- *
- * Ruben Swieringa created this component during his internship at the Factor.e (www.tfe.nl). Thanks to those guys for allowing me to publish the source-code online!
- *
- * @author Ruben Swieringa
- * and others.
- * http://github.com/rthesaint/FlexBook
- * @version 1.0.4
- * @see PageManager PageManager
- * @see Page Page
- * @see BookEvent BookEvent
- * @see BookError BookError
- * @see com.foxaweb.pageflip.PageFlip com.foxaweb.pageflip.PageFlip
- * @see org.flashsandy.display.DistortImage org.flashsandy.display.DistortImage
- * @see http://www.rubenswieringa.com/blog/flex-book-component-beta Rubens blog: Book component beta
- *
- *
- * @internal
- *
- *
- * Credits:
- * - Didier Brun
- * For making his pageflip rendering class (com.foxaweb.pageflip.PageFlip)
- * Site: www.foxaweb.com
- * - Thomas Pfeiffer (aka Kiroukou)
- * For letting me use his distortion class (org.flashsandy.display.DistortImage)
- * Site: www.flashsandy.org
- * - the Factor.e
- * For allowing me to publish the demo and the source-code.
- * Site: www.tfe.nl
- * - Maikel Sibbald
- * For helping me with (among a lot of things) thinking out the structure of this component. He also made a usage-example of Didier's pageflip class (labs.flexcoders.nl/?p=33) which I used as reference in the early days of the Book class.
- * Site: labs.flexcoders.nl
- * - Theo Aartsma (aka Sumeco)
- * For letting me use his artwork in the Book demo.
- * Site: www.sumeco.net
- *
- *
- * edit 5
- *
- *
- * View code documentation at:
- * http://www.rubenswieringa.com/code/as3/flex/Book/docs/
- *
- *
- * Copyright (c) 2005 Ruben Swieringa. All rights reserved.
- *
- * This class is part of the Book component, which is licensed under the CREATIVE COMMONS Attribution 3.0 Unported.
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- * http://creativecommons.org/licenses/by/3.0/deed.en
- *
- */
- public class Book extends PageManager {
-
-
- /**
- * Array in which the BitmapData for each Page instance is stored in. Note that after each pageflip this Array is cleaned.
- * @private
- */
- protected var bitmapData:Array = [];
- /**
- * DistortImage instance for rendering hard-back Pages during pageflips.
- * @see org.flashsandy.display.DistortImage
- * @private
- */
- protected var distortion:DistortImage;
- /**
- * @private
- */
- protected var pageCorner:Point = new Point();
- /**
- * @private
- */
- protected var pageCornerTarget:Point = new Point();
- /**
- * The direction of the last pageflip. 1 stands for forward flipping (flipping a page from right to left), -1 stands for backward flipping (left to right).
- * @private
- */
- protected var lastFlippedDirection:int = 0;
- /**
- * The side on which the last flipped Page laid before it was flipped. 0 stands for left, 1 for right.
- * @see Page#LEFT
- * @see Page#RIGHT
- * @private
- */
- limited var lastFlippedSide:uint;
- /**
- * The position of the last flipped page-corner. The x-value represents the horizontal value, the y-value represents the vertical value. So (0, 0) would be the upper-left corner, (1, 1) would be the lower right corner, etc.
- * @private
- */
- protected var lastFlippedCorner:Point;
- /**
- * Stores the timestamp at which a pageflip was started (mouse was pressed).
- * This property is part of the mechanism that decides whether or not a click should fire an automated pageflip.
- * @see Book#setLastFlippedTime()
- * @private
- */
- protected var lastFlippedTime:int;
- /**
- * False if the last flipped page fell back into its original position after being flipped. True if it slided over to the opposite side.
- * @private
- */
- protected var lastFlipSucceeded:Boolean;
- /**
- * Set by the gotoPage() method as a target-destination.
- * @see Book#gotoPage()
- * @private
- */
- protected var autoFlipIndex:int = -1;
- /**
- * False if the current pageflip is performed by the user dragging a pagecorner, true of not.
- * @see Book#gotoPage()
- * @see Book#tearPage()
- * @private
- */
- protected var autoFlipActive:Boolean = false;
- /**
- * Indicates if an automated pageflip is allowed to be interrupted by the user.
- * @see Book#gotoPage()
- * @see Book#tearPage()
- * @private
- */
- protected var autoFlipCancelable:Boolean = true;
- /**
- * True if a pageflip is triggered by a mouse-rollover, false if not.
- * @private
- */
- protected var hoverActive:Boolean = false;
- /**
- * True if the current pageflip is performed upon a page its side (instead of one of its corners).
- * @see Book#sideFlip
- * @private
- */
- protected var sideFlipActive:Boolean = false;
- /**
- * True if this pageflip consists of a Page being torn out of this Book.
- * @see Book#tearPage()
- * @private
- */
- limited var tearActive:Boolean = false;
- /**
- * The side of the page that is being torn out. 0 stands for left, 1 for right.
- * @see Book#tearPage()
- * @see Page#LEFT
- * @see Page#RIGHT
- * @private
- */
- protected var tearSide:uint;
- /**
- * True if a page is being torn out of the book top-first, false if bottom-first.
- * @see Book#tearPage()
- * @private
- */
- protected var tearFromTop:Boolean;
- /**
- * Array of Rectangle instances that indicate the areas by which a page can be flipped.
- * @see Book#createRegions()
- * @private
- */
- protected var regions:Array = [];
-
- // internals for accessors:
- /**
- * @see Book#easing
- * @private
- */
- protected var _easing:Number = 0.3;
- /**
- * @see Book#hardCover
- * @private
- */
- protected var _hardCover:Boolean = true;
- /**
- * @see Book#hover
- * @private
- */
- protected var _hoverEnabled:Boolean;
- /**
- * @see Book#regionSize
- * @private
- */
- protected var _regionSize:uint = 150;
- /**
- * @see Book#status
- * @private
- */
- protected var _status:String;
-
- // plain var properties:
- /**
- * The time (milliseconds) it takes to finish a pageflip performed by the gotoPage method.
- * The calculated speed will also be used for tearing out Pages.
- * @default 1000
- * @see Book#gotoPage()
- */
- public var autoFlipDuration:uint = 1000;
- /**
- * Whether or not the page should be turned when a page corner is clicked.
- * @default true
- */
- public var flipOnClick:Boolean = true;
- /**
- * Amount of perspective in a pageflip of a Page that has its hard property set to true.
- * This value is used as the maximum with which the outer side of a page is extended on both sides (upper and lower) during a hard pageflip.
- * @default 45
- */
- public var hardPerspective:uint = 45;
- /**
- * Indicates whether or not this Book plays animation while flipping one of its Pages.
- * When this property is set to true, all Page instances will return true from their liveBitmapping property.
- * Note that enabling liveBitmapping may result in decreased performance.
- * @default false
- * @see Page#liveBitmapping
- */
- public var liveBitmapping:Boolean = false;
- /**
- * Whether or not the outer side of a Page can be used to let it flip.
- * @default true
- */
- public var sideFlip:Boolean = true;
- /**
- * If true, no easing will be applied during pageflips.
- * @default false
- * @see Book#easing
- */
- public var snap:Boolean = false;
- /**
- * If true, all Pages will have tearing enabled.
- * @see Page#tearable
- * @default false
- */
- public var tearable:Boolean = false;
-
- // constants:
- /**
- * @private
- */
- protected static const AUTO_FLIP_CURVE:Number = 0.15;
- /**
- * @private
- */
- protected static const SIDE_FLIP_CURVE:Number = 0.04;
- /**
- * @private
- */
- protected static const MAX_EASING_DIFFERENCE:Number = 0.5;
- /**
- * @private
- */
- protected static const CLICK_INTERVAL:uint = 300;
-
-
- // CONSTRUCTOR:
-
-
- /**
- * Constructor.
- */
- public function Book ():void {
- super();
-
- // set initial status:
- this.setStatus(BookEvent.NOT_FLIPPING, false);
-
- // make sure certain getter/setter values are processed where appropriate:
- this.easing = 0.7;
- this.hover = true;
-
- // add event listener:
- this.addEventListener(MouseEvent.MOUSE_DOWN, this.startPageFlip);
-
- }
-
-
- // OVERRIDES:
-
-
- /**
- * Draw additional graphisc for Pages where necessary.
- *
- * @private
- */
- override protected function childrenCreated ():void {
- super.childrenCreated();
-
- // make sure that non-hard pages whose flipsides are hard don't have fold-gradients:
- for (var i:int=0; i= 0 && this.pageCorner.x <= this.width/2 && event != null){
- this.cancelGotoPage(false);
- return;
- }
- // stop if the clicked SuperViewStack does not show any Pages:
- if (((!this.pageL.visible && side == Page.LEFT) || (!this.pageR.visible && side == Page.RIGHT)) && this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_STARTED && this._status != BookEvent.HOVER_ENDING){
- return;
- }
- // stop if none of the pagecorners are hit:
- if (!this.isPageCornerHit() && (!this.sideFlip || !this.isPageSideHit()) && !this.autoFlipActive){
- return;
- }
- // don't flip if Page isn't allowed to:
- if (Page(this._pages.getItemAt(this._currentPage+side)).lock){
- this.autoFlipActive = false; // shut-down auto-pageflip if active
- return;
- }
- // stop if a page corner is flipping back into position
- if ((this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING) && !this.autoFlipActive && !this.lastFlipSucceeded && !this._status == BookEvent.PAGEFLIP_STARTED){
- // switch back to flipping mode if the same page corner was picked up:
- if (this.lastFlippedCorner.equals(this.getCurrentCorner()) && this.sideFlipActive == this.isPageSideHit()){
- this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
- var newStatus:String = (this.hoverActive) ? BookEvent.HOVER_STARTED : BookEvent.PAGEFLIP_STARTED;
- this.setStatus(newStatus, true, oldPage);
- }
- return;
- }
- // stop if the Book is not inactive and not hovering:
- if (this._status != BookEvent.NOT_FLIPPING && !this.hoverActive){
- return;
- }
- // if the page is hovering and an actual pageflip should begin:
- if (this.hoverActive && this._status == BookEvent.HOVER_STARTED){
- this.hoverActive = false;
- this.setLastFlippedTime(); // if necessary, remember time at which pageflip started
- this.setStatus(BookEvent.PAGEFLIP_STARTED, true, oldPage);
- return;
- }
-
- // throw an Error if the Page in question has no flipside:
- // in the above stated situation the right-hand side Page typically does not have a flipside, and consequently its index will be equal to the total amount of pages (minus one). Also note that any left-hand side Page will always have a flipside, because the first Page in a Book is always on the right-hand side.
- if (this._currentPage+1 == this._pages.length-1 && side == Page.RIGHT){
- throw new BookError(BookError.NO_FLIPSIDE);
- }
-
- // set hover-flag:
- this.hoverActive = attemptHover;
-
- // set direction markers and position the render:
- if ( this.autoFlipActive && !this.tearActive) this.lastFlippedCorner = new Point(side, 1);
- if ( this.autoFlipActive && this.tearActive) this.lastFlippedCorner = new Point(side, (this.tearFromTop) ? 0 : 1);
- if (!this.autoFlipActive) this.lastFlippedCorner = this.getCurrentCorner();
- this.lastFlippedSide = this.lastFlippedCorner.x;
- this.lastFlippedDirection = (side == Page.LEFT) ? -1 : 1;
- this.sideFlipActive = (!this.tearActive && this.sideFlip && this.isPageSideHit());
- this.render.x = this.lastFlippedSide * this.width/2;
-
- // specify front and flipside indexes:
- var frontIndex:uint = this._currentPage + this.lastFlippedSide;
- var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection;
- // save bitmapData:
- this.saveBitmapData(Page(this._pages.getItemAt(frontIndex)), Page(this._pages.getItemAt(backIndex)));
-
- // select pages in SuperViewStacks:
- if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
- this.pageL.visible = !this.isFirstPage(this._currentPage+1-2);
- if (this.pageL.visible){
- this.pageL.selectedChild = Page(this._pages.getItemAt(this._currentPage-2));
- }else{
- }
- }
- if (this.lastFlippedSide == Page.RIGHT){ // if right-hand page was flipped
- this.pageR.visible = !this.isLastPage(this._currentPage+2);
- if (this.pageR.visible){
- this.pageR.selectedChild = Page(this._pages.getItemAt(this._currentPage+1+2));
- }
- }
-
- // set page corner markers:
- this.pageCorner = new Point(this.lastFlippedCorner.x*this.width/2, this.lastFlippedCorner.y*this.height);
- if (this.autoFlipActive){
- this.pageCornerTarget = this.pageCorner.clone();
- }
-
- // if necessary, remember time at which pageflip started:
- this.setLastFlippedTime();
-
-
- // set status:
- this.setStatus((this.hoverActive) ? BookEvent.HOVER_STARTED : BookEvent.PAGEFLIP_STARTED, false); // false = dispatch Event later
-
- // add listeners:
- this.dragPageCorner();
- this.addEventListener(Event.ENTER_FRAME, this.dragPageCorner);
- this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
-
- // dispatch event:
- var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
- if (this.hoverActive){
- this.dispatchEvent(new BookEvent(BookEvent.HOVER_STARTED, this, page));
- }else{
- this.dispatchEvent(new BookEvent(BookEvent.PAGEFLIP_STARTED, this, page));
- }
-
- }
-
-
- /**
- * Performs the actual pageflip effect, typically called upon enter-frame.
- *
- * @see com.foxaweb.pageflip.PageFlip#computeFlip()
- * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
- *
- * @param event Event
- *
- * @private
- */
- protected function dragPageCorner (event:Event=null):void {
-
- // stop if the startPageFlip() or endPageFlip() have not been executed:
- if (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED &&
- this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_ENDING){
- return;
- }
-
- // create faux mouse to create easing:
- if (this._status == BookEvent.PAGEFLIP_STARTED || this._status == BookEvent.HOVER_STARTED){
- if (this.movePageCornerTarget() && this.autoFlipActive){
- this.endPageFlip();
- return;
- }
- }
- this.movePageCorner();
-
- // clear render:
- this.render.graphics.clear();
-
- // check if the pageflip has ended:
- if (this.pageCorner.equals(this.pageCornerTarget) && (this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING)){
- this.finishPageFlip();
- return;
- }
-
- // specify front and flipside indexes:
- var frontIndex:uint = this._currentPage + this.lastFlippedSide;
- var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection;
- var front:Page = Page(this._pages.getItemAt(frontIndex));
- var back:Page = Page(this._pages.getItemAt(backIndex));
-
- // if liveBitmapping is enabled, refresh corresponding values in bitmapData Array:
- if (front.liveBitmapping || back.liveBitmapping){
- this.saveBitmapData(front, back);
- }
-
- // perform pageflip:
- if (!front.explicitHard && !back.explicitHard){
- var ocf:Object = PageFlip.computeFlip (this.pageCorner.clone(),
- this.lastFlippedCorner,
- this.width/2,
- this.height,
- !this.tearActive,
- 1);
- PageFlip.drawBitmapSheet (ocf,
- this.render,
- this.bitmapData[frontIndex],
- this.bitmapData[backIndex]);
- // add shadows or highlights to the render:
- this.addSmoothGradients(ocf);
- // take ocf and find out whether we should start tearing this Page off:
- if (this._status == BookEvent.PAGEFLIP_STARTED && Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)).tearable && ocf.cPoints != null){
- this.evaluateTear(ocf.cPoints[2].clone());
- }
- }else{
- this.drawHardPage (this.bitmapData[frontIndex],
- this.bitmapData[backIndex]);
- }
-
-
- }
-
-
- /**
- * Makes preparations for the end of the pageflip effect, typically called upon mouse-release.
- *
- * @param event MouseEvent
- *
- * @private
- */
- protected function endPageFlip (event:MouseEvent=null):void {
-
- // stop if the Book is not currently performing a pageflip:
- if (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED){
- return;
- }
- // ignore mouse-up event if a Page is being torn out of the Book:
- if (this.tearActive && event != null){
- return;
- }
-
- // turn page if flipOnClick is enabled:
- if (this.flipOnClick && this.flipOnRelease && event != null){
- this.gotoPage(int(this._currentPage+this.lastFlippedDirection*2)); // cast to int because gotoPage has its page parameter typed with a * and will assume the value is an uint, and thus change any negative values to positives
- return;
- }
-
- // remove mouse-listener:
- this.stage.removeEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
-
- // make sure page corner slides to the appropriate position:
- if (!this.tearActive){
- var x:Number;
- var y:Number = this.lastFlippedCorner.y*this.height;
- if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
- this.lastFlipSucceeded = (this.pageCornerTarget.x > this.width/2);
- x = (this.lastFlipSucceeded) ? this.width : 0;
- }else{ // if right-hand page was flipped
- this.lastFlipSucceeded = (this.pageCornerTarget.x < 0);
- x = (this.lastFlipSucceeded) ? -this.width/2 : this.width/2;
- }
- this.pageCornerTarget = new Point(x, y);
- }else{
- this.lastFlipSucceeded = false;
- }
-
- // set status and dispatch event:
- var newStatus:String = (this._status == BookEvent.HOVER_STARTED) ? BookEvent.HOVER_ENDING : BookEvent.PAGEFLIP_ENDING;
- var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
- this.setStatus(newStatus, true, page);
-
- }
-
-
- /**
- * Ends the pageflip effect and displays the interactive content in the SuperViewStacks.
- *
- * @private
- */
- protected function finishPageFlip ():void {
-
-
- // stop is endFlip() has not already been called:
- if (this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_ENDING){
- return;
- }
-
- // stop the dragPageCorner() method from being executed:
- this.removeEventListener(Event.ENTER_FRAME, this.dragPageCorner);
-
- // remember current Page for Events dispatched later:
- var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
-
- // remove Pages if they were torn out:
- if (this.tearActive){
- var wasLastPage:Boolean = this.isLastPage(this._currentPage);
- this.removeChild(page.getFlipSide());
- this.removeChild(page);
- }
-
- // if the page has been flipped over, change the _currentPage property:
- if (this.lastFlipSucceeded && !this.tearActive){ // lastFlipSucceeded is always false when a Page was torn, but this makes the code more readable
- this._currentPage += this.lastFlippedDirection * 2;
- }
- // change _currentPage where appropriate after a page-tear:
- if (this.tearActive && this.lastFlippedSide == Page.LEFT && !wasLastPage){
- this._currentPage -= 2;
- }
-
- // make sure the SuperViewStacks display the right Pages
- this.refreshViewStacks();
-
- // set status:
- this.setStatus(BookEvent.NOT_FLIPPING, true, page, (this.hoverActive) ? BookEvent.HOVER_FINISHED : BookEvent.PAGEFLIP_FINISHED);
-
- // dispatch additional events:
- if (!this.hoverActive){
- if (!this.tearActive){
- if (this.lastFlipSucceeded){
- this.dispatchEvent(new BookEvent(BookEvent.PAGE_TURNED, this, page));
- if ((this.lastFlippedSide == Page.RIGHT && this._currentPage == 1) || (this.lastFlippedSide == Page.LEFT && this._currentPage == this._pages.length-3)){
- this.dispatchEvent(new BookEvent(BookEvent.BOOK_OPENED, this));
- }
- if ((this.lastFlippedSide == Page.LEFT && this._currentPage == -1) || (this.lastFlippedSide == Page.RIGHT && this._currentPage == this._pages.length-1)){
- this.dispatchEvent(new BookEvent(BookEvent.BOOK_CLOSED, this));
- }
- }else{
- this.dispatchEvent(new BookEvent(BookEvent.PAGE_NOT_TURNED, this, page));
- }
- }else{
- this.dispatchEvent(new BookEvent(BookEvent.PAGE_TORN, this, page));
- }
- }
-
- // if this was a pageflip triggered by gotoPage then switch back to normal mode:
- if (this.autoFlipActive){
- if (this.tearActive){
- this.tearActive = false;
- this.autoFlipActive = false;
- }else{
- if (this._currentPage == this.autoFlipIndex){
- this.autoFlipActive = false;
- }else{
- this.startPageFlip();
- }
- }
- }
-
- // turn off flags:
- this.hoverActive = false;
- this.sideFlipActive = false;
- this.tearActive = false;
-
- }
-
-
- /**
- * Draws a hardcover Page, similar to the drawBitmapSheet method of the PageFlip class.
- *
- * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
- *
- * @param front BitmapData of the facing side of a sheet
- * @param back BitmapData of the flipside of a sheet
- *
- * @private
- *
- */
- protected function drawHardPage (front:BitmapData, back:BitmapData):void {
-
- // calculate position correction:
- var w:Number;
- if (this.lastFlippedDirection == 1){
- w = this.pageCorner.x - (1-this.lastFlippedSide) * this.width/2;
- }else{
- w = this.pageCorner.x - this.width/2;
- }
- if (w < -this.width/2) w = -this.width/2;
- if (w > this.width/2) w = this.width/2;
-
- // calculate perspective:
- var closeness:Number = Math.sin(Math.acos(w / (this.width/2)));
-
- // define positions of page-corners:
- var pPoints:Array = [];
- pPoints[0] = new Point((1-this.lastFlippedSide)*this.width/2, 0);
- pPoints[1] = new Point(pPoints[0].x+w, 0-closeness*this.hardPerspective);
- pPoints[2] = new Point(pPoints[0].x+w, this.height+closeness*this.hardPerspective);
- pPoints[3] = new Point((1-this.lastFlippedSide)*this.width/2, this.height);
-
- // make sure the first Point in the Array is always the top-right, etc:
- var p:Array = [];
- p[0] = (pPoints[0].x < pPoints[1].x) ? pPoints[0] : pPoints[1];
- p[1] = (pPoints[0].x > pPoints[1].x) ? pPoints[0] : pPoints[1];
- p[2] = (pPoints[2].x > pPoints[3].x) ? pPoints[2] : pPoints[3];
- p[3] = (pPoints[2].x < pPoints[3].x) ? pPoints[2] : pPoints[3];
-
- // draw page:
- var bmd:BitmapData
- if (this.lastFlippedSide == 0){
- bmd = (this.pageCorner.x > this.width/2) ? back : front;
- }else{
- bmd = (this.pageCorner.x < 0) ? back : front;
- }
- this.distortion.setTransform(this.render.graphics, bmd, p[0], p[1], p[2], p[3]);
-
- // draw gradients:
- this.addHardGradients(pPoints);
-
- }
-
-
- /**
- * Considers a hover-effect for the four outer page-corners. This method is typically called upon enter-frame.
- *
- * @param event Event
- *
- * @private
- */
- protected function evaluateHover (event:Event=null):void {
-
- var side:uint = this.getCurrentSide();
- var pageCornerHit:Boolean = this.isPageCornerHit();
-
- // if the hovered ViewStack does not display any Pages:
- if (((side == Page.LEFT && !this.pageL.visible) || (side == Page.RIGHT && !this.pageR.visible)) && (!this.hoverActive || this._status == BookEvent.HOVER_ENDING)){
- return;
- }
- // don't hover hard Pages:
- if (Page(this._pages.getItemAt(this._currentPage+side)).hard){
- return;
- }
-
- // roll over:
- if ((pageCornerHit || (this.sideFlip && this.isPageSideHit())) && ((!this.hoverActive && this._status == BookEvent.NOT_FLIPPING) || this._status == BookEvent.HOVER_ENDING)){
- this.startPageFlip(null, true);
- }
- // roll out:
- if (!pageCornerHit && (!this.sideFlip || !this.isPageSideHit()) && this.hoverActive){
- this.endPageFlip();
- }
-
- }
-
-
- /**
- * Looks at a provided Point and considers tearing off the Page currently being flipped. This method is typically called from within the dragPageCorner method.
- *
- * @param point Point used to evaluate whether or not this Page should be torn out of this Book.
- *
- * @see Book#dragPageCorner()
- *
- * @private
- */
- protected function evaluateTear (point:Point):void {
- // no evaluation necessary if the Book has not even started flipping yet:
- if (this._status != BookEvent.PAGEFLIP_STARTED){
- return;
- }
- // stop if an auto-flip or page-tear is already active:
- if (this.tearActive || this.autoFlipActive){
- return;
- }
-
- // evaluate:
- if (Math.round(point.x) == (1-this.lastFlippedCorner.x) * this.width/2 && Math.round(point.y) == this.lastFlippedCorner.y * this.height){
- this.tearActive = true;
- this.autoFlipActive = true;
- this.autoFlipCancelable = false;
- }
- }
-
-
- /**
- * Performs a pageflip (or multiple flips) without the user having to drag the pagecorner.
- *
- * @param page int/uint or Page, indicating the index or instance of a Page.
- * @param cancelable Indicates whether or not this auto-flip should allow cancelGotoPage to be called.
- *
- * @see Book#jumptoPage()
- * @see Book#cancelGotoPage()
- * @see Book#autoFlipDuration
- *
- * @throws BookError Gets thrown when the child parameter is not an instance of the Page class nor a int/uint.
- * @see BookError#CHILD_NOT_PAGE
- *
- * @throws ArgumentsError Gets thrown when the child parameter is not a child of this Book.
- * @see BookError#PAGE_NOT_CHILD
- *
- */
- public function gotoPage (page:*, cancelable:Boolean=true):void {
-
- page = this.getPageIndex(page, true);
- if (page%2 != 1 && page != -1) page -= 1;
-
- // return if we're already at the specified Page:
- if (this._currentPage == page){
- return;
- }
-
- // set target index and start pageflip:
- this.autoFlipIndex = page;
- this.autoFlipCancelable = cancelable;
- if (!this.autoFlipActive){
- this.autoFlipActive = true;
- this.startPageFlip();
- }
- }
-
- /**
- * Force a single pageflip to change the current page.
- *
- * @param page int/uint or Page, indicating the index or instance of a Page.
- * @param cancelable Indicates whether or not this auto-flip should allow cancelGotoPage to be called.
- *
- * @see Book#jumptoPage()
- * @see Book#cancelGotoPage()
- * @see Book#autoFlipDuration
- *
- * @throws BookError Gets thrown when the child parameter is not an instance of the Page class nor a int/uint.
- * @see BookError#CHILD_NOT_PAGE
- *
- * @throws ArgumentsError Gets thrown when the child parameter is not a child of this Book.
- * @see BookError#PAGE_NOT_CHILD
- *
- */
- public function jumptoPage(page:*, cancelable:Boolean=true):void {
-
- page = this.getPageIndex(page, true);
- if (page%2 != 1 && page != -1) page -= 1;
-
- // return if we’re already at the specified Page:
- if (this._currentPage == page) {
- return;
- }
-
- // set target index and start pageflip:
- this.autoFlipIndex = page;
- this.autoFlipCancelable = cancelable;
-
- // set _currentPage before / after the page we want to get to,
- // according to currentPage position
- var prePage:int = (this._currentPage < page) ? page - 2 : page + 2;
-
- this._currentPage = prePage;
-
- if (!this.autoFlipActive) {
- this.autoFlipActive = true;
- this.startPageFlip();
- }
- }
-
- /**
- * Aborts a pageflip started by the gotoPage method or the jumptoPage method.
- *
- * @param finishFlip Boolean indicating whether or not to allow the auto-flip to finish. When true, the pageflip will finish. When false, the auto-flip will immediately stop, and the page corner will start sticking to the mouse.
- *
- * @see Book#gotoPage()
- * @see Book#jumptoPage()
- *
- */
- public function cancelGotoPage (finishFlip:Boolean=true):void {
- // stop if gotoPage is not active:
- if (!this.autoFlipActive){
- return;
- }
- // stop if gotoPage is not cancelable:
- if (!this.autoFlipCancelable){
- return;
- }
-
- // end the auto-flip:
- if (finishFlip){
- this.autoFlipIndex = this._currentPage + 2 * this.lastFlippedDirection;
- }else{
- this.autoFlipActive = false;
- }
- }
- /**
- * Performs a pageflip to the next page, without the user having to drag the pagecorner.
- *
- * @see Book#gotoPage()
- * @see Book#autoFlipDuration
- *
- */
- public function nextPage ():void {
- if (this._currentPage+2 <= this._pages.length-1){
- this.gotoPage(this._currentPage+2, false);
- }
- }
- /**
- * Performs a pageflip to the previous page, without the user having to drag the pagecorner.
- *
- * @see Book#gotoPage()
- * @see Book#autoFlipDuration
- *
- */
- public function prevPage ():void {
- if (this._currentPage-2 >= -1){
- this.gotoPage(this._currentPage-2, false);
- }
- }
-
-
- /**
- * Tears a Page out of this Book instance. When called, this method will be executed regardless of the value of the Page its tearable property.
- * Fails silently if the provided Page is not one of the currently visible Pages.
- *
- * @param page int/uint or Page, indicating the index or instance of a Page to be torn out of this Book.
- * @param fromTop If true, the Page will be torn out by its upper outer corner, if false by its lower outer corner.
- *
- * @see Book#tearPage()
- * @see Page#tearable
- *
- * @throws ArgumentError Gets thrown when page is an index-value and out of bounds.
- * @see BookError#OUT_OF_BOUNDS
- */
- public function tearPage (page:*, fromTop:Boolean=true):void {
- page = this.getPage(page, true);
-
- // stop if we're not currently inactive or hovering:
- if (this._status != BookEvent.NOT_FLIPPING && this._status != BookEvent.HOVER_STARTED){
- return;
- }
- // fail silently if the provided Page is not one of the currently visible Pages:
- if (page.index != this._currentPage && page.index != this._currentPage+1){
- return;
- }
- // don't tear hard Pages:
- if (page.hard){
- return;
- }
-
- // set vars to let the pageflip processing methods know that we're tearing:
- this.tearActive = true;
- this.tearSide = page.side;
- this.tearFromTop = fromTop;
- this.autoFlipActive = true;
- this.autoFlipCancelable = false;
- this.startPageFlip();
- }
-
-
- // GRADIENTS:
-
-
- /**
- * Adds shadows and highlights to the pageflip process of a hard Page.
- *
- * @param area Array of coordinates (Point instance) indicating the outlines of the flipping page
- *
- * @see Gradients#drawOutside()
- * @see Gradients#drawInside()
- *
- * @private
- */
- protected function addHardGradients (area:Array):void {
-
- // determine page:
- var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
-
- // determine shadow or highlight:
- var tint:String;
- if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
- tint = (this.pageCornerTarget.x > this.width/2) ? Gradients.DARK : Gradients.LIGHT;
- }else{ // if right-hand page was flipped
- tint = (this.pageCornerTarget.x < 0) ? Gradients.LIGHT : Gradients.DARK;
- }
-
- // draw gradients:
- page.gradients.drawOutside (this.render.graphics, area, tint);
- page.gradients.drawInside (this.render.graphics, area, tint);
-
- }
- /**
- * Adds shadows and highlights to the pageflip process of a non-hard Page.
- *
- * @see com.foxaweb.pageflip.PageFlip#computeFlip()
- * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
- *
- * @see Gradients#drawFlipside()
- * @see Gradients#drawOutside()
- * @see Gradients#drawInside()
- *
- * @param ocf Object returned by the computeFlip method of the PageFlip class
- *
- * @private
- */
- protected function addSmoothGradients (ocf:Object):void {
-
- // determine page:
- var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
-
- // determine rotation correction:
- var rotate:Number;
- if (this.lastFlippedCorner.equals(new Point(1,0)) || this.lastFlippedCorner.equals(new Point(0,1))){
- // if the upper right or lower left corner is being flipped and the Page isn't torn out of its Book, correct the angle with 45 degrees:
- rotate = (!this.tearActive) ? Gradients.ROTATE_HALF : Gradients.ROTATE_FULL;
- }
- if (this.lastFlippedCorner.equals(new Point(0,0)) || this.lastFlippedCorner.equals(new Point(1,1))){
- // if the upper left or lower right corner is being flipped and the Page isn't torn out of its Book, correct the angle with minus 45 degrees:
- rotate = (!this.tearActive) ? Gradients.ROTATE_FULL : Gradients.ROTATE_HALF;
- }
-
- // determine shadow or highlight:
- var tint1:String = (this.lastFlippedSide == 1) ? Gradients.LIGHT : Gradients.DARK;
- var tint2:String = (this.lastFlippedSide == 1) ? Gradients.DARK : Gradients.LIGHT;
-
- // draw gradients:
- if (ocf.cPoints != null) page.gradients.drawFlipside (this.render.graphics, ocf.cPoints, tint1, rotate);
- if (ocf.pPoints != null) page.gradients.drawOutside (this.render.graphics, ocf.pPoints, tint1, rotate);
- if (ocf.cPoints != null && !this.tearActive) page.gradients.drawInside (this.render.graphics, ocf.cPoints, tint2, rotate);
-
- }
-
-
- // PAGEFLIP ASSISTANCE:
-
-
- /**
- * Sets the position of the pageCorner property.
- *
- * @return Boolean indicating, if the gotoPage method is active, whether or not pageCornerTarget has reached the opposite corner. If gotoPage is not active, the return value will always be false.
- *
- * @see Book#movePageCorner()
- * @see Book#gotoPage()
- *
- * @private
- */
- protected function movePageCornerTarget ():Boolean {
-
- var x:Number;
- var y:Number;
-
- // if gotoPage is not active then set the page corner target equal to the mouse position:
- if (!this.autoFlipActive){
- // calculate coordinates:
- x = this.mouseX;
- y = (!this.sideFlipActive) ? this.mouseY : this.lastFlippedCorner.y * this.height;
- // adjust x per position of render:
- if (this.lastFlippedSide == Page.RIGHT){
- x -= (this.width/2);
- }
- // adjust x and y if a side-flip is active:
- if (this.sideFlipActive){
- // adjust x so that pageflip won't start acting weird when mouse is out of bounds:
- var xMin:Number = 0 - this.lastFlippedSide * (this.width/2);
- var xMax:Number = xMin + this.width;
- if (x < xMin) x = xMin;
- if (x > xMax) x = xMax;
- // adjust y to make the pageflip a bit more pretty:
- if (!this.hoverActive){
- y = this.getPageCornerYFromX(x, Book.SIDE_FLIP_CURVE);
- }
- }
- // set position:
- this.pageCornerTarget = new Point(x, y);
- }
-
- // if gotoPage is active then move the page corner target to the opposite corner:
- if (this.autoFlipActive){
- // calculate coordinates:
- x = (!this.tearActive) ? this.pageCornerTarget.x : this.lastFlippedSide * (this.width/2);
- y = this.pageCornerTarget.y;
- var opposite:Point = new Point();
- // if this is a normal auto-flip:
- if (!this.tearActive){
- opposite.x = this.lastFlippedSide * (this.width/2) - this.width*this.lastFlippedDirection;
- if (Math.abs(x-opposite.x) >= this.autoFlipSpeed){
- x += this.autoFlipSpeed * -this.lastFlippedDirection;
- }else{
- x = opposite.x;
- }
- y = this.getPageCornerYFromX(x);
- }
- // if this is a tearing auto-flip:
- if (this.tearActive){
- var directionY:Number = (this.lastFlippedCorner.y == 0) ? 1 : -1;
- opposite.y = (this.height/2) + (this.height*1.5 * directionY);
- if (Math.abs(y-opposite.y) >= this.autoFlipSpeed){
- y += this.autoFlipSpeed * directionY;
- }else{
- y = opposite.y;
- }
- }
- // set position:
- this.pageCornerTarget = new Point(x, y);
- // return true if pageCornerTarget has reached the opposite corner of where it started:
- if (this.pageCornerTarget.equals(this.pageCorner) &&
- ((this.tearActive && y == opposite.y) ||
- (!this.tearActive && x == opposite.x))){
- return true;
- }
- }
-
- // return value:
- return false;
-
- }
- /**
- * Moves the property pageCorner towards the position of pageCornerTarget.
- *
- * @see Book#movePageCornerTarget()
- *
- * @private
- */
- protected function movePageCorner ():void {
-
- // calculate differences:
- var corner:Point = this.pageCorner;
- var target:Point = this.pageCornerTarget;
- var diffX:Number = target.x - corner.x;
- var diffY:Number = target.y - corner.y;
-
- // apply easing if difference is substantial:
- if (Point.distance(this.pageCorner, this.pageCornerTarget) > Book.MAX_EASING_DIFFERENCE && (!this.snap || (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED))){
- this.pageCorner.x += diffX * this._easing;
- this.pageCorner.y += diffY * this._easing;
- }else{
- this.pageCorner = this.pageCornerTarget.clone();
- }
-
- // make sure pageCorner is within bounds so no weird animation will result:
- if ((this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING) && !this.tearActive){
- if (this.pageCorner.y < 0) this.pageCorner.y = 0;
- if (this.pageCorner.y > this.height) this.pageCorner.y = this.height;
- }
-
- }
-
-
- /**
- * Stores a Page its BitmapData in the bitmapData Array.
- *
- * @param front Facing side of a sheet
- * @param back Flipside of a sheet
- *
- * @private
- */
- protected function saveBitmapData (front:Page, back:Page):void {
- back.hideFoldGradient();
- this.bitmapData = []; // dispose of BitmapData of pages we won't need right now
- this.bitmapData[front.index] = front.getBitmapData();
- this.bitmapData[back.index] = back.getBitmapData();
- back.showFoldGradient();
- }
-
-
- /**
- * Returns a Point instance indicating the current corner in which the mouse is.
- *
- * @return Point
- *
- * @private
- */
- protected function getCurrentCorner ():Point {
- var corner:Point = new Point();
- // determine corner:
- corner.x = (this.mouseX < this.width/2 ) ? 0 : 1;
- corner.y = (this.mouseY < this.height/2) ? 0 : 1;
- // return value:
- return corner;
- }
-
-
- /**
- * Returns a Point instance indicating the corner that was last flipped.
- *
- * @return Point
- */
- public function getLastFlippedCorner ():Point {
- return this.lastFlippedCorner;
- }
-
-
- /**
- * Returns true if any of the four outer page-corners contain a specified Point.
- *
- * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used.
- *
- * @return Boolean
- *
- * @see Book#isPageSideHit()
- *
- * @private
- */
- protected function isPageCornerHit (point:Point=null):Boolean {
- // if no point was provided, use the mouse coordinates:
- if (point == null){
- point = new Point(this.mouseX, this.mouseY);
- }
- // return value:
- if (Geom.isPointInCorner(this.regions[0].TL, point, Geom.TL) ||
- Geom.isPointInCorner(this.regions[0].BL, point, Geom.BL) ||
- Geom.isPointInCorner(this.regions[1].TR, point, Geom.TR) ||
- Geom.isPointInCorner(this.regions[1].BR, point, Geom.BR) ){
- return true;
- }else{
- return false;
- }
- }
- /**
- * Returns true if any of the two outer page-sides contain a specified Point.
- *
- * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used.
- *
- * @return Boolean
- *
- * @see Book#isPageCornerHit()
- *
- * @private
- */
- protected function isPageSideHit (point:Point=null):Boolean {
- // if no point was provided, use the mouse coordinates:
- if (point == null){
- point = new Point(this.mouseX, this.mouseY);
- }
- // return value:
- if (this.regions[0].side.containsPoint(point) || this.regions[1].side.containsPoint(point)){
- return true;
- }else{
- return false;
- }
- }
-
-
- /**
- * Returns an integer indicating the current side at which a pageflip should start.
- *
- * @see Page#LEFT
- * @see Page#RIGHT
- *
- * @return Current side at which a pageflip should start.
- *
- * @private
- */
- protected function getCurrentSide ():uint {
- var side:uint;
- if (!this.autoFlipActive){
- side = (this.mouseX <= this.width/2) ? Page.LEFT : Page.RIGHT;
- }else{
- if (!this.tearActive){
- side = (this.autoFlipIndex < this._currentPage) ? Page.LEFT : Page.RIGHT;
- }else{
- side = this.tearSide;
- }
- }
- return side;
- }
-
-
- /**
- * Sets the time at which a pageflip started, if necessary.
- *
- * @private
- */
- protected function setLastFlippedTime ():void {
- if (this.flipOnClick && !this.autoFlipActive){
- this.lastFlippedTime = getTimer();
- }
- }
-
-
- /**
- * Sets _status property and dispatches a BookEvent.
- *
- * @param newStatus New status.
- * @param dispatchEvent Boolean indicating whether or not a BookEvent should be dispatched.
- * @param eventType eventType for the dispatched Event. If not provided, the new status value will be used.
- *
- * @see BookEvent
- *
- * @private
- */
- protected function setStatus (newStatus:String,
- dispatchEvent:Boolean=true,
- page:Page=null,
- eventType:String=""):void {
- if (this._status != newStatus){
- this._status = newStatus;
- this.dispatchEvent(new BookEvent(BookEvent.STATUS_CHANGED, this));
- }
- if (dispatchEvent){
- if (eventType == "") eventType = newStatus;
- this.dispatchEvent(new BookEvent(eventType, this, page));
- }
- }
-
-
- /**
- * Calculates a y value for pageCornerTarget at the hand of its x value. The nearer the x value is to the horizontal middle of the Book, the nearer the calculated y will be to the vertical middle of the Book. This method is typically used by the movePageCornerTarget method.
- *
- * @param x Current x position of pageCornerTarget.
- * @param curve The maximum offset from the top or bottom of the Book, measured in a decimal value (for example, 0.25 means 25% of the Book its height). Typically this value is either AUTO_FLIP_CURVE or SIDE_FLIP_CURVE.
- *
- * @return The calculated y value.
- *
- * @see Book#movePageCornerTarget()
- * @see Book#AUTO_FLIP_CURVE
- * @see Book#SIDE_FLIP_CURVE
- *
- * @private
- */
- protected function getPageCornerYFromX (x:Number, curve:Number=Book.AUTO_FLIP_CURVE):Number {
- var middleX:Number = this.lastFlippedSide * (this.width/2) - (this.width/2)*this.lastFlippedDirection;
- var progress:Number = Math.abs(middleX-x) / (this.width/2);
- var y:Number;
- if (this.lastFlippedCorner.y == 0){
- y = this.height * curve * (1-progress);
- }else{
- y = this.height * (1-curve) + progress * (this.height*curve);
- }
- return y;
- }
-
-
- // ACCESSORS:
-
-
- /**
- * Typically called by the endPageFlip method to find out whether the page was either dragged or clicked. In the case of the latter the gotoPage method should be called.
- * @see Book#endPageFlip()
- * @see Book#gotoPage()
- * @private
- */
- protected function get flipOnRelease ():Boolean {
- if (this.flipOnClick && !this.autoFlipActive){
- return (getTimer() - this.lastFlippedTime <= Book.CLICK_INTERVAL);
- }else{
- return false;
- }
- }
-
-
- /**
- * The distance the pageCornerTarget travels along its x axis when gotoPage is active, calculated at the hand of the autoFlipDuration property.
- * @see Book#gotoPage()
- * @see Book#tearPage()
- * @see Book#autoFlipDuration
- * @private
- */
- protected function get autoFlipSpeed ():uint {
- return this.width / ((this.autoFlipDuration/1000) * this.stage.frameRate);
- }
-
-
- /**
- * The precision with which the page-corner follows the mouse during a pageflip. Values range from 0 (no easing) to 1 (heavy easing).
- * @default 0.7
- */
- public function get easing ():Number {
- return (1 - this._easing) / 0.9;
- }
- public function set easing (value:Number):void {
- if (value < 0) value = 0;
- if (value > 1) value = 1;
- this._easing = 1 - (value * 0.9);
- }
-
-
- /**
- * If true, the first and last Pages in this Book will be hard, regardless of the value of their own hard properties.
- * @default true
- */
- public function get hardCover ():Boolean {
- return this._hardCover;
- }
- public function set hardCover (value:Boolean):void {
- // return if there is no change in value:
- if (this._hardCover == value){
- return;
- }
- // set value:
- this._hardCover = value;
- // erase or draw fold gradient for first and last Pages:
- if (StateManager.instance.getState(this) >= StateManager.CREATION_COMPLETE){
- Page(this._pages.getItemAt(0)).refreshFoldGradient();
- Page(this._pages.getItemAt(this._pages.length-1)).refreshFoldGradient();
- }
- }
-
-
- /**
- * Set this property to false if the four outer page-corners should not display a subtle flip-effect on mouse-over. Note that Pages with their hard property set to true do not display hover-effects at all.
- * @default true
- */
- public function get hover ():Boolean {
- return this._hoverEnabled;
- }
- public function set hover (value:Boolean):void {
- // return if there is no change in value:
- if (this._hoverEnabled == value){
- return;
- }
- // set value:
- this._hoverEnabled = value;
- // add/remove listeners:
- if (this._hoverEnabled){
- this.addEventListener(Event.ENTER_FRAME, this.evaluateHover);
- }else{
- this.removeEventListener(Event.ENTER_FRAME, this.evaluateHover);
- }
- }
-
-
- /**
- * Size of the hit-regions in the pagecorners.
- * @default 150
- */
- public function get regionSize ():uint {
- return this._regionSize
- }
- public function set regionSize (value:uint):void {
- this._regionSize = value;
- this.createRegions();
- }
- /**
- * Creates an Array with hit-regions for pagecorners.
- *
- * @private
- */
- protected function createRegions ():void {
- // specify regions for left-hand page:
- this.regions[0] = {};
- this.regions[0].TL = new Rectangle(0, 0, this._regionSize, this._regionSize);
- this.regions[0].TR = new Rectangle(0, 0, this._regionSize, this._regionSize);
- this.regions[0].BR = new Rectangle(0, 0, this._regionSize, this._regionSize);
- this.regions[0].BL = new Rectangle(0, 0, this._regionSize, this._regionSize);
- this.regions[0].TR.x += this.width / 2 - this.regionSize;
- this.regions[0].BR.x += this.width / 2 - this.regionSize;
- this.regions[0].BR.y += this.height - this.regionSize;
- this.regions[0].BL.y += this.height - this.regionSize;
- // specify regions for right-hand page:
- this.regions[1] = {};
- this.regions[1].TL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
- this.regions[1].TR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
- this.regions[1].BR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
- this.regions[1].BL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
- this.regions[1].TR.x += this.width / 2 - this.regionSize;
- this.regions[1].BR.x += this.width / 2 - this.regionSize;
- this.regions[1].BR.y += this.height - this.regionSize;
- this.regions[1].BL.y += this.height - this.regionSize;
- // specify side-flip regions for both pages:
- this.regions[0].side = new Rectangle (0,
- (this.height-this._regionSize)/2,
- this._regionSize/2,
- this._regionSize);
- this.regions[1].side = this.regions[0].side.clone()
- this.regions[0].side.x = this.width-this._regionSize/2;
- }
-
-
- /**
- * Current status of the Book.
- * @see BookEvent#PAGEFLIP_STARTED
- * @see BookEvent#NOT_FLIPPING
- * @see BookEvent#PAGEFLIP_ENDING
- */
- [Bindable(event='statusChanged')]
- public function get status ():String {
- return this._status;
- }
-
-
- }
-
-
+package com.rubenswieringa.book {
+ import com.foxaweb.pageflip.PageFlip;
+ import com.rubenswieringa.geom.Geom;
+ import com.rubenswieringa.managers.StateManager;
+
+ import org.flashsandy.display.DistortImage;
+
+ import flash.display.BitmapData;
+ import flash.display.DisplayObject;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.utils.*;
+
+ use namespace limited;
+
+
+ /**
+ * Dispatched when the user picks up the corner of a page.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_STARTED
+ * @see BookEvent#PAGEFLIP_STARTED
+ */
+ [Event(name="pageflipStarted", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when the user releases the corner of a page. Note that this Event is dispatched just before the page starts falling in place.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_ENDING
+ * @see BookEvent#PAGEFLIP_FINISHED
+ */
+ [Event(name="pageflipEnding", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when a page falls in place after being flipped. This Event is dispatched regardless of whether or not the page has been turned, or has fallen back into its original position.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGEFLIP_FINISHED
+ * @see BookEvent#PAGE_TURNED
+ */
+ [Event(name="pageflipFinished", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when the corner of a page is rolled over with the mouse.
+ * Only applicable if the hover property is set to true.
+ * @eventType com.rubenswieringa.book.BookEvent.HOVER_STARTED
+ * @see BookEvent#HOVER_STARTED
+ * @see Book#hover
+ */
+ [Event(name="hoverStarted", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when the corner of a page is rolled out of with the mouse. Note that this Event is dispatched just before the page starts falling back in place.
+ * Only applicable if the hover property is set to true.
+ * @eventType com.rubenswieringa.book.BookEvent.HOVER_ENDING
+ * @see BookEvent#PAGEFLIP_FINISHED
+ * @see Book#hover
+ */
+ [Event(name="hoverEnding", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when a page falls back in place after being rolled over with the mouse.
+ * Only applicable if the hover property is set to true.
+ * @eventType com.rubenswieringa.book.BookEvent.HOVER_FINISHED
+ * @see BookEvent#PAGE_TURNED
+ * @see Book#hover
+ */
+ [Event(name="hoverFinished", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when a pageflip is successful.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGE_TURNED
+ * @see BookEvent#PAGE_NOT_TURNED
+ */
+ [Event(name="pageTurned", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when a pageflip is not successful.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGE_NOT_TURNED
+ * @see BookEvent#PAGE_TURNED
+ */
+ [Event(name="pageNotTurned", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched when a Page is torn out of its Book.
+ * @eventType com.rubenswieringa.book.BookEvent.PAGE_TORN
+ */
+ [Event(name="pageTorn", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched at the same time as the page-turned Event, when the Book was previously closed, and the first or last Page was flipped successfully.
+ * @eventType com.rubenswieringa.book.BookEvent.BOOK_OPENED
+ * @see BookEvent#PAGE_TURNED
+ */
+ [Event(name="bookOpened", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched at the same time as the page-turned Event, when the Book was previously open, and the first or last Page was flipped successfully.
+ * @eventType com.rubenswieringa.book.BookEvent.BOOK_CLOSED
+ * @see BookEvent#PAGE_TURNED
+ */
+ [Event(name="bookClosed", type="com.rubenswieringa.book.BookEvent")]
+ /**
+ * Dispatched at the same time as the page-turned Event, when the Book was previously open, and the first or last Page was flipped successfully.
+ * @eventType com.rubenswieringa.book.BookEvent.BOOK_CLOSED
+ * @see BookEvent#STATUS_CHANGED
+ * @see Book#status
+ */
+ [Event(name="statusChanged", type="com.rubenswieringa.book.BookEvent")]
+
+
+ /**
+ * Book is a container class that creates a rich animated and interactive book from its contents, through which the end-user can browse by flipping the pages over (pageflip-effect).
+ * Its core functionality (for example storage of Page instances) is defined by the PageManager class, which the Book class extends.
+ * Book itself describes the functionality for the management of pageflips. The actual pageflip class was written by Didier Brun (also mentioned in the credits).
+ *
+ * Ruben Swieringa created this component during his internship at the Factor.e (www.tfe.nl). Thanks to those guys for allowing me to publish the source-code online!
+ *
+ * @author Ruben Swieringa
+ * and others.
+ * http://github.com/rthesaint/FlexBook
+ * @version 1.0.5
+ * @see PageManager PageManager
+ * @see Page Page
+ * @see BookEvent BookEvent
+ * @see BookError BookError
+ * @see com.foxaweb.pageflip.PageFlip com.foxaweb.pageflip.PageFlip
+ * @see org.flashsandy.display.DistortImage org.flashsandy.display.DistortImage
+ * @see http://www.rubenswieringa.com/blog/flex-book-component-beta Rubens blog: Book component beta
+ *
+ *
+ * @internal
+ *
+ *
+ * Credits:
+ * - Didier Brun
+ * For making his pageflip rendering class (com.foxaweb.pageflip.PageFlip)
+ * Site: www.foxaweb.com
+ * - Thomas Pfeiffer (aka Kiroukou)
+ * For letting me use his distortion class (org.flashsandy.display.DistortImage)
+ * Site: www.flashsandy.org
+ * - the Factor.e
+ * For allowing me to publish the demo and the source-code.
+ * Site: www.tfe.nl
+ * - Maikel Sibbald
+ * For helping me with (among a lot of things) thinking out the structure of this component. He also made a usage-example of Didier's pageflip class (labs.flexcoders.nl/?p=33) which I used as reference in the early days of the Book class.
+ * Site: labs.flexcoders.nl
+ * - Theo Aartsma (aka Sumeco)
+ * For letting me use his artwork in the Book demo.
+ * Site: www.sumeco.net
+ *
+ *
+ * edit 5
+ *
+ *
+ * View code documentation at:
+ * http://www.rubenswieringa.com/code/as3/flex/Book/docs/
+ *
+ *
+ * Copyright (c) 2005 Ruben Swieringa. All rights reserved.
+ *
+ * This class is part of the Book component, which is licensed under the CREATIVE COMMONS Attribution 3.0 Unported.
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ * http://creativecommons.org/licenses/by/3.0/deed.en
+ *
+ */
+ public class Book extends PageManager {
+
+
+ /**
+ * Array in which the BitmapData for each Page instance is stored in. Note that after each pageflip this Array is cleaned.
+ * @private
+ */
+ protected var bitmapData:Array = [];
+ /**
+ * DistortImage instance for rendering hard-back Pages during pageflips.
+ * @see org.flashsandy.display.DistortImage
+ * @private
+ */
+ protected var distortion:DistortImage;
+ /**
+ * @private
+ */
+ protected var pageCorner:Point = new Point();
+ /**
+ * @private
+ */
+ protected var pageCornerTarget:Point = new Point();
+ /**
+ * The direction of the last pageflip. 1 stands for forward flipping (flipping a page from right to left), -1 stands for backward flipping (left to right).
+ * @private
+ */
+ protected var lastFlippedDirection:int = 0;
+ /**
+ * The side on which the last flipped Page laid before it was flipped. 0 stands for left, 1 for right.
+ * @see Page#LEFT
+ * @see Page#RIGHT
+ * @private
+ */
+ limited var lastFlippedSide:uint;
+ /**
+ * The position of the last flipped page-corner. The x-value represents the horizontal value, the y-value represents the vertical value. So (0, 0) would be the upper-left corner, (1, 1) would be the lower right corner, etc.
+ * @private
+ */
+ protected var lastFlippedCorner:Point;
+ /**
+ * Stores the timestamp at which a pageflip was started (mouse was pressed).
+ * This property is part of the mechanism that decides whether or not a click should fire an automated pageflip.
+ * @see Book#setLastFlippedTime()
+ * @private
+ */
+ protected var lastFlippedTime:int;
+ /**
+ * False if the last flipped page fell back into its original position after being flipped. True if it slided over to the opposite side.
+ * @private
+ */
+ protected var lastFlipSucceeded:Boolean;
+ /**
+ * Set by the gotoPage() method as a target-destination.
+ * @see Book#gotoPage()
+ * @private
+ */
+ protected var autoFlipIndex:int = -1;
+ /**
+ * False if the current pageflip is performed by the user dragging a pagecorner, true of not.
+ * @see Book#gotoPage()
+ * @see Book#tearPage()
+ * @private
+ */
+ protected var autoFlipActive:Boolean = false;
+ /**
+ * Indicates if an automated pageflip is allowed to be interrupted by the user.
+ * @see Book#gotoPage()
+ * @see Book#tearPage()
+ * @private
+ */
+ protected var autoFlipCancelable:Boolean = true;
+ /**
+ * True if a pageflip is triggered by a mouse-rollover, false if not.
+ * @private
+ */
+ protected var hoverActive:Boolean = false;
+ /**
+ * True if the current pageflip is performed upon a page its side (instead of one of its corners).
+ * @see Book#sideFlip
+ * @private
+ */
+ protected var sideFlipActive:Boolean = false;
+ /**
+ * True if this pageflip consists of a Page being torn out of this Book.
+ * @see Book#tearPage()
+ * @private
+ */
+ limited var tearActive:Boolean = false;
+ /**
+ * The side of the page that is being torn out. 0 stands for left, 1 for right.
+ * @see Book#tearPage()
+ * @see Page#LEFT
+ * @see Page#RIGHT
+ * @private
+ */
+ protected var tearSide:uint;
+ /**
+ * True if a page is being torn out of the book top-first, false if bottom-first.
+ * @see Book#tearPage()
+ * @private
+ */
+ protected var tearFromTop:Boolean;
+ /**
+ * Array of Rectangle instances that indicate the areas by which a page can be flipped.
+ * @see Book#createRegions()
+ * @private
+ */
+ protected var regions:Array = [];
+
+ protected var _tempCurrentPage : int = -2;
+ protected var _jumpIndex : int = -2;
+
+ // internals for accessors:
+ /**
+ * @see Book#easing
+ * @private
+ */
+ protected var _easing:Number = 0.3;
+ /**
+ * @see Book#hardCover
+ * @private
+ */
+ protected var _hardCover:Boolean = true;
+ /**
+ * @see Book#hover
+ * @private
+ */
+ protected var _hoverEnabled:Boolean;
+ /**
+ * @see Book#regionSize
+ * @private
+ */
+ protected var _regionSize:uint = 150;
+ /**
+ * @see Book#status
+ * @private
+ */
+ protected var _status:String;
+
+ // plain var properties:
+ /**
+ * The time (milliseconds) it takes to finish a pageflip performed by the gotoPage method.
+ * The calculated speed will also be used for tearing out Pages.
+ * @default 1000
+ * @see Book#gotoPage()
+ */
+ public var autoFlipDuration:uint = 1000;
+ /**
+ * Whether or not the page should be turned when a page corner is clicked.
+ * @default true
+ */
+ public var flipOnClick:Boolean = true;
+ /**
+ * Amount of perspective in a pageflip of a Page that has its hard property set to true.
+ * This value is used as the maximum with which the outer side of a page is extended on both sides (upper and lower) during a hard pageflip.
+ * @default 45
+ */
+ public var hardPerspective:uint = 45;
+ /**
+ * Indicates whether or not this Book plays animation while flipping one of its Pages.
+ * When this property is set to true, all Page instances will return true from their liveBitmapping property.
+ * Note that enabling liveBitmapping may result in decreased performance.
+ * @default false
+ * @see Page#liveBitmapping
+ */
+ public var liveBitmapping:Boolean = false;
+ /**
+ * Whether or not the outer side of a Page can be used to let it flip.
+ * @default true
+ */
+ public var sideFlip:Boolean = true;
+ /**
+ * If true, no easing will be applied during pageflips.
+ * @default false
+ * @see Book#easing
+ */
+ public var snap:Boolean = false;
+ /**
+ * If true, all Pages will have tearing enabled.
+ * @see Page#tearable
+ * @default false
+ */
+ public var tearable:Boolean = false;
+
+ // constants:
+ /**
+ * @private
+ */
+ protected static const AUTO_FLIP_CURVE:Number = 0.15;
+ /**
+ * @private
+ */
+ protected static const SIDE_FLIP_CURVE:Number = 0.04;
+ /**
+ * @private
+ */
+ protected static const MAX_EASING_DIFFERENCE:Number = 0.5;
+ /**
+ * @private
+ */
+ protected static const CLICK_INTERVAL:uint = 300;
+
+
+ // CONSTRUCTOR:
+
+
+ /**
+ * Constructor.
+ */
+ public function Book ():void {
+ super();
+
+ // set initial status:
+ this.setStatus(BookEvent.NOT_FLIPPING, false);
+
+ // make sure certain getter/setter values are processed where appropriate:
+ this.easing = 0.7;
+ this.hover = true;
+
+ // add event listener:
+ this.addEventListener(MouseEvent.MOUSE_DOWN, this.startPageFlip);
+
+ }
+
+
+ // OVERRIDES:
+
+
+ /**
+ * Draw additional graphisc for Pages where necessary.
+ *
+ * @private
+ */
+ override protected function childrenCreated ():void {
+ super.childrenCreated();
+
+ // make sure that non-hard pages whose flipsides are hard don't have fold-gradients:
+ for (var i:int=0; i= 0 && this.pageCorner.x <= this.width/2 && event != null && this.flipOnClick) {
+ this.cancelGotoPage(false);
+ return;
+ }
+ // stop if the clicked SuperViewStack does not show any Pages:
+ if (((!this.pageL.visible && side == Page.LEFT) || (!this.pageR.visible && side == Page.RIGHT)) && this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_STARTED && this._status != BookEvent.HOVER_ENDING){
+ return;
+ }
+ // stop if none of the pagecorners are hit:
+ if (!this.isPageCornerHit() && (!this.sideFlip || !this.isPageSideHit()) && !this.autoFlipActive){
+ return;
+ }
+ // don't flip if Page isn't allowed to:
+ if (Page(this._pages.getItemAt(this._currentPage+side)).lock){
+ this.autoFlipActive = false; // shut-down auto-pageflip if active
+ return;
+ }
+ // stop if a page corner is flipping back into position
+ if ((this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING) && !this.autoFlipActive && !this.lastFlipSucceeded && !this._status == BookEvent.PAGEFLIP_STARTED){
+ // switch back to flipping mode if the same page corner was picked up:
+ if (this.lastFlippedCorner.equals(this.getCurrentCorner()) && this.sideFlipActive == this.isPageSideHit()){
+ this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
+ var newStatus:String = (this.hoverActive) ? BookEvent.HOVER_STARTED : BookEvent.PAGEFLIP_STARTED;
+ this.setStatus(newStatus, true, oldPage);
+ }
+ return;
+ }
+ // stop if the Book is not inactive and not hovering:
+ if (this._status != BookEvent.NOT_FLIPPING && !this.hoverActive){
+ return;
+ }
+ // if the page is hovering and an actual pageflip should begin:
+ if (this.hoverActive && this._status == BookEvent.HOVER_STARTED){
+ this.hoverActive = false;
+ this.setLastFlippedTime(); // if necessary, remember time at which pageflip started
+ this.setStatus(BookEvent.PAGEFLIP_STARTED, true, oldPage);
+ return;
+ }
+
+ // throw an Error if the Page in question has no flipside:
+ // in the above stated situation the right-hand side Page typically does not have a flipside, and consequently its index will be equal to the total amount of pages (minus one). Also note that any left-hand side Page will always have a flipside, because the first Page in a Book is always on the right-hand side.
+ if (this._currentPage+1 == this._pages.length-1 && side == Page.RIGHT){
+ throw new BookError(BookError.NO_FLIPSIDE);
+ }
+
+ // set hover-flag:
+ this.hoverActive = attemptHover;
+
+ // set direction markers and position the render:
+ if ( this.autoFlipActive && !this.tearActive) this.lastFlippedCorner = new Point(side, 1);
+ if ( this.autoFlipActive && this.tearActive) this.lastFlippedCorner = new Point(side, (this.tearFromTop) ? 0 : 1);
+ if (!this.autoFlipActive) this.lastFlippedCorner = this.getCurrentCorner();
+ this.lastFlippedSide = this.lastFlippedCorner.x;
+ this.lastFlippedDirection = (side == Page.LEFT) ? -1 : 1;
+ this.sideFlipActive = (!this.tearActive && this.sideFlip && this.isPageSideHit());
+ this.render.x = this.lastFlippedSide * this.width/2;
+
+ // specify front and flipside indexes (_tempCurrentPage is used on jumpToPage):
+ var frontIndex:uint = (_tempCurrentPage < -1 ? this._currentPage : _tempCurrentPage) + this.lastFlippedSide;
+ var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection;
+ // save bitmapData:
+ this.saveBitmapData(Page(this._pages.getItemAt(frontIndex)), Page(this._pages.getItemAt(backIndex)));
+
+ // select pages in SuperViewStacks:
+ if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
+ this.pageL.visible = !this.isFirstPage(this._currentPage+1-2);
+ if (this.pageL.visible){
+ this.pageL.selectedChild = Page(this._pages.getItemAt(this._currentPage-2));
+ }else{
+ }
+ }
+ if (this.lastFlippedSide == Page.RIGHT){ // if right-hand page was flipped
+ this.pageR.visible = !this.isLastPage(this._currentPage+2);
+ if (this.pageR.visible){
+ this.pageR.selectedChild = Page(this._pages.getItemAt(this._currentPage+1+2));
+ }
+ }
+
+ // set page corner markers:
+ this.pageCorner = new Point(this.lastFlippedCorner.x*this.width/2, this.lastFlippedCorner.y*this.height);
+ if (this.autoFlipActive){
+ this.pageCornerTarget = this.pageCorner.clone();
+ }
+
+ // if necessary, remember time at which pageflip started:
+ this.setLastFlippedTime();
+
+
+ // set status:
+ this.setStatus((this.hoverActive) ? BookEvent.HOVER_STARTED : BookEvent.PAGEFLIP_STARTED, false); // false = dispatch Event later
+
+ // add listeners:
+ this.dragPageCorner();
+ this.addEventListener(Event.ENTER_FRAME, this.dragPageCorner);
+ this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
+
+ // dispatch event:
+ var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
+ if (this.hoverActive){
+ this.dispatchEvent(new BookEvent(BookEvent.HOVER_STARTED, this, page));
+ }else{
+ this.dispatchEvent(new BookEvent(BookEvent.PAGEFLIP_STARTED, this, page));
+ }
+
+ }
+
+
+ /**
+ * Performs the actual pageflip effect, typically called upon enter-frame.
+ *
+ * @see com.foxaweb.pageflip.PageFlip#computeFlip()
+ * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
+ *
+ * @param event Event
+ *
+ * @private
+ */
+ protected function dragPageCorner (event:Event=null):void {
+
+ // stop if the startPageFlip() or endPageFlip() have not been executed:
+ if (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED &&
+ this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_ENDING){
+ return;
+ }
+
+ // create faux mouse to create easing:
+ if (this._status == BookEvent.PAGEFLIP_STARTED || this._status == BookEvent.HOVER_STARTED){
+ if (this.movePageCornerTarget() && this.autoFlipActive){
+ this.endPageFlip();
+ return;
+ }
+ }
+ this.movePageCorner();
+
+ // clear render:
+ this.render.graphics.clear();
+
+ // check if the pageflip has ended:
+ if (this.pageCorner.equals(this.pageCornerTarget) && (this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING)){
+ this.finishPageFlip();
+ return;
+ }
+
+ // specify front and flipside indexes (_tempCurrentPage is used on jumpToPage):
+ var frontIndex:uint = (_tempCurrentPage < -1 ? this._currentPage : _tempCurrentPage) + this.lastFlippedSide;
+ var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection;
+ var front:Page = Page(this._pages.getItemAt(frontIndex));
+ var back:Page = Page(this._pages.getItemAt(backIndex));
+
+ // if liveBitmapping is enabled, refresh corresponding values in bitmapData Array:
+ if (front.liveBitmapping || back.liveBitmapping){
+ this.saveBitmapData(front, back);
+ }
+
+ // perform pageflip:
+ if (!front.explicitHard && !back.explicitHard){
+ var ocf:Object = PageFlip.computeFlip (this.pageCorner.clone(),
+ this.lastFlippedCorner,
+ this.width/2,
+ this.height,
+ !this.tearActive,
+ 1);
+ PageFlip.drawBitmapSheet (ocf,
+ this.render,
+ this.bitmapData[0],
+ this.bitmapData[1]);
+ // add shadows or highlights to the render:
+ this.addSmoothGradients(ocf);
+ // take ocf and find out whether we should start tearing this Page off:
+ if (this._status == BookEvent.PAGEFLIP_STARTED && Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)).tearable && ocf.cPoints != null){
+ this.evaluateTear(ocf.cPoints[2].clone());
+ }
+ }else{
+ this.drawHardPage (this.bitmapData[0],
+ this.bitmapData[1]);
+ }
+
+
+ }
+
+
+ /**
+ * Makes preparations for the end of the pageflip effect, typically called upon mouse-release.
+ *
+ * @param event MouseEvent
+ *
+ * @private
+ */
+ protected function endPageFlip (event:MouseEvent=null):void {
+
+ // stop if the Book is not currently performing a pageflip:
+ if (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED){
+ return;
+ }
+ // ignore mouse-up event if a Page is being torn out of the Book:
+ if (this.tearActive && event != null){
+ return;
+ }
+
+ // turn page if flipOnClick is enabled:
+ if (this.flipOnClick && this.flipOnRelease && event != null){
+ this.gotoPage(int(this._currentPage+this.lastFlippedDirection*2)); // cast to int because gotoPage has its page parameter typed with a * and will assume the value is an uint, and thus change any negative values to positives
+ return;
+ }
+
+ // remove mouse-listener:
+ this.stage.removeEventListener(MouseEvent.MOUSE_UP, this.endPageFlip);
+
+ // make sure page corner slides to the appropriate position:
+ if (!this.tearActive){
+ var x:Number;
+ var y:Number = this.lastFlippedCorner.y*this.height;
+ if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
+ this.lastFlipSucceeded = (this.pageCornerTarget.x > this.width/2);
+ x = (this.lastFlipSucceeded) ? this.width : 0;
+ }else{ // if right-hand page was flipped
+ this.lastFlipSucceeded = (this.pageCornerTarget.x < 0);
+ x = (this.lastFlipSucceeded) ? -this.width/2 : this.width/2;
+ }
+ this.pageCornerTarget = new Point(x, y);
+ }else{
+ this.lastFlipSucceeded = false;
+ }
+
+ // set status and dispatch event:
+ var newStatus:String = (this._status == BookEvent.HOVER_STARTED) ? BookEvent.HOVER_ENDING : BookEvent.PAGEFLIP_ENDING;
+ var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
+ this.setStatus(newStatus, true, page);
+
+ }
+
+
+ /**
+ * Ends the pageflip effect and displays the interactive content in the SuperViewStacks.
+ *
+ * @private
+ */
+ protected function finishPageFlip ():void {
+
+
+ // stop is endFlip() has not already been called:
+ if (this._status != BookEvent.PAGEFLIP_ENDING && this._status != BookEvent.HOVER_ENDING){
+ return;
+ }
+
+ // stop the dragPageCorner() method from being executed:
+ this.removeEventListener(Event.ENTER_FRAME, this.dragPageCorner);
+
+ // remember current Page for Events dispatched later:
+ var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
+
+ // remove Pages if they were torn out:
+ if (this.tearActive){
+ var wasLastPage:Boolean = this.isLastPage(this._currentPage);
+ this.removeChild(page.getFlipSide());
+ this.removeChild(page);
+ }
+
+ // if the page has been flipped over, change the _currentPage property:
+ if (this.lastFlipSucceeded && !this.tearActive){ // lastFlipSucceeded is always false when a Page was torn, but this makes the code more readable
+ this._currentPage += this.lastFlippedDirection * 2;
+ }
+ // change _currentPage where appropriate after a page-tear:
+ if (this.tearActive && this.lastFlippedSide == Page.LEFT && !wasLastPage){
+ this._currentPage -= 2;
+ }
+
+ // make sure the SuperViewStacks display the right Pages
+ this.refreshViewStacks();
+
+ // set status:
+ this.setStatus(BookEvent.NOT_FLIPPING, true, page, (this.hoverActive) ? BookEvent.HOVER_FINISHED : BookEvent.PAGEFLIP_FINISHED);
+
+ // dispatch additional events:
+ if (!this.hoverActive){
+ if (!this.tearActive){
+ if (this.lastFlipSucceeded){
+ this.dispatchEvent(new BookEvent(BookEvent.PAGE_TURNED, this, page));
+ if ((this.lastFlippedSide == Page.RIGHT && this._currentPage == 1) || (this.lastFlippedSide == Page.LEFT && this._currentPage == this._pages.length-3)){
+ this.dispatchEvent(new BookEvent(BookEvent.BOOK_OPENED, this));
+ }
+ if ((this.lastFlippedSide == Page.LEFT && this._currentPage == -1) || (this.lastFlippedSide == Page.RIGHT && this._currentPage == this._pages.length-1)){
+ this.dispatchEvent(new BookEvent(BookEvent.BOOK_CLOSED, this));
+ }
+ }else{
+ this.dispatchEvent(new BookEvent(BookEvent.PAGE_NOT_TURNED, this, page));
+ }
+ }else{
+ this.dispatchEvent(new BookEvent(BookEvent.PAGE_TORN, this, page));
+ }
+ }
+
+ // if this was a pageflip triggered by gotoPage then switch back to normal mode:
+ if (this.autoFlipActive){
+ if (this.tearActive){
+ this.tearActive = false;
+ this.autoFlipActive = false;
+ }else{
+ if (this._currentPage == this.autoFlipIndex){
+ this.autoFlipActive = false;
+ }else{
+ if (_jumpIndex >= -1) {
+ this.autoFlipActive = false;
+ this.jumpToPage(_jumpIndex);
+ } else {
+ this.startPageFlip();
+ }
+ }
+ }
+ }
+
+ // turn off flags:
+ this.hoverActive = false;
+ this.sideFlipActive = false;
+ this.tearActive = false;
+ _tempCurrentPage = -2;
+ }
+
+
+ /**
+ * Draws a hardcover Page, similar to the drawBitmapSheet method of the PageFlip class.
+ *
+ * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
+ *
+ * @param front BitmapData of the facing side of a sheet
+ * @param back BitmapData of the flipside of a sheet
+ *
+ * @private
+ *
+ */
+ protected function drawHardPage (front:BitmapData, back:BitmapData):void {
+
+ // calculate position correction:
+ var w:Number;
+ if (this.lastFlippedDirection == 1){
+ w = this.pageCorner.x - (1-this.lastFlippedSide) * this.width/2;
+ }else{
+ w = this.pageCorner.x - this.width/2;
+ }
+ if (w < -this.width/2) w = -this.width/2;
+ if (w > this.width/2) w = this.width/2;
+
+ // calculate perspective:
+ var closeness:Number = Math.sin(Math.acos(w / (this.width/2)));
+
+ // define positions of page-corners:
+ var pPoints:Array = [];
+ pPoints[0] = new Point((1-this.lastFlippedSide)*this.width/2, 0);
+ pPoints[1] = new Point(pPoints[0].x+w, 0-closeness*this.hardPerspective);
+ pPoints[2] = new Point(pPoints[0].x+w, this.height+closeness*this.hardPerspective);
+ pPoints[3] = new Point((1-this.lastFlippedSide)*this.width/2, this.height);
+
+ // make sure the first Point in the Array is always the top-right, etc:
+ var p:Array = [];
+ p[0] = (pPoints[0].x < pPoints[1].x) ? pPoints[0] : pPoints[1];
+ p[1] = (pPoints[0].x > pPoints[1].x) ? pPoints[0] : pPoints[1];
+ p[2] = (pPoints[2].x > pPoints[3].x) ? pPoints[2] : pPoints[3];
+ p[3] = (pPoints[2].x < pPoints[3].x) ? pPoints[2] : pPoints[3];
+
+ // draw page:
+ var bmd:BitmapData;
+ if (this.lastFlippedSide == 0){
+ bmd = (this.pageCorner.x > this.width/2) ? back : front;
+ }else{
+ bmd = (this.pageCorner.x < 0) ? back : front;
+ }
+ this.distortion.setTransform(this.render.graphics, bmd, p[0], p[1], p[2], p[3]);
+
+ // draw gradients:
+ this.addHardGradients(pPoints);
+
+ }
+
+
+ /**
+ * Considers a hover-effect for the four outer page-corners. This method is typically called upon enter-frame.
+ *
+ * @param event Event
+ *
+ * @private
+ */
+ protected function evaluateHover (event:Event=null):void {
+
+ var side:uint = this.getCurrentSide();
+ var pageCornerHit:Boolean = this.isPageCornerHit();
+
+ // if the hovered ViewStack does not display any Pages:
+ if (((side == Page.LEFT && !this.pageL.visible) || (side == Page.RIGHT && !this.pageR.visible)) && (!this.hoverActive || this._status == BookEvent.HOVER_ENDING)){
+ return;
+ }
+ // don't hover hard Pages:
+ if (Page(this._pages.getItemAt(this._currentPage+side)).hard){
+ return;
+ }
+
+ // roll over:
+ if ((pageCornerHit || (this.sideFlip && this.isPageSideHit())) && ((!this.hoverActive && this._status == BookEvent.NOT_FLIPPING) || this._status == BookEvent.HOVER_ENDING)){
+ this.startPageFlip(null, true);
+ }
+ // roll out:
+ if (!pageCornerHit && (!this.sideFlip || !this.isPageSideHit()) && this.hoverActive){
+ this.endPageFlip();
+ }
+
+ }
+
+
+ /**
+ * Looks at a provided Point and considers tearing off the Page currently being flipped. This method is typically called from within the dragPageCorner method.
+ *
+ * @param point Point used to evaluate whether or not this Page should be torn out of this Book.
+ *
+ * @see Book#dragPageCorner()
+ *
+ * @private
+ */
+ protected function evaluateTear (point:Point):void {
+ // no evaluation necessary if the Book has not even started flipping yet:
+ if (this._status != BookEvent.PAGEFLIP_STARTED){
+ return;
+ }
+ // stop if an auto-flip or page-tear is already active:
+ if (this.tearActive || this.autoFlipActive){
+ return;
+ }
+
+ // evaluate:
+ if (Math.round(point.x) == (1-this.lastFlippedCorner.x) * this.width/2 && Math.round(point.y) == this.lastFlippedCorner.y * this.height){
+ this.tearActive = true;
+ this.autoFlipActive = true;
+ this.autoFlipCancelable = false;
+ }
+ }
+
+
+ /**
+ * Performs a pageflip (or multiple flips) without the user having to drag the pagecorner.
+ *
+ * @param page int/uint or Page, indicating the index or instance of a Page.
+ * @param cancelable Indicates whether or not this auto-flip should allow cancelGotoPage to be called.
+ *
+ * @see Book#jumptoPage()
+ * @see Book#cancelGotoPage()
+ * @see Book#autoFlipDuration
+ *
+ * @throws BookError Gets thrown when the child parameter is not an instance of the Page class nor a int/uint.
+ * @see BookError#CHILD_NOT_PAGE
+ *
+ * @throws ArgumentsError Gets thrown when the child parameter is not a child of this Book.
+ * @see BookError#PAGE_NOT_CHILD
+ *
+ */
+ public function gotoPage (page:*, cancelable:Boolean=true):void {
+
+ page = this.getPageIndex(page, true);
+ if (page%2 != 1 && page != -1) page -= 1;
+
+ // return if we're already at the specified Page:
+ if (this._currentPage == page){
+ return;
+ }
+
+ // set target index and start pageflip:
+ this.autoFlipIndex = page;
+ this.autoFlipCancelable = cancelable;
+ if (!this.autoFlipActive){
+ this.autoFlipActive = true;
+ this.startPageFlip();
+ }
+ }
+
+ /**
+ * Force a single pageflip to change the current page.
+ *
+ * @param page int/uint or Page, indicating the index or instance of a Page.
+ * @param cancelable Indicates whether or not this auto-flip should allow cancelGotoPage to be called.
+ *
+ * @see Book#jumptoPage()
+ * @see Book#cancelGotoPage()
+ * @see Book#autoFlipDuration
+ *
+ * @throws BookError Gets thrown when the child parameter is not an instance of the Page class nor a int/uint.
+ * @see BookError#CHILD_NOT_PAGE
+ *
+ * @throws ArgumentsError Gets thrown when the child parameter is not a child of this Book.
+ * @see BookError#PAGE_NOT_CHILD
+ *
+ */
+ public function jumpToPage(page:*, cancelable:Boolean=true):void {
+
+ page = this.getPageIndex(page, true);
+ if (page%2 != 1 && page != -1) page -= 1;
+
+ // return if we’re already at the specified Page:
+ if (this._currentPage == page) {
+ return;
+ }
+
+ // set target index and start pageflip:
+ this.autoFlipIndex = page;
+ this.autoFlipCancelable = cancelable;
+
+ if (Math.abs(_currentPage - page) == 1) {
+ gotoPage(page, cancelable);
+ return;
+ }
+
+ if (autoFlipActive) {
+ _jumpIndex = page;
+ return;
+ }
+ _jumpIndex = -2;
+
+ // set _currentPage before / after the page we want to get to,
+ // according to currentPage position
+ var prePage:int = (this._currentPage < page) ? page - 2 : page + 2;
+
+ _tempCurrentPage = _currentPage;
+ this._currentPage = prePage;
+
+ if (!this.autoFlipActive) {
+ this.autoFlipActive = true;
+ this.startPageFlip();
+ }
+ }
+
+ /**
+ * Aborts a pageflip started by the gotoPage method or the jumptoPage method.
+ *
+ * @param finishFlip Boolean indicating whether or not to allow the auto-flip to finish. When true, the pageflip will finish. When false, the auto-flip will immediately stop, and the page corner will start sticking to the mouse.
+ *
+ * @see Book#gotoPage()
+ * @see Book#jumptoPage()
+ *
+ */
+ public function cancelGotoPage (finishFlip:Boolean=true):void {
+ // stop if gotoPage is not active:
+ if (!this.autoFlipActive){
+ return;
+ }
+ // stop if gotoPage is not cancelable:
+ if (!this.autoFlipCancelable){
+ return;
+ }
+
+ // end the auto-flip:
+ if (finishFlip){
+ this.autoFlipIndex = this._currentPage + 2 * this.lastFlippedDirection;
+ }else{
+ this.autoFlipActive = false;
+ }
+ }
+ /**
+ * Performs a pageflip to the next page, without the user having to drag the pagecorner.
+ *
+ * @see Book#gotoPage()
+ * @see Book#autoFlipDuration
+ *
+ */
+ public function nextPage ():void {
+ if (this._currentPage+2 <= this._pages.length-1){
+ this.gotoPage(this._currentPage+2, false);
+ }
+ }
+ /**
+ * Performs a pageflip to the previous page, without the user having to drag the pagecorner.
+ *
+ * @see Book#gotoPage()
+ * @see Book#autoFlipDuration
+ *
+ */
+ public function prevPage ():void {
+ if (this._currentPage-2 >= -1){
+ this.gotoPage(this._currentPage-2, false);
+ }
+ }
+
+
+ /**
+ * Tears a Page out of this Book instance. When called, this method will be executed regardless of the value of the Page its tearable property.
+ * Fails silently if the provided Page is not one of the currently visible Pages.
+ *
+ * @param page int/uint or Page, indicating the index or instance of a Page to be torn out of this Book.
+ * @param fromTop If true, the Page will be torn out by its upper outer corner, if false by its lower outer corner.
+ *
+ * @see Book#tearPage()
+ * @see Page#tearable
+ *
+ * @throws ArgumentError Gets thrown when page is an index-value and out of bounds.
+ * @see BookError#OUT_OF_BOUNDS
+ */
+ public function tearPage (page:*, fromTop:Boolean=true):void {
+ page = this.getPage(page, true);
+
+ // stop if we're not currently inactive or hovering:
+ if (this._status != BookEvent.NOT_FLIPPING && this._status != BookEvent.HOVER_STARTED){
+ return;
+ }
+ // fail silently if the provided Page is not one of the currently visible Pages:
+ if (page.index != this._currentPage && page.index != this._currentPage+1){
+ return;
+ }
+ // don't tear hard Pages:
+ if (page.hard){
+ return;
+ }
+
+ // set vars to let the pageflip processing methods know that we're tearing:
+ this.tearActive = true;
+ this.tearSide = page.side;
+ this.tearFromTop = fromTop;
+ this.autoFlipActive = true;
+ this.autoFlipCancelable = false;
+ this.startPageFlip();
+ }
+
+
+ // GRADIENTS:
+
+
+ /**
+ * Adds shadows and highlights to the pageflip process of a hard Page.
+ *
+ * @param area Array of coordinates (Point instance) indicating the outlines of the flipping page
+ *
+ * @see Gradients#drawOutside()
+ * @see Gradients#drawInside()
+ *
+ * @private
+ */
+ protected function addHardGradients (area:Array):void {
+
+ // determine page:
+ var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
+
+ // determine shadow or highlight:
+ var tint:String;
+ if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped
+ tint = (this.pageCornerTarget.x > this.width/2) ? Gradients.DARK : Gradients.LIGHT;
+ }else{ // if right-hand page was flipped
+ tint = (this.pageCornerTarget.x < 0) ? Gradients.LIGHT : Gradients.DARK;
+ }
+
+ // draw gradients:
+ page.gradients.drawOutside (this.render.graphics, area, tint);
+ page.gradients.drawInside (this.render.graphics, area, tint);
+
+ }
+ /**
+ * Adds shadows and highlights to the pageflip process of a non-hard Page.
+ *
+ * @see com.foxaweb.pageflip.PageFlip#computeFlip()
+ * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet()
+ *
+ * @see Gradients#drawFlipside()
+ * @see Gradients#drawOutside()
+ * @see Gradients#drawInside()
+ *
+ * @param ocf Object returned by the computeFlip method of the PageFlip class
+ *
+ * @private
+ */
+ protected function addSmoothGradients (ocf:Object):void {
+
+ // determine page:
+ var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide));
+
+ // determine rotation correction:
+ var rotate:Number;
+ if (this.lastFlippedCorner.equals(new Point(1,0)) || this.lastFlippedCorner.equals(new Point(0,1))){
+ // if the upper right or lower left corner is being flipped and the Page isn't torn out of its Book, correct the angle with 45 degrees:
+ rotate = (!this.tearActive) ? Gradients.ROTATE_HALF : Gradients.ROTATE_FULL;
+ }
+ if (this.lastFlippedCorner.equals(new Point(0,0)) || this.lastFlippedCorner.equals(new Point(1,1))){
+ // if the upper left or lower right corner is being flipped and the Page isn't torn out of its Book, correct the angle with minus 45 degrees:
+ rotate = (!this.tearActive) ? Gradients.ROTATE_FULL : Gradients.ROTATE_HALF;
+ }
+
+ // determine shadow or highlight:
+ var tint1:String = (this.lastFlippedSide == 1) ? Gradients.LIGHT : Gradients.DARK;
+ var tint2:String = (this.lastFlippedSide == 1) ? Gradients.DARK : Gradients.LIGHT;
+
+ // draw gradients:
+ if (ocf.cPoints != null) page.gradients.drawFlipside (this.render.graphics, ocf.cPoints, tint1, rotate);
+ if (ocf.pPoints != null) page.gradients.drawOutside (this.render.graphics, ocf.pPoints, tint1, rotate);
+ if (ocf.cPoints != null && !this.tearActive) page.gradients.drawInside (this.render.graphics, ocf.cPoints, tint2, rotate);
+
+ }
+
+
+ // PAGEFLIP ASSISTANCE:
+
+
+ /**
+ * Sets the position of the pageCorner property.
+ *
+ * @return Boolean indicating, if the gotoPage method is active, whether or not pageCornerTarget has reached the opposite corner. If gotoPage is not active, the return value will always be false.
+ *
+ * @see Book#movePageCorner()
+ * @see Book#gotoPage()
+ *
+ * @private
+ */
+ protected function movePageCornerTarget ():Boolean {
+
+ var x:Number;
+ var y:Number;
+
+ // if gotoPage is not active then set the page corner target equal to the mouse position:
+ if (!this.autoFlipActive){
+ // calculate coordinates:
+ x = this.mouseX;
+ y = (!this.sideFlipActive) ? this.mouseY : this.lastFlippedCorner.y * this.height;
+ // adjust x per position of render:
+ if (this.lastFlippedSide == Page.RIGHT){
+ x -= (this.width/2);
+ }
+ // adjust x and y if a side-flip is active:
+ if (this.sideFlipActive){
+ // adjust x so that pageflip won't start acting weird when mouse is out of bounds:
+ var xMin:Number = 0 - this.lastFlippedSide * (this.width/2);
+ var xMax:Number = xMin + this.width;
+ if (x < xMin) x = xMin;
+ if (x > xMax) x = xMax;
+ // adjust y to make the pageflip a bit more pretty:
+ if (!this.hoverActive){
+ y = this.getPageCornerYFromX(x, Book.SIDE_FLIP_CURVE);
+ }
+ }
+ // set position:
+ this.pageCornerTarget = new Point(x, y);
+ }
+
+ // if gotoPage is active then move the page corner target to the opposite corner:
+ if (this.autoFlipActive){
+ // calculate coordinates:
+ x = (!this.tearActive) ? this.pageCornerTarget.x : this.lastFlippedSide * (this.width/2);
+ y = this.pageCornerTarget.y;
+ var opposite:Point = new Point();
+ // if this is a normal auto-flip:
+ if (!this.tearActive){
+ opposite.x = this.lastFlippedSide * (this.width/2) - this.width*this.lastFlippedDirection;
+ if (Math.abs(x-opposite.x) >= this.autoFlipSpeed){
+ x += this.autoFlipSpeed * -this.lastFlippedDirection;
+ }else{
+ x = opposite.x;
+ }
+ y = this.getPageCornerYFromX(x);
+ }
+ // if this is a tearing auto-flip:
+ if (this.tearActive){
+ var directionY:Number = (this.lastFlippedCorner.y == 0) ? 1 : -1;
+ opposite.y = (this.height/2) + (this.height*1.5 * directionY);
+ if (Math.abs(y-opposite.y) >= this.autoFlipSpeed){
+ y += this.autoFlipSpeed * directionY;
+ }else{
+ y = opposite.y;
+ }
+ }
+ // set position:
+ this.pageCornerTarget = new Point(x, y);
+ // return true if pageCornerTarget has reached the opposite corner of where it started:
+ if (this.pageCornerTarget.equals(this.pageCorner) &&
+ ((this.tearActive && y == opposite.y) ||
+ (!this.tearActive && x == opposite.x))){
+ return true;
+ }
+ }
+
+ // return value:
+ return false;
+
+ }
+ /**
+ * Moves the property pageCorner towards the position of pageCornerTarget.
+ *
+ * @see Book#movePageCornerTarget()
+ *
+ * @private
+ */
+ protected function movePageCorner ():void {
+
+ // calculate differences:
+ var corner:Point = this.pageCorner;
+ var target:Point = this.pageCornerTarget;
+ var diffX:Number = target.x - corner.x;
+ var diffY:Number = target.y - corner.y;
+
+ // apply easing if difference is substantial:
+ if (Point.distance(this.pageCorner, this.pageCornerTarget) > Book.MAX_EASING_DIFFERENCE && (!this.snap || (this._status != BookEvent.PAGEFLIP_STARTED && this._status != BookEvent.HOVER_STARTED))){
+ this.pageCorner.x += diffX * this._easing;
+ this.pageCorner.y += diffY * this._easing;
+ }else{
+ this.pageCorner = this.pageCornerTarget.clone();
+ }
+
+ // make sure pageCorner is within bounds so no weird animation will result:
+ if ((this._status == BookEvent.PAGEFLIP_ENDING || this._status == BookEvent.HOVER_ENDING) && !this.tearActive){
+ if (this.pageCorner.y < 0) this.pageCorner.y = 0;
+ if (this.pageCorner.y > this.height) this.pageCorner.y = this.height;
+ }
+
+ }
+
+
+ /**
+ * Stores a Page its BitmapData in the bitmapData Array.
+ *
+ * @param front Facing side of a sheet
+ * @param back Flipside of a sheet
+ *
+ * @private
+ */
+ protected function saveBitmapData (front:Page, back:Page):void {
+ back.hideFoldGradient();
+ this.bitmapData = []; // dispose of BitmapData of pages we won't need right now
+ this.bitmapData[0] = front.getBitmapData();
+ this.bitmapData[1] = back.getBitmapData();
+ back.showFoldGradient();
+ }
+
+
+ /**
+ * Returns a Point instance indicating the current corner in which the mouse is.
+ *
+ * @return Point
+ *
+ * @private
+ */
+ protected function getCurrentCorner ():Point {
+ var corner:Point = new Point();
+ // determine corner:
+ corner.x = (this.mouseX < this.width/2 ) ? 0 : 1;
+ corner.y = (this.mouseY < this.height/2) ? 0 : 1;
+ // return value:
+ return corner;
+ }
+
+
+ /**
+ * Returns a Point instance indicating the corner that was last flipped.
+ *
+ * @return Point
+ */
+ public function getLastFlippedCorner ():Point {
+ return this.lastFlippedCorner;
+ }
+
+
+ /**
+ * Returns true if any of the four outer page-corners contain a specified Point.
+ *
+ * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used.
+ *
+ * @return Boolean
+ *
+ * @see Book#isPageSideHit()
+ *
+ * @private
+ */
+ protected function isPageCornerHit (point:Point=null):Boolean {
+ // if no point was provided, use the mouse coordinates:
+ if (point == null){
+ point = new Point(this.mouseX, this.mouseY);
+ }
+ // return value:
+ if (Geom.isPointInCorner(this.regions[0].TL, point, Geom.TL) ||
+ Geom.isPointInCorner(this.regions[0].BL, point, Geom.BL) ||
+ Geom.isPointInCorner(this.regions[1].TR, point, Geom.TR) ||
+ Geom.isPointInCorner(this.regions[1].BR, point, Geom.BR) ){
+ return true;
+ }else{
+ return false;
+ }
+ }
+ /**
+ * Returns true if any of the two outer page-sides contain a specified Point.
+ *
+ * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used.
+ *
+ * @return Boolean
+ *
+ * @see Book#isPageCornerHit()
+ *
+ * @private
+ */
+ protected function isPageSideHit (point:Point=null):Boolean {
+ // if no point was provided, use the mouse coordinates:
+ if (point == null){
+ point = new Point(this.mouseX, this.mouseY);
+ }
+ // return value:
+ if (this.regions[0].side.containsPoint(point) || this.regions[1].side.containsPoint(point)){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+
+ /**
+ * Returns an integer indicating the current side at which a pageflip should start.
+ *
+ * @see Page#LEFT
+ * @see Page#RIGHT
+ *
+ * @return Current side at which a pageflip should start.
+ *
+ * @private
+ */
+ protected function getCurrentSide ():uint {
+ var side:uint;
+ if (!this.autoFlipActive){
+ side = (this.mouseX <= this.width/2) ? Page.LEFT : Page.RIGHT;
+ }else{
+ if (!this.tearActive){
+ side = (this.autoFlipIndex < this._currentPage) ? Page.LEFT : Page.RIGHT;
+ }else{
+ side = this.tearSide;
+ }
+ }
+ return side;
+ }
+
+
+ /**
+ * Sets the time at which a pageflip started, if necessary.
+ *
+ * @private
+ */
+ protected function setLastFlippedTime ():void {
+ if (this.flipOnClick && !this.autoFlipActive){
+ this.lastFlippedTime = getTimer();
+ }
+ }
+
+
+ /**
+ * Sets _status property and dispatches a BookEvent.
+ *
+ * @param newStatus New status.
+ * @param dispatchEvent Boolean indicating whether or not a BookEvent should be dispatched.
+ * @param eventType eventType for the dispatched Event. If not provided, the new status value will be used.
+ *
+ * @see BookEvent
+ *
+ * @private
+ */
+ protected function setStatus (newStatus:String,
+ dispatchEvent:Boolean=true,
+ page:Page=null,
+ eventType:String=""):void {
+ if (this._status != newStatus){
+ this._status = newStatus;
+ this.dispatchEvent(new BookEvent(BookEvent.STATUS_CHANGED, this));
+ }
+ if (dispatchEvent){
+ if (eventType == "") eventType = newStatus;
+ this.dispatchEvent(new BookEvent(eventType, this, page));
+ }
+ }
+
+
+ /**
+ * Calculates a y value for pageCornerTarget at the hand of its x value. The nearer the x value is to the horizontal middle of the Book, the nearer the calculated y will be to the vertical middle of the Book. This method is typically used by the movePageCornerTarget method.
+ *
+ * @param x Current x position of pageCornerTarget.
+ * @param curve The maximum offset from the top or bottom of the Book, measured in a decimal value (for example, 0.25 means 25% of the Book its height). Typically this value is either AUTO_FLIP_CURVE or SIDE_FLIP_CURVE.
+ *
+ * @return The calculated y value.
+ *
+ * @see Book#movePageCornerTarget()
+ * @see Book#AUTO_FLIP_CURVE
+ * @see Book#SIDE_FLIP_CURVE
+ *
+ * @private
+ */
+ protected function getPageCornerYFromX (x:Number, curve:Number=Book.AUTO_FLIP_CURVE):Number {
+ var middleX:Number = this.lastFlippedSide * (this.width/2) - (this.width/2)*this.lastFlippedDirection;
+ var progress:Number = Math.abs(middleX-x) / (this.width/2);
+ var y:Number;
+ if (this.lastFlippedCorner.y == 0){
+ y = this.height * curve * (1-progress);
+ }else{
+ y = this.height * (1-curve) + progress * (this.height*curve);
+ }
+ return y;
+ }
+
+
+ // ACCESSORS:
+
+
+ /**
+ * Typically called by the endPageFlip method to find out whether the page was either dragged or clicked. In the case of the latter the gotoPage method should be called.
+ * @see Book#endPageFlip()
+ * @see Book#gotoPage()
+ * @private
+ */
+ protected function get flipOnRelease ():Boolean {
+ if (this.flipOnClick && !this.autoFlipActive){
+ return (getTimer() - this.lastFlippedTime <= Book.CLICK_INTERVAL);
+ }else{
+ return false;
+ }
+ }
+
+
+ /**
+ * The distance the pageCornerTarget travels along its x axis when gotoPage is active, calculated at the hand of the autoFlipDuration property.
+ * @see Book#gotoPage()
+ * @see Book#tearPage()
+ * @see Book#autoFlipDuration
+ * @private
+ */
+ protected function get autoFlipSpeed ():uint {
+ return this.width / ((this.autoFlipDuration/1000) * this.stage.frameRate);
+ }
+
+
+ /**
+ * The precision with which the page-corner follows the mouse during a pageflip. Values range from 0 (no easing) to 1 (heavy easing).
+ * @default 0.7
+ */
+ public function get easing ():Number {
+ return (1 - this._easing) / 0.9;
+ }
+ public function set easing (value:Number):void {
+ if (value < 0) value = 0;
+ if (value > 1) value = 1;
+ this._easing = 1 - (value * 0.9);
+ }
+
+
+ /**
+ * If true, the first and last Pages in this Book will be hard, regardless of the value of their own hard properties.
+ * @default true
+ */
+ public function get hardCover ():Boolean {
+ return this._hardCover;
+ }
+ public function set hardCover (value:Boolean):void {
+ // return if there is no change in value:
+ if (this._hardCover == value){
+ return;
+ }
+ // set value:
+ this._hardCover = value;
+ // erase or draw fold gradient for first and last Pages:
+ if (StateManager.instance.getState(this) >= StateManager.CREATION_COMPLETE){
+ Page(this._pages.getItemAt(0)).refreshFoldGradient();
+ Page(this._pages.getItemAt(this._pages.length-1)).refreshFoldGradient();
+ }
+ }
+
+
+ /**
+ * Set this property to false if the four outer page-corners should not display a subtle flip-effect on mouse-over. Note that Pages with their hard property set to true do not display hover-effects at all.
+ * @default true
+ */
+ public function get hover ():Boolean {
+ return this._hoverEnabled;
+ }
+ public function set hover (value:Boolean):void {
+ // return if there is no change in value:
+ if (this._hoverEnabled == value){
+ return;
+ }
+ // set value:
+ this._hoverEnabled = value;
+ // add/remove listeners:
+ if (this._hoverEnabled){
+ this.addEventListener(Event.ENTER_FRAME, this.evaluateHover);
+ }else{
+ this.removeEventListener(Event.ENTER_FRAME, this.evaluateHover);
+ }
+ }
+
+
+ /**
+ * Size of the hit-regions in the pagecorners.
+ * @default 150
+ */
+ public function get regionSize ():uint {
+ return this._regionSize;
+ }
+ public function set regionSize (value:uint):void {
+ this._regionSize = value;
+ this.createRegions();
+ }
+ /**
+ * Creates an Array with hit-regions for pagecorners.
+ *
+ * @private
+ */
+ protected function createRegions ():void {
+ // specify regions for left-hand page:
+ this.regions[0] = {};
+ this.regions[0].TL = new Rectangle(0, 0, this._regionSize, this._regionSize);
+ this.regions[0].TR = new Rectangle(0, 0, this._regionSize, this._regionSize);
+ this.regions[0].BR = new Rectangle(0, 0, this._regionSize, this._regionSize);
+ this.regions[0].BL = new Rectangle(0, 0, this._regionSize, this._regionSize);
+ this.regions[0].TR.x += this.width / 2 - this.regionSize;
+ this.regions[0].BR.x += this.width / 2 - this.regionSize;
+ this.regions[0].BR.y += this.height - this.regionSize;
+ this.regions[0].BL.y += this.height - this.regionSize;
+ // specify regions for right-hand page:
+ this.regions[1] = {};
+ this.regions[1].TL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
+ this.regions[1].TR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
+ this.regions[1].BR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
+ this.regions[1].BL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize);
+ this.regions[1].TR.x += this.width / 2 - this.regionSize;
+ this.regions[1].BR.x += this.width / 2 - this.regionSize;
+ this.regions[1].BR.y += this.height - this.regionSize;
+ this.regions[1].BL.y += this.height - this.regionSize;
+ // specify side-flip regions for both pages:
+ this.regions[0].side = new Rectangle (0,
+ (this.height-this._regionSize)/2,
+ this._regionSize/2,
+ this._regionSize);
+ this.regions[1].side = this.regions[0].side.clone();
+ this.regions[0].side.x = this.width-this._regionSize/2;
+ }
+
+
+ /**
+ * Current status of the Book.
+ * @see BookEvent#PAGEFLIP_STARTED
+ * @see BookEvent#NOT_FLIPPING
+ * @see BookEvent#PAGEFLIP_ENDING
+ */
+ [Bindable(event='statusChanged')]
+ public function get status ():String {
+ return this._status;
+ }
+
+
+ }
+
+
}
\ No newline at end of file