diff --git a/build.xml b/build.xml index 6ef6d6b..bcc0cd8 100644 --- a/build.xml +++ b/build.xml @@ -32,6 +32,7 @@ + true @@ -42,12 +43,13 @@ - + true diff --git a/build/11.6/Scratch.swf b/build/11.6/Scratch.swf index 12c80e2..293df15 100644 Binary files a/build/11.6/Scratch.swf and b/build/11.6/Scratch.swf differ diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/scratch.gradle b/scratch.gradle index aaa9a76..0c1a953 100644 --- a/scratch.gradle +++ b/scratch.gradle @@ -42,7 +42,7 @@ def scratchFlashCommitID = getCommitID(commonDir) println "Commit ID for scratch-flash is: ${scratchFlashCommitID}" dependencies { - flexSDK group: 'org.apache', name: 'apache-flex-sdk', version: '4.10.0', ext: 'zip' + flexSDK group: 'org.apache', name: 'apache-flex-sdk', version: '4.14.0', ext: 'zip' external group: 'macromedia.com', name: 'playerglobal', version: playerVersion.replace('.', '_'), ext: 'swc' merged files( "${commonDir}/libs/as3corelib.swc", diff --git a/src/CSS.as b/src/CSS.as index 2c49f21..4eb5c10 100644 --- a/src/CSS.as +++ b/src/CSS.as @@ -37,13 +37,16 @@ public class CSS { // Colors public static const white:int = 0xFFFFFF; + public static const grey100:int = 0xF5F5F5; + public static const grey50:int = 0xFAFAFA; public static const backgroundColor_default:int = white; public static const topBarColor_default:int = 0x9C9EA2; public static const tabColor:int = 0xE6E8E8; public static const panelColor:int = 0xF2F2F2; public static const itemSelectedColor:int = 0xD0D0D0; - public static const borderColor:int = 0xD0D1D2; - public static const textColor:int = 0x5C5D5F; // 0x6C6D6F + public static const borderColor:int =0xF5F5F5;// 0xD0D1D2; + public static const seamColor:int=CSS.borderColor - 0x141414; + public static const textColor:int = 0x424242; // 0x6C6D6F public static const buttonLabelColor:int = textColor; public static const buttonLabelOverColor:int = 0xFBA939; public static const offColor:int = 0x8F9193; // 0x9FA1A3 @@ -52,7 +55,7 @@ public class CSS { public static const arrowColor:int = 0xA6A8AC; // Fonts - public static const font:String = Resources.chooseFont(['Arial', 'Verdana', 'DejaVu Sans']); + public static const font:String = Resources.chooseFont(['NotoSans','Arial', 'Verdana', 'DejaVu Sans']); public static const menuFontSize:int = 12; public static const normalTextFormat:TextFormat = new TextFormat(font, 12, textColor); public static const topBarButtonFormat:TextFormat = new TextFormat(font, 12, white, true); diff --git a/src/Scratch.as b/src/Scratch.as index 235fccd..834d62d 100644 --- a/src/Scratch.as +++ b/src/Scratch.as @@ -71,7 +71,11 @@ import uiwidgets.*; import util.*; import watchers.ListWatcher; - +import flash.filters.DropShadowFilter; +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.DropShadowFilterPlugin; +import com.greensock.plugins.HexColorsPlugin; public class Scratch extends Sprite { // Version public static const versionString:String = '1.4β'; @@ -91,9 +95,10 @@ public class Scratch extends Sprite { public var ignoreResize:Boolean = false; // If true, temporarily ignore resize events. public var isExtensionDevMode:Boolean = false; // If true, run in extension development mode (as on ScratchX) public var isMicroworld:Boolean = false; + public var tabsAndPane:Sprite=new Sprite(); public var presentationScale:Number; - + // Runtime public var runtime:ScratchRuntime; public var interp:Interpreter; @@ -107,7 +112,7 @@ public class Scratch extends Sprite { public var loadInProgress:Boolean; public var debugOps:Boolean = false; public var debugOpCmd:String = ''; - + public var MaxCloneCount:int = 300; protected var autostart:Boolean; @@ -129,6 +134,7 @@ public class Scratch extends Sprite { // UI Parts public var libraryPart:LibraryPart; protected var topBarPart:TopBarPart; + public var linesPart:LinesPart; public var stagePart:StagePart; private var tabsPart:TabsPart; protected var scriptsPart:ScriptsPart; @@ -139,6 +145,10 @@ public class Scratch extends Sprite { public var logger:Log = new Log(16); public function Scratch() { + + TweenPlugin.activate([DropShadowFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + //TweenLite.to(mc, 1, {dropShadowFilter:{blurX:5, blurY:5, distance:5, alpha:0.6}}); SVGTool.setStage(stage); loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler); app = this; @@ -224,6 +234,14 @@ public class Scratch extends Sprite { //Analyze.countMissingAssets(); handleStartupParameters(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; + //tabsAndPane.filters=[shadow]; + //this.add(TabsAndPane); } protected function handleStartupParameters():void { @@ -651,12 +669,17 @@ public class Scratch extends Sprite { for each (var o:ScratchObj in stagePane.allObjects()) o.applyFilters(); if (lp) fixLoadProgressLayout(); + + fixLayout(); + libraryPart.refresh(); + tabsPart.refresh(); + stagePane.applyFilters(); stagePane.updateCostume(); SCRATCH::allow3d { if (isIn3D) render3D.onStageResize(); } } - + public function isInPresentationMode() { return stagePart.isInPresentationMode(); } @@ -737,6 +760,9 @@ public class Scratch extends Sprite { Transition.step(null); stagePart.step(); libraryPart.step(); + if(this.getChildIndex(libraryPart) 0)) { tabName = 'scripts'; scriptsPart.updatePalette(); scriptsPane.viewScriptsFor(viewedObject); scriptsPart.updateSpriteWatermark(); - show(scriptsPart); + tabsAndPane.addChild(scriptsPart); + //show(scriptsPart); } - show(tabsPart); + + tabsAndPane.addChild(tabsPart); + //show(tabsPart); show(stagePart); // put stage in front + if(this.getChildIndex(libraryPart) 550) ? 16 : 0; // add padding for full-screen mode - var scale:Number = Math.min((w - extraW - pad) / 480, (h - extraH - pad) / 360); + var scale:Number = Math.min((w - extraW - pad) / 480, (h - extraH - pad-16) / 360); scale = Math.max(0.01, scale); var scaledW:int = Math.floor((scale * 480) / 4) * 4; // round down to a multiple of 4 scale = scaledW / 480; @@ -940,15 +994,16 @@ public class Scratch extends Sprite { var playerH:Number = (scale * 360) + extraH; stagePart.setWidthHeight(playerW, playerH, scale); stagePart.x = int((w - playerW) / 2); - stagePart.y = int((h - playerH) / 2); + stagePart.y = int((h - playerH-16) / 2); fixLoadProgressLayout(); + hide(linesPart); return; } libraryPart.x = stagePart.x; - libraryPart.y = stagePart.bottom() + 18; - libraryPart.setWidthHeight(stagePart.w, h - libraryPart.y); + libraryPart.y = stagePart.bottom() + 16+1; + libraryPart.setWidthHeight(stagePart.w, h - libraryPart.y-5); - tabsPart.x = stagePart.right() + 5; + tabsPart.x = stagePart.right() + 1; if (!isMicroworld) { tabsPart.y = topBarPart.bottom() + 5; tabsPart.fixLayout(); @@ -957,13 +1012,17 @@ public class Scratch extends Sprite { tabsPart.visible = false; // the content area shows the part associated with the currently selected tab: - var contentY:int = tabsPart.y + 27; + var contentY:int = tabsPart.y + tabsPart.height; if (!isMicroworld) w -= tipsWidth(); - updateContentArea(tabsPart.x, contentY, w - tabsPart.x - 6, h - contentY - 5, h); + linesPart.x=5; + linesPart.y=tabsPart.y; + linesPart.setWidthHeight(w-10,h-tabsPart.y-5); + updateContentArea(tabsPart.x, contentY, w - tabsPart.x - 6, h - contentY - 6, h); } protected function updateContentArea(contentX:int, contentY:int, contentW:int, contentH:int, fullH:int):void { + imagesPart.x = soundsPart.x = scriptsPart.x = contentX; imagesPart.y = soundsPart.y = scriptsPart.y = contentY; imagesPart.setWidthHeight(contentW, contentH); @@ -979,6 +1038,7 @@ public class Scratch extends Sprite { SCRATCH::allow3d { if (isIn3D) render3D.onStageResize(); } + drawBG(); } private function drawBG():void { @@ -986,6 +1046,10 @@ public class Scratch extends Sprite { g.clear(); g.beginFill(0); g.drawRect(0, 0, stage.stageWidth, stage.stageHeight); + this.graphics.clear(); + this.graphics.beginFill(0x2196F3); + //this.graphics.drawRect(0, 0, 100, 100); + this.graphics.drawRect(0, 0, this.width, this.height); } private var modalOverlay:Sprite; @@ -1063,7 +1127,7 @@ public class Scratch extends Sprite { m.showOnStage(stage, b.x, topBarPart.bottom() - 1); } - + public function stopVideo(b:*):void { runtime.stopVideo(); } @@ -1152,7 +1216,7 @@ public class Scratch extends Sprite { '\nCopyright © 2012 MIT Media Laboratory' + '\nAll rights reserved.', stage); } - + public function secretDevLogoMenu():void { var m:Menu = new Menu(null, 'Dev', CSS.topBarColor_default, 28); m.addItem('About', aboutDev); @@ -1161,11 +1225,11 @@ public class Scratch extends Sprite { m.addItem('Change dev password', devChangePass); m.showOnStage(stage, 1, 10); } - + public function aboutDev(b:*):void { DialogBox.notify('About Developer Mode', '\nComing soon...', stage) } - + public function devChangePass(b:*):void { DialogBox.notify('Change Password', '\nComing soon...', stage) } @@ -1201,7 +1265,7 @@ public class Scratch extends Sprite { } } }*/ - + //DIALOG FUNCTIONS (for primatives) /* public function primCustomDialog(param1:Function, param2:Function, param3:Function, param4:Function, param5:Function, param6:Function, param7:Function, param8:Function, param9:Function):String { var d:DialogBox = new DialogBox(); @@ -1217,7 +1281,7 @@ public class Scratch extends Sprite { d.showOnStage(stage); return "WIP"; }*/ - + public function confirmCloneCountChange(param1:int):void { function confirmChange():void { MaxCloneCount = (param1 - 2); @@ -1229,7 +1293,7 @@ public class Scratch extends Sprite { d.addButton('Decline', d.cancel); d.showOnStage(stage); } - + public function devMode() : void { var babyCheck:Function = null; @@ -1267,7 +1331,7 @@ public class Scratch extends Sprite { DialogBox.notify('Developer Mode', '\nThe password "' + Password + '" is incorrect.', stage); } } - + protected function createNewProjectAndThen(callback:Function = null):void { function clearProject():void { startNewProject('', ''); @@ -1375,7 +1439,7 @@ public class Scratch extends Sprite { interp.turboMode = true; stagePart.refresh(); } - + public function toggleSingleSteppingFast():void { interp.singleSteppingFast = true; interp.singleSteppingSlow = false; @@ -1389,10 +1453,10 @@ public class Scratch extends Sprite { interp.turboMode = false; stagePart.refresh(); } - + public function toggleSingleSteppingStop():void { interp.singleSteppingFast = false; - interp.singleSteppingSlow = false; + interp.singleSteppingSlow = false; interp.turboMode = false; stagePart.refresh(); } @@ -1403,7 +1467,7 @@ public class Scratch extends Sprite { interp.turboMode = true; stagePart.refresh(); } - + public function deactivateTurboMode():void { interp.turboMode = false; stagePart.refresh(); diff --git a/src/Specs.as b/src/Specs.as index a61e002..4fbee8e 100644 --- a/src/Specs.as +++ b/src/Specs.as @@ -41,6 +41,7 @@ public class Specs { public static const CALL_C:String = "callc"; public static const PROCEDURE_DEF:String = "procDef"; public static const GET_PARAM:String = "getParam"; + public static const GET_STACK:String = "getStack"; public static const GET_LOOP:String = "getLoop"; public static const motionCategory:int = 1; @@ -153,7 +154,7 @@ public class Specs { ["y position", "r", 1, "ypos"], ["direction", "r", 1, "heading"], ["rotation style", "r", 1, "rotationStyle"], - + // stage motion ["EXPERIMENTAL (WIP)", "h", 101, "experimentfakeprim"], ["scroll right %n", " ", 101, "scrollRight", 10], @@ -285,6 +286,8 @@ public class Specs { ["wait until %b", " ", 6, "doWaitUntil"], ["repeat until %b", "c", 6, "doUntil"], ["-"], + ["run %f", " ", 6, "run:", "say(\"hi\")"], + ["-"], ["stop %m.stop", "f", 6, "stopScripts", "all"], ["-"], ["when I start as a clone", "h", 6, "whenCloned"], @@ -311,6 +314,8 @@ public class Specs { ["wait until %b", " ", 106, "doWaitUntil"], ["repeat until %b", "c", 106, "doUntil"], ["-"], + ["run %f", " ", 106, "run:", "say(\"hi\")"], + ["-"], ["stop %m.stop", "f", 106, "stopScripts", "all"], ["-"], ["create clone of %m.spriteOnly", " ", 106, "createCloneOf"], @@ -380,6 +385,7 @@ public class Specs { ["username", "r", 107, "getUserName"], // operators + // ["%k + %k", " ", 8, "teststack", null, null], ["%n + %n", "r", 8, "+", "", ""], ["%n - %n", "r", 8, "-", "", ""], ["%n * %n", "r", 8, "*", "", ""], @@ -468,7 +474,7 @@ public class Specs { ["item %d.listItem of ☁ list %s", "r", 32, "cloudGetItem"], ["length of ☁ list %s", "r", 32, "cloudLength"], ["☁ list %s contains %s", "b", 32, "cloudContains"], - + //System 13 ["save %s to file %s", " ", 13, "save:toFile:", "hello world", "file.txt"], ["-"], @@ -484,7 +490,7 @@ public class Specs { ["read line %n of url %s", "R", 13, "readLine:ofUrl:", "1", "http://www/google.com"], ["-"], ["internet connection?", "b", 13, "internetConnection"], - + //Program 15 ["%m.screenMode mode", " ", 15, ":mode", "fullscreen"], ["fullscreen mode?", "b", 15, "fullscreenMode"], @@ -501,7 +507,7 @@ public class Specs { ["-"], ["set max. clone count to %n", " ", 15, "setMaxCloneCount", "300"], ["max. clone count", "r", 15, "maxCloneCount"], - + //Dialogs 14 ["dialog notify with title %s and message %s", " ", 14, "dialogNotify", "Well Done!", "Congratulations, you won!"], ["dialog confirm with title %s and message %s", "b", 14, "dialogConfirm", "Are you sure?", "Are you sure you would like to continue?"], @@ -520,8 +526,8 @@ public class Specs { ["buttons %s %s %s %s", "r", 14, "customDialogButtons", "OK", "Cancel"], ["-"], ["close all dialogs", " ", 14, "customDialogCloseAll"], - - + + //Strings 16 ["join %s %s", "r", 16, "concatenate:with:", "hello ", "world"], ["letter %n of %s", "r", 16, "letter:of:", 1, "world"], @@ -538,7 +544,7 @@ public class Specs { ["-"], ["ascii for %s", "r", 16, "asciiFor:", "A"], ["ascii %n as string", "r", 16, "ascii:asString", "65"], - + //Websockets 17 ["connect to ip %s port %s", " ", 17, "websocketConnect", "127.0.0.0", "80"], ["disconnect", " ", 17, "websocketDisconnect"], @@ -546,7 +552,7 @@ public class Specs { ["-"], ["send %s", " ", 17, "websocketSend", ""], ["when I recieve %s", "h", 17, "websocketRecieve", ""], - + //Color 18 ["color at pixel x: %n y: %n", "r", 18, "colorAtPixel", 0, 0], ["color %c", "r", 18, "colorColorInput"], diff --git a/src/assets/BlobA.svg b/src/assets/BlobA.svg new file mode 100644 index 0000000..254bce9 --- /dev/null +++ b/src/assets/BlobA.svg @@ -0,0 +1,78 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/assets/BlobB.svg b/src/assets/BlobB.svg new file mode 100644 index 0000000..30b7d28 --- /dev/null +++ b/src/assets/BlobB.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/assets/BlobC.svg b/src/assets/BlobC.svg new file mode 100644 index 0000000..465b65c --- /dev/null +++ b/src/assets/BlobC.svg @@ -0,0 +1,73 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/assets/BlobD.svg b/src/assets/BlobD.svg new file mode 100644 index 0000000..5c158ec --- /dev/null +++ b/src/assets/BlobD.svg @@ -0,0 +1,73 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/assets/Resources.as b/src/assets/Resources.as index fc161a1..d412fac 100644 --- a/src/assets/Resources.as +++ b/src/assets/Resources.as @@ -31,7 +31,7 @@ public class Resources { } return new resourceClass(); } - + public static function createDO(resourceName:String):DisplayObject { var resourceClass:Class = Resources[resourceName]; if (!resourceClass) { @@ -78,6 +78,8 @@ public class Resources { [Embed(source='fonts/OCRAEXT.ttf', fontName='OCR', embedAsCFF = 'false', advancedAntiAliasing = 'true')] private static const Font8:Class; [Embed(source='fonts/Bubble-Bath.ttf', fontName='Bubbles', embedAsCFF = 'false', advancedAntiAliasing = 'true')] private static const Font9:Class; [Embed(source='fonts/Good-Dog.ttf', fontName='Good Dog', embedAsCFF = 'false', advancedAntiAliasing = 'true')] private static const Font10:Class; + [Embed(source='fonts/NotoSans-Regular.ttf', fontName='NotoSans', embedAsCFF = 'false', advancedAntiAliasing = 'true',fontWeight= "normal")] private static const Font11:Class; + [Embed(source='fonts/NotoSans-Bold.ttf', fontName='NotoSans', embedAsCFF = 'false', advancedAntiAliasing = 'true',fontWeight= "bold")] private static const Font12:Class; // Block Icons (2x resolution to look better when scaled) [Embed(source='blocks/flagIcon.png')] private static const flagIcon:Class; @@ -337,7 +339,7 @@ public class Resources { [Embed(source='UI/paint/bitmapStampOn.png')] private static const bitmapStampOn:Class; [Embed(source='UI/paint/bitmapTextOff.png')] private static const bitmapTextOff:Class; [Embed(source='UI/paint/bitmapTextOn.png')] private static const bitmapTextOn:Class; - + //Recording [Embed(source='StopArrow.png')] private static const stopArrow:Class; [Embed(source='VideoShare.svg')] private static const videoShare:Class; diff --git a/src/assets/fonts/NotoSans LICENSE.txt b/src/assets/fonts/NotoSans LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/src/assets/fonts/NotoSans LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/assets/fonts/NotoSans-Bold.ttf b/src/assets/fonts/NotoSans-Bold.ttf new file mode 100644 index 0000000..6e00cdc Binary files /dev/null and b/src/assets/fonts/NotoSans-Bold.ttf differ diff --git a/src/assets/fonts/NotoSans-BoldItalic.ttf b/src/assets/fonts/NotoSans-BoldItalic.ttf new file mode 100644 index 0000000..51b7b29 Binary files /dev/null and b/src/assets/fonts/NotoSans-BoldItalic.ttf differ diff --git a/src/assets/fonts/NotoSans-Italic.ttf b/src/assets/fonts/NotoSans-Italic.ttf new file mode 100644 index 0000000..dc93fea Binary files /dev/null and b/src/assets/fonts/NotoSans-Italic.ttf differ diff --git a/src/assets/fonts/NotoSans-Regular.ttf b/src/assets/fonts/NotoSans-Regular.ttf new file mode 100644 index 0000000..9dd1019 Binary files /dev/null and b/src/assets/fonts/NotoSans-Regular.ttf differ diff --git a/src/blocks/Block.as b/src/blocks/Block.as index 7d6f38b..5870b31 100644 --- a/src/blocks/Block.as +++ b/src/blocks/Block.as @@ -73,6 +73,7 @@ public class Block extends Sprite { public var args:Array = []; public var defaultArgValues:Array = []; public var parameterIndex:int = -1; // cache of parameter index, used by GET_PARAM block + public var stackIndex:int=-1; public var parameterNames:Array; // used by procedure definition hats; null for other blocks public var warpProcFlag:Boolean; // used by procedure definition hats to indicate warp speed public var procedureType:*; // used by procedure definition hats: " ","r", or "b" @@ -94,11 +95,12 @@ public class Block extends Sprite { public var nextBlock:Block; public var subStack1:Block; public var subStack2:Block; - + public var substacks:Array=[]; + public var stackCounter:TextField=new TextField(); public var base:BlockShape; private var suppressLayout:Boolean; // used to avoid extra layouts during block initialization - private var labelsAndArgs:Array = []; + public var labelsAndArgs:Array = []; private var argTypes:Array = []; private var elseLabel:TextField; @@ -115,6 +117,11 @@ public class Block extends Sprite { private var originalParent:DisplayObjectContainer, originalRole:int, originalIndex:int, originalPosition:Point; public function Block(spec:String, type:String = " ", color:int = 0xD00000, op:* = "", defaultArgs:Array = null) { + stackCounter.autoSize = TextFieldAutoSize.LEFT; + stackCounter.selectable = false; + stackCounter.background = false; + stackCounter.defaultTextFormat = CSS.normalTextFormat; + stackCounter.textColor = CSS.white; this.spec = Translator.map(spec); this.type = type; this.op = op; @@ -132,7 +139,13 @@ public class Block extends Sprite { var shape:int; if ((type == " ") || (type == "") || (type == "w")) { - base = new BlockShape(BlockShape.CmdShape, color); + //if(defaultArgs==null){ +base = new BlockShape(BlockShape.CmdShape, color); + //}else{ + //base = new BlockShape(BlockShape.CmdShape, color); + + //base.setCrazyShape(defaultArgs,BlockShape.CmdShape, color); + //} indentTop = 3; } else if (type == "b" || type == "ob") { base = new BlockShape(type == "ob" ? BlockShape.BooleanOutlineShape : BlockShape.BooleanShape, color); @@ -180,6 +193,35 @@ public class Block extends Sprite { } addChildAt(base, 0); setSpec(this.spec, defaultArgs); + if ((type == " ") || (type == "") || (type == "w")) { + if(labelsAndArgs.length<1){ + + }else{ + removeChild(base); + base = new BlockShape(BlockShape.CmdShape, color); + base.owner=this; + base.setCrazyShape(labelsAndArgs,BlockShape.CmdShape, color); + addChildAt(base, 0); + base.redraw(); + } + if ((type == "o ") || (type == "o")) { + if(labelsAndArgs.length<1){ + + }else{ + removeChild(base); + base = new BlockShape(BlockShape.CmdOutlineShape, color); + base.filters = []; // no bezel + base.owner=this; + base.setCrazyShape(labelsAndArgs,BlockShape.CmdOutlineShape, color); + addChildAt(base, 0); + base.redraw(); + } + } + //this.addChild(stackCounter); + stackCounter.x=0; + stackCounter.y=5; + } + addEventListener(FocusEvent.KEY_FOCUS_CHANGE, focusChange); } @@ -278,21 +320,85 @@ public class Block extends Sprite { argTextFormat = new TextFormat(font, argSize, 0x505050, false); Block.vOffset = vOffset; } - + public function updateSpecialStacks():void{ + //fixArgLayout(); + while(base.substacks.length>this.substacks.length){ + this.substacks[this.substacks.length]=null; + } + } private function declarationBlock():Block { // Create a block representing a procedure declaration to be embedded in a // procedure definition header block. For each formal parameter, embed a // reporter for that parameter. - var b:Block = new Block(spec, "o" + procedureType, Specs.procedureColor, 'proc_declaration'); + var b:Block = new Block(spec, "" + procedureType, Specs.procedureColor, 'proc_declaration'); + + + b.base.setCrazyShape(b.labelsAndArgs,(procedureType==''||procedureType==' ')?BlockShape.CmdOutlineShape:(procedureType=='r')?BlockShape.NumberOutlineShape:(procedureType=='b')?BlockShape.BooleanOutlineShape:BlockShape.CmdOutlineShape, Specs.procedureColor); + b.base.redraw(); if (!parameterNames) parameterNames = []; + var argNum:int=0; + var argsNoLabels:Array=[]; + for(var joe:int=0;joesNum){ + nameForStack=stackNames[sNum]; + } + b.addChild(b.substacks[sNum]=new Block(nameForStack, ' ', Specs.parameterColor, Specs.GET_STACK)); + b.substacks[sNum].stackIndex=sNum; + b.substacks[sNum].x=15; + sNum++; + } } b.fixArgLayout(); - if (procedureType == "c") b.insertBlockSub1(new Block('substack', '', Specs.procedureColor, Specs.GET_LOOP)); + //if (procedureType == "c") b.insertBlockSub1(new Block("stack", '', Specs.procedureColor, Specs.GET_LOOP)); return b; } @@ -305,7 +411,7 @@ public class Block extends Sprite { } public function isEmbeddedParameter():Boolean { - if (!(op == Specs.GET_PARAM || op == Specs.GET_LOOP) || !(parent is Block)) return false; + if (!(op == Specs.GET_PARAM || op == Specs.GET_LOOP|| op == Specs.GET_STACK) || !(parent is Block)) return false; return Block(parent).op == 'proc_declaration'; } @@ -509,6 +615,13 @@ public class Block extends Sprite { } public function fixArgLayout():void { + for(var jim:int =0;jim0) { + b.base.substacks=this.substacks; + this.fixArgLayout(); + if (base.substacks.length>0) { + + for(var jim:int =0;jim0) { + + for(var jim:int =0;jim-1){ + substacks[substacks.indexOf(b)]=null; + } if (b == nextBlock) { nextBlock = null; } @@ -747,6 +921,23 @@ public class Block extends Sprite { if (old != null) b.appendBlock(old); topBlock().fixStackLayout(); } + public function insertBlockSubSpecial(pos:int,b:Block):void { + var old:Block = substacks[pos]; + if (old != null) old.parent.removeChild(old); + var oldPar:*=null; + if (b != null)if (b.parent != null) oldPar=b.parent; + if (b != null)if (b.parent != null) b.parent.removeChild(b); + if(oldPar!=null){ + if((oldPar is Block)){ + oldPar.fixStackLayout(); + } + } + addChild(b); + substacks[pos] = b; + this.fixArgLayout(); + if (old != null) b.appendBlock(old); + topBlock().fixStackLayout(); + } public function insertBlockSub2(b:Block):void { var old:Block = subStack2; @@ -829,6 +1020,8 @@ public class Block extends Sprite { if (argSpec == "m") return new BlockArg("m", c, s.slice(3) == "var" || s.slice(3) == "list", s.slice(3)); if (argSpec == "n") return new BlockArg("n", c, true); if (argSpec == "s") return new BlockArg("s", c, true); + if (argSpec == "f") return new BlockArg("f", c, true); + if (argSpec == "k") return new BlockArg("k", c, true); if (argSpec == "q") return new MultiBlockArg("q", c); } else if (s.length >= 2 && s.charAt(0) == "@") { // icon spec var icon:* = Specs.IconNamed(s.slice(1)); diff --git a/src/blocks/BlockArg.as b/src/blocks/BlockArg.as index aa8d727..cb32428 100644 --- a/src/blocks/BlockArg.as +++ b/src/blocks/BlockArg.as @@ -111,6 +111,11 @@ public class BlockArg extends Sprite { argValue = 0; } else if (type == 's') { base = new BlockShape(BlockShape.RectShape, c); + }else if (type == 'f') { + base = new BlockShape(BlockShape.RectShape, c); + }else if (type == 'k') { + base = new BlockShape(BlockShape.CmdShape, c); + base.setWidthAndTopHeight(40, 30); } else { // custom type; subclass is responsible for adding // the desired children, setting width and height, @@ -139,7 +144,7 @@ public class BlockArg extends Sprite { addChild(menuIcon); } - if (editable || numberType || (type.charAt(0) == 'm')) { // add a string field + if ((editable || numberType || (type.charAt(0) == 'm') )&& !(type == 'f')) { // add a string field field = makeTextField(); if ((type == 'm') && !editable) field.textColor = 0xFFFFFF; else base.setWidthAndTopHeight(30, Block.argTextFormat.size + 5); // 14 for normal arg font @@ -152,6 +157,19 @@ public class BlockArg extends Sprite { field.addEventListener(FocusEvent.FOCUS_OUT, stopEditing); addChild(field); textChanged(null); + }else if(type == 'f'){ + field = makeTextArea(); + if ((type == 'm') && !editable) field.textColor = 0xFFFFFF; + else base.setWidthAndTopHeight(30, Block.argTextFormat.size + 5); // 14 for normal arg font + field.text = numberType ? '10' : ''; + if (numberType) field.restrict = '0-9e.\\-'; // restrict to numeric characters + if (editable) { + base.setColor(0xFFFFFF); // if editable, set color to white + isEditable = true; + } + field.addEventListener(FocusEvent.FOCUS_OUT, stopEditing); + addChild(field); + textChanged(null); } else { base.redraw(); } @@ -220,6 +238,19 @@ public class BlockArg extends Sprite { tf.addEventListener(Event.CHANGE, textChanged); return tf; } + protected function makeTextArea():TextField { + var tf:TextField = new TextField(); + var offsets:Array = argTextInsets(type); + tf.x = offsets[0]; + tf.y = offsets[1]; + tf.multiline = true; + //tf.wordWrap = true; + tf.autoSize = TextFieldAutoSize.LEFT; + tf.defaultTextFormat = Block.argTextFormat; + tf.selectable = false; + tf.addEventListener(Event.CHANGE, textChanged); + return tf; + } protected function argTextInsets(type:String = ''):Array { if (type == 'b') return [5, 0]; @@ -247,6 +278,9 @@ public class BlockArg extends Sprite { var w:int = Math.max(14, field.textWidth + 6 + padding); if (menuIcon) menuIcon.x = w - menuIcon.width - 3; base.setWidth(w); + if(type== 'f'){ + base.setWidthAndTopHeight(w,Math.max(Block.argTextFormat.size + 5, field.textHeight+5)); + } base.redraw(); if (parent is Block) Block(parent).fixExpressionLayout(); if (parent is MultiBlockArg) MultiBlockArg(parent).fixLayout(); @@ -283,4 +317,4 @@ public class BlockArg extends Sprite { } type = 'm.[' + newMenuItems.join(',') + ']'; } -}} \ No newline at end of file +}} diff --git a/src/blocks/BlockIO.as b/src/blocks/BlockIO.as index 24b63ea..bc132c3 100644 --- a/src/blocks/BlockIO.as +++ b/src/blocks/BlockIO.as @@ -64,12 +64,17 @@ public class BlockIO { } private static function blockToArray(b:Block):Array { + if(b==null){ + return null; + } // Return an array structure for this block. var result:Array = [b.op]; if (b.op == Specs.GET_VAR) return [Specs.GET_VAR, b.spec]; // variable reporter if (b.op == Specs.GET_LIST) return [Specs.GET_LIST, b.spec]; // list reporter if (b.op == Specs.GET_PARAM) return [Specs.GET_PARAM, b.spec, b.type]; // parameter reporter if (b.op == Specs.GET_LOOP) return [Specs.GET_LOOP, b.spec, b.type]; // inside of custom c block + if (b.op == Specs.GET_STACK) return [Specs.GET_STACK, b.spec, b.stackIndex]; // inside of custom c block + if (b.op == Specs.PROCEDURE_DEF) // procedure definition return [Specs.PROCEDURE_DEF, b.spec, b.parameterNames, b.defaultArgValues, b.warpProcFlag, b.procedureType]; if (b.op == Specs.CALL) { @@ -96,6 +101,18 @@ public class BlockIO { } if (b.base.canHaveSubstack1()) result.push(stackToArray(b.subStack1)); if (b.base.canHaveSubstack2()) result.push(stackToArray(b.subStack2)); + for(var jim:int =0;jim= 3) ? cmd[2] : 'r'; return new Block(cmd[1], paramType, Specs.parameterColor, Specs.GET_PARAM); + case Specs.GET_STACK: + var stackIx:int = (cmd.length >= 3) ? cmd[2] : 0; + b=new Block(cmd[1], '', Specs.parameterColor, Specs.GET_STACK); + b.stackIndex=stackIx; + return b; case Specs.GET_LOOP: return new Block(cmd[1], '', Specs.parameterColor, Specs.GET_LOOP); case 'changeVariable': diff --git a/src/blocks/BlockShape.as b/src/blocks/BlockShape.as index 65e9997..fa48a26 100644 --- a/src/blocks/BlockShape.as +++ b/src/blocks/BlockShape.as @@ -25,6 +25,7 @@ package blocks { import flash.display.*; import flash.filters.*; + import flash.text.*; public class BlockShape extends Shape { @@ -69,14 +70,30 @@ public class BlockShape extends Shape { public var topH:int; private var substack1H:int = EmptySubstackH; private var substack2H:int = EmptySubstackH; + public var substackHs:Array=[]; + public var substackYs:Array=[]; + public var substacks:Array=[]; private var drawFunction:Function = drawRectShape; private var redrawNeeded:Boolean = true; + public var args:Array=[]; + public var owner:Block=null; + public function BlockShape(shape:int = 1, color:int = 0xFFFFFF) { this.color = color; this.shape = shape; setShape(shape); filters = blockShapeFilters(); + + } + + + public function setCrazyShape(args:Array,shape:int = 1, color:int = 0xFFFFFF):void { + this.color = color; + this.shape = shape; + this.args=args; + setSpecialShape(shape,args); + //filters = blockShapeFilters(); } public function setWidthAndTopHeight(newW:int, newTopH:int, doRedraw:Boolean = false):void { @@ -163,7 +180,7 @@ public class BlockShape extends Shape { private function blockShapeFilters():Array { // filters for command and reporter Block outlines var f:BevelFilter = new BevelFilter(1); - f.blurX = f.blurY = 3; + f.blurX = f.blurY = 2; f.highlightAlpha = 0.3; f.shadowAlpha = 0.6; return [f]; @@ -260,6 +277,190 @@ public class BlockShape extends Shape { case ProcHatShape: drawFunction = drawProcHatShape; break; } } + protected function setSpecialShape(shape:int,args:Array):void { + this.shape = shape; + drawFunction = drawSpecialShape; + } + private function drawSpecialShape(g:Graphics):void { + if(owner!=null){ + this.substacks=owner.substacks; + } + var leftX:int = 0 + (shape == BlockShape.BooleanShape ? 14 : shape == BlockShape.LoopShape ? 10 : 6); + var nextX:int = 0+ (shape == BlockShape.BooleanShape ? 14 : shape == BlockShape.LoopShape ? 10 : 6); + var nextY:int = 0 + 5; + var maxH:int = 20; + var maxW:int=0; + var preWasStack:Boolean=false; + var rowHeights:Array=[20]; + var rowI:int=0; + var rowY:int=0; + substackYs=[]; + for each (var o:DisplayObject in this.args) { + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (!preWasStack)) { + + nextX=leftX+0; + //nextY+=24; + } + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (preWasStack)) { + //rowY=rowY+BottomBarH; + nextX=leftX+0; + //nextY+=24; + } + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + + nextX=leftX+0; + rowY=rowY+rowHeights[rowI]; + rowI++; + substackYs[substackYs.length]=rowY; + var sSH:int=EmptySubstackH; + if(substacks.length>=substackYs.length){ + + if(owner!=null){ + if(owner.substacks[substackYs.length-1]!=null){ + if(substacks[substackYs.length-1].parent!=owner){ + owner.substacks[substackYs.length-1]=null; + substacks[substackYs.length-1]=null; + } + } + + } + if(substacks[substackYs.length-1]!=null){ + if(owner!=null){ + owner.substacks[substackYs.length-1].y=substackYs[substackYs.length-1]; + owner.substacks[substackYs.length-1].x=15; + } + substacks[substackYs.length-1].y=substackYs[substackYs.length-1]; + + sSH=substacks[substackYs.length-1].height; + } + + }else{ + substacks[substackYs.length-1]=null; + } + substackHs[substackYs.length-1]=sSH; + rowHeights[rowI]=Math.max(sSH, EmptySubstackH); + rowY=rowY+rowHeights[rowI]; + rowI++; + rowHeights[rowI]=DividerH; + + //nextY+=24; + }else{ + rowHeights[rowI]=Math.max(o.height+6,rowHeights[rowI]); + } + //o.x = nextX; + + //o.y = nextY + int((20 - o.height) / 2) + ((o is TextField) ? 1 : 1); + nextX += o.width + 4; + maxW=Math.max(maxW, nextX); + maxH=Math.max(rowY, maxH); + if ((o is BlockArg) && (BlockArg(o).type == 's')) nextX -= 2; + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + if(o.parent!=null) o.parent.removeChild(o); + nextX=leftX+0; + nextY+=24; + preWasStack=true; + }else{ + preWasStack=false; + } + } + if (preWasStack) { + + maxH=maxH+6; + } + preWasStack=false; + leftX = 0 + (shape == BlockShape.BooleanShape ? maxH/2+5 : shape == BlockShape.LoopShape ? 10 : shape == BlockShape.CmdShape ?6:maxH/2); + nextX= leftX+0; + nextY= 0 + 5; + rowI=0; + rowY=0; + var fild:Boolean=false; + var heightTot:int=10; + this.w = Math.max(40, maxW +leftX); + if(this.shape==BlockShape.CmdOutlineShape){ + g.endFill(); // do not fill + g.lineStyle(2, 0xFFFFFF, 0.2); + } + drawTop(g); + + for each (var o:DisplayObject in this.args) { + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (!preWasStack)) { + + nextX=leftX+0; + //nextY+=24; + } + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (preWasStack)) { + //rowY=rowY+BottomBarH; + nextX=leftX+0; + //nextY+=24; + } + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + drawRightAndBottom(g, rowY+rowHeights[rowI], true, SubstackInset); + nextX=leftX+0; + rowY=rowY+rowHeights[rowI]; + + rowI++; + //rowHeights[rowI]=Math.max(h, EmptySubstackH); + o.y = rowY + int((rowHeights[rowI] - o.height) / 2) + ((o is TextField) ? 1 : 1); + + rowY=rowY+rowHeights[rowI]; + heightTot=rowY; + drawArm(g, rowY); + rowI++; + fild=false; + //rowHeights[rowI]=20; + + //nextY+=24; + }else{ + if(!fild){ + //g.drawRect(15, rowY, maxW-15, rowHeights[rowI]); + heightTot=rowY+rowHeights[rowI]; + fild=true; + } + o.y = rowY + int((rowHeights[rowI] - o.height) / 2) + ((o is TextField) ? 0 : 0); + if ((o is BlockArg) && (!BlockArg(o).numberType)) o.y += 1; + + //rowHeights[rowI]=Math.max(o.height+6,rowHeights[rowI]); + } + o.x = nextX; + if((o is BlockArg)){ + if((BlockArg(o).type == 'k')){ + + }else{ + nextX += o.width + 4; + } + }else{ + nextX += o.width + 4; + } + + if ((o is BlockArg) && (BlockArg(o).type == 's')) nextX -= 2; + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + preWasStack=true; + }else{ + preWasStack=false; + } + } + + var blockW:int = Math.max(40, maxW + leftX - 0-12); + //g.drawRect(0, 0, 15, heightTot+(preWasStack?24:0)); + if(preWasStack){ + drawRightAndBottom(g, heightTot + BottomBarH, true); + //g.drawRect(15, heightTot+12, maxW-15, 12); + }else{ + drawRightAndBottom(g, heightTot , true); + } + if(this.shape==BlockShape.CmdOutlineShape){ + g.lineTo(0, CornerInset); + } + if(this.shape==BlockShape.BooleanOutlineShape){ +g.clear(); +drawBooleanOutlineShape(g); + } + if(this.shape==BlockShape.NumberOutlineShape){ +g.clear(); +drawNumberOutlineShape(g); + } + + } private function drawRectShape(g:Graphics):void { g.drawRect(0, 0, w, topH) } @@ -434,4 +635,4 @@ public class BlockShape extends Shape { graphics.curveTo(cx, cy, p2x, p2y); } -}} \ No newline at end of file +}} diff --git a/src/com/greensock/BlitMask.as b/src/com/greensock/BlitMask.as new file mode 100644 index 0000000..b2501f4 --- /dev/null +++ b/src/com/greensock/BlitMask.as @@ -0,0 +1,892 @@ +/** + * VERSION: 0.62 + * DATE: 2013-07-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock { + import flash.display.BitmapData; + import flash.display.DisplayObject; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.geom.ColorTransform; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.geom.Transform; +/** + * [AS3 only] A BlitMask is basically a rectangular Sprite that acts as a high-performance mask for a DisplayObject + * by caching a bitmap version of it and blitting only the pixels that should be visible at any given time, + * although its bitmapMode can be turned off to restore interactivity in the DisplayObject + * whenever you want. When scrolling very large images or text blocks, a BlitMask can greatly improve + * performance, especially on mobile devices that have weaker processors.

+ * + * Here are some of the conveniences BlitMask offers:
+ *
    + *
  • Excellent scrolling performance
  • + *
  • You don't need to do anything special with your target DisplayObject - move/scale/rotate it + * however you please and then update() the BlitMask and it syncs the pixels. + * The BlitMask basically sits on top of the DisplayObject in the display list and you can + * move it independently too if you want.
  • + *
  • Use the BlitMask's scrollX and scrollY properties to move the + * target DisplayObject inside the masked area. For example, to scroll from top to bottom over + * the course of 2 seconds, simply do:
    myBlitMask.scrollY = 0;
    + * TweenLite.to(myBlitMask, 2, {scrollY:1});
  • + *
  • Use the "wrap" feature to make the bitmap wrap around to the opposite side when it scrolls + * off one of the edges (only in bitmapMode of course), as though the BlitMask is + * filled with a grid of bitmap copies of the target.
  • + *
  • For maximum performance in bitmapMode, set smoothing to false or + * for maximum quality, set it to true
  • + *
  • You can toggle the bitmapMode to get either maximum performance or interactivity + * in the target DisplayObject anytime. (some other solutions out there are only viable for + * non-interactive bitmap content)
  • + *
  • MouseEvents are dispatched by the BlitMask, so you can listen for clicks, rollovers, rollouts, etc.
  • + *
+ * + * @example Example AS3 code:+ import com.greensock.~~; + + //create a 200x200 BlitMask positioned at x:20, y:50 to mask our "mc" object and turn smoothing on: + var blitMask:BlitMask = new BlitMask(mc, 20, 50, 200, 200, true); + + //position mc at the top left of the BlitMask using the scrollX and scrollY properties + blitMask.scrollX = 0; + blitMask.scrollY = 0; + + //tween the scrollY to make mc scroll to the bottom over the course of 3 seconds and then turn off bitmapMode so that mc becomes interactive: + TweenLite.to(blitMask, 3, {scrollY:1, onComplete:blitMask.disableBitmapMode}); + + //or simply position mc manually and then call update() to sync the display: + mc.x = 350; + blitMask.update(); + + + * + * Notes: + *
    + *
  • BlitMasks themselves should not be rotated or scaled (although technically you can alter the scaleX and scaleY + * but doing so will only change the width or height instead). You can, of course, alter their x, y, width, + * or height properties as much as you want.
  • + *
  • BlitMasks don't perform nearly as well in bitmapMode when the target is being scaled or rotated + * because it forces a flushing and recapture of the internal bitmap. BlitMasks are MUCH better when you are + * simply changing x/y properties (scrolling) because it can reuse the same cached bitmap over and over.
  • + *
  • If the target content is changing frequently (like if it has nested MovieClips that are animating on every frame), + * you'd need to call update(null, true) each time you want the BlitMask to redraw itself to sync with the changes + * in the target, but that's a relatively expensive operation so it's not a great use case for BlitMask. You may + * be better off just turning off bitmapMode during that animation sequence.
  • + *


+ * + * Copyright 2014-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership. + * + * @author Jack Doyle, jack@greensock.com + **/ + public class BlitMask extends Sprite { + /** @private **/ + public static var version:Number = 0.62; + + // In order to conserve memory and improve performance, we create a few instances of Rectangles, Sprites, Points, Matrices, and Arrays and reuse them rather than creating new instances over and over. + /** @private **/ + protected static var _tempContainer:Sprite = new Sprite(); + /** @private **/ + protected static var _sliceRect:Rectangle = new Rectangle(); + /** @private **/ + protected static var _drawRect:Rectangle = new Rectangle(); + /** @private **/ + protected static var _destPoint:Point = new Point(); + /** @private **/ + protected static var _tempMatrix:Matrix = new Matrix(); + /** @private **/ + protected static var _emptyArray:Array = []; + /** @private **/ + protected static var _colorTransform:ColorTransform = new ColorTransform(); + /** @private **/ + protected static var _mouseEvents:Array = [MouseEvent.CLICK, MouseEvent.DOUBLE_CLICK, MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_OUT, MouseEvent.MOUSE_OVER, MouseEvent.MOUSE_UP, MouseEvent.MOUSE_WHEEL, MouseEvent.ROLL_OUT, MouseEvent.ROLL_OVER, "gesturePressAndTap", "gesturePan", "gestureRotate", "gestureSwipe", "gestureZoom", "gestureTwoFingerTap", "touchBegin", "touchEnd", "touchMove", "touchOut", "touchOver", "touchRollOut", "touchRollOver", "touchTap"]; + + /** @private **/ + protected var _target:DisplayObject; + /** @private **/ + protected var _fillColor:uint; + /** @private **/ + protected var _smoothing:Boolean; + /** @private **/ + protected var _width:Number; + /** @private **/ + protected var _height:Number; + /** @private **/ + protected var _bd:BitmapData; + /** @private maximum number of pixels (minus one) that each BitmapData cell in the grid can be **/ + protected var _gridSize:int = 2879; + /** @private **/ + protected var _grid:Array; + /** @private **/ + protected var _bounds:Rectangle; + /** @private **/ + protected var _clipRect:Rectangle; + /** @private **/ + protected var _bitmapMode:Boolean; + /** @private **/ + protected var _rows:int; + /** @private **/ + protected var _columns:int; + /** @private **/ + protected var _scaleX:Number; + /** @private **/ + protected var _scaleY:Number; + /** @private **/ + protected var _prevMatrix:Matrix; + /** @private **/ + protected var _transform:Transform; + /** @private **/ + protected var _prevRotation:Number; + /** @private **/ + protected var _autoUpdate:Boolean; + /** @private **/ + protected var _wrap:Boolean; + /** @private **/ + protected var _wrapOffsetX:Number = 0; + /** @private **/ + protected var _wrapOffsetY:Number = 0; + + /** + * Constructor + * + * @param target The DisplayObject that will be masked by the BlitMask + * @param x x coorinate of the upper left corner of the BlitMask. If smoothing is false, the x coordinate will be rounded to the closest integer. + * @param y y coordinate of the upper right corner of the BlitMask + * @param width width of the BlitMask (in pixels) + * @param height height of the BlitMask (in pixels) + * @param smoothing If false (the default), the bitmap (and the BlitMask's x/y coordinates) will be rendered only on whole pixels which is faster in terms of processing. However, for the best quality and smoothest animation, set smoothing to true. + * @param autoUpdate If true, the BlitMask will automatically watch the target to see if its position/scale/rotation has changed on each frame (while bitmapMode is true) and if so, it will update() to make sure the BlitMask always stays synced with the target. This is the easiest way to use BlitMask but it is slightly less efficient than manually calling update() whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set its onUpdate to the BlitMask's update() method to keep things synced. Like onUpdate:myBlitMask.update. + * @param fillColor The ARGB hexadecimal color that should fill the empty areas of the BlitMask. By default, it is transparent (0x00000000). If you wanted a red color, for example, it would be 0xFFFF0000. + * @param wrap If true, the bitmap will be wrapped around to the opposite side when it scrolls off one of the edges (only in bitmapMode of course), like the BlitMask is filled with a grid of bitmap copies of the target. Use the wrapOffsetX and wrapOffsetY properties to affect how far apart the copies are from each other. + */ + public function BlitMask(target:DisplayObject, x:Number=0, y:Number=0, width:Number=100, height:Number=100, smoothing:Boolean=false, autoUpdate:Boolean=false, fillColor:uint=0x00000000, wrap:Boolean=false) { + super(); + if (width < 0 || height < 0) { + throw new Error("A FlexBlitMask cannot have a negative width or height."); + } + _width = width; + _height = height; + _scaleX = _scaleY = 1; + _smoothing = smoothing; + _fillColor = fillColor; + _autoUpdate = autoUpdate; + _wrap = wrap; + _grid = []; + _bounds = new Rectangle(); + if (_smoothing) { + super.x = x; + super.y = y; + } else { + super.x = (x < 0) ? (x - 0.5) >> 0 : (x + 0.5) >> 0; + super.y = (y < 0) ? (y - 0.5) >> 0 : (y + 0.5) >> 0; + } + _clipRect = new Rectangle(0, 0, _gridSize + 1, _gridSize + 1); + _bd = new BitmapData(width + 1, height + 1, true, _fillColor); + _bitmapMode = true; + this.target = target; + } + + /** @private **/ + protected function _captureTargetBitmap():void { + if (_bd == null || _target == null) { //must have been disposed, so don't update. + return; + } + + _disposeGrid(); + + //capturing when the target is masked (or has a scrollRect) can cause problems. + var prevMask:DisplayObject = _target.mask; + if (prevMask != null) { + _target.mask = null; + } + var prevScrollRect:Rectangle = _target.scrollRect; + if (prevScrollRect != null) { + _target.scrollRect = null; + } + var prevFilters:Array = _target.filters; + if (prevFilters.length != 0) { + _target.filters = _emptyArray; + } + + _grid = []; + if (_target.parent == null) { + _tempContainer.addChild(_target); + } + _bounds = _target.getBounds(_target.parent); + var w:Number = 0; + var h:Number = 0; + _columns = Math.ceil(_bounds.width / _gridSize); + _rows = Math.ceil(_bounds.height / _gridSize); + var cumulativeHeight:Number = 0; + var matrix:Matrix = _transform.matrix; + var xOffset:Number = matrix.tx - _bounds.x; + var yOffset:Number = matrix.ty - _bounds.y; + if (!_smoothing) { + xOffset = (xOffset + 0.5) >> 0; + yOffset = (yOffset + 0.5) >> 0; + } + + var bd:BitmapData, cumulativeWidth:Number; + for (var row:int = 0; row < _rows; row++) { + h = (_bounds.height - cumulativeHeight > _gridSize) ? _gridSize : _bounds.height - cumulativeHeight; + matrix.ty = -cumulativeHeight + yOffset; + cumulativeWidth = 0; + _grid[row] = []; + for (var column:int = 0; column < _columns; column++) { + w = (_bounds.width - cumulativeWidth > _gridSize) ? _gridSize : _bounds.width - cumulativeWidth; + _grid[row][column] = bd = new BitmapData(w + 1, h + 1, true, _fillColor); + matrix.tx = -cumulativeWidth + xOffset; + bd.draw(_target, matrix, null, null, _clipRect, _smoothing); + cumulativeWidth += w; + } + cumulativeHeight += h; + } + + if (_target.parent == _tempContainer) { + _tempContainer.removeChild(_target); + } + + if (prevMask != null) { + _target.mask = prevMask; + } + if (prevScrollRect != null) { + _target.scrollRect = prevScrollRect; + } + if (prevFilters.length != 0) { + _target.filters = prevFilters; + } + } + + /** @private **/ + protected function _disposeGrid():void { + var i:int = _grid.length, j:int, r:Array; + while (--i > -1) { + r = _grid[i]; + j = r.length; + while (--j > -1) { + BitmapData(r[j]).dispose(); + } + } + } + + /** + * Updates the BlitMask's internal bitmap to reflect the target's current position/scale/rotation. + * This is a very important method that you'll need to call whenever visual or transformational changes are made + * to the target so that the BlitMask remains synced with it. + * + * @param event An optional Event object (which isn't used at all internally) in order to make it easier to use update() as an event handler. For example, you could addEventListener(Event.ENTER_FRAME, myBlitMask.update) to make sure it is updated on every frame (although it would be more efficient to simply set autoUpdate to true in this case). + * @param forceRecaptureBitmap Normally, the cached bitmap of the target is only recaptured if its scale or rotation changed because doing so is rather processor-intensive, but you can force a full update (and regeneration of the cached bitmap) by setting forceRecaptureBitmap to true. + */ + public function update(event:Event=null, forceRecaptureBitmap:Boolean=false):void { + if (_bd == null) { + return; + } else if (_target == null) { + _render(); + } else if (_target.parent) { + _bounds = _target.getBounds(_target.parent); + if (this.parent != _target.parent) { + _target.parent.addChildAt(this, _target.parent.getChildIndex(_target)); + } + } + if (_bitmapMode || forceRecaptureBitmap) { + var m:Matrix = _transform.matrix; + if (forceRecaptureBitmap || _prevMatrix == null || m.a != _prevMatrix.a || m.b != _prevMatrix.b || m.c != _prevMatrix.c || m.d != _prevMatrix.d) { + _captureTargetBitmap(); + _render(); + } else if (m.tx != _prevMatrix.tx || m.ty != _prevMatrix.ty) { + _render(); + } else if (_bitmapMode && _target != null) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + } + _prevMatrix = m; + } + } + + /** @private **/ + protected function _render(xOffset:Number=0, yOffset:Number=0, clear:Boolean=true, limitRecursion:Boolean=false):void { + //note: the code in this method was optimized for speed rather than readability or succinctness (since the whole point of this class is to help things perform better) + if (clear) { + _sliceRect.x = _sliceRect.y = 0; + _sliceRect.width = _width + 1; + _sliceRect.height = _height + 1; + _bd.fillRect(_sliceRect, _fillColor); + + if (_bitmapMode && _target != null) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + } else { + this.filters = _emptyArray; + this.transform.colorTransform = _colorTransform; + } + } + + if (_bd == null) { + return; + } else if (_rows == 0) { //sometimes (especially in Flex) objects take a frame or two to render in Flash and properly report their width/height. Before that, their width/height is typically 0. This works around that issue and forces a refresh if we didn't capture any pixels last time we did a capture. + _captureTargetBitmap(); + } + + var x:Number = super.x + xOffset; + var y:Number = super.y + yOffset; + + + var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0; + var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0; + var g:Graphics = this.graphics; + + if (_bounds.width == 0 || _bounds.height == 0 || (_wrap && (wrapWidth == 0 || wrapHeight == 0)) || (!_wrap && (x + _width < _bounds.x || y + _height < _bounds.y || x > _bounds.right || y > _bounds.bottom))) { + g.clear(); + g.beginBitmapFill(_bd); + g.drawRect(0, 0, _width, _height); + g.endFill(); + return; + } + + var column:int = int((x - _bounds.x) / _gridSize); + if (column < 0) { + column = 0; + } + var row:int = int((y - _bounds.y) / _gridSize); + if (row < 0) { + row = 0; + } + + var maxColumn:int = int(((x + _width) - _bounds.x) / _gridSize); + if (maxColumn >= _columns) { + maxColumn = _columns - 1; + } + var maxRow:uint = int(((y + _height) - _bounds.y) / _gridSize); + if (maxRow >= _rows) { + maxRow = _rows - 1; + } + + var xNudge:Number = (_bounds.x - x) % 1; + var yNudge:Number = (_bounds.y - y) % 1; + + if (y <= _bounds.y) { + _destPoint.y = (_bounds.y - y) >> 0; + _sliceRect.y = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + + } else { + _destPoint.y = 0; + _sliceRect.y = Math.ceil(y - _bounds.y) - (row * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + if (clear && yNudge != 0) { + yNudge += 1; + } + + } + if (x <= _bounds.x) { + _destPoint.x = (_bounds.x - x) >> 0; + _sliceRect.x = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + + } else { + _destPoint.x = 0; + _sliceRect.x = Math.ceil(x - _bounds.x) - (column * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + if (clear && xNudge != 0) { + xNudge += 1; + } + } + + if (_wrap && clear) { + //make sure to offset appropriately so that we start drawing directly on the image. We must use consistent xNudge and yNudge values across all the recursive calls too, otherwise the copies may vibrate visually a bit as they move + _render(Math.ceil((_bounds.x - x) / wrapWidth) * wrapWidth, Math.ceil((_bounds.y - y) / wrapHeight) * wrapHeight, false, false); + } else if (_rows != 0) { + var xDestReset:Number = _destPoint.x; + var xSliceReset:Number = _sliceRect.x; + var columnReset:int = column; + var bd:BitmapData; + while (row <= maxRow) { + bd = _grid[row][0]; + _sliceRect.height = bd.height - _sliceRect.y; + _destPoint.x = xDestReset; + _sliceRect.x = xSliceReset; + column = columnReset; + while (column <= maxColumn) { + bd = _grid[row][column]; + _sliceRect.width = bd.width - _sliceRect.x; + + _bd.copyPixels(bd, _sliceRect, _destPoint); + + _destPoint.x += _sliceRect.width - 1; + _sliceRect.x = 0; + column++; + } + _destPoint.y += _sliceRect.height - 1; + _sliceRect.y = 0; + row++; + } + + } + + if (clear) { + _tempMatrix.tx = xNudge - 1; //subtract 1 to compensate for the pixel we added above. + _tempMatrix.ty = yNudge - 1; + g.clear(); + g.beginBitmapFill(_bd, _tempMatrix, false, _smoothing); + g.drawRect(0, 0, _width, _height); + g.endFill(); + } else if (_wrap) { + //if needed, recursively call _render() and adjust the offset(s) to wrap the bitmap. + if (x + _width > _bounds.right) { + _render(xOffset - wrapWidth, yOffset, false, true); + } + if (!limitRecursion && y + _height > _bounds.bottom) { + _render(xOffset, yOffset - wrapHeight, false, false); + } + } + } + + /** + * Sets the width and height of the BlitMask. + * Keep in mind that a BlitMask should not be rotated or scaled. + * You can also directly set the width or height properties. + * + * @param width The width of the BlitMask + * @param height The height of the BlitMask + * @see #width + * @see #height + **/ + public function setSize(width:Number, height:Number):void { + if (_width == width && _height == height) { + return; + } else if (width < 0 || height < 0) { + throw new Error("A BlitMask cannot have a negative width or height."); + } else if (_bd != null) { + _bd.dispose(); + } + _width = width; + _height = height; + _bd = new BitmapData(width + 1, height + 1, true, _fillColor); + _render(); + } + + /** @private **/ + protected function _mouseEventPassthrough(event:Event):void { + if (this.mouseEnabled && (!_bitmapMode || (event is MouseEvent && this.hitTestPoint(MouseEvent(event).stageX, MouseEvent(event).stageY, false)))) { + dispatchEvent(event); + } + } + + /** + * Identical to setting bitmapMode = true but this method simplifies adding that + * functionality to tweens or using it as an event handler. For example, to enable bitmapMode at + * the beginning of a tween and then disable it when the tween completes, you could do:

+ * + * TweenLite.to(mc, 3, {x:400, onStart:myBlitMask.enableBitmapMode, onUpdate:myBlitMask.update, onComplete:myBlitMask.disableBitmapMode}); + * + * + * @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myBlitMask.enableBitmapMode). + * @see #disableBitmapMode() + * @see #bitmapMode + */ + public function enableBitmapMode(event:Event=null):void { + this.bitmapMode = true; + } + + /** + * Identical to setting bitmapMode = false but this method simplifies adding that + * functionality to tweens or using it as an event handler. For example, to enable bitmapMode at + * the beginning of a tween and then disable it when the tween completes, you could do:

+ * + * TweenLite.to(mc, 3, {x:400, onStart:myBlitMask.enableBitmapMode, onUpdate:myBlitMask.update, onComplete:myBlitMask.disableBitmapMode}); + * + * + * @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myBlitMask.disableBitmapMode). + * @see #enableBitmapMode() + * @see #bitmapMode + */ + public function disableBitmapMode(event:Event=null):void { + this.bitmapMode = false; + } + + /** + * Repositions the target so that it is visible within the BlitMask, as though wrap + * was enabled (this method is called automatically when bitmapMode is disabled while wrap + * is true). For example, if you tween the target way off the edge of the BlitMask and + * have wrap enabled, it will appear to come back in from the other side even though the raw coordinates + * of the target would indicate that it is outside the BlitMask. If you want to force the coordinates to normalize + * so that they reflect that wrapped position, simply call normalizePosition(). It will automatically + * choose the coordinates that would maximize the visible portion of the target if a seam is currently showing. + **/ + public function normalizePosition():void { + if (_target && _bounds) { + var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0; + var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0; + var offsetX:Number = (_bounds.x - this.x) % wrapWidth; + var offsetY:Number = (_bounds.y - this.y) % wrapHeight; + + if (offsetX > (_width + _wrapOffsetX) / 2) { + offsetX -= wrapWidth; + } else if (offsetX < (_width + _wrapOffsetX) / -2) { + offsetX += wrapWidth; + } + if (offsetY > (_height + _wrapOffsetY) / 2) { + offsetY -= wrapHeight; + } else if (offsetY < (_height + _wrapOffsetY) / -2) { + offsetY += wrapHeight; + } + + _target.x += this.x + offsetX - _bounds.x; + _target.y += this.y + offsetY - _bounds.y; + } + } + + /** Disposes of the BlitMask and its internal BitmapData instances, releasing them for garbage collection. **/ + public function dispose():void { + if (_bd == null) { //already disposed. + return; + } + _disposeGrid(); + _bd.dispose(); + _bd = null; + this.bitmapMode = false; + this.autoUpdate = false; + if (_target != null) { + _target.mask = null; + } + if (this.parent != null) { + this.parent.removeChild(this); + } + this.target = null; + } + +//---- GETTERS / SETTERS -------------------------------------------------------------------- + + /** + * When true, the BlitMask optimizes itself for performance by setting the target's + * visible property to false (greatly reducing the load on Flash's graphics rendering + * routines) and uses its internally cached bitmap version of the target to redraw only the necessary + * pixels inside the masked area. Since only a bitmap version of the target is shown while in bitmapMode, + * the target won't be interactive. So if you have buttons and other objects that normally react to + * MouseEvents, they won't while in bitmapMode. If you need the interactivity, simply set bitmapMode + * to false and then it will turn the target's visible property back to true + * and its mask property to the BlitMask itself. Typically it is best to turn bitmapMode on at least when you're + * animating the target or the BlitMask itself, and then when the tween/animation is done and you need + * interactivity, set bitmapMode back to false. For example:

+ * + * var bm:BlitMask = new BlitMask(mc, 0, 0, 300, 200, true);

+ * + * TweenLite.to(mc, 3, {x:200, onUpdate:bm.update, onComplete:completeHandler});

+ * + * function completeHandler():void {
+ * bm.bitmapMode = false;
+ * }
+ *


+ * + * @see #enableBitmapMode() + * @see #disableBitmapMode() + **/ + public function get bitmapMode():Boolean { + return _bitmapMode; + } + public function set bitmapMode(value:Boolean):void { + if (_bitmapMode != value) { + _bitmapMode = value; + if (_target != null) { + _target.visible = !_bitmapMode; + update(null); + if (_bitmapMode) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + this.blendMode = _target.blendMode; + _target.mask = null; + } else { + this.filters = _emptyArray; + this.transform.colorTransform = _colorTransform; + this.blendMode = "normal"; + this.cacheAsBitmap = false; //if cacheAsBitmap is true on both the _target and the FlexBlitMask instance, the transparent areas of the mask will be...well...transparent which isn't what we want when bitmapMode is false (it could hide visible areas unless update(null, true) is called regularly, like if the target has animated children and bitmapMode is false) + _target.mask = this; + if (_wrap) { + normalizePosition(); + } + } + if (_bitmapMode && _autoUpdate) { + this.addEventListener(Event.ENTER_FRAME, update, false, -10, true); + } else { + this.removeEventListener(Event.ENTER_FRAME, update); + } + } + } + } + + /** + * If true, the BlitMask will automatically watch the target to see if + * its position/scale/rotation has changed on each frame (while bitmapMode is true) + * and if so, it will update() to make sure the BlitMask always stays synced with the target. + * This is the easiest way to use BlitMask but it is slightly less efficient than manually calling update() + * whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set + * its onUpdate to the BlitMask's update() method to keep things synced. + * Like onUpdate:myBlitMask.update. + **/ + public function get autoUpdate():Boolean { + return _autoUpdate; + } + public function set autoUpdate(value:Boolean):void { + if (_autoUpdate != value) { + _autoUpdate = value; + if (_bitmapMode && _autoUpdate) { + this.addEventListener(Event.ENTER_FRAME, update, false, -10, true); + } else { + this.removeEventListener(Event.ENTER_FRAME, update); + } + } + } + + /** The target DisplayObject that the BlitMask should mask **/ + public function get target():DisplayObject { + return _target; + } + public function set target(value:DisplayObject):void { + if (_target != value) { + var i:int = _mouseEvents.length; + if (_target != null) { + while (--i > -1) { + _target.removeEventListener(_mouseEvents[i], _mouseEventPassthrough); + } + } + _target = value; + if (_target != null) { + i = _mouseEvents.length; + while (--i > -1) { + _target.addEventListener(_mouseEvents[i], _mouseEventPassthrough, false, 0, true); + } + _prevMatrix = null; + _transform = _target.transform; + _bitmapMode = !_bitmapMode; + this.bitmapMode = !_bitmapMode; //forces a refresh (applying the mask, doing an update(), etc.) + } else { + _bounds = new Rectangle(); + } + } + } + + /** x coordinate of the BlitMask (it will automatically be forced to whole pixel values if smoothing is false). **/ + override public function get x():Number { + return super.x; + } + override public function set x(value:Number):void { + if (_smoothing) { + super.x = value; + } else if (value >= 0) { + super.x = (value + 0.5) >> 0; + } else { + super.x = (value - 0.5) >> 0; + } + if (_bitmapMode) { + _render(); + } + } + + /** y coordinate of the BlitMask (it will automatically be forced to whole pixel values if smoothing is false). **/ + override public function get y():Number { + return super.y; + } + override public function set y(value:Number):void { + if (_smoothing) { + super.y = value; + } else if (value >= 0) { + super.y = (value + 0.5) >> 0; + } else { + super.y = (value - 0.5) >> 0; + } + if (_bitmapMode) { + _render(); + } + } + + /** Width of the BlitMask **/ + override public function get width():Number { + return _width; + } + override public function set width(value:Number):void { + setSize(value, _height); + } + + /** Height of the BlitMask **/ + override public function get height():Number { + return _height; + } + override public function set height(value:Number):void { + setSize(_width, value); + } + + /** scaleX (warning: altering the scaleX won't actually change its value - instead, it affects the width property accordingly) **/ + override public function get scaleX():Number { + return 1; + } + override public function set scaleX(value:Number):void { + var oldScaleX:Number = _scaleX; + _scaleX = value; + setSize(_width * (_scaleX / oldScaleX), _height); + } + + /** scaleY (warning: altering the scaleY won't actually change its value - instead, it affects the height property accordingly) **/ + override public function get scaleY():Number { + return 1; + } + override public function set scaleY(value:Number):void { + var oldScaleY:Number = _scaleY; + _scaleY = value; + setSize(_width, _height * (_scaleY / oldScaleY)); + } + + /** Rotation of the BlitMask (always 0 because BlitMasks can't be rotated!) **/ + override public function set rotation(value:Number):void { + if (value != 0) { + throw new Error("Cannot set the rotation of a BlitMask to a non-zero number. BlitMasks should remain unrotated."); + } + } + + /** + * Typically a value between 0 and 1 indicating the target's position in relation to the BlitMask + * on the x-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled + * all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end + * over 5 seconds, you could do:

+ * + * myBlitMask.scrollX = 0;
+ * TweenLite.to(myBlitMask, 5, {scrollX:1}); + *
+ * @see #scrollY + **/ + public function get scrollX():Number { + return (super.x - _bounds.x) / (_bounds.width - _width); + } + public function set scrollX(value:Number):void { + if (_target != null && _target.parent) { + _bounds = _target.getBounds(_target.parent); + var dif:Number; + dif = (super.x - (_bounds.width - _width) * value) - _bounds.x; + _target.x += dif; + _bounds.x += dif; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * Typically a value between 0 and 1 indicating the target's position in relation to the BlitMask + * on the y-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled + * all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end + * over 5 seconds, you could do:

+ * + * myBlitMask.scrollY = 0;
+ * TweenLite.to(myBlitMask, 5, {scrollY:1}); + *
+ * @see #scrollX + **/ + public function get scrollY():Number { + return (super.y - _bounds.y) / (_bounds.height - _height); + } + public function set scrollY(value:Number):void { + if (_target != null && _target.parent) { + _bounds = _target.getBounds(_target.parent); + var dif:Number = (super.y - (_bounds.height - _height) * value) - _bounds.y; + _target.y += dif; + _bounds.y += dif; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * If false (the default), the bitmap (and the BlitMask's x/y coordinates) + * will be rendered only on whole pixels which is faster in terms of processing. However, + * for the best quality and smoothest animation, set smoothing to true. + **/ + public function get smoothing():Boolean { + return _smoothing; + } + public function set smoothing(value:Boolean):void { + if (_smoothing != value) { + _smoothing = value; + _captureTargetBitmap(); + if (_bitmapMode) { + _render(); + } + } + } + + /** + * The ARGB hexadecimal color that should fill the empty areas of the BlitMask. By default, + * it is transparent (0x00000000). If you wanted a red color, for example, it would be + * 0xFFFF0000. + **/ + public function get fillColor():uint { + return _fillColor; + } + public function set fillColor(value:uint):void { + if (_fillColor != value) { + _fillColor = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * If true, the bitmap will be wrapped around to the opposite side when it scrolls off + * one of the edges (only in bitmapMode of course), like the BlitMask is filled with a + * grid of bitmap copies of the target. Use the wrapOffsetX and wrapOffsetY + * properties to affect how far apart the copies are from each other. You can reposition the + * target anywhere and BlitMask will align the copies accordingly. + * @see #wrapOffsetX + * @see #wrapOffsetY + **/ + public function get wrap():Boolean { + return _wrap; + } + public function set wrap(value:Boolean):void { + if (_wrap != value) { + _wrap = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * When wrap is true, wrapOffsetX controls how many pixels + * along the x-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between + * the copies (although you can use a negative value or 0 to avoid any gap). + * @see #wrap + * @see #wrapOffsetY + **/ + public function get wrapOffsetX():Number { + return _wrapOffsetX; + } + public function set wrapOffsetX(value:Number):void { + if (_wrapOffsetX != value) { + _wrapOffsetX = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * When wrap is true, wrapOffsetY controls how many pixels + * along the y-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between + * the copies (although you can use a negative value or 0 to avoid any gap). + * @see #wrap + * @see #wrapOffsetX + **/ + public function get wrapOffsetY():Number { + return _wrapOffsetY; + } + public function set wrapOffsetY(value:Number):void { + if (_wrapOffsetY != value) { + _wrapOffsetY = value; + if (_bitmapMode) { + _render(); + } + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/FlexBlitMask.as b/src/com/greensock/FlexBlitMask.as new file mode 100644 index 0000000..848fb8c --- /dev/null +++ b/src/com/greensock/FlexBlitMask.as @@ -0,0 +1,925 @@ +/** + * VERSION: 0.61 + * DATE: 2012-10-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock { + import flash.display.BitmapData; + import flash.display.DisplayObject; + import flash.display.Graphics; + import flash.display.BlendMode; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.geom.ColorTransform; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.geom.Transform; + + import mx.core.UIComponent; +/** + * [AS3 only] A FlexBlitMask is basically a rectangular UIComponent that acts as a high-performance mask for a DisplayObject + * by caching a bitmap version of it and blitting only the pixels that should be visible at any given time, + * although its bitmapMode can be turned off to restore interactivity in the DisplayObject + * whenever you want. It is a Flex-friendly version of BlitMask. When scrolling very large images or text + * blocks, a FlexBlitMask can greatly improve performance, especially on mobile devices that have weaker + * processors.

+ * + * Here are some of the conveniences FlexBlitMask offers:
+ *
    + *
  • Excellent scrolling performance
  • + *
  • You don't need to do anything special with your target DisplayObject - move/scale/rotate it + * however you please and then update() the FlexBlitMask and it syncs the pixels. + * The FlexBlitMask basically sits on top of the DisplayObject in the display list and you can + * move it independently too if you want.
  • + *
  • Use the FlexBlitMask's scrollX and scrollY properties to move the + * target DisplayObject inside the masked area. For example, to scroll from top to bottom over + * the course of 2 seconds, simply do:
    myFlexBlitMask.scrollY = 0;
    + * TweenLite.to(myFlexBlitMask, 2, {scrollY:1});
  • + *
  • Use the "wrap" feature to make the bitmap wrap around to the opposite side when it scrolls + * off one of the edges (only in bitmapMode of course), as though the FlexBlitMask is + * filled with a grid of bitmap copies of the target.
  • + *
  • For maximum performance in bitmapMode, set smoothing to false or + * for maximum quality, set it to true
  • + *
  • You can toggle the bitmapMode to get either maximum performance or interactivity + * in the target DisplayObject anytime. (some other solutions out there are only viable for + * non-interactive bitmap content)
  • + *
  • MouseEvents are dispatched by the FlexBlitMask, so you can listen for clicks, rollovers, rollouts, etc.
  • + *
+ * + * @example Example AS3 code:+<?xml version="1.0" encoding="utf-8"?> +<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" frameRate="60" layout="absolute" xmlns:greensock="com.greensock.~~" creationComplete="tween()"> + <mx:Script> + <![CDATA[ + import com.greensock.TweenLite; + private function tween():void { + myText.y = 50; + TweenLite.to(myText, 6, {y:-100}); + } + ]]> + </mx:Script> + <greensock:FlexBlitMask id="blitMask" target="{myText}" wrap="false" x="20" y="50" width="300" height="200" smoothing="true" /> + <mx:Label text="FlexBlitMask Example" fontSize="24" /> + <mx:Text id="myText" x="20" y="50" width="135" height="500" text="FlexBlitMask can be great for high-performance scrolling. Performance is of paramount importance in mobile apps. There is, of course, a trade-off in memory because an extra bitmap version of the target needs to be captured/maintained, but overall performance while scrolling can be significantly improved. It doesn't make sense to use FlexBlitMask if you're tweening the scale or rotation of the target, though - it is primarily for scrolling (tweening the x and/or y properties)." /> + <mx:Button id="tweenButton" label="Tween" x="20" y="260" click="tween()" /> +</mx:Application> + + * + * Notes: + *
    + *
  • FlexBlitMasks themselves should not be rotated or scaled (although technically you can alter the scaleX and scaleY + * but doing so will only change the width or height instead). You can, of course, alter their x, y, width, + * or height properties as much as you want.
  • + *
  • FlexBlitMasks don't perform nearly as well in bitmapMode when the target is being scaled or rotated + * because it forces a flushing and recapture of the internal bitmap. FlexBlitMasks are MUCH better when you are + * simply changing x/y properties (scrolling) because it can reuse the same cached bitmap over and over.
  • + *
  • If the target content is changing frequently (like if it has nested MovieClips that are animating on every frame), + * you'd need to call update(null, true) each time you want the FlexBlitMask to redraw itself to sync with the changes + * in the target, but that's a relatively expensive operation so it's not a great use case for FlexBlitMask. You may + * be better off just turning off bitmapMode during that animation sequence.
  • + *


+ * + * Copyright 2014-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership. + * + * @author Jack Doyle, jack@greensock.com + **/ + public class FlexBlitMask extends UIComponent { + /** @private **/ + public static var version:Number = 0.6; + + // In order to conserve memory and improve performance, we create a few instances of Rectangles, Sprites, Points, Matrices, and Arrays and reuse them rather than creating new instances over and over. + /** @private **/ + protected static var _tempContainer:Sprite = new Sprite(); + /** @private **/ + protected static var _sliceRect:Rectangle = new Rectangle(); + /** @private **/ + protected static var _drawRect:Rectangle = new Rectangle(); + /** @private **/ + protected static var _destPoint:Point = new Point(); + /** @private **/ + protected static var _tempMatrix:Matrix = new Matrix(); + /** @private **/ + protected static var _emptyArray:Array = []; + /** @private **/ + protected static var _colorTransform:ColorTransform = new ColorTransform(); + /** @private **/ + protected static var _mouseEvents:Array = [MouseEvent.CLICK, MouseEvent.DOUBLE_CLICK, MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_OUT, MouseEvent.MOUSE_OVER, MouseEvent.MOUSE_UP, MouseEvent.MOUSE_WHEEL, MouseEvent.ROLL_OUT, MouseEvent.ROLL_OVER, "gesturePressAndTap", "gesturePan", "gestureRotate", "gestureSwipe", "gestureZoom", "gestureTwoFingerTap", "touchBegin", "touchEnd", "touchMove", "touchOut", "touchOver", "touchRollOut", "touchRollOver", "touchTap"]; + + /** @private **/ + protected var _target:DisplayObject; + /** @private **/ + protected var _fillColor:uint; + /** @private **/ + protected var _smoothing:Boolean; + /** @private **/ + protected var _width:Number; + /** @private **/ + protected var _height:Number; + /** @private **/ + protected var _bd:BitmapData; + /** @private maximum number of pixels (minus one) that each BitmapData cell in the grid can be **/ + protected var _gridSize:int = 2879; + /** @private **/ + protected var _grid:Array; + /** @private **/ + protected var _bounds:Rectangle; + /** @private **/ + protected var _clipRect:Rectangle; + /** @private **/ + protected var _bitmapMode:Boolean; + /** @private **/ + protected var _rows:int; + /** @private **/ + protected var _columns:int; + /** @private **/ + protected var _scaleX:Number; + /** @private **/ + protected var _scaleY:Number; + /** @private **/ + protected var _prevMatrix:Matrix; + /** @private **/ + protected var _transform:Transform; + /** @private **/ + protected var _prevRotation:Number; + /** @private **/ + protected var _autoUpdate:Boolean; + /** @private **/ + protected var _wrap:Boolean; + /** @private **/ + protected var _wrapOffsetX:Number = 0; + /** @private **/ + protected var _wrapOffsetY:Number = 0; + + /** + * Constructor + * + * @param target The DisplayObject that will be masked by the FlexBlitMask + * @param x x coorinate of the upper left corner of the FlexBlitMask. If smoothing is false, the x coordinate will be rounded to the closest integer. + * @param y y coordinate of the upper right corner of the FlexBlitMask + * @param width width of the FlexBlitMask (in pixels) + * @param height height of the FlexBlitMask (in pixels) + * @param smoothing If false (the default), the bitmap (and the FlexBlitMask's x/y coordinates) will be rendered only on whole pixels which is faster in terms of processing. However, for the best quality and smoothest animation, set smoothing to true. + * @param autoUpdate If true (the default), the FlexBlitMask will automatically watch the target to see if its position/scale/rotation has changed on each frame (while bitmapMode is true) and if so, it will update() to make sure the FlexBlitMask always stays synced with the target. This is the easiest way to use FlexBlitMask but it is slightly less efficient than manually calling update() whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set its onUpdate to the FlexBlitMask's update() method to keep things synced. Like onUpdate:myFlexBlitMask.update. + * @param fillColor The ARGB hexadecimal color that should fill the empty areas of the FlexBlitMask. By default, it is transparent (0x00000000). If you wanted a red color, for example, it would be 0xFFFF0000. + * @param wrap If true, the bitmap will be wrapped around to the opposite side when it scrolls off one of the edges (only in bitmapMode of course), like the FlexBlitMask is filled with a grid of bitmap copies of the target. Use the wrapOffsetX and wrapOffsetY properties to affect how far apart the copies are from each other. + */ + public function FlexBlitMask(target:DisplayObject=null, x:Number=0, y:Number=0, width:Number=100, height:Number=100, smoothing:Boolean=false, autoUpdate:Boolean=true, fillColor:uint=0x00000000, wrap:Boolean=false) { + super(); + if (width < 0 || height < 0) { + throw new Error("A FlexBlitMask cannot have a negative width or height."); + } + _width = width; + _height = height; + _scaleX = _scaleY = 1; + _smoothing = smoothing; + _fillColor = fillColor; + _autoUpdate = autoUpdate; + _wrap = wrap; + _grid = []; + _bounds = new Rectangle(); + if (_smoothing) { + super.x = x; + super.y = y; + } else { + super.x = (x < 0) ? (x - 0.5) >> 0 : (x + 0.5) >> 0; + super.y = (y < 0) ? (y - 0.5) >> 0 : (y + 0.5) >> 0; + } + _clipRect = new Rectangle(0, 0, _gridSize + 1, _gridSize + 1); + _bd = new BitmapData(width + 1, height + 1, true, _fillColor); + _bitmapMode = true; + this.target = target; + } + + /** @private **/ + protected function _captureTargetBitmap():void { + if (_bd == null || _target == null) { //must have been disposed, so don't update. + return; + } + + _disposeGrid(); + + //capturing when the target is masked (or has a scrollRect) can cause problems. + var prevMask:DisplayObject = _target.mask; + if (prevMask != null) { + _target.mask = null; + } + var prevScrollRect:Rectangle = _target.scrollRect; + if (prevScrollRect != null) { + _target.scrollRect = null; + } + var prevFilters:Array = _target.filters; + if (prevFilters.length != 0) { + _target.filters = _emptyArray; + } + + _grid = []; + if (_target.parent == null) { + _tempContainer.addChild(_target); + } + _bounds = _target.getBounds(_target.parent); + var w:Number = 0; + var h:Number = 0; + _columns = Math.ceil(_bounds.width / _gridSize); + _rows = Math.ceil(_bounds.height / _gridSize); + var cumulativeHeight:Number = 0; + var matrix:Matrix = _transform.matrix; + var xOffset:Number = matrix.tx - _bounds.x; + var yOffset:Number = matrix.ty - _bounds.y; + if (!_smoothing) { + xOffset = (xOffset + 0.5) >> 0; + yOffset = (yOffset + 0.5) >> 0; + } + + var bd:BitmapData, cumulativeWidth:Number; + for (var row:int = 0; row < _rows; row++) { + h = (_bounds.height - cumulativeHeight > _gridSize) ? _gridSize : _bounds.height - cumulativeHeight; + matrix.ty = -cumulativeHeight + yOffset; + cumulativeWidth = 0; + _grid[row] = []; + for (var column:int = 0; column < _columns; column++) { + w = (_bounds.width - cumulativeWidth > _gridSize) ? _gridSize : _bounds.width - cumulativeWidth; + _grid[row][column] = bd = new BitmapData(w + 1, h + 1, true, _fillColor); + matrix.tx = -cumulativeWidth + xOffset; + bd.draw(_target, matrix, null, null, _clipRect, _smoothing); + cumulativeWidth += w; + } + cumulativeHeight += h; + } + + if (_target.parent == _tempContainer) { + _tempContainer.removeChild(_target); + } + + if (prevMask != null) { + _target.mask = prevMask; + } + if (prevScrollRect != null) { + _target.scrollRect = prevScrollRect; + } + if (prevFilters.length != 0) { + _target.filters = prevFilters; + } + } + + /** @private **/ + protected function _disposeGrid():void { + var i:int = _grid.length, j:int, r:Array; + while (--i > -1) { + r = _grid[i]; + j = r.length; + while (--j > -1) { + BitmapData(r[j]).dispose(); + } + } + } + + /** + * Updates the FlexBlitMask's internal bitmap to reflect the target's current position/scale/rotation. + * This is a very important method that you'll need to call whenever visual or transformational changes are made + * to the target so that the FlexBlitMask remains synced with it. + * + * @param event An optional Event object (which isn't used at all internally) in order to make it easier to use update() as an event handler. For example, you could addEventListener(Event.ENTER_FRAME, myFlexBlitMask.update) to make sure it is updated on every frame (although it would be more efficient to simply set autoUpdate to true in this case). + * @param forceRecaptureBitmap Normally, the cached bitmap of the target is only recaptured if its scale or rotation changed because doing so is rather processor-intensive, but you can force a full update (and regeneration of the cached bitmap) by setting forceRecaptureBitmap to true. + */ + public function update(event:Event=null, forceRecaptureBitmap:Boolean=false):void { + if (_bd == null) { + return; + } else if (_target == null) { + _render(); + } else if (_target.parent) { + _bounds = _target.getBounds(_target.parent); + if (this.parent != _target.parent) { + if (_target.parent.hasOwnProperty("addElementAt")) { //for Flex compatibility (spark) + Object(_target.parent).addElementAt(this, Object(_target.parent).getChildIndex(_target)); + } else { + _target.parent.addChildAt(this, _target.parent.getChildIndex(_target)); + } + } + } + if (_bitmapMode || forceRecaptureBitmap) { + var m:Matrix = _transform.matrix; + if (forceRecaptureBitmap || _prevMatrix == null || m.a != _prevMatrix.a || m.b != _prevMatrix.b || m.c != _prevMatrix.c || m.d != _prevMatrix.d) { + _captureTargetBitmap(); + _render(); + } else if (m.tx != _prevMatrix.tx || m.ty != _prevMatrix.ty) { + _render(); + } else if (_bitmapMode && _target != null) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + } + _prevMatrix = m; + } + } + + /** @private **/ + protected function _render(xOffset:Number=0, yOffset:Number=0, clear:Boolean=true, limitRecursion:Boolean=false):void { + //note: the code in this method was optimized for speed rather than readability or succinctness (since the whole point of this class is to help things perform better) + if (clear) { + _sliceRect.x = _sliceRect.y = 0; + _sliceRect.width = _width + 1; + _sliceRect.height = _height + 1; + _bd.fillRect(_sliceRect, _fillColor); + + if (_bitmapMode && _target != null) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + } else { + this.filters = _emptyArray; + this.transform.colorTransform = _colorTransform; + } + } + + if (_bd == null) { + return; + } else if (_rows == 0) { //sometimes (especially in Flex) objects take a frame or two to render in Flash and properly report their width/height. Before that, their width/height is typically 0. This works around that issue and forces a refresh if we didn't capture any pixels last time we did a capture. + _captureTargetBitmap(); + } + + var x:Number = super.x + xOffset; + var y:Number = super.y + yOffset; + var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0; + var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0; + var g:Graphics = this.graphics; + + if (_bounds.width == 0 || _bounds.height == 0 || (_wrap && (wrapWidth == 0 || wrapHeight == 0)) || (!_wrap && (x + _width < _bounds.x || y + _height < _bounds.y || x > _bounds.right || y > _bounds.bottom))) { + g.clear(); + g.beginBitmapFill(_bd); + g.drawRect(0, 0, _width, _height); + g.endFill(); + return; + } + + var column:int = int((x - _bounds.x) / _gridSize); + if (column < 0) { + column = 0; + } + var row:int = int((y - _bounds.y) / _gridSize); + if (row < 0) { + row = 0; + } + + var maxColumn:int = int(((x + _width) - _bounds.x) / _gridSize); + if (maxColumn >= _columns) { + maxColumn = _columns - 1; + } + var maxRow:uint = int(((y + _height) - _bounds.y) / _gridSize); + if (maxRow >= _rows) { + maxRow = _rows - 1; + } + + var xNudge:Number = (_bounds.x - x) % 1; + var yNudge:Number = (_bounds.y - y) % 1; + + if (y <= _bounds.y) { + _destPoint.y = (_bounds.y - y) >> 0; + _sliceRect.y = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + + } else { + _destPoint.y = 0; + _sliceRect.y = Math.ceil(y - _bounds.y) - (row * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + if (clear && yNudge != 0) { + yNudge += 1; + } + + } + if (x <= _bounds.x) { + _destPoint.x = (_bounds.x - x) >> 0; + _sliceRect.x = -1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + + } else { + _destPoint.x = 0; + _sliceRect.x = Math.ceil(x - _bounds.x) - (column * _gridSize) - 1; //subtract 1 to make sure the whole image gets included - without this, a very slight vibration can occur on the edge during animation. + if (clear && xNudge != 0) { + xNudge += 1; + } + } + + if (_wrap && clear) { + //make sure to offset appropriately so that we start drawing directly on the image. We must use consistent xNudge and yNudge values across all the recursive calls too, otherwise the copies may vibrate visually a bit as they move + _render(Math.ceil((_bounds.x - x) / wrapWidth) * wrapWidth, Math.ceil((_bounds.y - y) / wrapHeight) * wrapHeight, false, false); + } else if (_rows != 0) { + var xDestReset:Number = _destPoint.x; + var xSliceReset:Number = _sliceRect.x; + var columnReset:int = column; + var bd:BitmapData; + while (row <= maxRow) { + bd = _grid[row][0]; + _sliceRect.height = bd.height - _sliceRect.y; + _destPoint.x = xDestReset; + _sliceRect.x = xSliceReset; + column = columnReset; + while (column <= maxColumn) { + bd = _grid[row][column]; + _sliceRect.width = bd.width - _sliceRect.x; + + _bd.copyPixels(bd, _sliceRect, _destPoint); + + _destPoint.x += _sliceRect.width - 1; + _sliceRect.x = 0; + column++; + } + _destPoint.y += _sliceRect.height - 1; + _sliceRect.y = 0; + row++; + } + + } + + if (clear) { + _tempMatrix.tx = xNudge - 1; //subtract 1 to compensate for the pixel we added above. + _tempMatrix.ty = yNudge - 1; + g.clear(); + g.beginBitmapFill(_bd, _tempMatrix, false, _smoothing); + g.drawRect(0, 0, _width, _height); + g.endFill(); + } else if (_wrap) { + //if needed, recursively call _render() and adjust the offset(s) to wrap the bitmap. + if (x + _width > _bounds.right) { + _render(xOffset - wrapWidth, yOffset, false, true); + } + if (!limitRecursion && y + _height > _bounds.bottom) { + _render(xOffset, yOffset - wrapHeight, false, false); + } + } + } + + /** + * Sets the width and height of the FlexBlitMask. + * Keep in mind that a FlexBlitMask should not be rotated or scaled. + * You can also directly set the width or height properties. + * + * @param width The width of the FlexBlitMask + * @param height The height of the FlexBlitMask + * @see #width + * @see #height + **/ + public function setSize(width:Number, height:Number):void { + if (_width == width && _height == height) { + return; + } else if (width < 0 || height < 0) { + throw new Error("A FlexBlitMask cannot have a negative width or height."); + } else if (_bd != null) { + _bd.dispose(); + } + _width = width; + _height = height; + _bd = new BitmapData(width + 1, height + 1, true, _fillColor); + _render(); + } + + /** @private **/ + protected function _mouseEventPassthrough(event:MouseEvent):void { + if (this.mouseEnabled && (!_bitmapMode || this.hitTestPoint(event.stageX, event.stageY, false))) { + dispatchEvent(event); + } + } + + /** + * Identical to setting bitmapMode = true but this method simplifies adding that + * functionality to tweens or using it as an event handler. For example, to enable bitmapMode at + * the beginning of a tween and then disable it when the tween completes, you could do:

+ * + * TweenLite.to(mc, 3, {x:400, onStart:myFlexBlitMask.enableBitmapMode, onUpdate:myFlexBlitMask.update, onComplete:myFlexBlitMask.disableBitmapMode}); + * + * + * @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myFlexBlitMask.enableBitmapMode). + * @see #disableBitmapMode() + * @see #bitmapMode + */ + public function enableBitmapMode(event:Event=null):void { + this.bitmapMode = true; + } + + /** + * Identical to setting bitmapMode = false but this method simplifies adding that + * functionality to tweens or using it as an event handler. For example, to enable bitmapMode at + * the beginning of a tween and then disable it when the tween completes, you could do:

+ * + * TweenLite.to(mc, 3, {x:400, onStart:myFlexBlitMask.enableBitmapMode, onUpdate:myFlexBlitMask.update, onComplete:myFlexBlitMask.disableBitmapMode}); + * + * + * @param event An optional Event that isn't used internally but makes it possible to use the method as an event handler like addEventListener(MouseEvent.CLICK, myFlexBlitMask.disableBitmapMode). + * @see #enableBitmapMode() + * @see #bitmapMode + */ + public function disableBitmapMode(event:Event=null):void { + this.bitmapMode = false; + } + + /** + * Repositions the target so that it is visible within the BlitMask, as though wrap + * was enabled (this method is called automatically when bitmapMode is disabled while wrap + * is true). For example, if you tween the target way off the edge of the BlitMask and + * have wrap enabled, it will appear to come back in from the other side even though the raw coordinates + * of the target would indicate that it is outside the BlitMask. If you want to force the coordinates to normalize + * so that they reflect that wrapped position, simply call normalizePosition(). It will automatically + * choose the coordinates that would maximize the visible portion of the target if a seam is currently showing. + **/ + public function normalizePosition():void { + if (_target && _bounds) { + var wrapWidth:int = (_bounds.width + _wrapOffsetX + 0.5) >> 0; + var wrapHeight:int = (_bounds.height + _wrapOffsetY + 0.5) >> 0; + var offsetX:Number = (_bounds.x - this.x) % wrapWidth; + var offsetY:Number = (_bounds.y - this.y) % wrapHeight; + + if (offsetX > (_width + _wrapOffsetX) / 2) { + offsetX -= wrapWidth; + } else if (offsetX < (_width + _wrapOffsetX) / -2) { + offsetX += wrapWidth; + } + if (offsetY > (_height + _wrapOffsetY) / 2) { + offsetY -= wrapHeight; + } else if (offsetY < (_height + _wrapOffsetY) / -2) { + offsetY += wrapHeight; + } + + _target.x += this.x + offsetX - _bounds.x; + _target.y += this.y + offsetY - _bounds.y; + } + } + + /** Disposes of the FlexBlitMask and its internal BitmapData instances, releasing them for garbage collection. **/ + public function dispose():void { + if (_bd == null) { //already disposed. + return; + } + _disposeGrid(); + _bd.dispose(); + _bd = null; + this.bitmapMode = false; + this.autoUpdate = false; + if (_target != null) { + _target.mask = null; + } + if (this.parent != null) { + if (this.parent.hasOwnProperty("removeElement")) { + Object(this.parent).removeElement(this); //for Flex compatibility (spark) + } else { + this.parent.removeChild(this); + } + } + this.target = null; + } + + /** @private **/ + override protected function measure():void { + if (this.parent) { + var bounds:Rectangle = this.getBounds(this.parent); + super.width = bounds.width; + super.height = bounds.height; + } + this.explicitWidth = _width; + this.explicitHeight = _height; + super.measure(); + } + + /** @inheritDoc **/ + override public function setActualSize(w:Number, h:Number):void { + setSize(w, h); + super.setActualSize(w, h); + } + + +//---- GETTERS / SETTERS -------------------------------------------------------------------- + + /** + * When true, the FlexBlitMask optimizes itself for performance by setting the target's + * visible property to false (greatly reducing the load on Flash's graphics rendering + * routines) and uses its internally cached bitmap version of the target to redraw only the necessary + * pixels inside the masked area. Since only a bitmap version of the target is shown while in bitmapMode, + * the target won't be interactive. So if you have buttons and other objects that normally react to + * MouseEvents, they won't while in bitmapMode. If you need the interactivity, simply set bitmapMode + * to false and then it will turn the target's visible property back to true + * and its mask property to the FlexBlitMask itself. Typically it is best to turn bitmapMode on at least when you're + * animating the target or the FlexBlitMask itself, and then when the tween/animation is done and you need + * interactivity, set bitmapMode back to false. For example:

+ * + * var bm:FlexBlitMask = new FlexBlitMask(mc, 0, 0, 300, 200, true);

+ * + * TweenLite.to(mc, 3, {x:200, onUpdate:bm.update, onComplete:completeHandler});

+ * + * function completeHandler():void {
+ * bm.bitmapMode = false;
+ * }
+ *


+ * + * @see #enableBitmapMode() + * @see #disableBitmapMode() + **/ + public function get bitmapMode():Boolean { + return _bitmapMode; + } + public function set bitmapMode(value:Boolean):void { + if (_bitmapMode != value) { + _bitmapMode = value; + if (_target != null) { + _target.visible = !_bitmapMode; + update(null); + if (_bitmapMode) { + this.filters = _target.filters; + this.transform.colorTransform = _transform.colorTransform; + if (_target.blendMode == "auto") { + this.blendMode = (_target.alpha == 0 || _target.alpha == 1) ? BlendMode.NORMAL : BlendMode.LAYER; + } else { + this.blendMode = _target.blendMode; + } + _target.mask = null; + } else { + this.filters = _emptyArray; + this.transform.colorTransform = _colorTransform; + this.blendMode = "normal"; + this.cacheAsBitmap = false; //if cacheAsBitmap is true on both the _target and the FlexBlitMask instance, the transparent areas of the mask will be...well...transparent which isn't what we want when bitmapMode is false (it could hide visible areas unless update(null, true) is called regularly, like if the target has animated children and bitmapMode is false) + _target.mask = this; + if (_wrap) { + normalizePosition(); + } + } + if (_bitmapMode && _autoUpdate) { + this.addEventListener(Event.ENTER_FRAME, update, false, -10, true); + } else { + this.removeEventListener(Event.ENTER_FRAME, update); + } + } + } + } + + /** + * If true, the FlexBlitMask will automatically watch the target to see if + * its position/scale/rotation has changed on each frame (while bitmapMode is true) + * and if so, it will update() to make sure the FlexBlitMask always stays synced with the target. + * This is the easiest way to use FlexBlitMask but it is slightly less efficient than manually calling update() + * whenever you need to. Keep in mind that if you're tweening with TweenLite or TweenMax, you can simply set + * its onUpdate to the FlexBlitMask's update() method to keep things synced. + * Like onUpdate:myFlexBlitMask.update. + **/ + public function get autoUpdate():Boolean { + return _autoUpdate; + } + public function set autoUpdate(value:Boolean):void { + if (_autoUpdate != value) { + _autoUpdate = value; + if (_bitmapMode && _autoUpdate) { + this.addEventListener(Event.ENTER_FRAME, update, false, -10, true); + } else { + this.removeEventListener(Event.ENTER_FRAME, update); + } + } + } + + /** The target DisplayObject that the FlexBlitMask should mask **/ + public function get target():DisplayObject { + return _target; + } + public function set target(value:DisplayObject):void { + if (_target != value) { + var i:int = _mouseEvents.length; + if (_target != null) { + while (--i > -1) { + _target.removeEventListener(_mouseEvents[i], _mouseEventPassthrough); + } + } + _target = value; + if (_target != null) { + i = _mouseEvents.length; + while (--i > -1) { + _target.addEventListener(_mouseEvents[i], _mouseEventPassthrough, false, 0, true); + } + _prevMatrix = null; + _transform = _target.transform; + _bitmapMode = !_bitmapMode; + this.bitmapMode = !_bitmapMode; //forces a refresh (applying the mask, doing an update(), etc.) + } else { + _bounds = new Rectangle(); + } + } + } + + /** x coordinate of the FlexBlitMask (it will automatically be forced to whole pixel values if smoothing is false). **/ + override public function get x():Number { + return super.x; + } + override public function set x(value:Number):void { + if (_smoothing) { + super.x = value; + } else if (value >= 0) { + super.x = (value + 0.5) >> 0; + } else { + super.x = (value - 0.5) >> 0; + } + if (_bitmapMode) { + _render(); + } + } + + /** y coordinate of the FlexBlitMask (it will automatically be forced to whole pixel values if smoothing is false). **/ + override public function get y():Number { + return super.y; + } + override public function set y(value:Number):void { + if (_smoothing) { + super.y = value; + } else if (value >= 0) { + super.y = (value + 0.5) >> 0; + } else { + super.y = (value - 0.5) >> 0; + } + if (_bitmapMode) { + _render(); + } + } + + /** Width of the FlexBlitMask **/ + override public function get width():Number { + return _width; + } + override public function set width(value:Number):void { + setSize(value, _height); + } + + /** Height of the FlexBlitMask **/ + override public function get height():Number { + return _height; + } + override public function set height(value:Number):void { + setSize(_width, value); + } + + /** scaleX (warning: altering the scaleX won't actually change its value - instead, it affects the width property accordingly) **/ + override public function get scaleX():Number { + return 1; + } + override public function set scaleX(value:Number):void { + var oldScaleX:Number = _scaleX; + _scaleX = value; + setSize(_width * (_scaleX / oldScaleX), _height); + } + + /** scaleY (warning: altering the scaleY won't actually change its value - instead, it affects the height property accordingly) **/ + override public function get scaleY():Number { + return 1; + } + override public function set scaleY(value:Number):void { + var oldScaleY:Number = _scaleY; + _scaleY = value; + setSize(_width, _height * (_scaleY / oldScaleY)); + } + + /** Rotation of the FlexBlitMask (always 0 because FlexBlitMasks can't be rotated!) **/ + override public function set rotation(value:Number):void { + if (value != 0) { + throw new Error("Cannot set the rotation of a FlexBlitMask to a non-zero number. FlexBlitMasks should remain unrotated."); + } + } + + /** + * Typically a value between 0 and 1 indicating the target's position in relation to the FlexBlitMask + * on the x-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled + * all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end + * over 5 seconds, you could do:

+ * + * myFlexBlitMask.scrollX = 0;
+ * TweenLite.to(myFlexBlitMask, 5, {scrollX:1}); + *
+ * @see #scrollY + **/ + public function get scrollX():Number { + return (super.x - _bounds.x) / (_bounds.width - _width); + } + public function set scrollX(value:Number):void { + if (_target != null && _target.parent) { + _bounds = _target.getBounds(_target.parent); + var dif:Number; + dif = (super.x - (_bounds.width - _width) * value) - _bounds.x; + _target.x += dif; + _bounds.x += dif; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * Typically a value between 0 and 1 indicating the target's position in relation to the FlexBlitMask + * on the y-axis where 0 is at the beginning, 0.5 is scrolled to exactly the halfway point, and 1 is scrolled + * all the way. This makes it very easy to animate the scroll. For example, to scroll from beginning to end + * over 5 seconds, you could do:

+ * + * myFlexBlitMask.scrollY = 0;
+ * TweenLite.to(myFlexBlitMask, 5, {scrollY:1}); + *
+ * @see #scrollX + **/ + public function get scrollY():Number { + return (super.y - _bounds.y) / (_bounds.height - _height); + } + public function set scrollY(value:Number):void { + if (_target != null && _target.parent) { + _bounds = _target.getBounds(_target.parent); + var dif:Number = (super.y - (_bounds.height - _height) * value) - _bounds.y; + _target.y += dif; + _bounds.y += dif; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * If false (the default), the bitmap (and the FlexBlitMask's x/y coordinates) + * will be rendered only on whole pixels which is faster in terms of processing. However, + * for the best quality and smoothest animation, set smoothing to true. + **/ + public function get smoothing():Boolean { + return _smoothing; + } + public function set smoothing(value:Boolean):void { + if (_smoothing != value) { + _smoothing = value; + _captureTargetBitmap(); + if (_bitmapMode) { + _render(); + } + } + } + + /** + * The ARGB hexadecimal color that should fill the empty areas of the FlexBlitMask. By default, + * it is transparent (0x00000000). If you wanted a red color, for example, it would be + * 0xFFFF0000. + **/ + public function get fillColor():uint { + return _fillColor; + } + public function set fillColor(value:uint):void { + if (_fillColor != value) { + _fillColor = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * If true, the bitmap will be wrapped around to the opposite side when it scrolls off + * one of the edges (only in bitmapMode of course), like the FlexBlitMask is filled with a + * grid of bitmap copies of the target. Use the wrapOffsetX and wrapOffsetY + * properties to affect how far apart the copies are from each other. You can reposition the + * target anywhere and FlexBlitMask will align the copies accordingly. + * @see #wrapOffsetX + * @see #wrapOffsetY + **/ + public function get wrap():Boolean { + return _wrap; + } + public function set wrap(value:Boolean):void { + if (_wrap != value) { + _wrap = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * When wrap is true, wrapOffsetX controls how many pixels + * along the x-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between + * the copies (although you can use a negative value or 0 to avoid any gap). + * @see #wrap + * @see #wrapOffsetY + **/ + public function get wrapOffsetX():Number { + return _wrapOffsetX; + } + public function set wrapOffsetX(value:Number):void { + if (_wrapOffsetX != value) { + _wrapOffsetX = value; + if (_bitmapMode) { + _render(); + } + } + } + + /** + * When wrap is true, wrapOffsetY controls how many pixels + * along the y-axis the wrapped copies of the bitmap are spaced. It is essentially the gap between + * the copies (although you can use a negative value or 0 to avoid any gap). + * @see #wrap + * @see #wrapOffsetX + **/ + public function get wrapOffsetY():Number { + return _wrapOffsetY; + } + public function set wrapOffsetY(value:Number):void { + if (_wrapOffsetY != value) { + _wrapOffsetY = value; + if (_bitmapMode) { + _render(); + } + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/TimelineLite.as b/src/com/greensock/TimelineLite.as new file mode 100644 index 0000000..78a85ed --- /dev/null +++ b/src/com/greensock/TimelineLite.as @@ -0,0 +1,1982 @@ +/** + * VERSION: 12.1.5 + * DATE: 2014-07-19 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com/timelinelite/ + **/ +package com.greensock { + import com.greensock.TweenLite; + import com.greensock.core.Animation; + import com.greensock.core.SimpleTimeline; +/** + * TimelineLite is a powerful sequencing tool that acts as a container for tweens and + * other timelines, making it simple to control them as a whole and precisely manage their + * timing in relation to each other. Without TimelineLite (or its big brother TimelineMax), building + * complex sequences would be far more cumbersome because you'd need to use the delay special property + * for everything which would make future edits far more tedious. Here is a basic example of a + * sequence without using TimelineLite (the tedious way): + * +TweenLite.to(mc, 1, {x:100}); +TweenLite.to(mc, 1, {y:50, delay:1}); +TweenLite.to(mc, 1, {alpha:0, delay:2}); + + * The above code animates mc.x to 100, then mc.y to 50, and finally + * mc.alpha to 0 (notice the delay in all but the first tween). But + * imagine if you wanted to increase the duration of the first tween to 1.5 - you'd need to + * adjust every delay thereafter. And what if you want to pause() the whole + * sequence or restart() it or reverse() it on-the-fly or jump to + * a specific point in the whole animation? This becomes quite messy (or flat-out impossible), + * but TimelineLite makes it incredibly simple: + * + * +var tl = new TimelineLite(); +tl.add( TweenLite.to(mc, 1, {x:100}) ); +tl.add( TweenLite.to(mc, 1, {y:50}) ); +tl.add( TweenLite.to(mc, 1, {alpha:0}) ); + +//then later, control the whole thing... +tl.pause(); +tl.resume(); +tl.seek(1.5); +tl.reverse(); +... + + * Or use the convenient to() method and chaining to make it even more concise: + * +var tl = new TimelineLite(); +tl.to(mc, 1, {x:100}).to(mc, 1, {y:50}).to(mc, 1, {alpha:0}); + + * + *

Now you can adjust any of the tweens without worrying about trickle-down + * changes to delays. Increase the duration of that first tween and everything automatically + * adjusts!

+ * + *

Here are some other benefits and features of TimelineLite:

+ * + *
    + *
  • Things can overlap on the timeline as much as you want. You have complete control + * over where tweens/timelines are placed. Most other animation tools can only do basic + * one-after-the-other sequencing but can't allow things to overlap. Imagine appending + * a tween that moves an object and you want it to start fading out 0.5 seconds before the + * end of that tween? With TimelineLite it's easy.
  • + * + *
  • Add labels, play(), stop(), seek(), restart(), and even reverse() smoothly anytime.
  • + * + *
  • Nest timelines within timelines as deeply as you want. This means you can modularize + * your code and make it far more efficient. Imagine building your app with common animateIn() + * and animateOut() methods that return a tween or timeline instance, then you can string + * things together like + * myTimeline.add( myObject.animateIn() ).add( myObject.animateOut(), "+=4").add( myObject2.animateIn(), "-=0.5")...
  • + * + *
  • Speed up or slow down the entire timeline with its timeScale() method. + * You can even tween it to gradually speed up or slow down the animation smoothly.
  • + * + *
  • Get or set the progress of the timeline using its progress() method. + * For example, to skip to the halfway point, set myTimeline.progress(0.5);
  • + * + *
  • Tween the time or progress to fastforward/rewind + * the timeline. You could even attach a slider to one of these properties to give the + * user the ability to drag forward/backward through the timeline.
  • + * + *
  • Add onComplete, onStart, onUpdate, and/or onReverseComplete + * callbacks using the constructor's vars object like + * var tl = new TimelineLite({onComplete:myFunction});
  • + * + *
  • Kill the tweens of a particular object inside the timeline with kill(null, target) + * or get the tweens of an object with getTweensOf() or get all the tweens/timelines + * in the timeline with getChildren()
  • + * + *
  • By passing useFrames:true in the vars parameter, you can + * base the timing on frames instead of seconds. Please note, however, that + * the timeline's timing mode dictates its childrens' timing mode as well.
  • + * + *
  • You can export all the tween/timelines from the root (master) timeline anytime into + * a TimelineLite instance using TimelineLite.exportRoot() so that + * you can pause() them all or reverse() or alter their + * timeScale, etc. without affecting tweens/timelines that you create in + * the future. Imagine a game that has all its animation driven by the GreenSock + * Animation Platform and it needs to pause or slow down while a status screen pops up. + * Very easy.
  • + * + *
  • If you need even more features like repeat, repeatDelay, yoyo, currentLabel(), + * getLabelAfter(), getLabelBefore(), addCallback(), removeCallback(), getActive(), + * AS3 event listeners and more, check out TimelineMax which extends TimelineLite.
  • + *
+ * + * + *

SPECIAL PROPERTIES:

+ *

You can optionally use the constructor's vars parameter to define any of + * the special properties below (syntax example: new TimelineLite({onComplete:myFunction, delay:2});

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the timeline should begin.
  • + * + *
  • paused : Boolean - + * If true, the timeline will pause itself immediately upon creation (by default, + * timelines automatically begin playing immediately). If you plan to create a TimelineLite and + * then populate it later (after one or more frames elapse), it is typically best to set + * paused:true and then play() after you populate it.
  • + * + *
  • onComplete : Function - + * A function that should be called when the timeline has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * new TimelineLite({onComplete:myFunction, onCompleteParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onCompleteParams:["{self}", "param2"]
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the timelines's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • tweens : Array - + * To immediately insert several tweens into the timeline, use the tweens + * special property to pass in an Array of TweenLite/TweenMax/TimelineLite/TimelineMax + * instances. You can use this in conjunction with the align and + * stagger special properties to set up complex sequences with minimal code. + * These values simply get passed to the add() method.
  • + * + *
  • align : String - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. The value simply gets passed to the + * add() method. The default is "normal". + * Options are: + *
      + *
    • "sequence": aligns the tweens one-after-the-other in a sequence
    • + *
    • "start": aligns the start times of all of the tweens (ignores delays)
    • + *
    • "normal": aligns the start times of all the tweens (honors delays)
    • + *
    + * The align special property does not force all child + * tweens/timelines to maintain relative positioning, so for example, if you use + * "sequence" and then later change the duration of one of the nested tweens, + * it does not force all subsequent timelines to change their position. + * The align special property only affects the alignment of the tweens that are + * initially placed into the timeline through the tweens special property of + * the vars object.
  • + * + *
  • stagger : Number - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be added immediately. It staggers the tweens by a set amount of time + * in seconds (or in frames if useFrames is true). For example, if the + * stagger value is 0.5 and the "align" property is set to "start", the + * second tween will start 0.5 seconds after the first one starts, then 0.5 seconds + * later the third one will start, etc. If the align property is "sequence", + * there would be 0.5 seconds added between each tween. This value simply gets + * passed to the add() method. Default is 0.
  • + * + *
  • onStart : Function - + * A function that should be called when the timeline begins (when its time + * changes from 0 to some other value which can happen more than once if the + * timeline is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * new TimelineLite({onStart:myFunction, onStartParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onStartParams:["{self}", "param2"]
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the timeline updates + * (on every frame while the timeline is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * new TimelineLite({onUpdate:myFunction, onUpdateParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onUpdateParams:["{self}", "param2"]
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the timeline has reached its beginning again from the + * reverse direction. For example, if reverse() is called, the timeline will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the timeline is placed in a TimelineLite or TimelineMax + * instance that gets reversed and plays the timeline backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * new TimelineLite({onReverseComplete:myFunction, onReverseCompleteParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onReverseCompleteParams:["{self}", "param2"]
  • + * + *
  • autoRemoveChildren : Boolean - + * If autoRemoveChildren is set to true, as soon as child + * tweens/timelines complete, they will automatically get killed/removed. This is normally + * undesireable because it prevents going backwards in time (like if you want to + * reverse() or set the progress lower, etc.). It can, however, + * improve speed and memory management. The root timelines use autoRemoveChildren:true.
  • + * + *
  • smoothChildTiming : Boolean - + * Controls whether or not child tweens/timelines are repositioned automatically + * (changing their startTime) in order to maintain smooth playback when + * properties are changed on-the-fly. For example, imagine that the timeline's playhead is + * on a child tween that is 75% complete, moving mc.x from 0 to 100 and then that tween's + * reverse() method is called. If smoothChildTiming is false + * (the default except for the root timelines), the tween would flip in place, keeping its + * startTime consistent. Therefore the playhead of the timeline would now be + * at the tween's 25% completion point instead of 75%. Remember, the timeline's playhead + * position and direction are unaffected by child tween/timeline changes. mc.x would jump + * from 75 to 25, but the tween's position in the timeline would remain consistent. However, + * if smoothChildTiming is true, that child tween's + * startTime would be adjusted so that the timeline's playhead intersects + * with the same spot on the tween (75% complete) as it had immediately before + * reverse() was called, thus playback appears perfectly smooth. mc.x + * would still be 75 and it would continue from there as the playhead moves on, but + * since the tween is reversed now mc.x will travel back towards 0 instead of 100. + * Ultimately it's a decision between prioritizing smooth on-the-fly playback + * (true) or consistent position(s) of child tweens/timelines + * (false). + * + * Some examples of on-the-fly changes to child tweens/timelines that could cause their + * startTime to change when smoothChildTiming is true + * are: reversed, timeScale, progress, totalProgress, time, totalTime, delay, pause, + * resume, duration, and totalDuration.
  • + * + *
+ * + * Sample code:+//create the timeline with an onComplete callback that calls myFunction() when the timeline completes +var tl = new TimelineLite({onComplete:myFunction}); + +//add a tween +tl.add( new TweenLite(mc, 1, {x:200, y:100}) ); + +//add another tween at the end of the timeline (makes sequencing easy) +tl.add( new TweenLite(mc, 0.5, {alpha:0}) ); + +//append a tween using the convenience method (shorter syntax) and offset it by 0.5 seconds +tl.to(mc, 1, {rotation:30}, "+=0.5"); + +//reverse anytime +tl.reverse(); + +//Add a "spin" label 3-seconds into the timeline +tl.add("spin", 3); + +//insert a rotation tween at the "spin" label (you could also define the insertion point as the time instead of a label) +tl.add( new TweenLite(mc, 2, {rotation:"360"}), "spin"); + +//go to the "spin" label and play the timeline from there +tl.play("spin"); + +//nest another TimelineLite inside your timeline... +var nested = new TimelineLite(); +nested.to(mc2, 1, {x:200})); +tl.add(nested); + + * + *

How do timelines work? What are the mechanics like?

+ *

Every animation (tween and timeline) is placed on a parent timeline (except the 2 root timelines - there's one for normal tweens and another for "useFrames" ones). + * In a sense, they all have their own playheads (that's what its "time" refers to, or "totalTime" which is identical except that it includes repeats and repeatDelays) + * but generally they're not independent because they're sitting on a timeline whose playhead moves. + * When the parent's playhead moves to a new position, it updates the childrens' too.

+ * + *

When a timeline renders at a particular time, it loops through its children and says "okay, you should render as if your playhead is at ____" and if that child + * is a timeline with children, it does the same to its children, right on down the line.

+ * + *

The only exception is when the tween/timeline is paused in which case its internal playhead acts like it's "locked". So in that case, + * it's possible (likely in fact) that the child's playhead would not be synced with the parent's. + * When you unpause it (resume()), it essentially picks it up and moves it so that its internal playhead + * is synchronized with wherever the parent's playhead is at that moment, thus things play perfectly smoothly. + * That is, unless the timeline's smoothChildTiming is to false in which case it won't move - + * its startTime will remain locked to where it was.

+ * + *

So basically, when smoothChildTiming is true, the engine will rearrange things on + * the fly to ensure the playheads line up so that playback is seamless and smooth. The same thing happens when you reverse() + * or alter the timeScale, etc. But sometimes you might not want that behavior - you prefer to have tight + * control over exactly where your tweens line up in the timeline - that's when smoothChildTiming:false is handy.

+ * + *

One more example: let's say you've got a 10-second tween that's just sitting on the root timeline and you're 2-seconds into the tween. + * Let's assume it started at exactly 0 on the root to make this easy, and then when it's at 2-seconds, you do tween.seek(5). + * The playhead of the root isn't affected - it keeps going exactly as it always did, but in order to make that tween jump to 5 seconds + * and play appropriately, the tween's startTime gets changed to -3. That way, the tween's playhead and the root + * playhead are perfectly aligned.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + * + **/ + public class TimelineLite extends SimpleTimeline { + /** @private **/ + public static const version:String = "12.1.5"; + + /** @private **/ + protected var _labels:Object; + + /** + * Constructor. + * + *

SPECIAL PROPERTIES

+ *

The following special properties may be passed in via the constructor's vars parameter, like + * new TimelineLite({paused:true, onComplete:myFunction})

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the timeline should begin.
  • + * + *
  • paused : Boolean - + * If true, the timeline will pause itself immediately upon creation (by default, + * timelines automatically begin playing immediately). If you plan to create a TimelineLite and + * then populate it later (after one or more frames elapse), it is typically best to set + * paused:true and then play() after you populate it.
  • + * + *
  • onComplete : Function - + * A function that should be called when the timeline has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * new TimelineLite({onComplete:myFunction, onCompleteParams:["param1", "param2"]});
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the timelines's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • tweens : Array - + * To immediately insert several tweens into the timeline, use the tweens + * special property to pass in an Array of TweenLite/TweenMax/TimelineLite/TimelineMax + * instances. You can use this in conjunction with the align and + * stagger special properties to set up complex sequences with minimal code. + * These values simply get passed to the add() method.
  • + * + *
  • align : String - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. The value simply gets passed to the + * add() method. The default is "normal". + * Options are: + *
      + *
    • "sequence": aligns the tweens one-after-the-other in a sequence
    • + *
    • "start": aligns the start times of all of the tweens (ignores delays)
    • + *
    • "normal": aligns the start times of all the tweens (honors delays)
    • + *
    + * The align special property does not force all child + * tweens/timelines to maintain relative positioning, so for example, if you use + * "sequence" and then later change the duration of one of the nested tweens, + * it does not force all subsequent timelines to change their position. + * The align special property only affects the alignment of the tweens that are + * initially placed into the timeline through the tweens special property of + * the vars object.
  • + * + *
  • stagger : Number - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. It staggers the tweens by a set amount of time + * in seconds (or in frames if useFrames is true). For example, if the + * stagger value is 0.5 and the "align" property is set to "start", the + * second tween will start 0.5 seconds after the first one starts, then 0.5 seconds + * later the third one will start, etc. If the align property is "sequence", + * there would be 0.5 seconds added between each tween. This value simply gets + * passed to the add() method. Default is 0.
  • + * + *
  • onStart : Function - + * A function that should be called when the timeline begins (when its time + * changes from 0 to some other value which can happen more than once if the + * timeline is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * new TimelineLite({onStart:myFunction, onStartParams:["param1", "param2"]});
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the timeline updates + * (on every frame while the timeline is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * new TimelineLite({onUpdate:myFunction, onUpdateParams:["param1", "param2"]});
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the timeline has reached its beginning again from the + * reverse direction. For example, if reverse() is called, the timeline will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the timeline is placed in a TimelineLite or TimelineMax + * instance that gets reversed and plays the timeline backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * new TimelineLite({onReverseComplete:myFunction, onReverseCompleteParams:["param1", "param2"]});
  • + * + *
  • autoRemoveChildren : Boolean - + * If autoRemoveChildren is set to true, as soon as child + * tweens/timelines complete, they will automatically get killed/removed. This is normally + * undesireable because it prevents going backwards in time (like if you want to + * reverse() or set the progress lower, etc.). It can, however, + * improve speed and memory management. The root timelines use autoRemoveChildren:true.
  • + * + *
  • smoothChildTiming : Boolean - + * Controls whether or not child tweens/timelines are repositioned automatically + * (changing their startTime) in order to maintain smooth playback when + * properties are changed on-the-fly. For example, imagine that the timeline's playhead is + * on a child tween that is 75% complete, moving mc.x from 0 to 100 and then that tween's + * reverse() method is called. If smoothChildTiming is false + * (the default except for the root timelines), the tween would flip in place, keeping its + * startTime consistent. Therefore the playhead of the timeline would now be + * at the tween's 25% completion point instead of 75%. Remember, the timeline's playhead + * position and direction are unaffected by child tween/timeline changes. mc.x would jump + * from 75 to 25, but the tween's position in the timeline would remain consistent. However, + * if smoothChildTiming is true, that child tween's + * startTime would be adjusted so that the timeline's playhead intersects + * with the same spot on the tween (75% complete) as it had immediately before + * reverse() was called, thus playback appears perfectly smooth. mc.x + * would still be 75 and it would continue from there as the playhead moves on, but + * since the tween is reversed now mc.x will travel back towards 0 instead of 100. + * Ultimately it's a decision between prioritizing smooth on-the-fly playback + * (true) or consistent position(s) of child tweens/timelines + * (false). + * + * Some examples of on-the-fly changes to child tweens/timelines that could cause their + * startTime to change when smoothChildTiming is true + * are: reversed, timeScale, progress, totalProgress, time, totalTime, delay, pause, + * resume, duration, and totalDuration.
  • + * + *
+ * + * @param vars optionally pass in special properties like onComplete, onCompleteParams, onUpdate, onUpdateParams, onStart, onStartParams, tweens, align, stagger, delay, useFrames, and/or autoRemoveChildren. + */ + public function TimelineLite(vars:Object=null) { + super(vars); + _labels = {}; + autoRemoveChildren = (this.vars.autoRemoveChildren == true); + smoothChildTiming = (this.vars.smoothChildTiming == true); + _sortChildren = true; + _onUpdate = this.vars.onUpdate; + var val:Object, p:String; + for (p in this.vars) { + val = this.vars[p]; + if (val is Array) if (val.join("").indexOf("{self}") !== -1) { + this.vars[p] = _swapSelfInParams(val as Array); + } + } + if (this.vars.tweens is Array) { + this.add(this.vars.tweens, 0, this.vars.align || "normal", this.vars.stagger || 0); + } + } + + +//---- START CONVENIENCE METHODS -------------------------------------- + + /** + * Adds a TweenLite.to() tween to the end of the timeline (or elsewhere using the "position" parameter) + * - this is a convenience method that accomplishes exactly the same thing as + * add( TweenLite.to(...) ) but with less code. In other + * words, the following two lines produce identical results: + * + * +myTimeline.add( TweenLite.to(mc, 1, {x:100, alpha:0.5}) ); +myTimeline.to(mc, 1, {x:100, alpha:0.5}); + + *

Keep in mind that you can chain these calls together and use other convenience + * methods like fromTo(), call(), set(), staggerTo(), etc. to build out + * sequences very quickly:

+ * + * +//create a timeline that calls myFunction() when it completes +var tl:TimelineLite = new TimelineLite({onComplete:myFunction}); + +//now we'll use chaining, but break each step onto a different line for readability... +tl.to(mc, 1, {x:100}) //tween mc.x to 100 + .to(mc, 1, {y:50}, "-=0.25") //then tween mc.y to 50, starting the tween 0.25 seconds before the previous one ends + .set(mc, {alpha:0}) //then set mc.alpha to 0.5 immediately + .call(otherFunction) //then call otherFunction() + .staggerTo([mc1, mc2, mc3], 1.5, {rotation:45}, 0.25); //finally tween the rotation of mc1, mc2, and mc3 to 45 and stagger the start times by 0.25 seconds + + *

If you don't want to append the tween and would rather have precise control + * of the insertion point, you can use the additional position parameter. + * Or use a regular add() like + * myTimeline.add( TweenLite.to(mc, 1, {x:100}), 2.75).

+ * + *

The 4th parameter is the position which controls the placement of the + * tween in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * which can be quite convenient.

+ * + * +tl.to(mc, 1, {x:100}); //appends to the end of the timeline +tl.to(mc, 1, {x:100}, 2); //appends it at exactly 2 seconds into the timeline (absolute position) +tl.to(mc, 1, {x:100}, "+=2"); //appends it 2 seconds after the end (with a gap of 2 seconds) +tl.to(mc, 1, {x:100}, "myLabel"); //places it at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tween is inserted there) +tl.to(mc, 1, {x:100}, "myLabel+=2"); //places it 2 seconds after "myLabel" + + * + * @param target Target object (or array of objects) whose properties the tween affects + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: myTimeline.to(mc, 1, {x:100, y:200, onComplete:myFunction}). + * @param position Controls the placement of the tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @return self (makes chaining easier) + * @see #from() + * @see #fromTo() + * @see #add() + * @see #remove() + */ + public function to(target:Object, duration:Number, vars:Object, position:*="+=0"):* { + return duration ? add( new TweenLite(target, duration, vars), position) : this.set(target, vars, position); + } + + /** + * Adds a TweenLite.from() tween to the end of the timeline (or elsewhere using the "position" parameter) + * - this is a convenience method that accomplishes exactly the same thing as + * add( TweenLite.from(...) ) but with less code. In other + * words, the following two lines produce identical results: + * + * +myTimeline.add( TweenLite.from(mc, 1, {x:100, alpha:0.5}) ); +myTimeline.from(mc, 1, {x:100, alpha:0.5}); + + *

Keep in mind that you can chain these calls together and use other convenience + * methods like to(), call(), set(), staggerTo(), etc. to build out + * sequences very quickly:

+ * + * +//create a timeline that calls myFunction() when it completes +var tl:TimelineLite = new TimelineLite({onComplete:myFunction}); + +//now we'll use chaining, but break each step onto a different line for readability... +tl.from(mc, 1, {x:-100}) //tween mc.x from -100 + .to(mc, 1, {y:50}) //then tween mc.y to 50 + .set(mc, {alpha:0}) //then set mc.alpha to 0.5 immediately + .call(otherFunction) //then call otherFunction() + .staggerTo([mc1, mc2, mc3], 1.5, {rotation:45}, 0.25); //finally tween the rotation of mc1, mc2, and mc3 to 45 and stagger the start times by 0.25 seconds + + *

If you don't want to append the tween and would rather have precise control + * of the insertion point, you can use the additional position parameter. + * Or use a regular add() like + * myTimeline.add( TweenLite.from(mc, 1, {x:100}), 2.75).

+ * + *

The 4th parameter is the position which controls the placement of the + * tween in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * there which can be quite convenient.

+ * + * +tl.from(mc, 1, {x:100}); //appends to the end of the timeline +tl.from(mc, 1, {x:100}, 2); //appends it at exactly 2 seconds into the timeline (absolute position) +tl.from(mc, 1, {x:100}, "+=2"); //appends it 2 seconds after the end (with a gap of 2 seconds) +tl.from(mc, 1, {x:100}, "myLabel"); //places it at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tween is inserted there) +tl.from(mc, 1, {x:100}, "myLabel+=2"); //places it 2 seconds after "myLabel" + + * + *

NOTE: By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins.

+ * + * @param target Target object (or array of objects) whose properties the tween affects + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param vars An object defining the starting value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 100 and mc.y from 200 and then call myFunction, do this: myTimeline.from(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @param position Controls the placement of the tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @return self (makes chaining easier) + * @see #to() + * @see #fromTo() + * @see #add() + * @see #remove() + */ + public function from(target:Object, duration:Number, vars:Object, position:*="+=0"):* { + return add( TweenLite.from(target, duration, vars), position); + } + + /** + * Adds a TweenLite.fromTo() tween to the end of the timeline - this is + * a convenience method that accomplishes exactly the same thing as + * add( TweenLite.fromTo(...) ) but with less code. In other + * words, the following two lines produce identical results: + * + * +myTimeline.add( TweenLite.fromTo(mc, 1, {x:0, alpha:1}, {x:100, alpha:0.5}) ); +myTimeline.fromTo(mc, 1, {x:0, alpha:1}, {x:100, alpha:0.5}); + + *

Keep in mind that you can chain these calls together and use other convenience + * methods like to(), call(), set(), staggerTo(), etc. to build out + * sequences very quickly:

+ * + * +//create a timeline that calls myFunction() when it completes +var tl:TimelineLite = new TimelineLite({onComplete:myFunction}); + +//now we'll use chaining, but break each step onto a different line for readability... +tl.fromTo(mc, 1, {x:0}, {x:-100}) //tween mc.x from 0 to -100 + .to(mc, 1, {y:50}, "-=0.25") //then tween mc.y to 50, starting it 0.25 seconds before the previous tween ends + .set(mc, {alpha:0}) //then set mc.alpha to 0.5 immediately + .call(otherFunction) //then call otherFunction() + .staggerTo([mc1, mc2, mc3], 1.5, {rotation:45}, 0.25); //finally tween the rotation of mc1, mc2, and mc3 to 45 and stagger the start times by 0.25 seconds + + *

If you don't want to append the tween and would rather have precise control + * of the insertion point, you can use the additional position parameter. + * Or use a regular add() like + * myTimeline.add( TweenLite.fromTo(mc, 1, {x:0}, {x:100}), 2.75).

+ * + *

The 4th parameter is the position which controls the placement of the + * tween in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * there which can be quite convenient.

+ * + * +tl.fromTo(mc, 1, {x:0}, {x:100}); //appends to the end of the timeline +tl.fromTo(mc, 1, {x:0}, {x:100}, 2); //appends it at exactly 2 seconds into the timeline (absolute position) +tl.fromTo(mc, 1, {x:0}, {x:100}, "+=2"); //appends it 2 seconds after the end (with a gap of 2 seconds) +tl.fromTo(mc, 1, {x:0}, {x:100}, "myLabel"); //places it at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tween is inserted there) +tl.fromTo(mc, 1, {x:0}, {x:100}, "myLabel+=2"); //places it 2 seconds after "myLabel" + + *

NOTE: by default, immediateRender is true in + * fromTo() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. This is done for convenience because it is + * often the preferred behavior when setting things up on the screen to animate into place, but + * you can override this behavior by passing immediateRender:false in the + * fromVars or toVars parameter so that it will wait to render + * the starting values until the tweens actually begin.

+ * + * @param target Target object (or array of objects) whose properties the tween affects + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param fromVars An object defining the starting value for each property that should be tweened. For example, to tween mc.x from 100 and mc.y from 200, fromVars would look like this: {x:100, y:200}. + * @param toVars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 0 to 100 and mc.y from 0 to 200 and then call myFunction, do this: myTimeline.fromTo(mc, 1, {x:0, y:0}, {x:100, y:200, onComplete:myFunction}); + * @param position Controls the placement of the tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @return self (makes chaining easier) + * @see #to() + * @see #from() + * @see #add() + * @see #remove() + */ + public function fromTo(target:Object, duration:Number, fromVars:Object, toVars:Object, position:*="+=0"):* { + return duration ? add(TweenLite.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position); + } + + /** + * Tweens an array of targets to a common set of destination values, but staggers their + * start times by a specified amount of time, creating an evenly-spaced sequence with a + * surprisingly small amount of code. For example, let's say you have an array containing + * references to a bunch of text fields that you'd like to fall away and fade out in a + * staggered fashion with 0.2 seconds between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +myTimeline.staggerTo(textFields, 1, {y:"+=150", ease:Cubic.easeIn}, 0.2); + + *

staggerTo() simply loops through the targets array and creates + * a to() tween for each object and then inserts it at the appropriate place on a + * new TimelineLite instance whose onComplete corresponds to the onCompleteAll + * (if you define one) and then appends that TimelineLite to the timeline (as a nested child).

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 6th parameter).

+ * + *

The 5th parameter is the position which controls the placement of the + * tweens in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the first tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the first tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the first tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tweens + * there which can be quite convenient.

+ * + * +tl.staggerTo(myArray, 1, {x:100}, 0.25); //appends to the end of the timeline +tl.staggerTo(myArray, 1, {x:100}, 0.25, 2); //appends at exactly 2 seconds into the timeline (absolute position) +tl.staggerTo(myArray, 1, {x:100}, 0.25, "+=2"); //appends 2 seconds after the end (with a gap of 2 seconds) +tl.staggerTo(myArray, 1, {x:100}, 0.25, "myLabel"); //places at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tweens are inserted there) +tl.staggerTo(myArray, 1, {x:100}, 0.25, "myLabel+=2"); //places 2 seconds after "myLabel" + + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (8th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like ease. For example, to tween x to 100 and y to 200 for mc1, mc2, and mc3, staggering their start time by 0.25 seconds and then call myFunction when they last one has finished, do this: myTimeline.staggerTo([mc1, mc2, mc3], 1, {x:100, y:200}, 0.25, 0, null, myFunction}). + * @param stagger Amount of time in seconds (or frames if the timeline is frames-based) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: myTimeline.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+=100", alpha:0}, 0.2). + * @param position Controls the placement of the first tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return self (makes chaining easier) + * @see #staggerFrom() + * @see #staggerFromTo() + */ + public function staggerTo(targets:Array, duration:Number, vars:Object, stagger:Number, position:*="+=0", onCompleteAll:Function=null, onCompleteAllParams:Array=null):* { + var tl:TimelineLite = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, smoothChildTiming:this.smoothChildTiming}); + for (var i:int = 0; i < targets.length; i++) { + if (vars.startAt != null) { + vars.startAt = _copy(vars.startAt); + } + tl.to(targets[i], duration, _copy(vars), i * stagger); + } + return add(tl, position); + } + + /** + * Tweens an array of targets from a common set of destination values (using the current + * values as the destination), but staggers their start times by a specified amount of time, + * creating an evenly-spaced sequence with a surprisingly small amount of code. For example, + * let's say you have an array containing references to a bunch of text fields that you'd + * like to drop into place while fading in, all in a staggered fashion with 0.2 seconds + * between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +myTimeline.staggerFrom(textFields, 1, {y:"+=150"}, 0.2); + + *

staggerFrom() simply loops through the targets array and creates + * a from() tween for each object and then inserts it at the appropriate place on a + * new TimelineLite instance whose onComplete corresponds to the onCompleteAll + * (if you define one) and then appends that TimelineLite to the timeline (as a nested child).

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 6th parameter).

+ * + *

The 5th parameter is the position which controls the placement of the + * tweens in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the first tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the first tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the first tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tweens + * there which can be quite convenient.

+ * + * +tl.staggerFrom(myArray, 1, {x:100}, 0.25); //appends to the end of the timeline +tl.staggerFrom(myArray, 1, {x:100}, 0.25, 2); //appends at exactly 2 seconds into the timeline (absolute position) +tl.staggerFrom(myArray, 1, {x:100}, 0.25, "+=2"); //appends 2 seconds after the end (with a gap of 2 seconds) +tl.staggerFrom(myArray, 1, {x:100}, 0.25, "myLabel"); //places at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tweens are inserted there) +tl.staggerFrom(myArray, 1, {x:100}, 0.25, "myLabel+=2"); //places 2 seconds after "myLabel" + + * + *

By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins.

+ * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (8th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param vars An object defining the beginning value for each property that should be tweened as well as any special properties like ease. For example, to tween x from 100 and y from 200 for mc1, mc2, and mc3, staggering their start time by 0.25 seconds and then call myFunction when they last one has finished, do this: myTimeline.staggerFrom([mc1, mc2, mc3], 1, {x:100, y:200}, 0.25, 0, null, myFunction}). + * @param stagger Amount of time in seconds (or frames if the timeline is frames-based) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: myTimeline.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+=100", alpha:0}, 0.2). + * @param position Controls the placement of the first tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return self (makes chaining easier) + * @see #staggerTo() + * @see #staggerFromTo() + */ + public function staggerFrom(targets:Array, duration:Number, vars:Object, stagger:Number=0, position:*="+=0", onCompleteAll:Function=null, onCompleteAllParams:Array=null):* { + vars = _prepVars(vars); + if (!("immediateRender" in vars)) { + vars.immediateRender = true; + } + vars.runBackwards = true; + return staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams); + } + + /** + * Tweens an array of targets from and to a common set of values, but staggers their + * start times by a specified amount of time, creating an evenly-spaced sequence with a + * surprisingly small amount of code. For example, let's say you have an array containing + * references to a bunch of text fields that you'd like to fade from alpha:1 to alpha:0 in a + * staggered fashion with 0.2 seconds between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +myTimeline.staggerFromTo(textFields, 1, {alpha:1}, {alpha:0}, 0.2); + + *

staggerFromTo() simply loops through the targets array and creates + * a fromTo() tween for each object and then inserts it at the appropriate place on + * a new TimelineLite instance whose onComplete corresponds to the onCompleteAll + * (if you define one) and then appends that TimelineLite to the timeline (as a nested child).

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 7th parameter).

+ * + *

The 6th parameter is the position which controls the placement of the + * tweens in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the first tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the first tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the first tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tweens + * there which can be quite convenient.

+ * + * +tl.staggerFromTo(myArray, 1, {x:0}, {x:100}, 0.25); //appends to the end of the timeline +tl.staggerFromTo(myArray, 1, {x:0}, {x:100}, 0.25, 2); //appends at exactly 2 seconds into the timeline (absolute position) +tl.staggerFromTo(myArray, 1, {x:0}, {x:100}, 0.25, "+=2"); //appends 2 seconds after the end (with a gap of 2 seconds) +tl.staggerFromTo(myArray, 1, {x:0}, {x:100}, 0.25, "myLabel"); //places at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tweens are inserted there) +tl.staggerFromTo(myArray, 1, {x:0}, {x:100}, 0.25, "myLabel+=2"); //places 2 seconds after "myLabel" + + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (9th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if the timeline is frames-based) + * @param fromVars An object defining the starting value for each property that should be tweened. For example, to tween x from 100 and y from 200, fromVars would look like this: {x:100, y:200}. + * @param toVars An object defining the end value for each property that should be tweened as well as any special properties like ease. For example, to tween x from 0 to 100 and y from 0 to 200, staggering the start times by 0.2 seconds and then call myFunction when they all complete, do this: myTimeline.staggerFromTo([mc1, mc2, mc3], 1, {x:0, y:0}, {x:100, y:200}, 0.2, 0, null, myFunction}); + * @param stagger Amount of time in seconds (or frames if the timeline is frames-based) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: myTimeline.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+=100", alpha:0}, 0.2). + * @param position Controls the placement of the first tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return self (makes chaining easier) + * @see #staggerTo() + * @see #staggerFrom() + */ + public function staggerFromTo(targets:Array, duration:Number, fromVars:Object, toVars:Object, stagger:Number=0, position:*="+=0", onCompleteAll:Function=null, onCompleteAllParams:Array=null):* { + toVars = _prepVars(toVars); + fromVars = _prepVars(fromVars); + toVars.startAt = fromVars; + toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); + return staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams); + } + + /** + * Adds a callback to the end of the timeline (or elsewhere using the "position" parameter) + * - this is a convenience method that accomplishes exactly the same thing as + * add( TweenLite.delayedCall(...) ) but with less code. In other + * words, the following two lines produce identical results: + * + * +myTimeline.add( TweenLite.delayedCall(0, myFunction, ["param1", "param2"]) ); +myTimeline.call(myFunction, ["param1", "param2"]); + + *

This is different than using the onComplete special property + * on the TimelineLite itself because once you append the callback, it stays in + * place whereas an onComplete is always called at the very end of + * the timeline. For example, if a timeline is populated with a 1-second tween and + * then you call(myFunction), it is placed at the 1-second spot. Then + * if you append another 1-second tween, the timeline's duration will now be 2 seconds + * but the myFunction callback will still be called at the 1-second spot. An + * onComplete would be called at the end (2 seconds).

+ * + *

Keep in mind that you can chain these calls together and use other convenience + * methods like to(), fromTo(), set(), staggerTo(), etc. to build out + * sequences very quickly:

+ * + * +//create a timeline that calls myFunction() when it completes +var tl:TimelineLite = new TimelineLite({onComplete:myFunction}); + +//now we'll use chaining, but break each step onto a different line for readability... +tl.to(mc, 1, {x:100}) //tween mc.x to 100 + .call(myCallback) //then call myCallback() + .set(mc, {alpha:0}) //then set mc.alpha to 0.5 immediately + .call(otherFunction, ["param1", "param2"]) //then call otherFunction("param1", "param2") + .staggerTo([mc1, mc2, mc3], 1.5, {rotation:45}, 0.25); //finally tween the rotation of mc1, mc2, and mc3 to 45 and stagger the start times by 0.25 seconds + + * + *

The 3rd parameter is the position which controls the placement of the + * tween in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * which can be quite convenient.

+ * + * +tl.call(func, ["param1"]); //appends to the end of the timeline +tl.call(func, ["param1"], 2); //appends it at exactly 2 seconds into the timeline (absolute position) +tl.call(func, ["param1"], "+=2"); //appends it 2 seconds after the end (with a gap of 2 seconds) +tl.call(func, ["param1"], "myLabel"); //places it at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tween is inserted there) +tl.call(func, ["param1"], "myLabel+=2"); //places it 2 seconds after "myLabel" + + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions the 3rd parameter is scope, but that parameter is omitted in the AS3 version.

+ * + * @param callback Function to call + * @param params An Array of parameters to pass the function. + * @param position Controls the placement of the callback in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the callback 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the callback inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the callback 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the callback there which can be quite convenient. + * @return self (makes chaining easier) + * @see #add() + * @see #remove() + */ + public function call(callback:Function, params:Array=null, position:*="+=0"):* { + return add( TweenLite.delayedCall(0, callback, params), position); + } + + /** + * Adds a zero-duration tween to the end of the timeline (or elsewhere using the "position" parameter) + * that sets values immediately (when the virtual playhead reaches that position + * on the timeline) - this is a convenience method that accomplishes exactly + * the same thing as add( TweenLite.to(target, 0, {...}) ) but + * with less code. In other words, the following two lines produce identical results: + * + * +myTimeline.add( TweenLite.to(mc, 0, {x:100, alpha:0.5, immediateRender:false}) ); +myTimeline.set(mc, {x:100, alpha:0.5}); + + *

Keep in mind that you can chain these calls together and use other convenience + * methods like to(), call(), fromTo(), staggerTo(), etc. to build out + * sequences very quickly:

+ * + * +//create a timeline that calls myFunction() when it completes +var tl:TimelineLite = new TimelineLite({onComplete:myFunction}); + +//now we'll use chaining, but break each step onto a different line for readability... +tl.to(mc, 1, {x:100}) //tween mc.x to 100 + .set(mc, {alpha:0}) //then set mc.alpha to 0.5 immediately + .to(mc, 1, {y:50}) //then tween mc.y to 50 + .call(otherFunction) //then call otherFunction() + .staggerTo([mc1, mc2, mc3], 1.5, {rotation:45}, 0.25); //finally tween the rotation of mc1, mc2, and mc3 to 45 and stagger the start times by 0.25 seconds + + *

The 3rd parameter is the position which controls the placement of the + * tween in the timeline (by default, it's at the end of the timeline). Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the tween inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * there which can be quite convenient.

+ * + * +tl.set(mc, {x:100}); //appends to the end of the timeline +tl.set(mc, {x:100}, 2); //appends it at exactly 2 seconds into the timeline (absolute position) +tl.set(mc, {x:100}, "+=2"); //appends it 2 seconds after the end (with a gap of 2 seconds) +tl.set(mc, {x:100}, "myLabel"); //places it at "myLabel" (and if "myLabel" doesn't exist yet, it's added to the end and then the tween is inserted there) +tl.set(mc, {x:100}, "myLabel+=2"); //places it 2 seconds after "myLabel" + + * + * @param target Target object (or array of objects) whose properties will be set. + * @param vars An object defining the value to which each property should be set. For example, to set mc.x to 100 and mc.y to 200, do this: myTimeline.set(mc, {x:100, y:200}); + * @param position Controls the placement of the zero-duration tween in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @return self (makes chaining easier) + * @see #to() + * @see #add() + * @see #remove() + */ + public function set(target:Object, vars:Object, position:*="+=0"):* { + position = _parseTimeOrLabel(position, 0, true); + vars = _prepVars(vars); + if (vars.immediateRender == null) { + vars.immediateRender = (position === _time && !_paused); + } + return add( new TweenLite(target, 0, vars), position); + } + + /** + * Inserts a special callback that pauses playback of the timeline at a + * particular time or label. This method is more accurate than using a simple callback of your own because + * it ensures that even if the virtual playhead had moved slightly beyond the pause position, it'll get moved + * back to precisely the correct position. + * + *

Remember, the virtual playhead moves to a new position on each tick (frame) of the core timing mechanism, + * so it is possible, for example for it to be at 0.99 and then the next render happens at 1.01, so if your + * callback was at exactly 1 second, the playhead would (in this example) move slightly past where you wanted to + * pause. Then, if you reverse(), it would run into that callback again and get paused almost immediately. However, + * if you use the addPause() method, it will calibrate things so that when the callback is + * hit, it'll move the playhead back to EXACTLY where it should be. Thus, if you reverse() + * it won't run into the same callback again.

+ * + * +//insert a pause at exactly 2 seconds into the timeline +timeline.addPause(2); + +//insert a pause at "yourLabel" +timeline.addPause("yourLabel"); + +//insert a pause 3 seconds after "yourLabel" and when that pause occurs, call yourFunction +timeline.addPause("yourLabel+=3", yourFunction); + +//insert a pause at exactly 4 seconds and then call yourFunction and pass it 2 parameters, "param1" and "param2" +timeline.addPause(4, yourFunction, ["param1", "param2"]); + + * + *

The special callback is just a zero-duration tween that utilizes an onComplete, so technically + * this callback is just like any other, and it is considered a child of the timeline.

+ * + * @param position Controls the placement of the pause in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the tween 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the tween inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the tween 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @param callback An optional callback that should be called immediately after the timeline is paused. + * @param params An optional array of parameters to pass the callback. + * @return self (makes chaining easier) + * @see #call() + */ + public function addPause(position:*="+=0", callback:Function=null, params:Array=null):* { + return call(_pauseCallback, ["{self}", callback, params], position); + } + + /** @private **/ + protected function _pauseCallback(tween:TweenLite, callback:Function=null, params:Array=null):void { + pause(tween._startTime); + if (callback != null) { + callback.apply(null, params); + } + } + + /** @private **/ + protected static function _prepVars(vars:Object):Object { //to accommodate TweenLiteVars and TweenMaxVars instances for strong data typing and code hinting + return (vars._isGSVars) ? vars.vars : vars; + } + + /** @private **/ + protected static function _copy(vars:Object):Object { + var copy:Object = {}, p:String; + for (p in vars) { + copy[p] = vars[p]; + } + return copy; + } + + + /** + * Seamlessly transfers all tweens, timelines, and [optionally] delayed calls from the root + * timeline into a new TimelineLite so that you can perform advanced tasks on a seemingly global + * basis without affecting tweens/timelines that you create after the export. For example, imagine + * a game that uses the GreenSock Animation Platform for all of its animations and at some point + * during the game, you want to slow everything down to a stop (tweening the + * timeScale) while at the same time animating a new popup window into place: + * + * +var tl = TimelineLite.exportRoot(); +TweenLite.to(tl, 0.5, {timeScale:0}); + +//this tween isn't affected because it's created after the export. +TweenLite.fromTo(myWindow, 1, {scaleX:0, scaleY:0}, {scaleX:1, scaleY:1}); + + *

You could then re-animate things when you're ready by tweening the timeScale + * back to 1. Or you could use exportRoot() to collect all the animations and + * pause() them and then animate the popup screen (or whatever). Then resume() + * that instance or even reverse().

+ * + *

You can exportRoot() as many times as you want; all it does is wrap all the + * loose tweens/timelines/delayedCalls into a TimelineLite which itself gets placed onto the root, + * so if you exportRoot() again, that TimelineLite would get wrapped into another one, + * etc. Things can be nested as deeply as you want.

+ * + *

Keep in mind, however, that completed tweens/timelines are removed from the root (for automatic + * garbage collection), so if you exportRoot() after a tween completes, it won't be + * included in the export. The only way around that is to set autoRemoveChildren + * property of the Animation._rootTimeline and Animation._rootFramesTimeline + * to false, but that is NOT recommended because you'd need to + * manually kill() your tweens/timelines manually to make them eligible for + * garbage collection.

+ * + * @param vars The vars parameter that's passed to the TimelineLite's constructor which allows you to define things like onUpdate, onComplete, etc. The useFrames special property determines which root timeline gets exported. There are two distinct root timelines - one for frames-based animations (useFrames:true) and one for time-based ones. By default, the time-based timeline is exported. + * @param omitDelayedCalls If true (the default), delayed calls will be left on the root rather than wrapped into the new TimelineLite. That way, if you pause() or alter the timeScale, or reverse(), they won't be affected. However, in some situations it might be very useful to have them included. + * @return A new TimelineLite instance containing the root tweens/timelines + */ + public static function exportRoot(vars:Object=null, omitDelayedCalls:Boolean=true):TimelineLite { + vars = vars || {}; + if (!("smoothChildTiming" in vars)) { + vars.smoothChildTiming = true; + } + var tl:TimelineLite = new TimelineLite(vars), + root:SimpleTimeline = tl._timeline; + root._remove(tl, true); + tl._startTime = 0; + tl._rawPrevTime = tl._time = tl._totalTime = root._time; + var tween:Animation = root._first, next:Animation; + while (tween) { + next = tween._next; + if (!omitDelayedCalls || !(tween is TweenLite && TweenLite(tween).target == tween.vars.onComplete)) { + tl.add(tween, tween._startTime - tween._delay); + } + tween = next; + } + root.add(tl, 0); + return tl; + } + +//---- END CONVENIENCE METHODS ---------------------------------------- + + + /** + * @private + * [Deprecated in favor of add()] + * Inserts a tween, timeline, callback, or label into the timeline at a specific time, frame, + * or label. This gives you complete control over the insertion point (append() + * always puts things at the end). + * + * + //insert a tween so that it starts at 1 second into the timeline + myAnimation.insert(TweenLite.to(mc, 2, {x:100}), 1); + + //insert a callback at 1.5 seconds + myAnimation.insert(myFunction, 1.5); + + //insert a label at 3 seconds + myAnimation.insert("myLabel", 3); + + //create another timeline that we will insert + var nested = new TimelineLite(); + + //insert the timeline where the "myLabel" label is + myAnimation.insert(nested, "myLabel"); + + * + * @param value The tween, timeline, callback, or label to insert + * @param timeOrLabel The time in seconds (or frames for frames-based timelines) or label at which to insert. For example, myTimeline.insert(myTween, 3) would insert myTween 3-seconds into the timeline, and myTimeline.insert(myTween, "myLabel") would insert it at the "myLabel" label. If you define a label that doesn't exist yet, one is appended to the end of the timeline. + * @return self (makes chaining easier) + * @see #add() + */ + override public function insert(value:*, timeOrLabel:*=0):* { + return add(value, timeOrLabel || 0); + } + + /** + * Adds a tween, timeline, callback, or label (or an array of them) to the timeline. + * + *

The position parameter gives you complete control over the insertion point. + * By default, it's at the end of the timeline. Use a number to indicate + * an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string + * with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. + * For example, "+=2" would place the object 2 seconds after the end, leaving a 2-second gap. + * "-=2" would create a 2-second overlap. You may also use a label like "myLabel" + * to have the object inserted exactly at the label or combine a label and a relative offset like + * "myLabel+=2" to insert the object 2 seconds after "myLabel" or "myLabel-=3" + * to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it + * will automatically be added to the end of the timeline before inserting the tween + * there which can be quite convenient.

+ * + * +//add a tween to the end of the timeline +tl.add( TweenLite.to(mc, 2, {x:100}) ); + +//add a callback at 1.5 seconds +tl.add(func, 1.5); + +//add a label 2 seconds after the end of the timeline (with a gap of 2 seconds) +tl.add("myLabel", "+=2"); + +//add another timeline at "myLabel" +tl.add(otherTimeline, "myLabel"); + +//add an array of tweens 2 seconds after "myLabel" +tl.add([tween1, tween2, tween3], "myLabel+=2"); + +//add an array of tweens so that they are sequenced one-after-the-other with 0.5 seconds inbetween them, starting 2 seconds after the end of the timeline +tl.add([tween1, tween2, tween3], "+=2", "sequence", 0.5); + + * + * @param value The tween, timeline, callback, or label (or array of them) to add + * @param position Controls the placement of the object in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the object 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the object inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the object 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the tween there which can be quite convenient. + * @param align [only relevant when the first parameter, value, is an array] Determines how the tweens/timelines/callbacks/labels in the array that is being added will be aligned in relation to each other before getting inserted. Options are: "sequence" (aligns them one-after-the-other in a sequence), "start" (aligns the start times of all of the objects (ignoring delays)), and "normal" (aligns the start times of all the tweens (honoring delays)). The default is "normal". + * @param stagger [only relevant when the first parameter, value, is an array] Staggers the inserted objects from the array the is being added by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the "align" parameter is set to "start", the second one will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is "sequence", there would be 0.5 seconds added between each tween. Default is 0. + * @return self (makes chaining easier) + */ + override public function add(value:*, position:*="+=0", align:String="normal", stagger:Number=0):* { + if (typeof(position) !== "number") { + position = _parseTimeOrLabel(position, 0, true, value); + } + if (!(value is Animation)) { + if (value is Array) { + var i:int, + curTime:Number = Number(position), + l:Number = value.length, + child:*; + for (i = 0; i < l; i++) { + child = value[i]; + if (child is Array) { + child = new TimelineLite({tweens:child}); + } + add(child, curTime); + if (typeof(child) === "string" || typeof(child) === "function") { + //do nothing + } else if (align === "sequence") { + curTime = child._startTime + (child.totalDuration() / child._timeScale); + } else if (align === "start") { + child._startTime -= child.delay(); + } + curTime += stagger; + } + return _uncache(true); + } else if (typeof(value) === "string") { + return addLabel(String(value), position); + } else if (typeof(value) === "function") { + value = TweenLite.delayedCall(0, value); + } else { + trace("Cannot add " + value + " into the TimelineLite/Max: it is not a tween, timeline, function, or string."); + return this; + } + } + + super.add(value, position); + + //if the timeline has already ended but the inserted tween/timeline extends the duration, we should enable this timeline again so that it renders properly. We should also align the playhead with the parent timeline's when appropriate. + if (_gc || _time === _duration) if (!_paused) if (_duration < duration()) { + //in case any of the anscestors had completed but should now be enabled... + var tl:SimpleTimeline = this, + beforeRawTime:Boolean = (tl.rawTime() > value._startTime); //if the tween is placed on the timeline so that it starts BEFORE the current rawTime, we should align the playhead (move the timeline). This is because sometimes users will create a timeline, let it finish, and much later append a tween and expect it to run instead of jumping to its end state. While technically one could argue that it should jump to its end state, that's not what users intuitively expect. + while (tl._timeline) { + if (beforeRawTime && tl._timeline.smoothChildTiming) { + tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it. + } else if (tl._gc) { + tl._enabled(true, false); + } + tl = tl._timeline; + } + } + + return this; + } + + + /** + * Removes a tween, timeline, callback, or label (or array of them) from the timeline. + * + * @param value The tween, timeline, callback, or label that should be removed from the timeline (or an array of them) + * @return self (makes chaining easier) + */ + public function remove(value:*):* { + if (value is Animation) { + return _remove(value, false); + } else if (value is Array) { + var i:Number = value.length; + while (--i > -1) { + remove(value[i]); + } + return this; + } else if (typeof(value) == "string") { + return removeLabel(String(value)); + } + return kill(null, value); + } + + /** @private **/ + override public function _remove(tween:Animation, skipDisable:Boolean=false):* { + super._remove(tween, skipDisable); + if (_last == null) { + _time = _totalTime = _duration = _totalDuration = 0; + } else if (_time > _last._startTime + _last._totalDuration / _last._timeScale) { + _time = duration(); + _totalTime = _totalDuration; + } + return this; + } + + /** + * @private + * [Deprecated in favor of add()] + * Appends a tween, timeline, callback, or label to the end of the timeline, + * optionally offsetting its insertion point by a certain amount (to make it overlap with the end of + * the timeline or leave a gap before its insertion point). + * This makes it easy to build sequences by continuing to append() tweens or timelines. You can + * chain append() calls together or use the convenience methods like to(), from(), fromTo(), + * call(), set(), staggerTo(), staggerFrom(), and staggerFromTo() to build + * sequences with minimal code. + * + *

To insert the tween/timeline/callback/label at a specific position on the timeline + * rather than appending it to the end, use the insert() method.

+ * + *

If you define a label (string) as the offsetOrLabel parameter, + * the tween/timeline/callback will be inserted wherever that label is, but if the + * label doesn't exist yet, it will be added to the end of the timeline first and + * then the tween/timeline/callback will be inserted there. This makes it easier + * to build things as you go with concise code, adding labels as things get appended.

+ * + * +//append a tween +myTimeline.append(TweenLite.to(mc, 1, {x:100})); + +//use the to() convenience method to add several sequenced tweens +myTimeline.to(mc, 1, {x:50}).to(mc, 1, {y:100}).to(mc2, 1, {alpha:0}); + +//append a callback +myTimeline.append(myFunction); + +//append a label +myTimeline.append("myLabel"); + +//create another timeline and then append it +var nested = new TimelineLite(); +myTimeline.append(nested); + + * + * @param value The tween, timeline, callback, or label to append. You can even pass in an array of them. + * @param offsetOrLabel Either a number indicating how many seconds (or frames for frames-based timelines) to offset the insertion point from the end of the timeline (positive values create a gap, negative values create an overlap) or a string indicating the label at which the tween/timeline/callback should be inserted. If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before the tween/timeline/callback gets appended there. For example, to append a tween 3 seconds after the end of the timeline (leaving a 3-second gap), set the offsetOrLabel to 3. Or to have the tween appended so that it overlaps with the last 2 seconds of the timeline, set the offsetOrLabel to -2. The default is 0 so that the insertion point is exactly at the end of the timeline. + * @return self (makes chaining easier) + * @see #add() + * @see #to() + * @see #from() + * @see #fromTo() + * @see #call() + * @see #set() + */ + public function append(value:*, offsetOrLabel:*=0):* { + return add(value, _parseTimeOrLabel(null, offsetOrLabel, true, value)); + } + + /** + * @private + * [Deprecated in favor of add()] + * Inserts multiple tweens/timelines/callbacks/labels into the timeline at once, optionally aligning them + * (as a sequence for example) and/or staggering the timing. You can use the insert() method + * instead if you are not defining a stagger or align (either way works). + * + * @param tweens An array containing the tweens, timelines, callbacks, or labels that should be inserted + * @param timeOrLabel Time in seconds (or frame if the timeline is frames-based) or label that serves as the insertion point. For example, the number 2 would insert the first object in the array at 2-seconds into the timeline, or "myLabel" would ihsert them wherever "myLabel" is. + * @param align Determines how the tweens/timelines/callbacks/labels will be aligned in relation to each other before getting inserted. Options are: "sequence" (aligns them one-after-the-other in a sequence), "start" (aligns the start times of all of the objects (ignoring delays)), and "normal" (aligns the start times of all the tweens (honoring delays)). The default is "normal". + * @param stagger Staggers the tweens by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the "align" parameter is set to "start", the second one will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is "sequence", there would be 0.5 seconds added between each tween. Default is 0. + * @return self (makes chaining easier) + * @see #add() + * @see #staggerTo() + * @see #staggerFrom() + * @see #staggerFromTo() + */ + public function insertMultiple(tweens:Array, timeOrLabel:*=0, align:String="normal", stagger:Number=0):* { + return add(tweens, timeOrLabel || 0, align, stagger); + } + + /** + * @private + * [Deprecated in favor of add()] + * Appends multiple tweens/timelines/callbacks/labels to the end of the timeline at once, optionally + * offsetting the insertion point by a certain amount, aligning them (as a sequence for example), and/or + * staggering their relative timing. You can use the add() method + * instead if you are not defining a stagger or align (either way works). + * Check out the staggerTo() method for an even easier way to create and append + * a sequence of evenly-spaced tweens. + * + * @param tweens An array containing the tweens, timelines, callbacks, and/or labels that should be appended + * @param offsetOrLabel Either a number indicating how many seconds (or frames for frames-based timelines) to offset the insertion point from the end of the timeline (positive values create a gap, negative values create an overlap) or a string indicating the label at which the tween should be inserted. If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before the tweens/timelines/callbacks gets appended there. For example, to begin appending the tweens 3 seconds after the end of the timeline (leaving a 3-second gap), set the offsetOrLabel to 3. Or to begin appending the tweens/timelines/callbacks so that they overlap with the last 2 seconds of the timeline, set the offsetOrLabel to -2. The default is 0 so that the insertion point is exactly at the end of the timeline. + * @param align Determines how the objects will be aligned in relation to each other before getting appended. Options are: TweenAlign.SEQUENCE (aligns the tweens one-after-the-other in a sequence), TweenAlign.START (aligns the start times of all of the tweens (ignores delays)), and TweenAlign.NORMAL (aligns the start times of all the tweens (honors delays)). The default is NORMAL. + * @param stagger Staggers the tweens by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the "align" parameter is set to "start", the second one will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is "sequence", there would be 0.5 seconds added between each tween. Default is 0. + * @return The array of tweens that were appended + */ + public function appendMultiple(tweens:Array, offsetOrLabel:*=0, align:String="normal", stagger:Number=0):* { + return add(tweens, _parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger); + } + + /** + * Adds a label to the timeline, making it easy to mark important positions/times. You can then + * reference that label in other methods, like seek("myLabel") or add(myTween, "myLabel") + * or reverse("myLabel"). You could also use the add() method to insert a label. + * + * @param label The name of the label + * @param position Controls the placement of the label in the timeline (by default, it's the end of the timeline, like "+=0"). Use a number to indicate an absolute time in terms of seconds (or frames for frames-based timelines), or you can use a string with a "+=" or "-=" prefix to offset the insertion point relative to the END of the timeline. For example, "+=2" would place the label 2 seconds after the end, leaving a 2-second gap. "-=2" would create a 2-second overlap. You may also use a label like "myLabel" to have the label inserted exactly at the label or combine a label and a relative offset like "myLabel+=2" to insert the label 2 seconds after "myLabel" or "myLabel-=3" to insert it 3 seconds before "myLabel". If you define a label that doesn't exist yet, it will automatically be added to the end of the timeline before inserting the label there which can be quite convenient. + */ + public function addLabel(label:String, position:*="+=0"):* { + _labels[label] = _parseTimeOrLabel(position); + return this; + } + + /** + * + * Removes a label from the timeline and returns the time of that label. You could + * also use the remove() method to accomplish the same task. + * + * @param label The name of the label to remove + * @return Time associated with the label that was removed + */ + public function removeLabel(label:String):* { + delete _labels[label]; + return this; + } + + /** + * Returns the time associated with a particular label. If the label isn't found, -1 is returned. + * + * @param label Label name + * @return Time associated with the label (or -1 if there is no such label) + */ + public function getLabelTime(label:String):Number { + return (label in _labels) ? Number(_labels[label]) : -1; + } + + /** @private **/ + protected function _parseTimeOrLabel(timeOrLabel:*, offsetOrLabel:*=0, appendIfAbsent:Boolean=false, ignore:Object=null):Number { + var i:int; + //if we're about to add a tween/timeline (or an array of them) that's already a child of this timeline, we should remove it first so that it doesn't contaminate the duration(). + if (ignore is Animation && ignore.timeline === this) { + remove(ignore); + } else if (ignore is Array) { + i = ignore.length; + while (--i > -1) { + if (ignore[i] is Animation && ignore[i].timeline === this) { + remove(ignore[i]); + } + } + } + if (typeof(offsetOrLabel) === "string") { + return _parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && !(offsetOrLabel in _labels)) ? timeOrLabel - duration() : 0, appendIfAbsent); + } + offsetOrLabel = offsetOrLabel || 0; + if (typeof(timeOrLabel) === "string" && (isNaN(timeOrLabel) || (timeOrLabel in _labels))) { //if the string is a number like "1", check to see if there's a label with that name, otherwise interpret it as a number (absolute value). + i = timeOrLabel.indexOf("="); + if (i === -1) { + if (!(timeOrLabel in _labels)) { + return appendIfAbsent ? (_labels[timeOrLabel] = duration() + offsetOrLabel) : offsetOrLabel; + } + return _labels[timeOrLabel] + offsetOrLabel; + } + offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1)); + timeOrLabel = (i > 1) ? _parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : duration(); + } else if (timeOrLabel == null) { + timeOrLabel = duration(); + } + return Number(timeOrLabel) + offsetOrLabel; + } + + /** + * Jumps to a specific time (or label) without affecting whether or not the instance + * is paused or reversed. + * + *

If there are any events/callbacks inbetween where the playhead was and the new time, + * they will not be triggered because by default suppressEvents (the 2nd parameter) + * is true. Think of it like picking the needle up on a record player and moving it + * to a new position before placing it back on the record. If, however, you do not want the + * events/callbacks suppressed during that initial move, simply set the suppressEvents + * parameter to false.

+ * + * +//jumps to exactly 2 seconds +myAnimation.seek(2); + +//jumps to exactly 2 seconds but doesn't suppress events during the initial move: +myAnimation.seek(2, false); + +//jumps to the "myLabel" label +myAnimation.seek("myLabel"); + + * + * @param position The position to go to, described in any of the following ways: a numeric value indicates an absolute position, like 3 would be exactly 3 seconds from the beginning of the timeline. A string value can be either a label (i.e. "myLabel") or a relative value using the "+=" or "-=" prefixes like "-=2" (2 seconds before the end of the timeline) or a combination like "myLabel+=2" to indicate 2 seconds after "myLabel". + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the time parameter. + * @return self (makes chaining easier) + * @see #time() + * @see #totalTime() + * @see #play() + * @see #reverse() + * @see #pause() + */ + override public function seek(position:*, suppressEvents:Boolean=true):* { + return totalTime((typeof(position) === "number") ? Number(position) : _parseTimeOrLabel(position), suppressEvents); + } + + /** [deprecated] Pauses the timeline (used for consistency with Flash's MovieClip.stop() functionality, but essentially accomplishes the same thing as pause() without the parameter) @return self (makes chaining easier) **/ + public function stop():* { + return paused(true); + } + + /** + * @private + * [deprecated] + * Skips to a particular time, frame, or label and plays the timeline forward from there (unpausing it) + * + * @param position The position to go to, described in any of the following ways: a numeric value indicates an absolute position, like 3 would be exactly 3 seconds from the beginning of the timeline. A string value can be either a label (i.e. "myLabel") or a relative value using the "+=" or "-=" prefixes like "-=2" (2 seconds before the end of the timeline) or a combination like "myLabel+=2" to indicate 2 seconds after "myLabel". + * @param suppressEvents If true, no events or callbacks will be triggered as the "virtual playhead" moves to the new position (onComplete, onUpdate, onReverseComplete, etc. of this timeline and any of its child tweens/timelines won't be triggered, nor will any of the associated events be dispatched) + */ + public function gotoAndPlay(position:*, suppressEvents:Boolean=true):* { + return play(position, suppressEvents); + } + + /** + * @private + * [deprecated] + * Skips to a particular time, frame, or label and stops the timeline (pausing it) + * + * @param position The position to go to, described in any of the following ways: a numeric value indicates an absolute position, like 3 would be exactly 3 seconds from the beginning of the timeline. A string value can be either a label (i.e. "myLabel") or a relative value using the "+=" or "-=" prefixes like "-=2" (2 seconds before the end of the timeline) or a combination like "myLabel+=2" to indicate 2 seconds after "myLabel". + * @param suppressEvents If true, no events or callbacks will be triggered as the "virtual playhead" moves to the new position (onComplete, onUpdate, onReverseComplete, etc. of this timeline and any of its child tweens/timelines won't be triggered, nor will any of the associated events be dispatched) + */ + public function gotoAndStop(position:*, suppressEvents:Boolean=true):* { + return pause(position, suppressEvents); + } + + /** + * @private + * Renders all tweens and sub-timelines in the state they'd be at a particular time (or frame for frames-based timelines). + * + * @param time time in seconds (or frames for frames-based timelines) that should be rendered. + * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.) + * @param force Normally the tween will skip rendering if the time matches the cachedTotalTime (to improve performance), but if force is true, it forces a render. This is primarily used internally for tweens with durations of zero in TimelineLite/Max instances. + */ + override public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + if (_gc) { + _enabled(true, false); + } + var totalDur:Number = (!_dirty) ? _totalDuration : totalDuration(), + prevTime:Number = _time, + prevStart:Number = _startTime, + prevTimeScale:Number = _timeScale, + prevPaused:Boolean = _paused, + tween:Animation, isComplete:Boolean, next:Animation, callback:String, internalForce:Boolean; + if (time >= totalDur) { + _totalTime = _time = totalDur; + if (!_reversed) if (!_hasPausedChild()) { + isComplete = true; + callback = "onComplete"; + if (_duration === 0) if (time === 0 || _rawPrevTime < 0 || _rawPrevTime === _tinyNum) if (_rawPrevTime !== time && _first != null) { + internalForce = true; + if (_rawPrevTime > _tinyNum) { + callback = "onReverseComplete"; + } + } + } + _rawPrevTime = (_duration !== 0 || !suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + time = totalDur + 0.0001; //to avoid occasional floating point rounding errors in Flash - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when Flash performed _time - tween._startTime, floating point errors would return a value that was SLIGHTLY off) + + } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. + _totalTime = _time = 0; + if (prevTime !== 0 || (_duration === 0 && _rawPrevTime !== _tinyNum && (_rawPrevTime > 0 || (time < 0 && _rawPrevTime >= 0)))) { + callback = "onReverseComplete"; + isComplete = _reversed; + } + if (time < 0) { + _active = false; + if (_rawPrevTime >= 0 && _first != null) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + internalForce = true; + } + _rawPrevTime = time; + } else { + _rawPrevTime = (_duration || !suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline) + if (!_initted) { + internalForce = true; + } + } + + } else { + _totalTime = _time = _rawPrevTime = time; + } + + if ((_time == prevTime || !_first) && !force && !internalForce) { + return; + } else if (!_initted) { + _initted = true; + } + if (!_active) if (!_paused && _time !== prevTime && time > 0) { + _active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example. + } + if (prevTime == 0) if (vars.onStart) if (_time != 0) if (!suppressEvents) { + vars.onStart.apply(null, vars.onStartParams); + } + + if (_time >= prevTime) { + tween = _first; + while (tween) { + next = tween._next; //record it here because the value could change after rendering... + if (_paused && !prevPaused) { //in case a tween pauses the timeline when rendering + break; + } else if (tween._active || (tween._startTime <= _time && !tween._paused && !tween._gc)) { + + if (!tween._reversed) { + tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); + } else { + tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); + } + + } + tween = next; + } + } else { + tween = _last; + while (tween) { + next = tween._prev; //record it here because the value could change after rendering... + if (_paused && !prevPaused) { //in case a tween pauses the timeline when rendering + break; + } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { + + if (!tween._reversed) { + tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); + } else { + tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); + } + + } + tween = next; + } + } + + if (_onUpdate != null) if (!suppressEvents) { + _onUpdate.apply(null, vars.onUpdateParams); + } + + if (callback) if (!_gc) if (prevStart == _startTime || prevTimeScale != _timeScale) if (_time == 0 || totalDur >= totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate + if (isComplete) { + if (_timeline.autoRemoveChildren) { + _enabled(false, false); + } + _active = false; + } + if (!suppressEvents) if (vars[callback]) { + vars[callback].apply(null, vars[callback + "Params"]); + } + } + } + + + /** + * @private + * Checks the timeline to see if it has any paused children (tweens/timelines). + * + * @return Indicates whether or not the timeline contains any paused children + */ + public function _hasPausedChild():Boolean { + var tween:Animation = _first; + while (tween) { + if (tween._paused || ((tween is TimelineLite) && TimelineLite(tween)._hasPausedChild())) { + return true; + } + tween = tween._next; + } + return false; + } + + /** + * Returns an array containing all the tweens and/or timelines nested in this timeline. + * Callbacks (delayed calls) are considered zero-duration tweens. + * + * @param nested Determines whether or not tweens and/or timelines that are inside nested timelines should be returned. If you only want the "top level" tweens/timelines, set this to false. + * @param tweens Determines whether or not tweens (TweenLite and TweenMax instances) should be included in the results + * @param timelines Determines whether or not timelines (TimelineLite and TimelineMax instances) should be included in the results + * @param ignoreBeforeTime All children with start times that are less than this value will be ignored. + * @return an Array containing the child tweens/timelines. + */ + public function getChildren(nested:Boolean=true, tweens:Boolean=true, timelines:Boolean=true, ignoreBeforeTime:Number=-9999999999):Array { + var a:Array = [], + tween:Animation = _first, + cnt:int = 0; + while (tween) { + if (tween._startTime < ignoreBeforeTime) { + //do nothing + } else if (tween is TweenLite) { + if (tweens) { + a[cnt++] = tween; + } + } else { + if (timelines) { + a[cnt++] = tween; + } + if (nested) { + a = a.concat(TimelineLite(tween).getChildren(true, tweens, timelines)); + cnt = a.length; + } + } + tween = tween._next; + } + return a; + } + + /** + * Returns the tweens of a particular object that are inside this timeline. + * + * @param target The target object of the tweens + * @param nested Determines whether or not tweens that are inside nested timelines should be returned. If you only want the "top level" tweens/timelines, set this to false. + * @return an Array of TweenLite and/or TweenMax instances + */ + public function getTweensOf(target:Object, nested:Boolean=true):Array { + var disabled:Boolean = this._gc, + a:Array = [], + cnt:int = 0, + tweens:Array, i:int; + if (disabled) { + _enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here. + } + tweens = TweenLite.getTweensOf(target); + i = tweens.length; + while (--i > -1) { + if (tweens[i].timeline === this || (nested && _contains(tweens[i]))) { + a[cnt++] = tweens[i]; + } + } + if (disabled) { + _enabled(false, true); + } + return a; + } + + /** @private **/ + private function _contains(tween:Animation):Boolean { + var tl:SimpleTimeline = tween.timeline; + while (tl) { + if (tl == this) { + return true; + } + tl = tl.timeline; + } + return false; + } + + /** + * Shifts the startTime of the timeline's children by a certain amount and optionally adjusts labels too. + * This can be useful when you want to prepend children or splice them into a certain spot, moving existing + * ones back to make room for the new ones. + * + * @param amount Number of seconds (or frames for frames-based timelines) to move each child. + * @param adjustLabels If true, the timing of all labels will be adjusted as well. + * @param ignoreBeforeTime All children that begin at or after the startAtTime will be affected by the shift (the default is 0, causing all children to be affected). This provides an easy way to splice children into a certain spot on the timeline, pushing only the children after that point back to make room. + * @return self (makes chaining easier) + */ + public function shiftChildren(amount:Number, adjustLabels:Boolean=false, ignoreBeforeTime:Number=0):* { + var tween:Animation = _first; + while (tween) { + if (tween._startTime >= ignoreBeforeTime) { + tween._startTime += amount; + } + tween = tween._next; + } + if (adjustLabels) { + for (var p:String in _labels) { + if (_labels[p] >= ignoreBeforeTime) { + _labels[p] += amount; + } + } + } + _uncache(true); + return this; + } + + /** @private **/ + override public function _kill(vars:Object=null, target:Object=null):Boolean { + if (vars == null) if (target == null) { + return _enabled(false, false); + } + var tweens:Array = (target == null) ? getChildren(true, true, false) : getTweensOf(target), + i:int = tweens.length, + changed:Boolean = false; + while (--i > -1) { + if (tweens[i]._kill(vars, target)) { + changed = true; + } + } + return changed; + } + + + /** + * Empties the timeline of all tweens, timelines, and callbacks (and optionally labels too). + * Event callbacks (like onComplete, onUpdate, onStart, etc.) are not removed. If you need + * to remove event callbacks, use the eventCallback() method and set them to null + * like myTimeline.eventCallback("onComplete", null); + * + * @param labels If true (the default), labels will be cleared too. + * @return self (makes chaining easier) + */ + public function clear(labels:Boolean=true):* { + var tweens:Array = getChildren(false, true, true), + i:int = tweens.length; + _time = _totalTime = 0; + while (--i > -1) { + tweens[i]._enabled(false, false); + } + if (labels) { + _labels = {}; + } + return _uncache(true); + } + + + /** @inheritDoc **/ + override public function invalidate():* { + var tween:Animation = _first; + while (tween) { + tween.invalidate(); + tween = tween._next; + } + return this; + } + + /** @private **/ + override public function _enabled(enabled:Boolean, ignoreTimeline:Boolean=false):Boolean { + if (enabled == _gc) { + var tween:Animation = _first; + while (tween) { + tween._enabled(enabled, true); + tween = tween._next; + } + } + return super._enabled(enabled, ignoreTimeline); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------------------------------------- + + + /** + * Gets the timeline's duration or, if used as a setter, adjusts the timeline's + * timeScale to fit it within the specified duration. duration() is identical + * to totalDuration() except for TimelineMax instances that have a non-zero repeat + * in which case totalDuration includes repeats and repeatDelays whereas duration doesn't. + * For example, if a TimelineMax instance has a duration of 2 and a repeat of 3, + * its totalDuration would be 8 (one standard play plus 3 repeats equals 4 total cycles). + * + *

Due to the fact that a timeline's duration is dictated by its contents, + * using this method as a setter will simply cause the timeScale to be adjusted + * to fit the current contents into the specified duration, but the duration + * value itself will remain unchanged. For example, if there are 20-seconds worth of tweens in the timeline + * and you do myTimeline.duration(10), the timeScale would be changed to 2. + * If you checked the duration again immediately after that, it would still return 20 because + * technically that is how long all the child tweens/timelines are but upon playback the speed would + * be doubled because of the timeScale.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.duration(2).play(1);

+ * + * + var currentDuration = myAnimation.duration(); //gets current duration + myAnimation.duration( 10 ); //adjusts the timeScale of myAnimation so that it fits into exactly 10 seconds on its parent timeline + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #totalDuration() + * @see #timeScale() + **/ + override public function duration(value:Number=NaN):* { + if (!arguments.length) { + if (_dirty) { + totalDuration(); //just triggers recalculation + } + return _duration; + } + if (duration() !== 0) if (value !== 0) { + timeScale(_duration / value); + } + return this; + } + + /** + * Gets the timeline's total duration or, if used as a setter, adjusts the timeline's + * timeScale to fit it within the specified duration. For example, if a TimelineMax instance has + * a duration of 2 and a repeat of 3, its totalDuration + * would be 8 (one standard play plus 3 repeats equals 4 total cycles). + * + *

Due to the fact that a timeline's totalDuration is dictated by its contents, + * using this method as a setter will simply cause the timeScale to be adjusted + * to fit the current contents into the specified totalDuration. For example, + * if there are 20-seconds worth of tweens in the timeline and you do myTimeline.totalDuration(10), + * the timeScale would be changed to 2. If you checked the totalDuration again + * immediately after that, it would still return 20 because technically that is how long all the + * child tweens/timelines are but upon playback the speed would be doubled because of the + * timeScale.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.totalDuration(2).play(1);

+ * + * +var ctd = myAnimation.totalDuration(); //gets current total duration +myAnimation.totalDuration( 20 ); //adjusts the timeScale so that myAnimation fits into exactly 20 seconds on its parent timeline + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #timeScale() + * @see #duration() + **/ + override public function totalDuration(value:Number=NaN):* { + if (!arguments.length) { + if (_dirty) { + var max:Number = 0, + tween:Animation = _last, + prevStart:Number = Infinity, + prev:Animation, end:Number; + while (tween) { + prev = tween._prev; //record it here in case the tween changes position in the sequence... + if (tween._dirty) { + tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it. + } + if (tween._startTime > prevStart && _sortChildren && !tween._paused) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence + add(tween, tween._startTime - tween._delay); + } else { + prevStart = tween._startTime; + } + if (tween._startTime < 0 && !tween._paused) { //children aren't allowed to have negative startTimes unless smoothChildTiming is true, so adjust here if one is found. + max -= tween._startTime; + if (_timeline.smoothChildTiming) { + _startTime += tween._startTime / _timeScale; + } + shiftChildren(-tween._startTime, false, -9999999999); + prevStart = 0; + } + end = tween._startTime + (tween._totalDuration / tween._timeScale); + if (end > max) { + max = end; + } + tween = prev; + } + _duration = _totalDuration = max; + _dirty = false; + } + return _totalDuration; + } + if (totalDuration() != 0) if (value != 0) { + timeScale( _totalDuration / value ); + } + return this; + } + + /** + * [READ-ONLY] If true, the timeline's timing mode is frames-based instead of + * seconds. This can only be set to true by passing useFrames:true in + * the vars parameter of the constructor, or by nesting this timeline in another whose + * timing mode is frames-based. An animation's timing mode is always determined by its parent timeline). + **/ + public function usesFrames():Boolean { + var tl:SimpleTimeline = _timeline; + while (tl._timeline) { + tl = tl._timeline; + } + return (tl == _rootFramesTimeline); + } + + /** + * @private + * Reports the totalTime of the timeline without capping the number at the totalDuration (max) + * and zero (minimum) which can be useful when unpausing tweens/timelines. Imagine a case where a paused + * tween is in a timeline that has already reached the end, but then the tween gets unpaused - it needs a + * way to place itself accurately in time AFTER what was previously the timeline's end time. + * + * @return The totalTime of the timeline without capping the number at the totalDuration (max) and zero (minimum) + */ + override public function rawTime():Number { + return _paused ? _totalTime : (_timeline.rawTime() - _startTime) * _timeScale; + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/TimelineMax.as b/src/com/greensock/TimelineMax.as new file mode 100644 index 0000000..88f0f99 --- /dev/null +++ b/src/com/greensock/TimelineMax.as @@ -0,0 +1,1524 @@ +/** + * VERSION: 12.1.5 + * DATE: 2014-07-19 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com/timelinemax/ + **/ +package com.greensock { + import com.greensock.core.SimpleTimeline; + import com.greensock.core.Animation; + import com.greensock.easing.Ease; + import com.greensock.events.TweenEvent; + + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; +/** + * TimelineMax extends TimelineLite, offering exactly the same functionality plus useful + * (but non-essential) features like repeat, repeatDelay, yoyo, currentLabel(), addCallback(), + * removeCallback(), tweenTo(), tweenFromTo(), getLabelAfter(), getLabelBefore(), + * getActive(), AS3 event dispatching (and probably more in the future). It is the ultimate + * sequencing tool that acts like a container for tweens and other timelines, making it + * simple to control them as a whole and precisely manage their timing. Without TimelineMax + * (or its little brother TimelineLite), building complex sequences would be far more cumbersome + * because you'd need to use the delay special property for everything which would + * make future edits far more tedius. Here is a basic example: + * +TweenLite.to(mc, 1, {x:100}); +TweenLite.to(mc, 1, {y:50, delay:1}); +TweenLite.to(mc, 1, {alpha:0, delay:2}); + + * The above code animates mc.x to 100, then mc.y to 50, and finally + * mc.alpha to 0 (notice the delay in all but the first tween). But + * imagine if you wanted to increase the duration of the first tween to 1.5 - you'd need to + * adjust every delay thereafter. And what if you want to pause() the whole + * sequence or restart() it or reverse() it on-the-fly or repeat + * it twice? This becomes quite messy (or flat-out impossible), but TimelineMax makes it + * incredibly simple: + * + * +var tl = new TimelineMax({repeat:2, repeatDelay:1}); +tl.add( TweenLite.to(mc, 1, {x:100}) ); +tl.add( TweenLite.to(mc, 1, {y:50}) ); +tl.add( TweenLite.to(mc, 1, {alpha:0}) ); + +//then later, control the whole thing... +tl.pause(); +tl.resume(); +tl.seek(1.5); +tl.reverse(); +... + + * Or use the convenient to() method and chaining to make it even shorter: + * +var tl = new TimelineMax(); +tl.to(mc, 1, {x:100}).to(mc, 1, {y:50}).to(mc, 1, {alpha:0}); + + * + *

Now you can feel free to adjust any of the tweens without worrying about trickle-down + * changes to delays. Increase the duration of that first tween and everything automatically + * adjusts.

+ * + *

Here are some other benefits and features of TimelineMax:

+ * + *
    + *
  • Things can overlap on the timeline as much as you want. You have complete control + * over where tweens/timelines are placed. Most other animation tools can only do basic + * one-after-the-other sequencing but can't allow things to overlap. Imagine appending + * a tween that moves an object and you want it to start fading out 0.5 seconds before the + * end of that tween? With TimelineMax it's easy.
  • + * + *
  • Add labels, callbacks, play(), stop(), seek(), restart(), and even reverse() smoothly anytime.
  • + * + *
  • Nest timelines within timelines as deeply as you want. This means you can modularize + * your code and make it far more efficient. Imagine building your app with common animateIn() + * and animateOut() methods that return a tween or timeline instance, then you can string + * things together like + * myTimeline.add( myObject.animateIn() ).add( myObject.animateOut(), "+=4").add( myObject2.animateIn(), "-=0.5")...
  • + * + *
  • Speed up or slow down the entire timeline with its timeScale() method. + * You can even tween it to gradually speed up or slow down the animation smoothly.
  • + * + *
  • Get or set the progress of the timeline using its progress() or + * totalProgress() methods. For example, to skip to the halfway point, + * set myTimeline.progress(0.5);
  • + * + *
  • Tween the time, totalTime, progress, or totalProgress to + * fastforward/rewind the timeline. You could even attach a slider to one of these to give the + * user the ability to drag forward/backward through the timeline.
  • + * + *
  • Add onComplete, onStart, onUpdate, onRepeat and/or onReverseComplete + * callbacks using the constructor's vars object like + * var tl = new TimelineMax({onComplete:myFunction});
  • + * + *
  • Kill the tweens of a particular object inside the timeline with kill(null, target) + * or get the tweens of an object with getTweensOf() or get all the tweens/timelines + * in the timeline with getChildren()
  • + * + *
  • Set the timeline to repeat any number of times or indefinitely. You can even set a delay + * between each repeat cycle and/or cause the repeat cycles to yoyo, appearing to reverse direction + * every other cycle.
  • + * + *
  • listen for START, UPDATE, REPEAT, REVERSE_COMPLETE, and COMPLETE events.
  • + * + *
  • get the active tweens in the timeline with getActive().
  • + * + *
  • By passing useFrames:true in the vars parameter, you can + * base the timing on frames instead of seconds. Please note, however, that + * the timeline's timing mode dictates its childrens' timing mode as well.
  • + * + *
  • Get the currentLabel() or find labels at various positions in the timeline + * using getLabelAfter() and getLabelBefore()
  • + * + *
  • You can export all the tween/timelines from the root (master) timeline anytime into + * a TimelineLite instance using TimelineLite.exportRoot() so that + * you can pause() them all or reverse() or alter their + * timeScale, etc. without affecting tweens/timelines that you create in + * the future. Imagine a game that has all its animation driven by the GreenSock + * Animation Platform and it needs to pause or slow down while a status screen pops up. + * Very easy.
  • + * + *
+ * + * + *

SPECIAL PROPERTIES:

+ *

You can optionally use the constructor's vars parameter to define any of + * the special properties below (syntax example: new TimelineMax({onComplete:myFunction, repeat:2, repeatDelay:1, yoyo:true});

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the timeline should begin.
  • + * + *
  • paused : Boolean - + * If true, the timeline will pause itself immediately upon creation (by default, + * timelines automatically begin playing immediately). If you plan to create a TimelineMax and + * then populate it later (after one or more frames elapse), it is typically best to set + * paused:true and then play() after you populate it.
  • + * + *
  • onComplete : Function - + * A function that should be called when the timeline has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * new TimelineMax({onComplete:myFunction, onCompleteParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onCompleteParams:["{self}", "param2"]
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the timelines's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • tweens : Array - + * To immediately insert several tweens into the timeline, use the tweens + * special property to pass in an Array of TweenLite/TweenMax/TimelineLite/TimelineMax + * instances. You can use this in conjunction with the align and + * stagger special properties to set up complex sequences with minimal code. + * These values simply get passed to the add() method.
  • + * + *
  • align : String - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. The value simply gets passed to the + * add() method. The default is "normal". + * Options are: + *
      + *
    • "sequence": aligns the tweens one-after-the-other in a sequence
    • + *
    • "start": aligns the start times of all of the tweens (ignores delays)
    • + *
    • "normal": aligns the start times of all the tweens (honors delays)
    • + *
    + * The align special property does not force all child + * tweens/timelines to maintain relative positioning, so for example, if you use + * "sequence" and then later change the duration of one of the nested tweens, + * it does not force all subsequent timelines to change their position. + * The align special property only affects the alignment of the tweens that are + * initially placed into the timeline through the tweens special property of + * the vars object.
  • + * + *
  • stagger : Number - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. It staggers the tweens by a set amount of time + * in seconds (or in frames if useFrames is true). For example, if the + * stagger value is 0.5 and the "align" property is set to "start", the + * second tween will start 0.5 seconds after the first one starts, then 0.5 seconds + * later the third one will start, etc. If the align property is "sequence", + * there would be 0.5 seconds added between each tween. This value simply gets + * passed to the add() method. Default is 0.
  • + * + *
  • onStart : Function - + * A function that should be called when the timeline begins (when its time + * changes from 0 to some other value which can happen more than once if the + * timeline is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * new TimelineMax({onStart:myFunction, onStartParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onStartParams:["{self}", "param2"]
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the timeline updates + * (on every frame while the timeline is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * new TimelineMax({onUpdate:myFunction, onUpdateParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onUpdateParams:["{self}", "param2"]
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the timeline has reached its beginning again from the + * reverse direction. For example, if reverse() is called, the timeline will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the timeline is placed in a TimelineLite or TimelineMax + * instance that gets reversed and plays the timeline backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * new TimelineMax({onReverseComplete:myFunction, onReverseCompleteParams:["param1", "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onReverseCompleteParams:["{self}", "param2"]
  • + * + *
  • autoRemoveChildren : Boolean - + * If autoRemoveChildren is set to true, as soon as child + * tweens/timelines complete, they will automatically get killed/removed. This is normally + * undesireable because it prevents going backwards in time (like if you want to + * reverse() or set the progress lower, etc.). It can, however, + * improve speed and memory management. The root timelines use autoRemoveChildren:true.
  • + * + *
  • smoothChildTiming : Boolean - + * Controls whether or not child tweens/timelines are repositioned automatically + * (changing their startTime) in order to maintain smooth playback when + * properties are changed on-the-fly. For example, imagine that the timeline's playhead is + * on a child tween that is 75% complete, moving mc.x from 0 to 100 and then that tween's + * reverse() method is called. If smoothChildTiming is false + * (the default except for the root timelines), the tween would flip in place, keeping its + * startTime consistent. Therefore the playhead of the timeline would now be + * at the tween's 25% completion point instead of 75%. Remember, the timeline's playhead + * position and direction are unaffected by child tween/timeline changes. mc.x would jump + * from 75 to 25, but the tween's position in the timeline would remain consistent. However, + * if smoothChildTiming is true, that child tween's + * startTime would be adjusted so that the timeline's playhead intersects + * with the same spot on the tween (75% complete) as it had immediately before + * reverse() was called, thus playback appears perfectly smooth. mc.x + * would still be 75 and it would continue from there as the playhead moves on, but + * since the tween is reversed now mc.x will travel back towards 0 instead of 100. + * Ultimately it's a decision between prioritizing smooth on-the-fly playback + * (true) or consistent position(s) of child tweens/timelines + * (false). + * + * Some examples of on-the-fly changes to child tweens/timelines that could cause their + * startTime to change when smoothChildTiming is true + * are: reversed, timeScale, progress, totalProgress, time, totalTime, delay, pause, + * resume, duration, and totalDuration.
  • + * + *
  • repeat : Number - + * Number of times that the timeline should repeat after its first iteration. For example, + * if repeat is 1, the timeline will play a total of twice (the initial play + * plus 1 repeat). To repeat indefinitely, use -1. repeat should always be an integer.
  • + * + *
  • repeatDelay : Number - + * Amount of time in seconds (or frames for frames-based timelines) between repeats. For example, + * if repeat is 2 and repeatDelay is 1, the timeline will play initially, + * then wait for 1 second before it repeats, then play again, then wait 1 second again before + * doing its final repeat.
  • + * + *
  • yoyo : Boolean - + * If true, every other repeat cycle will run in the opposite + * direction so that the timeline appears to go back and forth (forward then backward). + * This has no affect on the "reversed" property though. So if repeat + * is 2 and yoyo is false, it will look like: + * start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, + * it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end.
  • + * + *
  • onRepeat : Function - + * A function that should be called each time the timeline repeats
  • + * + *
  • onRepeatParams : Array - + * An Array of parameters to pass the onRepeat function. For example, + * new TimelineMax({repeat:3, onRepeat:myFunction, onRepeatParams:[mc, "param2"]}); + * To self-reference the timeline instance itself in one of the parameters, use "{self}", + * like: onRepeatParams:["{self}", "param2"]
  • + * + *
  • onStartListener : Function (AS3 only) - + * A function that should be called (and passed an event parameter) when the timeline begins + * (when its totalTime changes from 0 to some other value which can happen more + * than once if the timeline is restarted multiple times). Identical to onStart except + * that the function will always be passed an event parameter whose target property points + * to the timeline. It's the same as doing myTimeline.addEventListener("start", myFunction);. + * Unless you need the event parameter, it's better/faster to use onStart.
  • + * + *
  • onUpdateListener : Function (AS3 only) - + * A function that should be called (and passed an event parameter) each time the timeline updates + * (on every frame while the timeline is active). Identical to onUpdate except + * that the function will always be passed an event parameter whose target property points + * to the timeline. It's the same as doing myTimeline.addEventListener("update", myFunction);. + * Unless you need the event parameter, it's better/faster to use onUpdate.
  • + * + *
  • onCompleteListener : Function (AS3 only) - + * A function that should be called (and passed an event parameter) each time the timeline completes. + * Identical to onComplete except that the function will always be passed an event + * parameter whose target property points to the timeline. It's the same as doing + * myTimeline.addEventListener("complete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onComplete.
  • + * + *
  • onReverseCompleteListener : Function (AS3 only) - + * A function that should be called (and passed an event parameter) each time the timeline has reached + * its beginning again from the reverse direction. For example, if reverse() is called + * the timeline will move back towards its beginning and when its totalTime reaches 0, + * onReverseCompleteListener will be called. This can also happen if the timeline is placed + * in another TimelineLite or TimelineMax instance that gets reversed and plays the timeline backwards to + * (or past) the beginning. Identical to onReverseComplete except that the function + * will always be passed an event parameter whose target property points to the timeline. + * It's the same as doing myTimeline.addEventListener("reverseComplete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onReverseComplete.
  • + * + *
  • onRepeatListener : Function (AS3 only) - + * A function that should be called (and passed an event parameter) each time the timeline repeats. + * Identical to onRepeat except that the function will always be passed an event + * parameter whose target property points to the timeline. It's the same as doing + * myTimeline.addEventListener("repeat", myFunction);. + * Unless you need the event parameter, it's better/faster to use onRepeat.
  • + * + *
+ * + * @example Sample code:+//create the timeline that repeats 3 times with 1 second between each repeat and then calls myFunction() when it completes +var tl = new TimelineMax({repeat:3, repeatDelay:1, onComplete:myFunction}); + +//add a tween +tl.add( new TweenLite(mc, 1, {x:200, y:100}) ); + +//add another tween at the end of the timeline (makes sequencing easy) +tl.add( new TweenLite(mc, 0.5, {alpha:0}) ); + +//append a tween using the convenience method (shorter syntax) and offset it by 0.5 seconds +tl.to(mc, 1, {rotation:30}, "+=0.5"); + +//reverse anytime +tl.reverse(); + +//Add a "spin" label 3-seconds into the timeline +tl.addLabel("spin", 3); + +//insert a rotation tween at the "spin" label (you could also define the insertion point as the time instead of a label) +tl.add( new TweenLite(mc, 2, {rotation:"360"}), "spin"); + +//go to the "spin" label and play the timeline from there +tl.play("spin"); + +//nest another TimelineMax inside your timeline... +var nested = new TimelineMax(); +nested.to(mc2, 1, {x:200})); +tl.add(nested); + + * + *

How do timelines work? What are the mechanics like?

+ *

Every animation (tween and timeline) is placed on a parent timeline (except the 2 root timelines - there's one for normal tweens and another for "useFrames" ones). + * In a sense, they all have their own playheads (that's what its "time" refers to, or "totalTime" which is identical except that it includes repeats and repeatDelays) + * but generally they're not independent because they're sitting on a timeline whose playhead moves. + * When the parent's playhead moves to a new position, it updates the childrens' too.

+ * + *

When a timeline renders at a particular time, it loops through its children and says "okay, you should render as if your playhead is at ____" and if that child + * is a timeline with children, it does the same to its children, right on down the line.

+ * + *

The only exception is when the tween/timeline is paused in which case its internal playhead acts like it's "locked". So in that case, + * it's possible (likely in fact) that the child's playhead would not be synced with the parent's. + * When you unpause it (resume()), it essentially picks it up and moves it so that its internal playhead + * is synchronized with wherever the parent's playhead is at that moment, thus things play perfectly smoothly. + * That is, unless the timeline's smoothChildTiming is to false in which case it won't move - + * its startTime will remain locked to where it was.

+ * + *

So basically, when smoothChildTiming is true, the engine will rearrange things on + * the fly to ensure the playheads line up so that playback is seamless and smooth. The same thing happens when you reverse() + * or alter the timeScale, etc. But sometimes you might not want that behavior - you prefer to have tight + * control over exactly where your tweens line up in the timeline - that's when smoothChildTiming:false is handy.

+ * + *

One more example: let's say you've got a 10-second tween that's just sitting on the root timeline and you're 2-seconds into the tween. + * Let's assume it started at exactly 0 on the root to make this easy, and then when it's at 2-seconds, you do tween.seek(5). + * The playhead of the root isn't affected - it keeps going exactly as it always did, but in order to make that tween jump to 5 seconds + * and play appropriately, the tween's startTime gets changed to -3. That way, the tween's playhead and the root + * playhead are perfectly aligned.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + * + **/ + public class TimelineMax extends TimelineLite implements IEventDispatcher { + /** @private **/ + public static const version:String = "12.1.5"; + /** @private **/ + protected static var _listenerLookup:Object = {onCompleteListener:TweenEvent.COMPLETE, onUpdateListener:TweenEvent.UPDATE, onStartListener:TweenEvent.START, onRepeatListener:TweenEvent.REPEAT, onReverseCompleteListener:TweenEvent.REVERSE_COMPLETE}; + /** @private **/ + protected static var _easeNone:Ease = new Ease(null, null, 1, 0); + + /** @private **/ + protected var _repeat:int; + /** @private **/ + protected var _repeatDelay:Number; + /** @private **/ + protected var _cycle:int = 0; + /** @private **/ + protected var _locked:Boolean; + /** @private **/ + protected var _dispatcher:EventDispatcher; + /** @private **/ + protected var _hasUpdateListener:Boolean; + + /** + * @private + * Works in conjunction with the repeat property, determining the behavior of each cycle; when yoyo is true, + * the timeline will go back and forth, appearing to reverse every other cycle (this has no affect on the reversed property though). + * So if repeat is 2 and yoyo is false, it will look like: start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. + * But if repeat is 2 and yoyo is true, it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end. + **/ + protected var _yoyo:Boolean; + + /** + * Constructor. + * + *

SPECIAL PROPERTIES

+ *

The following special properties may be passed in via the constructor's vars parameter, like + * new TimelineMax({paused:true, onComplete:myFunction, repeat:2, yoyo:true})

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the timeline should begin.
  • + * + *
  • paused : Boolean - + * If true, the timeline will pause itself immediately upon creation (by default, + * timelines automatically begin playing immediately). If you plan to create a TimelineMax and + * then populate it later (after one or more frames elapse), it is typically best to set + * paused:true and then play() after you populate it.
  • + * + *
  • onComplete : Function - + * A function that should be called when the timeline has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * new TimelineMax({onComplete:myFunction, onCompleteParams:["param1", "param2"]});
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the timelines's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • tweens : Array - + * To immediately insert several tweens into the timeline, use the tweens + * special property to pass in an Array of TweenLite/TweenMax/TimelineLite/TimelineMax + * instances. You can use this in conjunction with the align and + * stagger special properties to set up complex sequences with minimal code. + * These values simply get passed to the add() method.
  • + * + *
  • align : String - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. The value simply gets passed to the + * add() method. The default is "normal". + * Options are: + *
      + *
    • "sequence": aligns the tweens one-after-the-other in a sequence
    • + *
    • "start": aligns the start times of all of the tweens (ignores delays)
    • + *
    • "normal": aligns the start times of all the tweens (honors delays)
    • + *
    + * The align special property does not force all child + * tweens/timelines to maintain relative positioning, so for example, if you use + * "sequence" and then later change the duration of one of the nested tweens, + * it does not force all subsequent timelines to change their position. + * The align special property only affects the alignment of the tweens that are + * initially placed into the timeline through the tweens special property of + * the vars object.
  • + * + *
  • stagger : Number - + * Only used in conjunction with the tweens special property when multiple + * tweens are to be inserted immediately. It staggers the tweens by a set amount of time + * in seconds (or in frames if useFrames is true). For example, if the + * stagger value is 0.5 and the "align" property is set to "start", the + * second tween will start 0.5 seconds after the first one starts, then 0.5 seconds + * later the third one will start, etc. If the align property is "sequence", + * there would be 0.5 seconds added between each tween. This value simply gets + * passed to the add() method. Default is 0.
  • + * + *
  • onStart : Function - + * A function that should be called when the timeline begins (when its time + * changes from 0 to some other value which can happen more than once if the + * timeline is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * new TimelineMax({onStart:myFunction, onStartParams:["param1", "param2"]});
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the timeline updates + * (on every frame while the timeline is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * new TimelineMax({onUpdate:myFunction, onUpdateParams:["param1", "param2"]});
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the timeline has reached its beginning again from the + * reverse direction. For example, if reverse() is called, the timeline will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the timeline is placed in a TimelineLite or TimelineMax + * instance that gets reversed and plays the timeline backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * new TimelineMax({onReverseComplete:myFunction, onReverseCompleteParams:["param1", "param2"]});
  • + * + *
  • autoRemoveChildren : Boolean - + * If autoRemoveChildren is set to true, as soon as child + * tweens/timelines complete, they will automatically get killed/removed. This is normally + * undesireable because it prevents going backwards in time (like if you want to + * reverse() or set the progress lower, etc.). It can, however, + * improve speed and memory management. The root timelines use autoRemoveChildren:true.
  • + * + *
  • smoothChildTiming : Boolean - + * Controls whether or not child tweens/timelines are repositioned automatically + * (changing their startTime) in order to maintain smooth playback when + * properties are changed on-the-fly. For example, imagine that the timeline's playhead is + * on a child tween that is 75% complete, moving mc.x from 0 to 100 and then that tween's + * reverse() method is called. If smoothChildTiming is false + * (the default except for the root timelines), the tween would flip in place, keeping its + * startTime consistent. Therefore the playhead of the timeline would now be + * at the tween's 25% completion point instead of 75%. Remember, the timeline's playhead + * position and direction are unaffected by child tween/timeline changes. mc.x would jump + * from 75 to 25, but the tween's position in the timeline would remain consistent. However, + * if smoothChildTiming is true, that child tween's + * startTime would be adjusted so that the timeline's playhead intersects + * with the same spot on the tween (75% complete) as it had immediately before + * reverse() was called, thus playback appears perfectly smooth. mc.x + * would still be 75 and it would continue from there as the playhead moves on, but + * since the tween is reversed now mc.x will travel back towards 0 instead of 100. + * Ultimately it's a decision between prioritizing smooth on-the-fly playback + * (true) or consistent position(s) of child tweens/timelines + * (false). + * + * Some examples of on-the-fly changes to child tweens/timelines that could cause their + * startTime to change when smoothChildTiming is true + * are: reversed, timeScale, progress, totalProgress, time, totalTime, delay, pause, + * resume, duration, and totalDuration.
  • + * + *
  • repeat : Number - + * Number of times that the timeline should repeat after its first iteration. For example, + * if repeat is 1, the timeline will play a total of twice (the initial play + * plus 1 repeat). To repeat indefinitely, use -1. repeat should always be an integer.
  • + * + *
  • repeatDelay : Number - + * Amount of time in seconds (or frames for frames-based timelines) between repeats. For example, + * if repeat is 2 and repeatDelay is 1, the timeline will play initially, + * then wait for 1 second before it repeats, then play again, then wait 1 second again before + * doing its final repeat.
  • + * + *
  • yoyo : Boolean - + * If true, every other repeat cycle will run in the opposite + * direction so that the timeline appears to go back and forth (forward then backward). + * This has no affect on the "reversed" property though. So if repeat + * is 2 and yoyo is false, it will look like: + * start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, + * it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end.
  • + * + *
  • onRepeat : Function - + * A function that should be called each time the timeline repeats
  • + * + *
  • onRepeatParams : Array - + * An Array of parameters to pass the onRepeat function. For example, + * new TimelineMax({repeat:3, onRepeat:myFunction, onRepeatParams:[mc, "param2"]});
  • + * + *
  • onStartListener : Function - + * A function that should be called (and passed an event parameter) when the timeline begins + * (when its totalTime changes from 0 to some other value which can happen more + * than once if the timeline is restarted multiple times). Identical to onStart except + * that the function will always be passed an event parameter whose target property points + * to the timeline. It's the same as doing myTimeline.addEventListener("start", myFunction);. + * Unless you need the event parameter, it's better/faster to use onStart.
  • + * + *
  • onUpdateListener : Function - + * A function that should be called (and passed an event parameter) each time the timeline updates + * (on every frame while the timeline is active). Identical to onUpdate except + * that the function will always be passed an event parameter whose target property points + * to the timeline. It's the same as doing myTimeline.addEventListener("update", myFunction);. + * Unless you need the event parameter, it's better/faster to use onUpdate.
  • + * + *
  • onCompleteListener : Function - + * A function that should be called (and passed an event parameter) each time the timeline completes. + * Identical to onComplete except that the function will always be passed an event + * parameter whose target property points to the timeline. It's the same as doing + * myTimeline.addEventListener("complete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onComplete.
  • + * + *
  • onReverseCompleteListener : Function - + * A function that should be called (and passed an event parameter) each time the timeline has reached + * its beginning again from the reverse direction. For example, if reverse() is called + * the timeline will move back towards its beginning and when its totalTime reaches 0, + * onReverseCompleteListener will be called. This can also happen if the timeline is placed + * in another TimelineLite or TimelineMax instance that gets reversed and plays the timeline backwards to + * (or past) the beginning. Identical to onReverseComplete except that the function + * will always be passed an event parameter whose target property points to the timeline. + * It's the same as doing myTimeline.addEventListener("reverseComplete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onReverseComplete.
  • + * + *
  • onRepeatListener : Function - + * A function that should be called (and passed an event parameter) each time the timeline repeats. + * Identical to onRepeat except that the function will always be passed an event + * parameter whose target property points to the timeline. It's the same as doing + * myTimeline.addEventListener("repeat", myFunction);. + * Unless you need the event parameter, it's better/faster to use onRepeat.
  • + * + *
+ * + * @param vars optionally pass in special properties like useFrames, onComplete, onCompleteParams, onUpdate, onUpdateParams, onStart, onStartParams, tweens, align, stagger, delay, autoRemoveChildren, onCompleteListener, onStartListener, onUpdateListener, repeat, repeatDelay, and/or yoyo. + */ + public function TimelineMax(vars:Object=null) { + super(vars); + _repeat = this.vars.repeat || 0; + _repeatDelay = this.vars.repeatDelay || 0; + _yoyo = (this.vars.yoyo == true); + _dirty = true; + if (this.vars.onCompleteListener || this.vars.onUpdateListener || this.vars.onStartListener || this.vars.onRepeatListener || this.vars.onReverseCompleteListener) { + _initDispatcher(); + } + } + + /** @inheritDoc **/ + override public function invalidate():* { + _yoyo = Boolean(this.vars.yoyo == true); + _repeat = this.vars.repeat || 0; + _repeatDelay = this.vars.repeatDelay || 0; + _hasUpdateListener = false; + _initDispatcher(); + _uncache(true); + return super.invalidate(); + } + + /** + * Inserts a callback at a particular position. The callback is technically considered a + * zero-duration tween, so if you getChildren() there will be a tween returned for each callback. + * You can discern a callback from other tweens by the fact that its target is a function matching + * its vars.onComplete and its duration is zero. + * + *

If your goal is to append the callback to the end of the timeline, it would be easier + * (more concise) to use the call() method. Technically the add() method + * can accommodate adding a callback too (like myTimeline.add(myFunction, 2) + * or myTimeline.add(myFunction, "+=2")) but add() doesn't accommodate parameters.

+ * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (4th) parameter for scope.

+ * + * @param function The function to be called + * @param position The time in seconds (or frames for frames-based timelines) or label at which the callback should be inserted. For example, myTimeline.addCallback(myFunction, 3) would call myFunction() 3 seconds into the timeline, and myTimeline.addCallback(myFunction, "myLabel") would call it at the "myLabel" label. myTimeline.addCallback(myFunction, "+=2") would insert the callback 2 seconds after the end of the timeline. + * @param params An Array of parameters to pass the callback + * @return self (makes chaining easier) + * + * @see #call() + * @see #add() + * @see #removeCallback() + */ + public function addCallback(callback:Function, position:*, params:Array=null):TimelineMax { + return add( TweenLite.delayedCall(0, callback, params), position) as TimelineMax; + } + + /** + * Removes a callback. If the position parameter + * is null, all callbacks of that function are removed from the timeline. + * + * @param function callback function to be removed + * @param position the time in seconds (or frames for frames-based timelines) or label from which the callback should be removed. For example, myTimeline.removeCallback(myFunction, 3) would remove the callback from 3-seconds into the timeline, and myTimeline.removeCallback(myFunction, "myLabel") would remove it from the "myLabel" label, and myTimeline.removeCallback(myFunction, null) would remove ALL callbacks of that function regardless of where they are on the timeline. + * @return self (makes chaining easier) + * + * @see #addCallback() + * @see #call() + * @see #kill() + */ + public function removeCallback(callback:Function, position:*=null):TimelineMax { + if (callback != null) { + if (position == null) { + _kill(null, callback); + } else { + var a:Array = getTweensOf(callback, false), + i:int = a.length, + time:Number = _parseTimeOrLabel(position); + while (--i > -1) { + if (a[i]._startTime === time) { + a[i]._enabled(false, false); + } + } + } + } + return this; + } + + /** + * Creates a linear tween that essentially scrubs the playhead to a particular time or label and + * then stops. For example, to make the TimelineMax play to the "myLabel2" label, simply do: + * + *

+ * myTimeline.tweenTo("myLabel2"); + *

+ * + *

If you want advanced control over the tween, like adding an onComplete or changing the ease or + * adding a delay, just pass in a vars object with the appropriate properties. For example, + * to tween to the 5-second point on the timeline and then call a function named myFunction + * and pass in a parameter that's references this TimelineMax and use a Strong.easeOut ease, you'd do:

+ * + *

+ * myTimeline.tweenTo(5, {onComplete:myFunction, onCompleteParams:[myTimeline], ease:Strong.easeOut}); + *

+ * + *

Remember, this method simply creates a TweenLite instance that pauses the timeline and then tweens + * the time() of the timeline. So you can store a reference to that tween if you want, and + * you can kill() it anytime. Also note that tweenTo() does NOT affect the timeline's + * reversed state. So if your timeline is oriented normally (not reversed) and you tween to + * a time/label that precedes the current time, it will appear to go backwards but the reversed + * state will not change to true. Also note that tweenTo() + * pauses the timeline immediately before tweening its time(), and it does not automatically + * resume after the tween completes. If you need to resume playback, you could always use an onComplete + * to call the timeline's resume() method.

+ * + *

If you plan to sequence multiple playhead tweens one-after-the-other, it is typically better to use + * tweenFromTo() so that you can define the starting point and ending point, allowing the + * duration to be accurately determined immediately.

+ * + * @param position The destination time in seconds (or frame if the timeline is frames-based) or label to which the timeline should play. For example, myTimeline.tweenTo(5) would play from wherever the timeline is currently to the 5-second point whereas myTimeline.tweenTo("myLabel") would play to wherever "myLabel" is on the timeline. + * @param vars An optional vars object that will be passed to the TweenLite instance. This allows you to define an onComplete, ease, delay, or any other TweenLite special property. + * @return A TweenLite instance that handles tweening the timeline to the desired time/label. + * + * @see #tweenFromTo() + * @see #seek() + */ + public function tweenTo(position:*, vars:Object=null):TweenLite { + vars = vars || {}; + var copy:Object = {ease:_easeNone, overwrite:(vars.delay ? 2 : 1), useFrames:usesFrames(), immediateRender:false}; + for (var p:String in vars) { + copy[p] = vars[p]; + } + copy.time = _parseTimeOrLabel(position); + var duration:Number = (Math.abs(Number(copy.time) - _time) / _timeScale) || 0.001; + var t:TweenLite = new TweenLite(this, duration, copy); + copy.onStart = function():void { + t.target.paused(true); + if (t.vars.time != t.target.time() && duration === t.duration()) { //don't make the duration zero - if it's supposed to be zero, don't worry because it's already initting the tween and will complete immediately, effectively making the duration zero anyway. If we make duration zero, the tween won't run at all. + t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale ); + } + if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it. + vars.onStart.apply(null, vars.onStartParams); + } + } + return t; + } + + /** + * Creates a linear tween that essentially scrubs the playhead from a particular time or label + * to another time or label and then stops. If you plan to sequence multiple playhead tweens + * one-after-the-other, tweenFromTo() is better to use than tweenTo() + * because it allows the duration to be determined immediately, ensuring that subsequent tweens + * that are appended to a sequence are positioned appropriately. For example, to make the + * TimelineMax play from the label "myLabel1" to the "myLabel2" label, and then from "myLabel2" + * back to the beginning (a time of 0), simply do: + * + * +var tl:TimelineMax = new TimelineMax(); +tl.add( myTimeline.tweenFromTo("myLabel1", "myLabel2") ); +tl.add( myTimeline.tweenFromTo("myLabel2", 0) ); + + * + *

If you want advanced control over the tween, like adding an onComplete or changing the ease + * or adding a delay, just pass in a vars object with the appropriate properties. For example, + * to tween from the start (0) to the 5-second point on the timeline and then call a function + * named myFunction and pass in a parameter that references this TimelineMax and + * use a Strong.easeOut ease, you'd do:

+ * + *

+ * myTimeline.tweenFromTo(0, 5, {onComplete:myFunction, onCompleteParams:[myTimeline], ease:Strong.easeOut}); + *

+ * + *

Remember, this method simply creates a TweenLite instance that tweens the time() + * of your timeline. So you can store a reference to that tween if you want, and you can kill() + * it anytime. Also note that tweenFromTo() does NOT affect the timeline's + * reversed property. So if your timeline is oriented normally (not reversed) and you + * tween to a time/label that precedes the current time, it will appear to go backwards but the + * reversed property will not change to true. Also note that + * tweenFromTo() pauses the timeline immediately before tweening its time(), + * and it does not automatically resume after the tween completes. If you need to resume playback, + * you can always use an onComplete to call the resume() method.

+ * + *

Like all from-type methods in GSAP, immediateRender is true by default, + * meaning the timeline will immediately jump to the "from" time/label unless you set immediateRender:false

+ * + * @param fromPosition The beginning time in seconds (or frame if the timeline is frames-based) or label from which the timeline should play. For example, myTimeline.tweenTo(0, 5) would play from 0 (the beginning) to the 5-second point whereas myTimeline.tweenFromTo("myLabel1", "myLabel2") would play from "myLabel1" to "myLabel2". + * @param toPosition The destination time in seconds (or frame if the timeline is frames-based) or label to which the timeline should play. For example, myTimeline.tweenTo(0, 5) would play from 0 (the beginning) to the 5-second point whereas myTimeline.tweenFromTo("myLabel1", "myLabel2") would play from "myLabel1" to "myLabel2". + * @param vars An optional vars object that will be passed to the TweenLite instance. This allows you to define an onComplete, ease, delay, or any other TweenLite special property. onInit is the only special property that is not available (tweenFromTo() sets it internally) + * @return TweenLite instance that handles tweening the timeline between the desired times/labels. + * + * @see #tweenTo() + * @see #seek() + */ + public function tweenFromTo(fromPosition:*, toPosition:*, vars:Object=null):TweenLite { + vars = vars || {}; + fromPosition = _parseTimeOrLabel(fromPosition); + vars.startAt = {onComplete:seek, onCompleteParams:[fromPosition]}; + vars.immediateRender = (vars.immediateRender !== false); + var t:TweenLite = tweenTo(toPosition, vars); + return t.duration((Math.abs( t.vars.time - fromPosition) / _timeScale) || 0.001) as TweenLite; + } + + + /** @private **/ + override public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + if (_gc) { + _enabled(true, false); + } + var totalDur:Number = (!_dirty) ? _totalDuration : totalDuration(), + prevTime:Number = _time, + prevTotalTime:Number = _totalTime, + prevStart:Number = _startTime, + prevTimeScale:Number = _timeScale, + prevRawPrevTime:Number = _rawPrevTime, + prevPaused:Boolean = _paused, + prevCycle:int = _cycle, + tween:Animation, isComplete:Boolean, next:Animation, dur:Number, callback:String, internalForce:Boolean; + + if (time >= totalDur) { + if (!_locked) { + _totalTime = totalDur; + _cycle = _repeat; + } + if (!_reversed) if (!_hasPausedChild()) { + isComplete = true; + callback = "onComplete"; + if (_duration === 0) if (time === 0 || _rawPrevTime < 0 || _rawPrevTime === _tinyNum) if (_rawPrevTime !== time && _first != null) { + internalForce = true; + if (_rawPrevTime > _tinyNum) { + callback = "onReverseComplete"; + } + } + } + _rawPrevTime = (_duration || !suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + if (_yoyo && (_cycle & 1) != 0) { + _time = time = 0; + } else { + _time = _duration; + time = _duration + 0.0001; //to avoid occasional floating point rounding errors in Flash - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when Flash performed _time - tween._startTime, floating point errors would return a value that was SLIGHTLY off) + } + + } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. + if (!_locked) { + _totalTime = _cycle = 0; + } + _time = 0; + if (prevTime !== 0 || (_duration === 0 && _rawPrevTime !== _tinyNum && (_rawPrevTime > 0 || (time < 0 && _rawPrevTime >= 0)) && !_locked)) { + callback = "onReverseComplete"; + isComplete = _reversed; + } + if (time < 0) { + _active = false; + if (_rawPrevTime >= 0 && _first) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + internalForce = true; + } + _rawPrevTime = time; + } else { + _rawPrevTime = (_duration || !suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline) + if (!_initted) { + internalForce = true; + } + } + + } else { + if (_duration === 0 && _rawPrevTime < 0) { //without this, zero-duration repeating timelines (like with a simple callback nested at the very beginning and a repeatDelay) wouldn't render the first time through. + internalForce = true; + } + _time = _rawPrevTime = time; + if (!_locked) { + _totalTime = time; + if (_repeat != 0) { + var cycleDuration:Number = _duration + _repeatDelay; + _cycle = (_totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but Flash reports it as 0.79999999!) + if (_cycle !== 0) if (_cycle === _totalTime / cycleDuration) { + _cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning) + } + _time = _totalTime - (_cycle * cycleDuration); + if (_yoyo) if ((_cycle & 1) != 0) { + _time = _duration - _time; + } + if (_time > _duration) { + _time = _duration; + time = _duration + 0.0001; //to avoid occasional floating point rounding errors in Flash - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when Flash performed _time - tween._startTime, floating point errors would return a value that was SLIGHTLY off) + } else if (_time < 0) { + _time = time = 0; + } else { + time = _time; + } + } + } + } + + if (_cycle != prevCycle) if (!_locked) { + /* + make sure children at the end/beginning of the timeline are rendered properly. If, for example, + a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which + would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there + could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So + we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must + ensure that zero-duration tweens at the very beginning or end of the TimelineMax work. + */ + var backwards:Boolean = (_yoyo && (prevCycle & 1) !== 0), + wrap:Boolean = (backwards == (_yoyo && (_cycle & 1) !== 0)), + recTotalTime:Number = _totalTime, + recCycle:int = _cycle, + recRawPrevTime:Number = _rawPrevTime, + recTime:Number = _time; + + _totalTime = prevCycle * _duration; + if (_cycle < prevCycle) { + backwards = !backwards; + } else { + _totalTime += _duration; + } + _time = prevTime; //temporarily revert _time so that render() renders the children in the correct order. Without this, tweens won't rewind correctly. We could arhictect things in a "cleaner" way by splitting out the rendering queue into a separate method but for performance reasons, we kept it all inside this method. + + _rawPrevTime = prevRawPrevTime; + _cycle = prevCycle; + _locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render() + prevTime = (backwards) ? 0 : _duration; + render(prevTime, suppressEvents, false); + if (!suppressEvents) if (!_gc) { + if (vars.onRepeat) { + vars.onRepeat.apply(null, vars.onRepeatParams); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.REPEAT)); + } + } + if (wrap) { + prevTime = (backwards) ? _duration + 0.0001 : -0.0001; + render(prevTime, true, false); + } + _locked = false; + if (_paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible) + return; + } + _time = recTime; + _totalTime = recTotalTime; + _cycle = recCycle; + _rawPrevTime = recRawPrevTime; + } + + if ((_time == prevTime || !_first) && !force && !internalForce) { + if (prevTotalTime !== _totalTime) if (_onUpdate != null) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate. + _onUpdate.apply(vars.onUpdateScope || this, vars.onUpdateParams); + } + return; + } else if (!_initted) { + _initted = true; + } + + if (!_active) if (!_paused && _totalTime !== prevTotalTime && time > 0) { + _active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example. + } + + if (prevTotalTime == 0) if (_totalTime != 0) if (!suppressEvents) { + if (vars.onStart) { + vars.onStart.apply(this, vars.onStartParams); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.START)); + } + } + + if (_time >= prevTime) { + tween = _first; + while (tween) { + next = tween._next; //record it here because the value could change after rendering... + if (_paused && !prevPaused) { //in case a tween pauses the timeline when rendering + break; + } else if (tween._active || (tween._startTime <= _time && !tween._paused && !tween._gc)) { + + if (!tween._reversed) { + tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); + } else { + tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); + } + + } + tween = next; + } + } else { + tween = _last; + while (tween) { + next = tween._prev; //record it here because the value could change after rendering... + if (_paused && !prevPaused) { //in case a tween pauses the timeline when rendering + break; + } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { + + if (!tween._reversed) { + tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); + } else { + tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); + } + + } + tween = next; + } + } + + if (_onUpdate != null) if (!suppressEvents) { + _onUpdate.apply(null, vars.onUpdateParams); + } + if (_hasUpdateListener) if (!suppressEvents) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.UPDATE)); + } + + if (callback) if (!_locked) if (!_gc) if (prevStart === _startTime || prevTimeScale !== _timeScale) if (_time === 0 || totalDur >= totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate + if (isComplete) { + if (_timeline.autoRemoveChildren) { + _enabled(false, false); + } + _active = false; + } + if (!suppressEvents) { + if (vars[callback]) { + vars[callback].apply(null, vars[callback + "Params"]); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(((callback == "onComplete") ? TweenEvent.COMPLETE : TweenEvent.REVERSE_COMPLETE))); + } + } + } + } + + /** + * Returns the tweens/timelines that are currently active in the timeline, meaning the timeline's + * playhead is positioned on the child tween/timeline and the child isn't paused. + * + * @param nested Determines whether or not tweens and/or timelines that are inside nested timelines should be returned. If you only want the "top level" tweens/timelines, set this to false. + * @param tweens Determines whether or not tweens (TweenLite and TweenMax instances) should be included in the results + * @param timelines Determines whether or not child timelines (TimelineLite and TimelineMax instances) should be included in the results + * @return an Array of active tweens/timelines + */ + public function getActive(nested:Boolean=true, tweens:Boolean=true, timelines:Boolean=false):Array { + var a:Array = [], + all:Array = getChildren(nested, tweens, timelines), + cnt:int = 0, + l:int = all.length, + i:int, tween:Animation; + for (i = 0; i < l; i++) { + tween = all[i]; + //note: we cannot just check tween.active because timelines that contain paused children will continue to have "active" set to true even after the playhead passes their end point (technically a timeline can only be considered complete after all of its children have completed too, but paused tweens are...well...just waiting and until they're unpaused we don't know where their end point will be). + if (!tween._paused) if (tween._timeline._time >= tween._startTime) if (tween._timeline._time < tween._startTime + tween._totalDuration / tween._timeScale) if (!_getGlobalPaused(tween._timeline)) { + a[cnt++] = tween; + } + } + return a; + } + + /** @private **/ + protected static function _getGlobalPaused(tween:Animation):Boolean { + while (tween) { + if (tween._paused) { + return true; + } + tween = tween._timeline; + } + return false; + } + + /** + * Returns the next label (if any) that occurs after the time parameter. + * It makes no difference if the timeline is reversed ("after" means later in the timeline's local time zone). + * A label that is positioned exactly at the same time as the time parameter will be ignored. + * + *

You could use getLabelAfter() in conjunction with tweenTo() to make + * the timeline tween to the next label like this:

+ * + *

+ * myTimeline.tweenTo( myTimeline.getLabelAfter() ); + *

+ * + * @param time Time after which the label is searched for. If you do not pass a time in, the current time will be used. + * @return Name of the label that is after the time passed to getLabelAfter() + * + * @see #getLabelBefore() + * @see #currentLabel() + */ + public function getLabelAfter(time:Number=NaN):String { + if (!time) if (time != 0) { //faster than isNan() + time = _time; + } + var labels:Array = getLabelsArray(), + l:int = labels.length, + i:int; + for (i = 0; i < l; i++) { + if (labels[i].time > time) { + return labels[i].name; + } + } + return null; + } + + /** + * Returns the previous label (if any) that occurs before the time parameter. + * It makes no difference if the timeline is reversed ("before" means earlier in the timeline's local time zone). + * A label that is positioned exactly at the same time as the time parameter will be ignored. + * + *

You could use getLabelBefore() in conjunction with tweenTo() to make + * the timeline tween back to the previous label like this:

+ * + *

+ * myTimeline.tweenTo( myTimeline.getLabelBefore() ); + *

+ * + * @param time Time before which the label is searched for. If you do not pass a time in, the current time will be used. + * @return Name of the label that is before the time passed to getLabelBefore() + * + * @see #getLabelBefore() + * @see #currentLabel() + */ + public function getLabelBefore(time:Number=NaN):String { + if (!time) if (time != 0) { //faster than isNan() + time = _time; + } + var labels:Array = getLabelsArray(), + i:int = labels.length; + while (--i > -1) { + if (labels[i].time < time) { + return labels[i].name; + } + } + return null; + } + + /** + * Returns an Array of label objects, each with a "time" and "name" property, in the order that they occur in the timeline. + * For example, to loop through all the labels in order and trace() them to the screen (or console.log() in JavaScript): + * + * +var labels = myTimeline.getLabelsArray(); +for (var i = 0; i < labels.length; i++) { + trace("label name: " + labels[i].name + ", time: " + labels[i].time); //or in JS, console.log("label name: " + labels[i].name + ", time: " + labels[i].time); +} + + *

Note: changing the values in this array will have no effect on the actual labels inside the TimelineMax. To add/remove labels, + * use the corresponding methods (addLabel(), removeLabel()).

+ * + * @return An array of generic objects (one for each label) with a "name" property and a "time" property in the order they occur in the TimelineMax. + **/ + public function getLabelsArray():Array { + var a:Array = [], + cnt:int = 0, + p:String; + for (p in _labels) { + a[cnt++] = {time:_labels[p], name:p}; + } + a.sortOn("time", Array.NUMERIC); + return a; + } + + +//---- EVENT DISPATCHING ---------------------------------------------------------------------------------------------------------- + + /** @private **/ + protected function _initDispatcher():Boolean { + var found:Boolean = false, p:String; + for (p in _listenerLookup) { + if (p in vars) if (vars[p] is Function) { + if (_dispatcher == null) { + _dispatcher = new EventDispatcher(this); + } + _dispatcher.addEventListener(_listenerLookup[p], vars[p], false, 0, true); + found = true; + } + } + return found; + } + + /** + * (AS3 only) + * Registers a function that should be called each time a particular type of event occurs, like + * "complete" or "update". The function will be passed a single "event" + * parameter whose "target" property refers to the timeline. Typically it is more efficient + * to use callbacks like onComplete, onUpdate, onStart, onReverseComplete, and onRepeat + * unless you need the event parameter or if you need to register more than one listener for the same + * type of event. + * + * If you no longer need an event listener, remove it by calling removeEventListener(), or memory + * problems could result. Event listeners are not automatically removed from memory because the garbage + * collector does not remove the listener as long as the dispatching object exists (unless the + * useWeakReference parameter is set to true). + * + * @param type The type of event + * @param listener The listener function that processes the event. This function must accept an Event object as its only parameter + * @param useCapture (not typically used) Determines whether the listener works in the capture phase or the target and bubbling phases. If useCapture is set to true, the listener processes the event only during the capture phase and not in the target or bubbling phase. If useCapture is false, the listener processes the event only during the target or bubbling phase. To listen for the event in all three phases, call addEventListener twice, once with useCapture set to true, then again with useCapture set to false. + * @param priority The priority level of the event listener. The priority is designated by a signed 32-bit integer. The higher the number, the higher the priority. All listeners with priority n are processed before listeners of priority n-1. If two or more listeners share the same priority, they are processed in the order in which they were added. The default priority is 0. + * @param useWeakReference Determines whether the reference to the listener is strong or weak. A strong reference (the default) prevents your listener from being garbage-collected. A weak reference does not. + * @see #removeEventListener() + **/ + public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + if (_dispatcher == null) { + _dispatcher = new EventDispatcher(this); + } + if (type == TweenEvent.UPDATE) { + _hasUpdateListener = true; + } + _dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** + * (AS3 only) + * Removes a listener from the EventDispatcher object. If there is no matching listener registered + * with the EventDispatcher object, a call to this method has no effect. + * + * @param type The type of event + * @param listener The listener object to remove. + * @param useCapture Specifies whether the listener was registered for the capture phase or the target and bubbling phases. If the listener was registered for both the capture phase and the target and bubbling phases, two calls to removeEventListener() are required to remove both, one call with useCapture() set to true, and another call with useCapture() set to false. + **/ + public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void { + if (_dispatcher != null) { + _dispatcher.removeEventListener(type, listener, useCapture); + } + } + + /** @private **/ + public function hasEventListener(type:String):Boolean { + return (_dispatcher == null) ? false : _dispatcher.hasEventListener(type); + } + + /** @private **/ + public function willTrigger(type:String):Boolean { + return (_dispatcher == null) ? false : _dispatcher.willTrigger(type); + } + + /** @private **/ + public function dispatchEvent(event:Event):Boolean { + return (_dispatcher == null) ? false : _dispatcher.dispatchEvent(event); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------------------------------------- + + /** + * Gets or sets the timeline's progress which is a value between 0 and 1 indicating the position + * of the virtual playhead (excluding repeats) where 0 is at the beginning, 0.5 is halfway complete, + * and 1 is complete. If the timeline has a non-zero repeat defined, progress + * and totalProgress will be different because progress doesn't include any + * repeats or repeatDelays whereas totalProgress does. For example, if a TimelineMax instance + * is set to repeat once, at the end of the first cycle totalProgress would only be 0.5 + * whereas progress would be 1. If you watched both properties over the course of the entire + * animation, you'd see progress go from 0 to 1 twice (once for each cycle) in the + * same time it takes the totalProgress to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTimeline.progress(0.5).play();

+ * + * +var progress = myTimeline.progress(); //gets current progress +myTimeline.progress( 0.25 ); //sets progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #totalProgress() + * @see #seek() + * @see #time() + * @see #totalTime() + **/ + override public function progress(value:Number=NaN, suppressEvents:Boolean=false):* { + return (!arguments.length) ? _time / duration() : totalTime( duration() * ((_yoyo && (_cycle & 1) !== 0) ? 1 - value : value) + (_cycle * (_duration + _repeatDelay)), suppressEvents); + } + + /** + * Gets or sets the timeline's total progress which is a value between 0 and 1 indicating the position + * of the virtual playhead (including repeats) where 0 is at the beginning, 0.5 is + * at the halfway point, and 1 is at the end (complete). If the timeline has a non-zero repeat defined, + * progress() and totalProgress() will be different because progress() + * doesn't include the repeat or repeatDelay whereas totalProgress() does. For example, + * if a TimelineMax instance is set to repeat once, at the end of the first cycle totalProgress() + * would only be 0.5 whereas progress would be 1. If you watched both properties over the + * course of the entire animation, you'd see progress go from 0 to 1 twice (once for + * each cycle) in the same time it takes the totalProgress() to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.totalProgress(0.5).play();

+ * + * +var progress = myAnimation.totalProgress(); //gets total progress +myAnimation.totalProgress(0.25); //sets total progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #progress() + * @see #seek() + * @see #time() + * @see #totalTime() + **/ + override public function totalProgress(value:Number=NaN, suppressEvents:Boolean=true):* { + return (!arguments.length) ? _totalTime / totalDuration() : totalTime( totalDuration() * value, suppressEvents); + } + + /** + * Gets or sets the total duration of the timeline in seconds (or frames for frames-based timelines) + * including any repeats or repeatDelays. duration, by contrast, does + * NOT include repeats and repeatDelays. For example, if the timeline has a + * duration of 10, a repeat of 1 and a repeatDelay of 2, + * the totalDuration would be 22. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var total = myTimeline.totalDuration(); //gets total duration +myTimeline.totalDuration(10); //sets the total duration + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #duration() + * @see #timeScale() + **/ + override public function totalDuration(value:Number=NaN):* { + if (!arguments.length) { + if (_dirty) { + super.totalDuration(); //just forces refresh + //Instead of Infinity, we use 999999999999 so that we can accommodate reverses. + _totalDuration = (_repeat == -1) ? 999999999999 : _duration * (_repeat + 1) + (_repeatDelay * _repeat); + } + return _totalDuration; + } + return (_repeat == -1) ? this : duration( (value - (_repeat * _repeatDelay)) / (_repeat + 1) ); + } + + /** + * Gets or sets the local position of the playhead (essentially the current time), not + * including any repeats or repeatDelays. If the timeline has a non-zero repeat, its time + * goes back to zero upon repeating even though the totalTime continues forward linearly + * (or if yoyo is true, the time alternates between moving forward + * and backward). time never exceeds the duration whereas the totalTime reflects + * the overall time including any repeats and repeatDelays. + * + *

For example, if a TimelineMax instance has a duration of 2 and a repeat of 3, + * totalTime will go from 0 to 8 during the course of the timeline (plays once then + * repeats 3 times, making 4 total cycles) whereas time would go from 0 to 2 a + * total of 4 times.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var currentTime = myTimeline.time(); //gets current time +myTimeline.time(2); //sets time, jumping to new value just like seek(). + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position defined in the value parameter. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #seek() + * @see #play() + * @see #reverse() + * @see #pause() + * @see #totalTime() + **/ + override public function time(value:Number=NaN, suppressEvents:Boolean=false):* { + if (!arguments.length) { + return _time; + } + if (_dirty) { + totalDuration(); + } + if (value > _duration) { + value = _duration; + } + if (_yoyo && (_cycle & 1) !== 0) { + value = (_duration - value) + (_cycle * (_duration + _repeatDelay)); + } else if (_repeat != 0) { + value += _cycle * (_duration + _repeatDelay); + } + return totalTime(value, suppressEvents); + } + + /** + * Gets or sets the number of times that the timeline should repeat after its first iteration. For + * example, if repeat is 1, the timeline will play a total of twice (the initial play + * plus 1 repeat). To repeat indefinitely, use -1. repeat should always be an integer. + * + *

To cause the repeats to alternate between forward and backward, set yoyo to + * true. To add a time gap between repeats, use repeatDelay. You can + * set the initial repeat value via the vars parameter, like:

+ * + *

+ * var tl = new TimelineMax({repeat:2}); + *

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTimeline.repeat(2).yoyo(true).play();

+ * + * +var repeat = myTimeline.repeat(); //gets current repeat value +myTimeline.repeat(2); //sets repeat to 2 + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeatDelay() + * @see #yoyo() + **/ + public function repeat(value:Number=0):* { + if (!arguments.length) { + return _repeat; + } + _repeat = value; + return _uncache(true); + } + + /** + * Gets or sets the amount of time in seconds (or frames for frames-based timelines) between repeats. + * For example, if repeat is 2 and repeatDelay is 1, the timeline will + * play initially, then wait for 1 second before it repeats, then play again, then wait 1 second + * again before doing its final repeat. You can set the initial repeatDelay value + * via the vars parameter, like: + * + *

+ * var tl = new TimelineMax({repeat:2, repeatDelay:1}); + *

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTimeline.repeat(2).yoyo(true).repeatDelay(0.5).play();

+ * + * +var repeatDelay = myTimeline.repeatDelay(); //gets current repeatDelay value +myTimeline.repeatDelay(2); //sets repeatDelay to 2 + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeat() + * @see #yoyo() + **/ + public function repeatDelay(value:Number=0):* { + if (!arguments.length) { + return _repeatDelay; + } + _repeatDelay = value; + return _uncache(true); + } + + /** + * Gets or sets the timeline's yoyo state, where true causes + * the timeline to go back and forth, alternating backward and forward on each + * repeat. yoyo works in conjunction with repeat, + * where repeat controls how many times the timeline repeats, and yoyo + * controls whether or not each repeat alternates direction. So in order to make a timeline yoyo, + * you must set its repeat to a non-zero value. + * Yoyo-ing, has no affect on the timeline's "reversed" property. For example, + * if repeat is 2 and yoyo is false, it will look like: + * start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, + * it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end. + * + *

You can set the yoyo property initially by passing yoyo:true + * in the vars parameter, like: new TimelineMax({repeat:1, yoyo:true});

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTimeline.yoyo(true).repeat(3).timeScale(2).play(0.5);

+ * + * +var yoyo = myTimeline.yoyo(); //gets current yoyo state +myTimeline.yoyo( true ); //sets yoyo to true + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeat() + * @see #repeatDelay() + **/ + public function yoyo(value:Boolean=false):* { + if (!arguments.length) { + return _yoyo; + } + _yoyo = value; + return this; + } + + /** + * Gets the closest label that is at or before the current time, or jumps to a provided label + * (behavior depends on whether or not you pass a parameter to the method). + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #getLabelAfter() + * @see #getLabelBefore() + **/ + public function currentLabel(value:String=null):* { + if (!arguments.length) { + return getLabelBefore(_time + 0.00000001); + } + return seek(value, true); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/TweenAlign.as b/src/com/greensock/TweenAlign.as new file mode 100644 index 0000000..498651f --- /dev/null +++ b/src/com/greensock/TweenAlign.as @@ -0,0 +1,22 @@ +/** + * VERSION: 0.86 + * DATE: 6/15/2009 + * AS2 (AS3 version is also available) + * UPDATES AND DOCUMENTATION AT: http://www.TweenLite.com + **/ +package com.greensock { +/** + * @private + * Static constants for defining tween alignment. + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + public class TweenAlign { + public static const NORMAL:String = "normal"; + public static const SEQUENCE:String = "sequence"; + public static const START:String = "start"; + } + +} \ No newline at end of file diff --git a/src/com/greensock/TweenLite.as b/src/com/greensock/TweenLite.as new file mode 100644 index 0000000..3704f33 --- /dev/null +++ b/src/com/greensock/TweenLite.as @@ -0,0 +1,1353 @@ +/** + * VERSION: 12.1.5 + * DATE: 2014-07-19 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock { + import com.greensock.core.Animation; + import com.greensock.core.PropTween; + import com.greensock.core.SimpleTimeline; + import com.greensock.easing.Ease; + + import flash.display.Shape; + import flash.events.Event; + import flash.utils.Dictionary; + +/** + * TweenLite is an extremely fast, lightweight, and flexible animation tool that serves as the foundation of + * the GreenSock Animation Platform (GSAP), available in AS2, AS3, and JavaScript. A TweenLite instance handles + * tweening one or more properties of any object (or array of objects) over time. TweenLite + * can be used on its own to accomplish most animation chores with minimal file size or it can be use in + * conjuction with advanced sequencing tools like TimelineLite or TimelineMax to make complex tasks much + * simpler. With scores of other animation frameworks to choose from, why consider the GreenSock Animation Platform?: + * + *
    + *
  • SPEED - The platform has been highly optimized for maximum performance. See some + * speed comparisons yourself at http://www.greensock.com/tweening-speed-test/
  • + * + *
  • Freakishly robust feature set - In addition to tweening any numeric property of any object, + * TweenLite has plugins that give it the ability to tween hex colors, beziers, arrays, filters, plus + * LOTS more. It can round values, use relative values, smoothly reverse() on the + * fly, automatically detect and accommodate getter/setter functions, employ virtually any easing + * equation, pause()/resume() anytime, and intelligently manage conflicting tweens of + * the same object with various overwrite modes. TweenMax extends TweenLite and adds even + * more capabilities like repeat, yoyo, repeatDelay, on-the-fly destination value + * updates and more.
  • + * + *
  • Sequencing, grouping, and management features - TimelineLite and TimelineMax + * make it surprisingly simple to create complex sequences or groups of tweens that you can + * control as a whole. play(), pause(), restart(), or reverse(). You can even tween a timeline's + * time or progress to fastforward or rewind the entire timeline. Add + * labels, change the timeline's timeScale, nest timelines within timelines, and much more. + * This can revolutionize your animation workflow, making it more modular and concise.
  • + * + *
  • AS3, AS2, and JavaScript - Most other engines are only developed for one language, + * but the GreenSock Animation Platform allows you to use a consistent API across all your Flash and + * HTML5 projects.
  • + * + *
  • Ease of use - Designers and Developers alike rave about how intuitive the platform is.
  • + * + *
  • Support and reliability - With frequent updates, dedicated forums, + * committed authorship, a solid track record, a proven funding mechansim, and a thriving community of users, + * the platform is a safe long-term bet (unlike many open source projects).
  • + * + *
  • Expandability - With its plugin architecture, you can activate as many (or as few) + * extra features as your project requires. Write your own plugin to handle particular special + * properties in custom ways. Minimize bloat and maximize performance.
  • + * + *
+ * + *

USAGE

+ *

The most common type of tween is a to() tween which allows you + * to define the destination values:

+ * + *

+ * TweenLite.to(myObject, 2, {x:100, y:200}); + *

+ * + *

The above code will tween myObject.x from whatever it currently is to 100 and + * myObject.y property to 200 over the course of 2 seconds. Notice the x and y values are + * defined inside a generic object (between curly braces). Put as many properties there as you want.

+ * + *

By default, tweens begin immediately, although you can delay them using the delay + * special property or pause them initially using the paused special property (see below).

+ * + *

The target can also be an array of objects. For example, the following tween will + * tween the alpha property to 0.5 and y property to 100 for obj1, obj2, and obj3:

+ * + *

+ * TweenLite.to([obj1, obj2, obj3], 1, {alpha:0.5, y:100}); + *

+ * + *

You can also use a from() tween if you want to define the + * starting values instead of the ending values so that the target tweens from + * the defined values to wherever they currently are. Or a fromTo() + * lets you define both starting and ending values.

+ * + *

Although the to(), from(), and fromTo() static methods + * are popular because they're quick and can avoid some garbage collection hassles, you can also + * use the more object-oriented syntax like this:

+ * + *

+ * var tween = new TweenLite(myObject, 2, {x:100, y:200}); + *

+ * + *

or even:

+ * + *

+ * var tween = TweenLite.to(myObject, 2, {x:100, y:200}); + *

+ * + * + *

SPECIAL PROPERTIES (no plugins required):

+ *

Typically the vars parameter is used to define ending values for tweening + * properties of the target (or beginning values for from() tweens) + * like {x:100, y:200, alpha:0}, but the following optional special properties + * serve other purposes:

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the tween should begin.
  • + * + *
  • ease : Ease (or Function) - + * You can choose from various eases to control the rate of change during + * the animation, giving it a specific "feel". For example, ElasticOut.ease + * or StrongInOut.ease. For best performance, use one of the GreenSock eases + * (which are in the com.greensock.easing package). TweenLite also works with + * any standard easing equation that uses the typical 4 parameters (time, start, + * change, duration) like Adobe's fl.motion.easing eases. + * The default is Power1.easeOut. For linear animation, use the GreenSock + * Linear.ease ease
  • + * + *
  • onComplete : Function - + * A function that should be called when the tween has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * TweenLite.to(mc, 1, {x:100, onComplete:myFunction, onCompleteParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onCompleteParams:["{self}", "param2"]
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the tweens's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • easeParams : Array [deprecated] - + * Some GreenSock eases (like OutIn or ElasticOut) have a config() + * method that allows them to be configured to change their behavior (like TweenLite.to(mc, 1, {x:100, ease:ElasticOut.ease.config(0.5, 1)}) + * but if you are using a non-GreenSock ease that accepts extra parameters like Adobe's + * fl.motion.easing.Elastic, easeParams allows you to define + * those extra parameters as an array like TweenLite.to(mc, 1, {x:100, ease:Elastic.easeOut, easeParams:[0.5, 1]}). + * Most easing equations, however, don't require extra parameters so you won't need to + * pass in any easeParams. GreenSock eases provide the best performance, so use them + * whenever possible.
  • + * + *
  • immediateRender : Boolean - + * Normally when you create a tween, it begins rendering on the very next frame (update cycle) + * unless you specify a delay. However, if you prefer to force the tween to + * render immediately when it is created, set immediateRender to true. + * Or to prevent a from() from rendering immediately, set immediateRender + * to false. By default, from() tweens set immediateRender to true.
  • + * + *
  • onStart : Function - + * A function that should be called when the tween begins (when its time + * changes from 0 to some other value which can happen more than once if the + * tween is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * TweenLite.to(mc, 1, {x:100, delay:1, onStart:myFunction, onStartParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onStartParams:["{self}", "param2"]
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the tween updates + * (on every frame while the tween is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * TweenLite.to(mc, 1, {x:100, onUpdate:myFunction, onUpdateParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onUpdateParams:["{self}", "param2"]
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the tween has reached its beginning again from the + * reverse direction. For example, if reverse() is called the tween will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the tween is placed in a TimelineLite or TimelineMax instance + * that gets reversed and plays the tween backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * TweenLite.to(mc, 1, {x:100, onReverseComplete:myFunction, onReverseCompleteParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onReverseCompleteParams:["{self}", "param2"]
  • + * + *
  • paused : Boolean - + * If true, the tween will pause itself immediately upon creation.
  • + * + *
  • overwrite : String (or integer) - + * Controls how (and if) other tweens of the same target are overwritten. + * There are several modes to choose from, but "auto" is the default (although + * you can change the default mode using the TweenLite.defaultOverwrite property): + *
      + *
    • "none" (0) (or false) - no overwriting will occur.
    • + * + *
    • "all" (1) (or true) - immediately overwrites all existing + * tweens of the same target even if they haven't started yet or don't have + * conflicting properties.
    • + * + *
    • "auto" (2) - when the tween renders for the first time, it will analyze + * tweens of the same target that are currently active/running and only overwrite + * individual tweening properties that overlap/conflict. Tweens that haven't begun + * yet are ignored. For example, if another active tween is found that is tweening + * 3 properties, only 1 of which it shares in common with the new tween, the other + * 2 properties will be left alone. Only the conflicting property gets overwritten/killed. + * This is the default mode and typically the most intuitive for developers.
    • + * + *
    • "concurrent" (3) - when the tween renders for the first time, it kills + * only the active (in-progress) tweens of the same target regardless of whether + * or not they contain conflicting properties. Like a mix of "all" + * and "auto". Good for situations where you only want one tween + * controling the target at a time.
    • + * + *
    • "allOnStart" (4) - Identical to "all" but waits to run + * the overwrite logic until the tween begins (after any delay). Kills + * tweens of the same target even if they don't contain conflicting properties + * or haven't started yet.
    • + * + *
    • "preexisting" (5) - when the tween renders for the first time, it kills + * only the tweens of the same target that existed BEFORE this tween was created + * regardless of their scheduled start times. So, for example, if you create a tween + * with a delay of 10 and then a tween with a delay of 1 and then a tween with a + * delay of 2 (all of the same target), the 2nd tween would overwrite the first + * but not the second even though scheduling might seem to dictate otherwise. + * "preexisting" only cares about the order in which the instances + * were actually created. This can be useful when the order in which your code runs + * plays a critical role.
    • + * + *
  • + *
+ * + *

AS3 note: In AS3, using a TweenLiteVars + * instance instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + * + *

PLUGINS:

+ * + *

Think of plugins like special properties that are dynamically added, delivering extra abilities without + * forcing them to be baked into the core engine, keeping it relatively lean and mean. Each plugin is associated + * with a property name and it takes responsibility for handling that property. For example, the TintPlugin + * is associated with the "tint" property name so if it is activated it will intercept the "tint" property + * in the following tween and manage it uniquely:

+ * + *

+ * TweenLite.to(mc, 1, {tint:0xFF0000}); + *

+ * + *

If the TintPlugin wasn't activated, TweenLite would act as though you were trying to literally tween the + * mc.tint property (and there is no such thing).

+ * + *

In the JavaScript version of TweenLite, activating a plugin is as simple as loading the associated .js file. + * No extra activation code is necessary. In the ActionScript version, activating a plugin requires a single line + * of code and you only need to do it once, so it's pretty easy. Simply pass an Array containing the names of all + * the plugins you'd like to activate to the TweenPlugin.activate() method, like this:

+ * + *

+ * TweenPlugin.activate([FrameLabelPlugin, ColorTransformPlugin, TintPlugin]); + *

+ * + *

To make it even easier, there is a Plugin Explorer + * which writes the code for you. All you need to do is select the plugins and copy/paste the code + * from the bottom of the tool. It also displays interactive examples of each plugin and the assocaited + * code so that it’s easy to see the correct syntax.

+ * + * + *

EXAMPLES:

+ * + *

Please see http://www.greensock.com for examples, tutorials, and interactive demos.

+ * + * NOTES / TIPS: + *
    + *
  • Passing values as Strings and a preceding "+=" or "-=" will make the tween relative to the + * current value. For example, if you do TweenLite.to(mc, 2, {x:"-=20"}); it'll + * tween mc.x to the left 20 pixels. {x:"+=20"} would move it to the right.
  • + * + *
  • You can change the TweenLite.defaultEase if you prefer something other + * than Power1.easeOut.
  • + * + *
  • Kill all tweens of a particular object anytime with TweenLite.killTweensOf(myObject);
  • + * + *
  • You can kill all delayedCalls to a particular function using TweenLite.killDelayedCallsTo(myFunction); + * or TweenLite.killTweensOf(myFunction);
  • + * + *
  • Use the TweenLite.from() method to animate things into place. For example, + * if you have things set up on the stage in the spot where they should end up, and you + * just want to animate them into place, you can pass in the beginning x and/or y and/or + * alpha (or whatever properties you want).
  • + * + *
  • If you find this class useful, please consider joining Club GreenSock + * which not only helps to sustain ongoing development, but also gets you bonus plugins, classes + * and other benefits that are ONLY available to members. Learn more at + * http://www.greensock.com/club/
  • + *
+ * + *

Copyright 2006-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenLite extends Animation { + + /** @private **/ + public static const version:String = "12.1.5"; + + /** Provides An easy way to change the default easing equation. Choose from any of the GreenSock eases in the com.greensock.easing package. @default Power1.easeOut **/ + public static var defaultEase:Ease = new Ease(null, null, 1, 1); + + /** Provides An easy way to change the default overwrite mode. Choose from any of the following: "auto", "all", "none", "allOnStart", "concurrent", "preexisting". @default "auto" **/ + public static var defaultOverwrite:String = "auto"; + + /** + * The object that dispatches a "tick" event each time the engine updates, making it easy for + * you to add your own listener(s) to run custom logic after each update (great for game developers). + * Add as many listeners as you want. The basic syntax is the same for all versions (AS2, AS3, and JavaScript): + * + *

Basic example (AS2, AS3, and JavaScript):

+ //add listener + TweenLite.ticker.addEventListener("tick", myFunction); + + function myFunction(event) { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenLite.ticker.removeEventListener("tick", myFunction); + + * + *

Due to differences in the core languages (and to maximize efficiency), the advanced syntax is slightly different + * for the AS3 version compared to AS2 and JavaScript. The parameters beyond the first 2 in the addEventListener() + * method are outlined below:

+ * + *

JavaScript and AS2

+ *

addEventListener(type, callback, scope, useParam, priority)

+ *

Parameters: + *

    + *
  1. type : String - type of listener, should always be "tick"
  2. + *
  3. callback : Function - the function to call when the event occurs
  4. + *
  5. scope : Object - binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in JavaScript and AS2 because scope isn't generally maintained.
  6. + *
  7. useParam : Boolean - if true, an event object will be generated and fed to the callback each time the event occurs. The event is a generic object and has two properties: type (always "tick") and target which refers to the ticker instance. The default for useParam is false because it improves performance.
  8. + *
  9. priority : Integer - influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
  10. + *
+ *

+ * + *

Advanced example (JavaScript and AS2):

+ //add listener that requests an event object parameter, binds scope to the current scope (this), and sets priority to 1 so that it is called before any other listeners that had a priority lower than 1... + TweenLite.ticker.addEventListener("tick", myFunction, this, true, 1); + + function myFunction(event) { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenLite.ticker.removeEventListener("tick", myFunction); + + * + *

AS3

+ *

The AS3 version uses the standard EventDispatcher.addEventListener() syntax which + * basically allows you to define a priority and whether or not to use weak references (see Adobe's + * docs for details).

+ * + *

Advanced example [AS3 only]:

+ import flash.events.Event; + + //add listener with weak reference (standard syntax - notice the 5th parameter is true) + TweenLite.ticker.addEventListener("tick", myFunction, false, 0, true); + + function myFunction(event:Event):void { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenLite.ticker.removeEventListener("tick", myFunction); + + **/ + public static var ticker:Shape = Animation.ticker; + + /** @private When plugins are activated, the class is added (named based on the special property) to this object so that we can quickly look it up in the _initProps() method.**/ + public static var _plugins:Object = {}; + + /** @private For notifying plugins of significant events like when the tween finishes initializing or when it is disabled/enabled (some plugins need to take actions when those events occur). TweenPlugin sets this (in order to keep file size small, avoiding dependencies on that or other classes) **/ + public static var _onPluginEvent:Function; + + /** @private Holds references to all our tween instances organized by target for quick lookups (for overwriting). **/ + protected static var _tweenLookup:Dictionary = new Dictionary(false); + + /** @private Lookup for all of the reserved "special property" keywords (excluding plugins).**/ + protected static var _reservedProps:Object = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, onCompleteListener:1, onUpdateListener:1, onStartListener:1, onReverseCompleteListener:1, onRepeatListener:1, orientToBezier:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1}; + + /** @private An object for associating String overwrite modes with their corresponding integers (faster) **/ + protected static var _overwriteLookup:Object; + + + /** [READ-ONLY] Target object (or array of objects) whose properties the tween affects. **/ + public var target:Object; + + /** @private The result of feeding the tween's current progress (0-1) into the easing equation - typically between 0 and 1 but not always (like with ElasticOut.ease). **/ + public var ratio:Number; + + /** @private Lookup object for PropTween objects. For example, if this tween is handling the "x" and "y" properties of the target, the _propLookup object will have an "x" and "y" property, each pointing to the associated PropTween object (for tweens with targets that are arrays, _propTween will be an Array with corresponding objects). This can be very helpful for speeding up overwriting. **/ + public var _propLookup:Object; + + /** @private First PropTween instance in the linked list. **/ + public var _firstPT:PropTween; + + /** @private Only used for tweens whose target is an array. **/ + protected var _targets:Array; + + /** @private Ease to use which determines the rate of change during the animation. Examples are ElasticOut.ease, StrongIn.ease, etc. (all in the com.greensock.easing package) **/ + public var _ease:Ease; + + /** @private To speed the handling of the ease, we store the type here (1 = easeOut, 2 = easeIn, 3 = easeInOut, and 0 = none of these) **/ + protected var _easeType:int; + + /** @private To speed handling of the ease, we store its strength here (Linear is 0, Quad is 1, Cubic is 2, Quart is 3, Quint (and Strong) is 4, etc.) **/ + protected var _easePower:int; + + /** @private The array that stores the tweens of the same target (or targets) for the purpose of speeding overwrites. **/ + protected var _siblings:Array; + + /** @private Overwrite mode (0 = none, 1 = all, 2 = auto, 3 = concurrent, 4 = allOnStart, 5 = preexisting) **/ + protected var _overwrite:int; + + /** @private When properties are overwritten in this tween, the properties get added to this object because sometimes properties are overwritten BEFORE the tween inits. **/ + protected var _overwrittenProps:Object; + + /** @private If this tween has any TweenPlugins that need to be notified of a change in the "enabled" status, this will be true. (speeds things up in the _enable() setter) **/ + protected var _notifyPluginsOfEnabled:Boolean; + + /** @private Only used in tweens where a startAt is defined (like fromTo() tweens) so that we can record the pre-tween starting values and revert to them properly if/when the playhead on the timeline moves backwards, before this tween started. In other words, if alpha is at 1 and then someone does a fromTo() tween that makes it go from 0 to 1 and then the playhead moves BEFORE that tween, alpha should jump back to 1 instead of reverting to 0. **/ + protected var _startAt:TweenLite; + + + /** + * Constructor + * + * @param target Target object (or array of objects) whose properties this tween affects + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: new TweenLite(mc, 1, {x:100, y:200, onComplete:myFunction}). + */ + public function TweenLite(target:Object, duration:Number, vars:Object) { + super(duration, vars); + + if (target == null) { + throw new Error("Cannot tween a null object. Duration: "+duration+", data: "+this.data); + } + + if (!_overwriteLookup) { + _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0}; + ticker.addEventListener("enterFrame", _dumpGarbage, false, -1, true); + } + + ratio = 0; + this.target = target; + _ease = defaultEase; //temporary - we'll replace it in _init(). We need to set it here for speed purposes so that on the first render(), it doesn't throw an error. + + _overwrite = (!("overwrite" in this.vars)) ? _overwriteLookup[defaultOverwrite] : (typeof(this.vars.overwrite) === "number") ? this.vars.overwrite >> 0 : _overwriteLookup[this.vars.overwrite]; + + if (this.target is Array && typeof(this.target[0]) === "object") { + _targets = this.target.concat(); + _propLookup = []; + _siblings = []; + var i:int = _targets.length; + while (--i > -1) { + _siblings[i] = _register(_targets[i], this, false); + if (_overwrite == 1) if (_siblings[i].length > 1) { + _applyOverwrite(_targets[i], this, null, 1, _siblings[i]); + } + } + + } else { + _propLookup = {}; + _siblings = _tweenLookup[target] + if (_siblings == null) { //the next few lines accomplish the same thing as _siblings = _register(target, this, false) but faster and only slightly more verbose. + _siblings = _tweenLookup[target] = [this]; + } else { + _siblings[_siblings.length] = this; + if (_overwrite == 1) { + _applyOverwrite(target, this, null, 1, _siblings); + } + } + } + + if (this.vars.immediateRender || (duration == 0 && _delay == 0 && this.vars.immediateRender != false)) { + render(-_delay, false, true); + } + } + + /** + * @private + * Initializes the tween + */ + protected function _init():void { + var immediate:Boolean = vars.immediateRender, + i:int, initPlugins:Boolean, pt:PropTween, p:String, copy:Object; + if (vars.startAt) { + if (_startAt != null) { + _startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change. + } + vars.startAt.overwrite = 0; + vars.startAt.immediateRender = true; + _startAt = new TweenLite(target, 0, vars.startAt); + if (immediate) { + if (_time > 0) { + _startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()). + } else if (_duration !== 0) { + return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting. + } + } + } else if (vars.runBackwards && _duration !== 0) { + //from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards) + if (_startAt != null) { + _startAt.render(-1, true); + _startAt = null; + } else { + copy = {}; + for (p in vars) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through. + if (!(p in _reservedProps)) { + copy[p] = vars[p]; + } + } + copy.overwrite = 0; + copy.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in HTML5's CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in. + _startAt = TweenLite.to(target, 0, copy); + if (!immediate) { + _startAt.render(-1, true); //for tweens that aren't rendered immediately, we still need to use the _startAt to record the starting values so that we can revert to them if the parent timeline's playhead goes backward beyond the beginning, but we immediately revert the tween back otherwise the parent tween that's currently instantiating wouldn't see the wrong starting values (since they were changed by the _startAt tween) + } else if (_time === 0) { + return; + } + } + } + + if (vars.ease is Ease) { + _ease = (vars.easeParams is Array) ? vars.ease.config.apply(vars.ease, vars.easeParams) : vars.ease; + } else if (typeof(vars.ease) === "function") { + _ease = new Ease(vars.ease, vars.easeParams); + } else { + _ease = defaultEase; + } + _easeType = _ease._type; + _easePower = _ease._power; + _firstPT = null; + + if (_targets) { + i = _targets.length; + while (--i > -1) { + if ( _initProps( _targets[i], (_propLookup[i] = {}), _siblings[i], (_overwrittenProps ? _overwrittenProps[i] : null)) ) { + initPlugins = true; + } + } + } else { + initPlugins = _initProps(target, _propLookup, _siblings, _overwrittenProps); + } + + if (initPlugins) { + _onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite + } + if (_overwrittenProps) if (_firstPT == null) if (typeof(target) !== "function") { //if all tweening properties have been overwritten, kill the tween. If the target is a function, it's most likely a delayedCall so let it live. + _enabled(false, false); + } + if (vars.runBackwards) { + pt = _firstPT; + while (pt) { + pt.s += pt.c; + pt.c = -pt.c; + pt = pt._next; + } + } + _onUpdate = vars.onUpdate; + _initted = true; + } + + /** @private Loops through the vars properties, captures starting values, triggers overwriting if necessary, etc. **/ + protected function _initProps(target:Object, propLookup:Object, siblings:Array, overwrittenProps:Object):Boolean { + var vars:Object = this.vars, + p:String, i:int, initPlugins:Boolean, plugin:Object, val:Object; + if (target == null) { + return false; + } + for (p in vars) { + val = vars[p]; + if (p in _reservedProps) { + if (val is Array) if (val.join("").indexOf("{self}") !== -1) { + vars[p] = _swapSelfInParams(val as Array); + } + + } else if ((p in _plugins) && (plugin = new _plugins[p]())._onInitTween(target, val, this)) { + _firstPT = new PropTween(plugin, "setRatio", 0, 1, p, true, _firstPT, plugin._priority); + i = plugin._overwriteProps.length; + while (--i > -1) { + propLookup[plugin._overwriteProps[i]] = _firstPT; + } + if (plugin._priority || ("_onInitAllProps" in plugin)) { + initPlugins = true; + } + if (("_onDisable" in plugin) || ("_onEnable" in plugin)) { + _notifyPluginsOfEnabled = true; + } + + } else { + _firstPT = propLookup[p] = new PropTween(target, p, 0, 1, p, false, _firstPT); + _firstPT.s = (!_firstPT.f) ? Number(target[p]) : target[ ((p.indexOf("set") || !("get" + p.substr(3) in target)) ? p : "get" + p.substr(3)) ](); + _firstPT.c = (typeof(val) === "number") ? Number(val) - _firstPT.s : (typeof(val) === "string" && val.charAt(1) === "=") ? int(val.charAt(0)+"1") * Number(val.substr(2)) : Number(val) || 0; + } + } + + if (overwrittenProps) if (_kill(overwrittenProps, target)) { //another tween may have tried to overwrite properties of this tween before init() was called (like if two tweens start at the same time, the one created second will run first) + return _initProps(target, propLookup, siblings, overwrittenProps); + } + if (_overwrite > 1) if (_firstPT != null) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, _overwrite, siblings)) { + _kill(propLookup, target); + return _initProps(target, propLookup, siblings, overwrittenProps); + } + return initPlugins; + } + + + + /** @private (see Animation.render() for notes) **/ + override public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + var isComplete:Boolean, callback:String, pt:PropTween, rawPrevTime:Number, prevTime:Number = _time; + if (time >= _duration) { + _totalTime = _time = _duration; + ratio = _ease._calcEnd ? _ease.getRatio(1) : 1; + if (!_reversed) { + isComplete = true; + callback = "onComplete"; + } + if (_duration == 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + rawPrevTime = _rawPrevTime; + if (_startTime === _timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate. + time = 0; + } + if (time === 0 || rawPrevTime < 0 || rawPrevTime === _tinyNum) if (rawPrevTime !== time) { + force = true; + if (rawPrevTime > 0 && rawPrevTime !== _tinyNum) { + callback = "onReverseComplete"; + } + } + _rawPrevTime = rawPrevTime = (!suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + } + + } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. + _totalTime = _time = 0; + ratio = _ease._calcEnd ? _ease.getRatio(0) : 0; + if (prevTime !== 0 || (_duration === 0 && _rawPrevTime > 0 && _rawPrevTime !== _tinyNum)) { + callback = "onReverseComplete"; + isComplete = _reversed; + } + if (time < 0) { + _active = false; + if (_duration == 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + if (_rawPrevTime >= 0) { + force = true; + } + _rawPrevTime = rawPrevTime = (!suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + } + } else if (!_initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately. + force = true; + } + + } else { + _totalTime = _time = time; + if (_easeType) { + var r:Number = time / _duration; + if (_easeType == 1 || (_easeType == 3 && r >= 0.5)) { + r = 1 - r; + } + if (_easeType == 3) { + r *= 2; + } + if (_easePower == 1) { + r *= r; + } else if (_easePower == 2) { + r *= r * r; + } else if (_easePower == 3) { + r *= r * r * r; + } else if (_easePower == 4) { + r *= r * r * r * r; + } + if (_easeType == 1) { + ratio = 1 - r; + } else if (_easeType == 2) { + ratio = r; + } else if (time / _duration < 0.5) { + ratio = r / 2; + } else { + ratio = 1 - (r / 2); + } + + } else { + ratio = _ease.getRatio(time / _duration); + } + + } + + if (_time == prevTime && !force) { + return; + } else if (!_initted) { + _init(); + if (!_initted || _gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example. + return; + } + //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently. + if (_time && !isComplete) { + ratio = _ease.getRatio(_time / _duration); + } else if (isComplete && _ease._calcEnd) { + ratio = _ease.getRatio((_time === 0) ? 0 : 1); + } + } + + if (!_active) if (!_paused && _time !== prevTime && time >= 0) { + _active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done. + } + if (prevTime == 0) { + if (_startAt != null) { + if (time >= 0) { + _startAt.render(time, suppressEvents, force); + } else if (!callback) { + callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area. + } + } + if (vars.onStart) if (_time != 0 || _duration == 0) if (!suppressEvents) { + vars.onStart.apply(null, vars.onStartParams); + } + } + + pt = _firstPT; + while (pt) { + if (pt.f) { + pt.t[pt.p](pt.c * ratio + pt.s); + } else { + pt.t[pt.p] = pt.c * ratio + pt.s; + } + pt = pt._next; + } + + if (_onUpdate != null) { + if (time < 0 && _startAt != null && _startTime != 0) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. + _startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete. + } + if (!suppressEvents) if (_time !== prevTime || isComplete) { + _onUpdate.apply(null, vars.onUpdateParams); + } + } + + if (callback) if (!_gc) { //check gc because there's a chance that kill() could be called in an onUpdate + + if (time < 0 && _startAt != null && _onUpdate == null && _startTime != 0) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. + _startAt.render(time, suppressEvents, force); + } + if (isComplete) { + if (_timeline.autoRemoveChildren) { + _enabled(false, false); + } + _active = false; + } + if (!suppressEvents) if (vars[callback]) { + vars[callback].apply(null, vars[callback + "Params"]); + } + if (_duration === 0 && _rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless. + _rawPrevTime = 0; + } + } + + } + + /** @private Same as kill() except that it returns a Boolean indicating if any significant properties were changed (some plugins like MotionBlurPlugin may perform cleanup tasks that alter alpha, etc.). **/ + override public function _kill(vars:Object=null, target:Object=null):Boolean { + if (vars === "all") { + vars = null; + } + if (vars == null) if (target == null || target == this.target) { + return _enabled(false, false); + } + target = target || _targets || this.target; + var i:int, overwrittenProps:Object, p:String, pt:PropTween, propLookup:Object, changed:Boolean, killProps:Object, record:Boolean; + if (target is Array && typeof(target[0]) === "object") { + i = target.length; + while (--i > -1) { + if (_kill(vars, target[i])) { + changed = true; + } + } + } else { + if (_targets) { + i = _targets.length; + while (--i > -1) { + if (target === _targets[i]) { + propLookup = _propLookup[i] || {}; + _overwrittenProps = _overwrittenProps || []; + overwrittenProps = _overwrittenProps[i] = vars ? _overwrittenProps[i] || {} : "all"; + break; + } + } + } else if (target !== this.target) { + return false; + } else { + propLookup = _propLookup; + overwrittenProps = _overwrittenProps = vars ? _overwrittenProps || {} : "all"; + } + if (propLookup) { + killProps = vars || propLookup; + record = (vars != overwrittenProps && overwrittenProps != "all" && vars != propLookup && (typeof(vars) != "object" || vars._tempKill != true)); //_tempKill is a super-secret way to delete a particular tweening property but NOT have it remembered as an official overwritten property (like in BezierPlugin) + for (p in killProps) { + pt = propLookup[p] + if (pt != null) { + if (pt.pg && pt.t._kill(killProps)) { + changed = true; //some plugins need to be notified so they can perform cleanup tasks first + } + if (!pt.pg || pt.t._overwriteProps.length === 0) { + if (pt._prev) { + pt._prev._next = pt._next; + } else if (pt == _firstPT) { + _firstPT = pt._next; + } + if (pt._next) { + pt._next._prev = pt._prev; + } + pt._next = pt._prev = null; + } + delete propLookup[p]; + } + if (record) { + overwrittenProps[p] = 1; + } + } + if (_firstPT == null && _initted) { //if all tweening properties are killed, kill the tween. Without this line, if there's a tween with multiple targets and then you killTweensOf() each target individually, the tween would technically still remain active and fire its onComplete even though there aren't any more properties tweening. + _enabled(false, false); + } + } + } + return changed; + } + + /** @inheritDoc **/ + override public function invalidate():* { + if (_notifyPluginsOfEnabled) { + _onPluginEvent("_onDisable", this); + } + _firstPT = null; + _overwrittenProps = null; + _onUpdate = null; + _startAt = null; + _initted = _active = _notifyPluginsOfEnabled = false; + _propLookup = (_targets) ? {} : []; + return this; + } + + /** @private (see Animation._enabled() for notes) **/ + override public function _enabled(enabled:Boolean, ignoreTimeline:Boolean=false):Boolean { + if (enabled && _gc) { + if (_targets) { + var i:int = _targets.length; + while (--i > -1) { + _siblings[i] = _register(_targets[i], this, true); + } + } else { + _siblings = _register(target, this, true); + } + } + super._enabled(enabled, ignoreTimeline); + if (_notifyPluginsOfEnabled) if (_firstPT != null) { + return _onPluginEvent(((enabled) ? "_onEnable" : "_onDisable"), this); + } + return false; + } + + + +//---- STATIC FUNCTIONS ----------------------------------------------------------------------------------- + + /** + * Static method for creating a TweenLite instance that animates to the specified destination values + * (from the current values). The following lines of code all produce identical results: + * + * +TweenLite.to(mc, 1, {x:100}); +var myTween = new TweenLite(mc, 1, {x:100}); +var myTween = TweenLite.to(mc, 1, {x:100}); + + * + *

Each line above will tween the "x" property of the mc object + * to a value of 100 over the coarse of 1 second. They each use a slightly different syntax, + * all of which are valid. If you don't need to store a reference of the tween, just use the + * static TweenLite.to( ) call.

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the x property of mc1, mc2, and mc3 to a value of 100 simultaneously:

+ * + * +TweenLite.to([mc1, mc2, mc3], 1, {x:100}); + + *

Even though 3 objects are animating, there is still only one tween created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerTo() method of TimelineLite or TweenMax.

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenLite.to(mc, 1, {x:100, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical to() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: TweenLite.to(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenLite instance + * @see com.greensock.TimelineLite#to() + * @see com.greensock.TimelineLite#staggerTo() + * @see com.greensock.TweenMax#staggerTo() + * @see #from() + * @see #fromTo() + */ + public static function to(target:Object, duration:Number, vars:Object):TweenLite { + return new TweenLite(target, duration, vars); + } + + /** + * Static method for creating a TweenLite instance that tweens backwards - + * you define the BEGINNING values and the current values are used + * as the destination values which is great for doing things like animating objects + * onto the screen because you can set them up initially the way you want them to look + * at the end of the tween and then animate in from elsewhere. + * + *

NOTE: By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins (often the desired behavior when inserting + * into TimelineLite or TimelineMax instances). To illustrate the default behavior, the + * following code will immediately set the alpha of mc + * to 0 and then wait 2 seconds before tweening the alpha back to 1 over + * the course of 1.5 seconds:

+ * + *

+ * TweenLite.from(mc, 1.5, {alpha:0, delay:2}); + *

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the alpha property of mc1, mc2, and mc3 from a value of 0 simultaneously:

+ * + * +TweenLite.from([mc1, mc2, mc3], 1.5, {alpha:0}); + + *

Even though 3 objects are animating, there is still only one tween created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerFrom() method of TimelineLite or TweenMax.

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenLite.from(mc, 1, {alpha:0, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical from() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the starting value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 100 and mc.y from 200 and then call myFunction, do this: TweenLite.from(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenLite instance + * @see #to() + * @see #fromTo() + * @see com.greensock.TimelineLite#from() + * @see com.greensock.TimelineLite#staggerFrom() + * @see com.greensock.TweenMax#staggerFrom() + */ + public static function from(target:Object, duration:Number, vars:Object):TweenLite { + vars = _prepVars(vars, true); + vars.runBackwards = true; + return new TweenLite(target, duration, vars); + } + + /** + * Static method for creating a TweenLite instance that allows you to define both the starting + * and ending values (as opposed to to() and from() tweens which are + * based on the target's current values at one end or the other). + * + *

NOTE: Only put starting values in the fromVars parameter - all + * special properties for the tween (like onComplete, onUpdate, delay, etc.) belong in the toVars + * parameter.

+ * + *

By default, immediateRender is true in + * fromTo() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. This is done for convenience because it is + * often the preferred behavior when setting things up on the screen to animate into place, but + * you can override this behavior by passing immediateRender:false in the + * fromVars or toVars parameter so that it will wait to render + * the starting values until the tween actually begins (often the desired behavior when inserting + * into TimelineLite or TimelineMax instances).

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the x property of mc1, mc2, and mc3 from 0 to 100 simultaneously:

+ * + * +TweenLite.fromTo([mc1, mc2, mc3], 1, {x:0}, {x:100}); + + *

Even though 3 objects are animating, there is still only one tween created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerFromTo() method of TimelineLite or TweenMax.

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenLite.fromTo(mc, 1, {x:0}, {x:100, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical fromTo() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param fromVars An object defining the starting value for each property that should be tweened. For example, to tween mc.x from 100 and mc.y from 200, fromVars would look like this: {x:100, y:200}. + * @param toVars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 0 to 100 and mc.y from 0 to 200 and then call myFunction, do this: TweenLite.fromTo(mc, 1, {x:0, y:0}, {x:100, y:200, onComplete:myFunction}); + * @return TweenLite instance + * @see #to() + * @see #from() + * @see com.greensock.TimelineLite#fromTo() + * @see com.greensock.TimelineLite#staggerFromTo() + * @see com.greensock.TweenMax#staggerFromTo() + */ + public static function fromTo(target:Object, duration:Number, fromVars:Object, toVars:Object):TweenLite { + toVars = _prepVars(toVars, true); + fromVars = _prepVars(fromVars); + toVars.startAt = fromVars; + toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); + return new TweenLite(target, duration, toVars); + } + + /** @private Accommodates TweenLiteVars instances for strong data typing and code hinting **/ + protected static function _prepVars(vars:Object, immediateRender:Boolean=false):Object { + if (vars._isGSVars) { + vars = vars.vars; + } + if (immediateRender && !("immediateRender" in vars)) { + vars.immediateRender = true; + } + return vars; + } + + /** + * Provides a simple way to call a function after a set amount of time (or frames). You can + * optionally pass any number of parameters to the function too. + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions the 4th parameter is scope, bumping useFrames + * back to the 5th parameter:

+ * + *

TweenLite.delayedCall(delay, callback, params, scope, useFrames) [JavaScript and AS2 only]

+ * + * +//calls myFunction after 1 second and passes 2 parameters: +TweenLite.delayedCall(1, myFunction, ["param1", 2]); + +function myFunction(param1, param2) { + //do stuff +} + + * + * @param delay Delay in seconds (or frames if useFrames is true) before the function should be called + * @param callback Function to call + * @param params An Array of parameters to pass the function (optional). + * @param useFrames If the delay should be measured in frames instead of seconds, set useFrames to true (default is false) + * @return TweenLite instance + * @see com.greensock.TimelineLite#call() + * @see com.greensock.TimelineMax#addCallback() + */ + public static function delayedCall(delay:Number, callback:Function, params:Array=null, useFrames:Boolean=false):TweenLite { + return new TweenLite(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onReverseComplete:callback, onReverseCompleteParams:params, immediateRender:false, useFrames:useFrames, overwrite:0}); + } + + /** + * Immediately sets properties of the target accordingly - essentially a zero-duration to() tween with a more + * intuitive name. So the following lines produce identical results: + * + * +TweenLite.set(myObject, {x:100, y:50, alpha:0}); +TweenLite.to(myObject, 0, {x:100, y:50, alpha:0}); + + * + *

And of course you can use an array to set the properties of multiple targets at the same time, like:

+ * + * +TweenLite.set([obj1, obj2, obj3], {x:100, y:50, alpha:0}); + + * + * @param target Target object (or array of objects) whose properties will be affected. + * @param vars An object defining the value for each property that should be set. For example, to set mc.x to 100 and mc.y to 200, do this: TweenLite.set(mc, {x:100, y:200}); + * @return A TweenLite instance (with a duration of 0) which can optionally be inserted into a TimelineLite/Max instance (although it's typically more concise to just use the timeline's set() method). + */ + public static function set(target:Object, vars:Object):TweenLite { + return new TweenLite(target, 0, vars); + } + + /** @private **/ + private static function _dumpGarbage(event:Event):void { + if ((_rootFrame / 60) >> 0 === _rootFrame / 60) { //faster than !(_rootFrame % 60) + var i:int, a:Array, tgt:Object; + for (tgt in _tweenLookup) { + a = _tweenLookup[tgt]; + i = a.length; + while (--i > -1) { + if (a[i]._gc) { + a.splice(i, 1); + } + } + if (a.length === 0) { + delete _tweenLookup[tgt]; + } + } + } + } + + + + /** + * Kills all the tweens (or specific tweening properties) of a particular object or delayedCalls + * to a particular function. If, for example, you want to kill all tweens of myObject, + * you'd do this: + * + *

+ * TweenLite.killTweensOf(myObject); + *

+ * + *

To kill only active (currently animating) tweens of myObject, you'd do this:

+ * + *

+ * TweenLite.killTweensOf(myObject, true); + *

+ * + *

To kill only particular tweening properties of the object, use the third parameter. + * For example, if you only want to kill all the tweens of myObject.alpha and + * myObject.x, you'd do this:

+ * + *

+ * TweenLite.killTweensOf(myObject, false, {alpha:true, x:true}); + *

+ * + *

To kill all the delayedCalls that were created like TweenLite.delayedCall(5, myFunction);, + * you can simply call TweenLite.killTweensOf(myFunction); because delayedCalls + * are simply tweens that have their target and onComplete set to + * the same function (as well as a delay of course).

+ * + *

killTweensOf() affects tweens that haven't begun yet too. If, for example, + * a tween of myObject has a delay of 5 seconds and + * TweenLite.killTweensOf(mc) is called 2 seconds after the tween was created, + * it will still be killed even though it hasn't started yet.

+ * + * @param target Object whose tweens should be killed immediately or selector text to feed the selector engine to find the target(s). + * @param onlyActive If true, only tweens that are currently active will be killed (a tween is considered "active" if the virtual playhead is actively moving across the tween and it is not paused, nor are any of its ancestor timelines paused). + * @param vars To kill only specific properties, use a generic object containing enumerable properties corresponding to the ones that should be killed like {x:true, y:true}. The values assigned to each property of the object don't matter - the sole purpose of the object is for iteration over the named properties (in this case, x and y). If no object (or null) is defined, all matched tweens will be killed in their entirety. + **/ + public static function killTweensOf(target:*, onlyActive:*=false, vars:Object=null):void { + if (typeof(onlyActive) === "object") { + vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted) + onlyActive = false; + } + var a:Array = TweenLite.getTweensOf(target, onlyActive), + i:int = a.length; + while (--i > -1) { + a[i]._kill(vars, target); + } + } + + /** + * Immediately kills all of the delayedCalls to a particular function. If, for example, + * you want to kill all delayedCalls to myFunction, you'd do this: + * + *

+ * TweenLite.killDelayedCallsTo(myFunction); + *

+ * + *

Since a delayedCall is just a tween that uses the function/callback as both its target + * and its onComplete, TweenLite.killTweensOf(myFunction) produces exactly the + * same result as TweenLite.killDelayedCallsTo(myFunction).

+ * + *

This method affects all delayedCalls that were created using TweenLite.delayedCall() + * or TweenMax.delayedCall() or the call() or addCallback() methods + * of TimelineLite or TimelineMax. Basically, any tween whose target is the function you supply will + * be killed.

+ * + * @param func The function for which all delayedCalls should be killed/cancelled. + **/ + public static function killDelayedCallsTo(func:Function):void { + killTweensOf(func); + } + + /** + * Returns an array containing all the tweens of a particular target (or group of targets) that have not + * been released for garbage collection yet which typically happens within a few seconds after the tween completes. + * For example, TweenLite.getTweensOf(myObject) returns an array of all tweens + * of myObject, even tweens that haven't begun yet. TweenLite.getTweensOf([myObject1, myObject2]); + * will return a condensed array of the tweens of myObject1 plus all the tweens + * of myObject2 combined into one array with duplicates removed. + * + *

Since the method only finds tweens that haven't been released for garbage collection, if you create a tween + * and then let it finish and then a while later try to find it with getTweensOf(), it may not be found + * because it was released by the engine for garbage collection. Remember, one of the best parts of GSAP is that it + * saves you from the headache of managing gc. Otherwise, you'd need to manually dispose each tween you create, making + * things much more cumbersome.

+ * + * +TweenLite.to(myObject1, 1, {x:100}); +TweenLite.to(myObject2, 1, {x:100}); +TweenLite.to([myObject1, myObject2], 1, {alpha:0}); + +var a1 = TweenLite.getTweensOf(myObject1); //finds 2 tweens +var a2 = TweenLite.getTweensOf([myObject1, myObject2]); //finds 3 tweens + + * @param target The target whose tweens should be returned, or an array of such targets + * @param onlyActive If true, only tweens that are currently active will be returned (a tween is considered "active" if the virtual playhead is actively moving across the tween and it is not paused, nor are any of its ancestor timelines paused). + * @return An array of tweens + **/ + public static function getTweensOf(target:*, onlyActive:Boolean=false):Array { + var i:int, a:Array, j:int, t:TweenLite; + if (target is Array && typeof(target[0]) != "string" && typeof(target[0]) != "number") { + i = target.length; + a = []; + while (--i > -1) { + a = a.concat(getTweensOf(target[i], onlyActive)); + } + i = a.length; + //now get rid of any duplicates (tweens of arrays of objects could cause duplicates) + while (--i > -1) { + t = a[i]; + j = i; + while (--j > -1) { + if (t === a[j]) { + a.splice(i, 1); + } + } + } + } else { + a = _register(target).concat(); + i = a.length; + while (--i > -1) { + if (a[i]._gc || (onlyActive && !a[i].isActive())) { + a.splice(i, 1); + } + } + } + return a; + } + + /** + * @private + * Used for one or more of the following purposes: + * 1) Register a target, putting it into the lookup/Dictionary for easy lookup later + * 2) Returns an array of sibling tweens (tweens of the same target) + * 3) scrubs the siblings array of duplicate instances of the tween (typically only used when re-enabling a tween instance). + **/ + protected static function _register(target:Object, tween:TweenLite=null, scrub:Boolean=false):Array { + var a:Array = _tweenLookup[target], + i:int; + if (a == null) { + a = _tweenLookup[target] = []; + } + if (tween) { + i = a.length; + a[i] = tween; + if (scrub) { + while (--i > -1) { + if (a[i] === tween) { + a.splice(i, 1); + } + } + } + } + return a; + } + + /** @private Performs overwriting **/ + protected static function _applyOverwrite(target:Object, tween:TweenLite, props:Object, mode:int, siblings:Array):Boolean { + var i:int, changed:Boolean, curTween:TweenLite; + if (mode == 1 || mode >= 4) { + var l:int = siblings.length; + for (i = 0; i < l; i++) { + curTween = siblings[i]; + if (curTween != tween) { + if (!curTween._gc) if (curTween._enabled(false, false)) { + changed = true; + } + } else if (mode == 5) { + break; + } + } + return changed; + } + //NOTE: Add 0.0000000001 to overcome floating point errors that can cause the startTime to be VERY slightly off (when a tween's time() is set for example) + var startTime:Number = tween._startTime + 0.0000000001, overlaps:Array = [], oCount:int = 0, zeroDur:Boolean = (tween._duration == 0), globalStart:Number; + i = siblings.length; + while (--i > -1) { + curTween = siblings[i]; + if (curTween === tween || curTween._gc || curTween._paused) { + //ignore + } else if (curTween._timeline != tween._timeline) { + globalStart = globalStart || _checkOverlap(tween, 0, zeroDur); + if (_checkOverlap(curTween, globalStart, zeroDur) === 0) { + overlaps[oCount++] = curTween; + } + } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) { + overlaps[oCount++] = curTween; + } + } + + i = oCount; + while (--i > -1) { + curTween = overlaps[i]; + if (mode == 2) if (curTween._kill(props, target)) { + changed = true; + } + if (mode !== 2 || (!curTween._firstPT && curTween._initted)) { + if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween. + changed = true; + } + } + } + return changed; + } + + /** + * @private + * Checks if a tween overlaps with a particular global time value. "reference" is the point in time on the global (root) timeline, + * and if the tween overlaps with it, 0 is returned. If the tween starts AFTER the reference, the difference between the two (positive + * value) is returned. If reference is AFTER the end of the tween, the negative offset is given (reference time minus where the end of + * the tween is on the global timeline). If the tween lands EXACTLY on the reference, it will check to see if the tween's _initted property + * is true. If not, 0.0000000001 is returned, indicating that the tween shouldn't be overwritten. If any of the child's anscestor timelines + * are paused, -100 is returned. This wraps a lot of functionality into a relatively concise method (keeps file size low and performance high) + **/ + private static function _checkOverlap(tween:Animation, reference:Number, zeroDur:Boolean):Number { + var tl:SimpleTimeline = tween._timeline, + ts:Number = tl._timeScale, + t:Number = tween._startTime, + min:Number = 0.0000000001; + while (tl._timeline) { + t += tl._startTime; + ts *= tl._timeScale; + if (tl._paused) { + return -100; + } + tl = tl._timeline; + } + t /= ts; + return (t > reference) ? t - reference : ((zeroDur && t == reference) || (!tween._initted && t - reference < 2 * min)) ? min : ((t += tween.totalDuration() / tween._timeScale / ts) > reference + min) ? 0 : t - reference - min; + } + + + } + +} + diff --git a/src/com/greensock/TweenMax.as b/src/com/greensock/TweenMax.as new file mode 100644 index 0000000..29823b7 --- /dev/null +++ b/src/com/greensock/TweenMax.as @@ -0,0 +1,2045 @@ +/** + * VERSION: 12.1.5 + * DATE: 2014-07-19 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock { + import com.greensock.TweenLite; + import com.greensock.core.Animation; + import com.greensock.core.PropTween; + import com.greensock.core.SimpleTimeline; + import com.greensock.events.TweenEvent; + import com.greensock.plugins.*; + + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.display.Shape; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.IEventDispatcher; + import flash.utils.getTimer; +/** + * TweenMax extends TweenLite, adding many useful (but non-essential) features like repeat(), + * repeatDelay(), yoyo(), AS3 event dispatching, updateTo(), pauseAll(), and more. It also activates many extra plugins + * by default, making it extremely full-featured. Any of the plugins can work with TweenLite too, but TweenMax saves + * you the step of activating the common ones. Since TweenMax extends TweenLite, it can do ANYTHING + * TweenLite can do plus more. The syntax is identical. You can mix and match TweenLite and TweenMax in your + * project as you please, but if file size is a concern it is best to stick with TweenLite unless you need + * a particular TweenMax-only feature. + * + *

Like TweenLite, a TweenMax instance handles tweening one or more properties of any object + * (or array of objects) over time. TweenMax can be used on its own or in conjuction with advanced sequencing + * tools like TimelineLite or TimelineMax to make complex tasks much simpler. With scores of other animation + * frameworks to choose from, why consider the GreenSock Animation Platform?:

+ * + *
    + *
  • SPEED - The platform has been highly optimized for maximum performance. + * See some speed comparisons yourself at + * http://www.greensock.com/tweening-speed-test/
  • + * + *
  • Freakishly robust feature set - In addition to tweening any numeric property + * of any object, plugins can be activated to tween hex colors, beziers, arrays, filters, plus + * LOTS more. It can round values, use relative values, smoothly reverse() on the + * fly, automatically detect and accommodate getter/setter functions, employ virtually any easing + * equation, pause()/resume() anytime, and intelligently manage conflicting tweens of + * the same object with various overwrite modes. TweenMax extends TweenLite and adds even + * more capabilities like repeat, yoyo, repeatDelay, on-the-fly destination value + * updates and more.
  • + * + *
  • Sequencing, grouping, and management features - TimelineLite and TimelineMax + * make it surprisingly simple to create complex sequences or groups of tweens that you can + * control as a whole. play(), pause(), restart(), or reverse(). You can even tween a timeline's + * time or progress to fastforward or rewind the entire timeline. Add + * labels, change the timeline's timeScale, nest timelines within timelines, and much more. + * This can revolutionize your animation workflow, making it more modular and concise.
  • + * + *
  • AS3, AS2, and JavaScript - Most other engines are only developed for one language, + * but the GreenSock Animation Platform allows you to use a consistent API across all your Flash and + * HTML5 projects.
  • + * + *
  • Ease of use - Designers and Developers alike rave about how intuitive the platform is.
  • + * + *
  • Support and reliability - With frequent updates, dedicated forums, + * committed authorship, a solid track record, a proven funding mechansim, and a thriving community of users, + * the platform is a safe long-term bet (unlike many open source projects).
  • + * + *
  • Expandability - With its plugin architecture, you can activate as many (or as few) + * extra features as your project requires. Write your own plugin to handle particular special + * properties in custom ways. Minimize bloat and maximize performance.
  • + * + *
+ * + *

USAGE

+ *

The most common type of tween is a to() tween which allows you + * to define the destination values:

+ * + *

+ * TweenMax.to(myObject, 2, {x:100, y:200}); + *

+ * + *

The above code will tween myObject.x from whatever it currently is to 100 and + * myObject.y property to 200 over the course of 2 seconds. Notice the x and y values are + * defined inside a generic object (between curly braces). Put as many properties there as you want.

+ * + *

By default, tweens begin immediately, although you can delay them using the delay + * special property or pause them initially using the paused special property (see below).

+ * + *

The target can also be an array of objects. For example, the following tween will + * tween the alpha property to 0.5 and y property to 100 for obj1, obj2, and obj3:

+ * + *

+ * TweenMax.to([obj1, obj2, obj3], 1, {alpha:0.5, y:100}); + *

+ * + *

You can also use a from() tween if you want to define the + * starting values instead of the ending values so that the target tweens from + * the defined values to wherever they currently are. Or a fromTo() + * lets you define both starting and ending values.

+ * + *

Although the to(), from(), and fromTo() static methods + * are popular because they're quick and can avoid some garbage collection hassles, you can also + * use the more object-oriented syntax like this:

+ * + *

+ * var tween = new TweenMax(myObject, 2, {x:100, y:200}); + *

+ * + *

or even:

+ * + *

+ * var tween = TweenMax.to(myObject, 2, {x:100, y:200}); + *

+ * + * + *

SPECIAL PROPERTIES:

+ *

Typically the vars parameter is used to define ending values for tweening + * properties of the target (or beginning values for from() tweens) + * like {x:100, y:200, alpha:0}, but the following optional special properties + * serve other purposes:

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the tween should begin.
  • + * + *
  • ease : Ease (or Function) - + * You can choose from various eases to control the rate of change during + * the animation, giving it a specific "feel". For example, ElasticOut.ease + * or StrongInOut.ease. For best performance, use one of the GreenSock eases + * (which are in the com.greensock.easing package). TweenMax also works with + * any standard easing equation that uses the typical 4 parameters (time, start, + * change, duration) like Adobe's fl.motion.easing eases. + * The default is Power1.easeOut. For linear animation, use the GreenSock + * Linear.ease ease
  • + * + *
  • onComplete : Function - + * A function that should be called when the tween has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * TweenMax.to(mc, 1, {x:100, onComplete:myFunction, onCompleteParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onCompleteParams:["{self}", "param2"]
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the tweens's timing will be + * based on frames instead of seconds because it is intially added to the root + * frames-based timeline. This causes both its duration + * and delay to be based on frames. An animations's timing mode is + * always determined by its parent timeline.
  • + * + *
  • easeParams : Array [deprecated] - + * Some GreenSock eases (like OutIn or ElasticOut) have a config() + * method that allows them to be configured to change their behavior (like TweenMax.to(mc, 1, {x:100, ease:ElasticOut.ease.config(0.5, 1)}) + * but if you are using a non-GreenSock ease that accepts extra parameters like Adobe's + * fl.motion.easing.Elastic, easeParams allows you to define + * those extra parameters as an array like TweenMax.to(mc, 1, {x:100, ease:Elastic.easeOut, easeParams:[0.5, 1]}). + * Most easing equations, however, don't require extra parameters so you won't need to + * pass in any easeParams. GreenSock eases provide the best performance, so use them + * whenever possible.
  • + * + *
  • immediateRender : Boolean - + * Normally when you create a tween, it begins rendering on the very next frame (update cycle) + * unless you specify a delay. However, if you prefer to force the tween to + * render immediately when it is created, set immediateRender to true. + * Or to prevent a from() from rendering immediately, set immediateRender + * to false. By default, from() tweens set immediateRender to true.
  • + * + *
  • onStart : Function - + * A function that should be called when the tween begins (when its time + * changes from 0 to some other value which can happen more than once if the + * tween is restarted multiple times).
  • + * + *
  • onStartParams : Array - + * An Array of parameters to pass the onStart function. For example, + * TweenMax.to(mc, 1, {x:100, delay:1, onStart:myFunction, onStartParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onStartParams:["{self}", "param2"]
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the tween updates + * (on every frame while the tween is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * TweenMax.to(mc, 1, {x:100, onUpdate:myFunction, onUpdateParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onUpdateParams:["{self}", "param2"]
  • + * + *
  • onReverseComplete : Function - + * A function that should be called when the tween has reached its beginning again from the + * reverse direction. For example, if reverse() is called the tween will move + * back towards its beginning and when its time reaches 0, onReverseComplete + * will be called. This can also happen if the tween is placed in a TimelineLite or TimelineMax instance + * that gets reversed and plays the tween backwards to (or past) the beginning.
  • + * + *
  • onReverseCompleteParams : Array - + * An Array of parameters to pass the onReverseComplete function. For example, + * TweenMax.to(mc, 1, {x:100, onReverseComplete:myFunction, onReverseCompleteParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onReverseCompleteParams:["{self}", "param2"]
  • + * + *
  • paused : Boolean - + * If true, the tween will pause itself immediately upon creation.
  • + * + *
  • overwrite : String (or integer) - + * Controls how (and if) other tweens of the same target are overwritten. + * There are several modes to choose from, but "auto" is the default (although + * you can change the default mode using the TweenLite.defaultOverwrite property): + *
      + *
    • "none" (0) (or false) - no overwriting will occur.
    • + * + *
    • "all" (1) (or true) - immediately overwrites all existing + * tweens of the same target even if they haven't started yet or don't have + * conflicting properties.
    • + * + *
    • "auto" (2) - when the tween renders for the first time, it will analyze + * tweens of the same target that are currently active/running and only overwrite + * individual tweening properties that overlap/conflict. Tweens that haven't begun + * yet are ignored. For example, if another active tween is found that is tweening + * 3 properties, only 1 of which it shares in common with the new tween, the other + * 2 properties will be left alone. Only the conflicting property gets overwritten/killed. + * This is the default mode and typically the most intuitive for developers.
    • + * + *
    • "concurrent" (3) - when the tween renders for the first time, it kills + * only the active (in-progress) tweens of the same target regardless of whether + * or not they contain conflicting properties. Like a mix of "all" + * and "auto". Good for situations where you only want one tween + * controling the target at a time.
    • + * + *
    • "allOnStart" (4) - Identical to "all" but waits to run + * the overwrite logic until the tween begins (after any delay). Kills + * tweens of the same target even if they don't contain conflicting properties + * or haven't started yet.
    • + * + *
    • "preexisting" (5) - when the tween renders for the first time, it kills + * only the tweens of the same target that existed BEFORE this tween was created + * regardless of their scheduled start times. So, for example, if you create a tween + * with a delay of 10 and then a tween with a delay of 1 and then a tween with a + * delay of 2 (all of the same target), the 2nd tween would overwrite the first + * but not the second even though scheduling might seem to dictate otherwise. + * "preexisting" only cares about the order in which the instances + * were actually created. This can be useful when the order in which your code runs + * plays a critical role.
    • + * + *
  • + * + *
  • repeat : Number - + * Number of times that the tween should repeat after its first iteration. For example, + * if repeat is 1, the tween will play a total of twice (the initial play + * plus 1 repeat). To repeat indefinitely, use -1. repeat should always be an integer.
  • + * + *
  • repeatDelay : Number - + * Amount of time in seconds (or frames for frames-based tweens) between repeats. For example, + * if repeat is 2 and repeatDelay is 1, the tween will play initially, + * then wait for 1 second before it repeats, then play again, then wait 1 second again before + * doing its final repeat.
  • + * + *
  • yoyo : Boolean - + * If true, every other repeat cycle will run in the opposite + * direction so that the tween appears to go back and forth (forward then backward). + * This has no affect on the "reversed" property though. So if repeat + * is 2 and yoyo is false, it will look like: + * start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, + * it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end.
  • + * + *
  • onRepeat : Function - + * A function that should be called each time the tween repeats
  • + * + *
  • onRepeatParams : Array - + * An Array of parameters to pass the onRepeat function. For example, + * TweenMax.to(mc, 1, {x:100, onRepeat:myFunction, onRepeatParams:[mc, "param2"]}); + * To self-reference the tween instance itself in one of the parameters, use "{self}", + * like: onRepeatParams:["{self}", "param2"]
  • + * + *
  • onStartListener : Function [AS3 only] - + * A function that should be called (and passed an event parameter) when the tween begins + * (when its totalTime changes from 0 to some other value which can happen more + * than once if the tween is restarted multiple times). Identical to onStart except + * that the function will always be passed an event parameter whose target property points + * to the tween. It's the same as doing myTween.addEventListener("start", myFunction);. + * Unless you need the event parameter, it's better/faster to use onStart.
  • + * + *
  • onUpdateListener : Function [AS3 only] - + * A function that should be called (and passed an event parameter) each time the tween updates + * (on every frame while the tween is active). Identical to onUpdate except + * that the function will always be passed an event parameter whose target property points + * to the tween. It's the same as doing myTween.addEventListener("update", myFunction);. + * Unless you need the event parameter, it's better/faster to use onUpdate.
  • + * + *
  • onCompleteListener : Function [AS3 only] - + * A function that should be called (and passed an event parameter) each time the tween completes. + * Identical to onComplete except that the function will always be passed an event + * parameter whose target property points to the tween. It's the same as doing + * myTween.addEventListener("complete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onComplete.
  • + * + *
  • onReverseCompleteListener : Function [AS3 only] - + * A function that should be called (and passed an event parameter) each time the tween has reached + * its beginning again from the reverse direction. For example, if reverse() is called + * the tween will move back towards its beginning and when its totalTime reaches 0, + * onReverseCompleteListener will be called. This can also happen if the tween is placed + * in a TimelineLite or TimelineMax instance that gets reversed and plays the tween backwards to + * (or past) the beginning. Identical to onReverseComplete except that the function + * will always be passed an event parameter whose target property points to the tween. + * It's the same as doing myTween.addEventListener("reverseComplete", myFunction);. + * Unless you need the event parameter, it's better/faster to use onReverseComplete.
  • + * + *
  • onRepeatListener : Function [AS3 only] - + * A function that should be called (and passed an event parameter) each time the tween repeats. + * Identical to onRepeat except that the function will always be passed an event + * parameter whose target property points to the tween. It's the same as doing + * myTween.addEventListener("repeat", myFunction);. + * Unless you need the event parameter, it's better/faster to use onRepeat.
  • + * + *
  • startAt : Object - + * Allows you to define the starting values for tweening properties. Typically, TweenMax uses + * the current value (whatever it happens to be at the time the tween begins) as the starting + * value, but startAt allows you to override that behavior. Simply pass an object + * in with whatever properties you'd like to set just before the tween begins. For example, + * if mc.x is currently 100, and you'd like to tween it from 0 to 500, do + * TweenMax.to(mc, 2, {x:500, startAt:{x:0}});
  • + *
+ * + *

AS3 note: In AS3, using a TweenMaxVars + * instance instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + * + * + *

PLUGINS:

+ * + *

Think of plugins like special properties that are dynamically added, delivering extra abilities without + * forcing them to be baked into the core engine, keeping it relatively lean and mean. Each plugin is associated + * with a property name and it takes responsibility for handling that property. For example, the TintPlugin + * is associated with the "tint" property name so if it is activated it will intercept the "tint" property + * in the following tween and manage it uniquely:

+ * + *

+ * TweenLite.to(mc, 1, {tint:0xFF0000}); + *

+ * + *

If the TintPlugin wasn't activated, TweenLite would act as though you were trying to literally tween the + * mc.tint property (and there is no such thing).

+ * + *

In the JavaScript version of TweenMax, activating a plugin is as simple as loading the associated .js file. + * No extra activation code is necessary. And by default, the JavaScript version of TweenMax includes the CSSPlugin + * and RoundPropsPlugin so you don't need to load those separately. In the ActionScript version, activating a plugin + * requires a single line of code and you only need to do it once, so it's pretty easy. Simply pass an Array containing + * the names of all the plugins you'd like to activate to the TweenPlugin.activate() method, like this:

+ * + *

+ * TweenPlugin.activate([FrameLabelPlugin, ColorTransformPlugin, TintPlugin]); + *

+ * + *

To make it even easier, there is a Plugin Explorer + * which writes the code for you. All you need to do is select the plugins and copy/paste the code + * from the bottom of the tool. It also displays interactive examples of each plugin and the assocaited + * code so that it’s easy to see the correct syntax.

+ * + *

The following plugins are automatically activated by TweenMax:

+ * + *
    + *
  • autoAlpha : Number - + * autoAlpha is identical to tweening alpha except that it also + * automatically hides the target when the value hits zero, and shows the target when the + * value isn't zero. In AS3, this means it toggles the target's visible property. + * In AS2, the _visible property is toggled, and in JS the visibility + * style property is set to "hidden" to hide. This can help improve rendering performance.
  • + * + *
  • visible : Boolean [AS3/AS2 only] - + * hides or shows the target when the tween completes. In AS3, this means it toggles the target's + * visible property. In AS2, the _visible property is toggled, and in + * JS the display style is set to "none" to hide.
  • + * + *
  • volume : Number [AS3/AS2 only] - + * Tweens the volume of an object. In AS3, it can handle anything with a soundTransform + * property (MovieClip/SoundChannel/NetStream, etc.). In AS2, it is for MovieClips or Sound objects.
  • + * + *
  • tint : Number [AS3/AS2 only] - + * Tweens the color (tint) of the target. Use a hex value, for example: 0xFF0000 for red or 0x0000FF + * for blue, etc. To remove the tint, use null.
  • + * + *
  • frame : Number [AS3/AS2 only] - + * Tweens a MovieClip to a particular frame. To tween to a label, use the FrameLabelPlugin.
  • + * + *
  • bezier : Array - + * Bezier tweening allows you to tween in a non-linear way. For example, you may want to tween + * the target's position from the origin (0,0) 500 pixels to the right (500,0) but curve downwards + * through the middle of the tween. Simply pass as many objects in the bezier array as you'd like, + * one for each "control point". See the BezierPlugin documentation for more details. In this example, + * let's say the control point would be at x/y coordinates 250,50. Just make sure your mc is at + * coordinates 0,0 and then do: TweenMax.to(my_mc, 3, {bezier:[{x:250, y:50}, {x:500, y:0}]});
  • + * + *
  • bezierThrough : Array - + * Identical to bezier except that instead of passing bezier control point values, you + * pass values through which the bezier values should move. This can be more intuitive than using + * control points.
  • + * + *
  • orientToBezier : Boolean (or Array) - + * When doing a bezier or bezierThrough tween, you can use + * orientToBezier to cause the target to alter its rotation in the direction + * of the bezier, making it appear to turn with the curves. The simplest way is to set + * orientToBezier to true, but you can accomplish advanced effects + * like using a different property than "rotation" or adding a certain number of degrees to the + * standard rotational value, etc. by using an array instead. The array should contain the + * following 4 elements (in this order): + *
      + *
    1. Position property 1 (typically "x")
    2. + *
    3. Position property 2 (typically "y")
    4. + *
    5. Rotational property (typically "rotation")
    6. + *
    7. Number of degrees to add (optional - makes it easy to orient your target properly)
    8. + *
    + * For maximum flexibility, you can pass in any number of arrays inside the container array, one + * for each rotational property. This can be convenient when working in 3D because you can rotate + * on multiple axis. If you're doing a standard 2D x/y tween on a bezier, you can simply pass + * in a boolean value of true and TweenMax will use a typical setup, [["x", "y", "rotation", 0]]. + * Hint: Don't forget the container Array (notice the double outer brackets)
  • + * + *
  • hexColors : Object - + * Although hex colors are technically numbers, if you try to tween them conventionally, + * you'll notice that they don't tween smoothly. To tween them properly, the red, green, and + * blue components must be extracted and tweened independently. TweenMax makes it easy. To tween + * a property of your object that's a hex color, just pass an Object with properties named the + * same as your object's hex color properties that should be tweened. For example, if your mc + * object has a "myHexProp" property that you'd like to tween to red (0xFF0000) over the course + * of 2 seconds, do: TweenMax.to(mc, 2, {hexColors:{myHexProp:0xFF0000}}); + * You can pass in any number of hexColor properties.
  • + * + *
  • shortRotation : Object - + * For rotational tweens, it can be useful to have the engine figure out the shortest direction + * to the destination value and go that way. For example, if the target's rotation property is + * at 0 and you need to rotate to 270, it would actually be shorter to go from 0 to -90. + * If rotation is currently 170 degrees and you want to tween it to -170 degrees, + * a normal rotation tween would travel a total of 340 degrees in the counter-clockwise direction, + * but if you use shortRotation, it would travel 20 degrees in the clockwise direction instead. + * In order to accommodate any rotational property (including 3D ones like rotationX, rotationY, + * and rotationZ or even a custom one), shortRotation should be an object whose properties + * correspond to the ones you want tweened. For example, to tween mc.rotation to 270 in ths shortest + * direction, do: TweenMax.to(mc, 1, {shortRotation:{rotation:270}}); or to tween + * its rotationX to -80 and rotationY to 30 in the shortest direction, do: + * TweenMax.to(mc, 1, {shortRotation:{rotationX:-80, rotationY:30}});
  • + * + *
  • roundProps : String - + * A comma-delimited list of property names whose value should be rounded to the nearest integer + * anytime they are updated during the tween. For example, if you're tweening the + * x, y, and alpha properties of mc and you want to round the x and y values (not alpha) + * every time the tween is rendered, do: + * TweenMax.to(mc, 2, {x:300, y:200, alpha:0.5, roundProps:"x,y"});
  • + * + *
  • blurFilter : Object [AS3/AS2 only] - + * Creates a BlurFilter tween affecting any of the following properties: + * blurX, blurY, quality, remove, addFilter, index. For example, + * to blur the object 20 pixels on each axis, do: + * TweenMax.to(mc, 1, {blurFilter:{blurX:20, blurY:20}}); + * To remove the filter as soon as the tween completes, set remove:true + * inside the blurFilter object.
  • + * + *
  • glowFilter : Object [AS3/AS2 only] - + * Creates a GlowFilter tween affecting any of the following properties: + * alpha, blurX, blurY, color, strength, quality, inner, knockout, remove, addFilter, index. + * For example, to create a 20 pixel red glow with a strength of 1.5 and alpha of 1, do: + * TweenMax.to(mc, 1, {glowFilter:{blurX:20, blurY:20, color:0xFF0000, strength:1.5, alpha:1}}); + * To remove the filter as soon as the tween completes, set remove:true + * inside the glowFilter object.
  • + * + *
  • colorMatrixFilter : Object [AS3/AS2 only] - + * Creates a ColorMatrixFilter tween affecting any of the following properties: + * colorize, amount, contrast, brightness, saturation, hue, threshold, relative, matrix, remove, addFilter, index + * For example, to completely desaturate the target, do: + * TweenMax.to(mc, 1, {colorMatrixFilter:{saturation:0}}); + * Or to colorize the object red at 50% strength, do: + * TweenMax.to(mc, 1, {colorMatrixFilter:{colorize:0xFF0000, amount:0.5}}); + * To remove the filter as soon as the tween completes, set remove:true + * inside the colorMatrixFilter object.
  • + * + *
  • dropShadowFilter : Object [AS3/AS2 only] - + * Creates a DropShadowFilter tween affecting any of the following properties: + * alpha, angle, blurX, blurY, color, distance, strength, quality, remove, addFilter, index + * For example, to create a 10 pixel red drop shadow with an alpha of 0.8 and an angle of 45, do: + * TweenMax.to(mc, 1, {dropShadowFilter:{blurX:10, blurY:10, color:0xFF0000, angle:45, alpha:0.8}}); + * To remove the filter as soon as the tween completes, set remove:true + * inside the dropShadowFilter object.
  • + * + *
  • bevelFilter : Object [AS3/AS2 only] - + * Creates a BevelFilter tween affecting any of the following properties: + * angle, blurX, blurY, distance, highlightAlpha, highlightColor, shadowAlpha, shadowColor, strength, quality, remove, addFilter, index + * For example, to create a 10 pixel bevel with a strength of 1.5 and distance of 10 and shadowAlpha of 0.8, do: + * TweenMax.to(mc, 1, {bevelFilter:{blurX:10, blurY:10, strength:1.5, distance:10, shadowAlpha:0.8}}); + * To remove the filter as soon as the tween completes, set remove:true + * inside the bevelFilter object.
  • + *
+ * + * + *

EXAMPLES:

+ *

Please see http://www.greensock.com for + * examples, tutorials, and interactive demos.

+ * + * NOTES / TIPS: + *
    + *
  • Passing values as Strings and a preceding "+=" or "-=" will make the tween relative to the + * current value. For example, if you do TweenMax.to(mc, 2, {x:"-=20"}); it'll + * tween mc.x to the left 20 pixels. {x:"+=20"} would move it to the right.
  • + * + *
  • You can use addEventListener() to add listeners to the tween instance manually + * instead of using the onCompleteListener, onStartListener, and onUpdateListener special properties. + * Like myTween.addEventListener("complete", myFunction);
  • + * + *
  • You can change the default ease by setting the TweenLite.defaultEase static property. + * The default is Power1.easeOut.
  • + * + *
  • You can kill all tweens of a particular object anytime with TweenMax.killTweensOf(myObject);
  • + * + *
  • You can kill all delayedCalls to a particular function with TweenMax.killDelayedCallsTo(myFunction) + * or TweenMax.killTweensOf(myFunction);
  • + * + *
  • Use the TweenMax.from() method to animate things into place. For example, + * if you have things set up on the stage in the spot where they should end up, and you + * just want to animate them into place, you can pass in the beginning x and/or y and/or + * alpha (or whatever properties you want).
  • + * + *
  • If you find this class useful, please consider joining Club GreenSock + * which not only helps to sustain ongoing development, but also gets you bonus plugins, classes + * and other benefits that are ONLY available to members. Learn more at + * http://www.greensock.com/club/
  • + *
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenMax extends TweenLite implements IEventDispatcher { + /** @private **/ + public static const version:String = "12.1.5"; + + TweenPlugin.activate([ + + //ACTIVATE (OR DEACTIVATE) PLUGINS HERE... + + AutoAlphaPlugin, //tweens alpha and then toggles "visible" to false if/when alpha is zero + EndArrayPlugin, //tweens numbers in an Array + FramePlugin, //tweens MovieClip frames + RemoveTintPlugin, //allows you to remove a tint + TintPlugin, //tweens tints + VisiblePlugin, //tweens a target's "visible" property + VolumePlugin, //tweens the volume of a MovieClip or SoundChannel or anything with a "soundTransform" property + BevelFilterPlugin, //tweens BevelFilters + BezierPlugin, //enables bezier tweening + BezierThroughPlugin, //enables bezierThrough tweening + BlurFilterPlugin, //tweens BlurFilters + ColorMatrixFilterPlugin, //tweens ColorMatrixFilters (including hue, saturation, colorize, contrast, brightness, and threshold) + ColorTransformPlugin, //tweens advanced color properties like exposure, brightness, tintAmount, redOffset, redMultiplier, etc. + DropShadowFilterPlugin, //tweens DropShadowFilters + FrameLabelPlugin, //tweens a MovieClip to particular label + GlowFilterPlugin, //tweens GlowFilters + HexColorsPlugin, //tweens hex colors + RoundPropsPlugin, //enables the roundProps special property for rounding values + ShortRotationPlugin //tweens rotation values in the shortest direction + + ]); + + /** @private **/ + protected static var _listenerLookup:Object = {onCompleteListener:TweenEvent.COMPLETE, onUpdateListener:TweenEvent.UPDATE, onStartListener:TweenEvent.START, onRepeatListener:TweenEvent.REPEAT, onReverseCompleteListener:TweenEvent.REVERSE_COMPLETE}; + + /** + * The object that dispatches a "tick" event each time the engine updates, making it easy for + * you to add your own listener(s) to run custom logic after each update (great for game developers). + * Add as many listeners as you want. The basic syntax is the same for all versions (AS2, AS3, and JavaScript): + * + *

Basic example (AS2, AS3, and JavaScript):

+ //add listener + TweenMax.ticker.addEventListener("tick", myFunction); + + function myFunction(event) { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenMax.ticker.removeEventListener("tick", myFunction); + + * + *

Due to differences in the core languages (and to maximize efficiency), the advanced syntax is slightly different + * for the AS3 version compared to AS2 and JavaScript. The parameters beyond the first 2 in the addEventListener() + * method are outlined below:

+ * + *

JavaScript and AS2

+ *

addEventListener(type, callback, scope, useParam, priority)

+ *

Parameters: + *

    + *
  1. type : String - type of listener, should always be "tick"
  2. + *
  3. callback : Function - the function to call when the event occurs
  4. + *
  5. scope : Object - binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in JavaScript and AS2 because scope isn't generally maintained.
  6. + *
  7. useParam : Boolean - if true, an event object will be generated and fed to the callback each time the event occurs. The event is a generic object and has two properties: type (always "tick") and target which refers to the ticker instance. The default for useParam is false because it improves performance.
  8. + *
  9. priority : Integer - influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
  10. + *
+ *

+ * + *

Advanced example (JavaScript and AS2):

+ //add listener that requests an event object parameter, binds scope to the current scope (this), and sets priority to 1 so that it is called before any other listeners that had a priority lower than 1... + TweenMax.ticker.addEventListener("tick", myFunction, this, true, 1); + + function myFunction(event) { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenMax.ticker.removeEventListener("tick", myFunction); + + * + *

AS3

+ *

The AS3 version uses the standard EventDispatcher.addEventListener() syntax which + * basically allows you to define a priority and whether or not to use weak references (see Adobe's + * docs for details).

+ * + *

Advanced example [AS3 only]:

+ import flash.events.Event; + + //add listener with weak reference (standard syntax - notice the 5th parameter is true) + TweenMax.ticker.addEventListener("tick", myFunction, false, 0, true); + + function myFunction(event:Event):void { + //executes on every tick after the core engine updates + } + + //to remove the listener later... + TweenMax.ticker.removeEventListener("tick", myFunction); + + **/ + public static var ticker:Shape = Animation.ticker; + + + /** + * Kills all the tweens (or specific tweening properties) of a particular object or + * the delayedCalls to a particular function. If, for example, you want to kill all + * tweens of myObject, you'd do this: + * + *

+ * TweenMax.killTweensOf(myObject); + *

+ * + *

To kill only active (currently animating) tweens of myObject, you'd do this:

+ * + *

+ * TweenLite.killTweensOf(myObject, true); + *

+ * + *

To kill only particular tweening properties of the object, use the second parameter. + * For example, if you only want to kill all the tweens of myObject.alpha and + * myObject.x, you'd do this:

+ * + *

+ * TweenMax.killTweensOf(myObject, false, {alpha:true, x:true}); + *

+ * + *

To kill all the delayedCalls (like ones created using TweenMax.delayedCall(5, myFunction);), + * you can simply call TweenMax.killTweensOf(myFunction); because delayedCalls + * are simply tweens that have their target and onComplete set to + * the same function (as well as a delay of course).

+ * + *

killTweensOf() affects tweens that haven't begun yet too. If, for example, + * a tween of myObject has a delay of 5 seconds and + * TweenLite.killTweensOf(mc) is called 2 seconds after the tween was created, + * it will still be killed even though it hasn't started yet.

+ * + * @param target Object whose tweens should be killed immediately + * @param onlyActive If true, only tweens that are currently active will be killed (a tween is considered "active" if the virtual playhead is actively moving across the tween and it is not paused, nor are any of its ancestor timelines paused). + * @param vars To kill only specific properties, use a generic object containing enumerable properties corresponding to the ones that should be killed like {x:true, y:true}. The values assigned to each property of the object don't matter - the sole purpose of the object is for iteration over the named properties (in this case, x and y). If no object (or null) is defined, all matched tweens will be killed in their entirety. + */ + public static function killTweensOf(target:*, onlyActive:*=false, vars:Object=null):void { + TweenLite.killTweensOf(target, onlyActive, vars); + } + + /** + * Immediately kills all of the delayedCalls to a particular function. If, for example, + * you want to kill all delayedCalls to myFunction, you'd do this: + * + *

+ * TweenMax.killDelayedCallsTo(myFunction); + *

+ * + *

Since a delayedCall is just a tween that uses the function/callback as both its target + * and its onComplete, TweenMax.killTweensOf(myFunction) produces exactly the + * same result as TweenMax.killDelayedCallsTo(myFunction).

+ * + *

This method affects all delayedCalls that were created using TweenLite.delayedCall() + * or TweenMax.delayedCall() or the call() or addCallback() methods + * of TimelineLite or TimelineMax. Basically, any tween whose target is the function you supply will + * be killed.

+ * + * @param func The function for which all delayedCalls should be killed/cancelled. + **/ + public static function killDelayedCallsTo(func:Function):void { + TweenLite.killTweensOf(func); + } + + /** + * Returns an array containing all the tweens of a particular target (or group of targets) that have not + * been released for garbage collection yet which typically happens within a few seconds after the tween completes. + * For example, TweenMax.getTweensOf(myObject) returns an array of all tweens + * of myObject, even tweens that haven't begun yet. TweenMax.getTweensOf([myObject1, myObject2]); + * will return a condensed array of the tweens of myObject1 plus all the tweens + * of myObject2 combined into one array with duplicates removed. + * + *

Since the method only finds tweens that haven't been released for garbage collection, if you create a tween + * and then let it finish and then a while later try to find it with getTweensOf(), it may not be found + * because it was released by the engine for garbage collection. Remember, one of the best parts of GSAP is that it + * saves you from the headache of managing gc. Otherwise, you'd need to manually dispose each tween you create, making + * things much more cumbersome.

+ * + * +TweenMax.to(myObject1, 1, {x:100}); +TweenMax.to(myObject2, 1, {x:100}); +TweenMax.to([myObject1, myObject2], 1, {alpha:0}); + +var a1 = TweenMax.getTweensOf(myObject1); //finds 2 tweens +var a2 = TweenMax.getTweensOf([myObject1, myObject2]); //finds 3 tweens + + * @param target The target whose tweens should be returned, or an array of such targets + * @param onlyActive If true, only tweens that are currently active will be returned (a tween is considered "active" if the virtual playhead is actively moving across the tween and it is not paused, nor are any of its ancestor timelines paused). + * @return An array of tweens + **/ + public static function getTweensOf(target:*, onlyActive:Boolean=false):Array { + return TweenLite.getTweensOf(target, onlyActive); + } + + /** @private **/ + protected var _dispatcher:EventDispatcher; + /** @private **/ + protected var _hasUpdateListener:Boolean; + /** @private **/ + protected var _repeat:int = 0; + /** @private **/ + protected var _repeatDelay:Number = 0; + /** @private **/ + protected var _cycle:int = 0; + /** @private **/ + public var _yoyo:Boolean; + + /** + * Constructor + * + * @param target Target object (or array of objects) whose properties this tween affects + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: new TweenMax(mc, 1, {x:100, y:200, onComplete:myFunction}). + */ + public function TweenMax(target:Object, duration:Number, vars:Object) { + super(target, duration, vars); + _yoyo = (this.vars.yoyo == true); + _repeat = int(this.vars.repeat); + _repeatDelay = this.vars.repeatDelay || 0; + _dirty = true; //ensures that if there is any repeat, the _totalDuration will get recalculated to accurately report it. + if (this.vars.onCompleteListener || this.vars.onUpdateListener || this.vars.onStartListener || this.vars.onRepeatListener || this.vars.onReverseCompleteListener) { + _initDispatcher(); + if (_duration == 0) if (_delay == 0) if (this.vars.immediateRender) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.UPDATE)); + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.COMPLETE)); + } + } + } + + /** @inheritDoc **/ + override public function invalidate():* { + _yoyo = Boolean(this.vars.yoyo == true); + _repeat = this.vars.repeat || 0; + _repeatDelay = this.vars.repeatDelay || 0; + _hasUpdateListener = false; + _initDispatcher(); + _uncache(true); + return super.invalidate(); + } + + /** + * Updates tweening values on the fly so that they appear to seamlessly change course even if + * the tween is in-progress. Think of it like dynamically updating the vars object + * that was passed in to the tween when it was originally created. You do NOT + * need to redefine all of the vars properties/values - only the ones that you want + * to update. You can even define new properties that you didn't define in the original vars + * object. + * + *

If the resetDuration parameter is true and the tween has already + * started (or finished), updateTo() will restart the tween. Otherwise, the tween's + * timing will be honored. And if resetDuration is false and the tween + * is in-progress, the starting values of each property will be adjusted so that the tween appears + * to seamlessly redirect to the new destination values. This is typically not advisable if you + * plan to reverse the tween later on or jump to a previous point because the starting values would + * have been adjusted.

+ * + *

updateTo() is only meant for non-plugin values. It's much more complicated to + * dynamically update values that are being handled inside plugins - that is not what this method + * is intended to do.

+ * + *

Note: If you plan to constantly update values, please look into using the DynamicPropsPlugin.

+ * + * +//create the tween +var tween:TweenMax = new TweenMax(mc, 2, {x:100, y:200, alpha:0.5}); + +//then later, update the destination x and y values, restarting the tween +tween.updateTo({x:300, y:0}, true); + +//or to update the values mid-tween without restarting, do this: +tween.updateTo({x:300, y:0}, false); + + * + * @param vars Object containing properties with the destination values that should be udpated. You do NOT need to redefine all of the original vars values - only the ones that should be updated (although if you change a plugin value, you will need to fully define it). For example, to update the destination x value to 300 and the destination y value to 500, pass: {x:300, y:500}. + * @param resetDuration If the tween has already started (or finished) and resetDuration is true, the tween will restart. If resetDuration is false, the tween's timing will be honored (no restart) and each tweening property's starting value will be adjusted so that it appears to seamlessly redirect to the new destination value. + * @return self (makes chaining easier) + **/ + public function updateTo(vars:Object, resetDuration:Boolean=false):* { + var curRatio:Number = ratio; + if (resetDuration) if (_startTime < _timeline._time) { + _startTime = _timeline._time; + _uncache(false); + if (_gc) { + _enabled(true, false); + } else { + _timeline.insert(this, _startTime - _delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct. + } + } + for (var p:String in vars) { + this.vars[p] = vars[p]; + } + if (_initted) { + if (resetDuration) { + _initted = false; + } else { + if (_gc) { + _enabled(true, false); + } + if (_notifyPluginsOfEnabled) if (_firstPT != null) { + _onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks + } + if (_time / _duration > 0.998) { //if the tween has finished (or come extremely close to finishing), we just need to rewind it to 0 and then render it again at the end which forces it to re-initialize (parsing the new vars). We allow tweens that are close to finishing (but haven't quite finished) to work this way too because otherwise, the values are so small when determining where to project the starting values that binary math issues creep in and can make the tween appear to render incorrectly when run backwards. + var prevTime:Number = _time; + render(0, true, false); + _initted = false; + render(prevTime, true, false); + } else if (_time > 0) { + _initted = false; + _init(); + var inv:Number = 1 / (1 - curRatio), + pt:PropTween = _firstPT, endValue:Number; + while (pt) { + endValue = pt.s + pt.c; + pt.c *= inv; + pt.s = endValue - pt.c; + pt = pt._next; + } + } + } + } + return this; + } + + /** + * @private + * Renders the tween at a particular time (or frame number for frames-based tweens). + * The time is based simply on the overall duration. For example, if a tween's duration + * is 3, renderTime(1.5) would render it at the halfway finished point. + * + * @param time time (or frame number for frames-based tweens) to render. + * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.) + * @param force Normally the tween will skip rendering if the time matches the cachedTotalTime (to improve performance), but if force is true, it forces a render. This is primarily used internally for tweens with durations of zero in TimelineLite/Max instances. + */ + override public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + if (!_initted) if (_duration === 0 && vars.repeat) { //zero duration tweens that render immediately have render() called from TweenLite's constructor, before TweenMax's constructor has finished setting _repeat, _repeatDelay, and _yoyo which are critical in determining totalDuration() so we need to call invalidate() which is a low-kb way to get those set properly. + invalidate(); + } + var totalDur:Number = (!_dirty) ? _totalDuration : totalDuration(), + prevTime:Number = _time, + prevTotalTime:Number = _totalTime, + prevCycle:Number = _cycle, + isComplete:Boolean, callback:String, pt:PropTween, rawPrevTime:Number; + if (time >= totalDur) { + _totalTime = totalDur; + _cycle = _repeat; + if (_yoyo && (_cycle & 1) != 0) { + _time = 0; + ratio = _ease._calcEnd ? _ease.getRatio(0) : 0; + } else { + _time = _duration; + ratio = _ease._calcEnd ? _ease.getRatio(1) : 1; + } + if (!_reversed) { + isComplete = true; + callback = "onComplete"; + } + if (_duration == 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + rawPrevTime = _rawPrevTime; + if (_startTime === _timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate. + time = 0; + } + if (time === 0 || rawPrevTime < 0 || rawPrevTime === _tinyNum) if (rawPrevTime !== time) { + force = true; + if (rawPrevTime > _tinyNum) { + callback = "onReverseComplete"; + } + } + _rawPrevTime = rawPrevTime = (!suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + } + + } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. + _totalTime = _time = _cycle = 0; + ratio = _ease._calcEnd ? _ease.getRatio(0) : 0; + if (prevTotalTime !== 0 || (_duration === 0 && _rawPrevTime > 0 && _rawPrevTime !== _tinyNum)) { + callback = "onReverseComplete"; + isComplete = _reversed; + } + if (time < 0) { + _active = false; + if (_duration == 0) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. + if (_rawPrevTime >= 0) { + force = true; + } + _rawPrevTime = rawPrevTime = (!suppressEvents || time !== 0 || _rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. + } + } else if (!_initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately. + force = true; + } + } else { + _totalTime = _time = time; + if (_repeat != 0) { + var cycleDuration:Number = _duration + _repeatDelay; + _cycle = (_totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but Flash reports it as 0.79999999!) + if (_cycle !== 0) if (_cycle === _totalTime / cycleDuration) { + _cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning) + } + _time = _totalTime - (_cycle * cycleDuration); + if (_yoyo) if ((_cycle & 1) != 0) { + _time = _duration - _time; + } + if (_time > _duration) { + _time = _duration; + } else if (_time < 0) { + _time = 0; + } + } + if (_easeType) { + var r:Number = _time / _duration, type:int = _easeType, pow:int = _easePower; + if (type == 1 || (type == 3 && r >= 0.5)) { + r = 1 - r; + } + if (type == 3) { + r *= 2; + } + if (pow == 1) { + r *= r; + } else if (pow == 2) { + r *= r * r; + } else if (pow == 3) { + r *= r * r * r; + } else if (pow == 4) { + r *= r * r * r * r; + } + + if (type == 1) { + ratio = 1 - r; + } else if (type == 2) { + ratio = r; + } else if (_time / _duration < 0.5) { + ratio = r / 2; + } else { + ratio = 1 - (r / 2); + } + + } else { + ratio = _ease.getRatio(_time / _duration); + } + } + + if (prevTime == _time && !force && _cycle === prevCycle) { + if (prevTotalTime !== _totalTime) if (_onUpdate != null) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate. + _onUpdate.apply(vars.onUpdateScope || this, vars.onUpdateParams); + } + return; + } else if (!_initted) { + _init(); + if (!_initted || _gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example. + return; + } + //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently. + if (_time && !isComplete) { + ratio = _ease.getRatio(_time / _duration); + } else if (isComplete && _ease._calcEnd) { + ratio = _ease.getRatio((_time === 0) ? 0 : 1); + } + } + if (!_active) if (!_paused && _time !== prevTime && time >= 0) { + _active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done. + } + if (prevTotalTime == 0) { + if (_startAt != null) { + if (time >= 0) { + _startAt.render(time, suppressEvents, force); + } else if (!callback) { + callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area. + } + } + if (_totalTime != 0 || _duration == 0) if (!suppressEvents) { + if (vars.onStart) { + vars.onStart.apply(null, vars.onStartParams); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.START)); + } + } + } + + pt = _firstPT; + while (pt) { + if (pt.f) { + pt.t[pt.p](pt.c * ratio + pt.s); + } else { + pt.t[pt.p] = pt.c * ratio + pt.s; + } + pt = pt._next; + } + + if (_onUpdate != null) { + if (time < 0 && _startAt != null && _startTime != 0) { + _startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete. + } + if (!suppressEvents) if (_totalTime !== prevTotalTime || isComplete) { + _onUpdate.apply(null, vars.onUpdateParams); + } + } + if (_hasUpdateListener) { + if (time < 0 && _startAt != null && _onUpdate == null && _startTime != 0) { + _startAt.render(time, suppressEvents, force); + } + if (!suppressEvents) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.UPDATE)); + } + } + if (_cycle != prevCycle) if (!suppressEvents) if (!_gc) { + if (vars.onRepeat) { + vars.onRepeat.apply(null, vars.onRepeatParams); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(TweenEvent.REPEAT)); + } + } + if (callback) if (!_gc) { //check gc because there's a chance that kill() could be called in an onUpdate + if (time < 0 && _startAt != null && _onUpdate == null && !_hasUpdateListener && _startTime != 0) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. + _startAt.render(time, suppressEvents, true); + } + if (isComplete) { + if (_timeline.autoRemoveChildren) { + _enabled(false, false); + } + _active = false; + } + if (!suppressEvents) { + if (vars[callback]) { + vars[callback].apply(null, vars[callback + "Params"]); + } + if (_dispatcher) { + _dispatcher.dispatchEvent(new TweenEvent(((callback == "onComplete") ? TweenEvent.COMPLETE : TweenEvent.REVERSE_COMPLETE))); + } + } + if (_duration === 0 && _rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless. + _rawPrevTime = 0; + } + } + } + + +//---- EVENT DISPATCHING ---------------------------------------------------------------------------------------------------------- + + /** + * @private + * Initializes Event dispatching functionality + */ + protected function _initDispatcher():Boolean { + var found:Boolean = false, p:String; + for (p in _listenerLookup) { + if (p in vars) if (vars[p] is Function) { + if (_dispatcher == null) { + _dispatcher = new EventDispatcher(this); + } + _dispatcher.addEventListener(_listenerLookup[p], vars[p], false, 0, true); + found = true; + } + } + return found; + } + + /** + * [AS3 only] + * Registers a function that should be called each time a particular type of event occurs, like + * "complete" or "update". The function will be passed a single "event" + * parameter whose "target" property refers to the tween. Typically it is more efficient + * to use callbacks like onComplete, onUpdate, onStart, onReverseComplete, and onRepeat + * unless you need the event parameter or if you need to register more than one listener for the same + * type of event. + * + *

If you no longer need an event listener, remove it by calling removeEventListener(), or memory + * problems could result. Event listeners are not automatically removed from memory because the garbage + * collector does not remove the listener as long as the dispatching object exists (unless the + * useWeakReference parameter is set to true).

+ * + * @param type The type of event + * @param listener The listener function that processes the event. This function must accept an Event object as its only parameter + * @param useCapture (not typically used) Determines whether the listener works in the capture phase or the target and bubbling phases. If useCapture is set to true, the listener processes the event only during the capture phase and not in the target or bubbling phase. If useCapture is false, the listener processes the event only during the target or bubbling phase. To listen for the event in all three phases, call addEventListener twice, once with useCapture set to true, then again with useCapture set to false. + * @param priority The priority level of the event listener. The priority is designated by a signed 32-bit integer. The higher the number, the higher the priority. All listeners with priority n are processed before listeners of priority n-1. If two or more listeners share the same priority, they are processed in the order in which they were added. The default priority is 0. + * @param useWeakReference Determines whether the reference to the listener is strong or weak. A strong reference (the default) prevents your listener from being garbage-collected. A weak reference does not. + * @see #removeEventListener() + **/ + public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + if (_dispatcher == null) { + _dispatcher = new EventDispatcher(this); + } + if (type == TweenEvent.UPDATE) { + _hasUpdateListener = true; + } + _dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** + * [AS3 only] + * Removes a listener from the EventDispatcher object. If there is no matching listener registered + * with the EventDispatcher object, a call to this method has no effect. + * + * @param type The type of event + * @param listener The listener object to remove. + * @param useCapture Specifies whether the listener was registered for the capture phase or the target and bubbling phases. If the listener was registered for both the capture phase and the target and bubbling phases, two calls to removeEventListener() are required to remove both, one call with useCapture() set to true, and another call with useCapture() set to false. + **/ + public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void { + if (_dispatcher) { + _dispatcher.removeEventListener(type, listener, useCapture); + } + } + + /** @private **/ + public function hasEventListener(type:String):Boolean { + return (_dispatcher == null) ? false : _dispatcher.hasEventListener(type); + } + + /** @private **/ + public function willTrigger(type:String):Boolean { + return (_dispatcher == null) ? false : _dispatcher.willTrigger(type); + } + + /** @private **/ + public function dispatchEvent(event:Event):Boolean { + return (_dispatcher == null) ? false : _dispatcher.dispatchEvent(event); + } + + +//---- STATIC FUNCTIONS ----------------------------------------------------------------------------------------------------------- + + /** + * Static method for creating a TweenMax instance that animates to the specified destination values + * (from the current values). This static method can be more intuitive for some developers + * and shields them from potential garbage collection issues that could arise when assigning a + * tween instance to a persistent variable. The following lines of code produce identical results: + * + * +TweenMax.to(mc, 1, {x:100}); +var myTween = new TweenMax(mc, 1, {x:100}); +var myTween = TweenMax.to(mc, 1, {x:100}); + + *

Each line above will tween the "x" property of the mc object + * to a value of 100 over the coarse of 1 second. They each use a slightly different syntax, + * all of which are valid. If you don't need to store a reference of the tween, just use the + * static TweenMax.to( ) call.

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the x property of mc1, mc2, and mc3 to a value of 100 simultaneously:

+ * + * +TweenMax.to([mc1, mc2, mc3], 1, {x:100}); + + *

Even though 3 objects are animating, there is still only one tween created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerTo() method (TimelineLite has one too).

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenMax.to(mc, 1, {x:100, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical to() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: TweenMax.to(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenMax instance + * @see #from() + * @see #fromTo() + * @see #staggerTo() + * @see com.greensock.TimelineLite#to() + * @see com.greensock.TimelineLite#staggerTo() + */ + public static function to(target:Object, duration:Number, vars:Object):TweenMax { + return new TweenMax(target, duration, vars); + } + + /** + * Static method for creating a TweenMax instance that tweens backwards - + * you define the BEGINNING values and the current values are used + * as the destination values which is great for doing things like animating objects + * onto the screen because you can set them up initially the way you want them to look + * at the end of the tween and then animate in from elsewhere. + * + *

NOTE: By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins (often the desired behavior when inserting + * into TimelineLite or TimelineMax instances). To illustrate the default behavior, the + * following code will immediately set the alpha of mc + * to 0 and then wait 2 seconds before tweening the alpha back to 1 over + * the course of 1.5 seconds:

+ * + *

+ * TweenMax.from(mc, 1.5, {alpha:0, delay:2}); + *

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the alpha property of mc1, mc2, and mc3 from a value of 0 simultaneously:

+ * + * +TweenMax.from([mc1, mc2, mc3], 1.5, {alpha:0}); + + *

Even though 3 objects are animating, there is still only one tween that is created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerFrom() method (TimelineLite has one too).

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenMax.from(mc, 1, {alpha:0, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical from() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the starting value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 100 and mc.y from 200 and then call myFunction, do this: TweenMax.from(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenMax instance + * @see #to() + * @see #fromTo() + * @see #staggerFrom() + * @see com.greensock.TimelineLite#from() + * @see com.greensock.TimelineLite#staggerFrom() + */ + public static function from(target:Object, duration:Number, vars:Object):TweenMax { + vars = _prepVars(vars, true); + vars.runBackwards = true; + return new TweenMax(target, duration, vars); + } + + /** + * Static method for creating a TweenMax instance that allows you to define both the starting + * and ending values (as opposed to to() and from() tweens which are + * based on the target's current values at one end or the other). + * + *

NOTE: Only put starting values in the fromVars parameter - all + * special properties for the tween (like onComplete, onUpdate, delay, etc.) belong in the toVars + * parameter.

+ * + *

By default, immediateRender is true in + * fromTo() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. This is done for convenience because it is + * often the preferred behavior when setting things up on the screen to animate into place, but + * you can override this behavior by passing immediateRender:false in the + * fromVars or toVars parameter so that it will wait to render + * the starting values until the tween actually begins (often the desired behavior when inserting + * into TimelineLite or TimelineMax instances).

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the x property of mc1, mc2, and mc3 from 0 to 100 simultaneously:

+ * + * +TweenMax.fromTo([mc1, mc2, mc3], 1, {x:0}, {x:100}); + + *

Even though 3 objects are animating, there is still only one tween created. + * In order to stagger or offset the start times of each object animating, please see + * the staggerFromTo() method (TimelineLite has one too).

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenMax.fromTo(mc, 1, {x:0}, {x:100, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical fromTo() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param fromVars An object defining the starting value for each property that should be tweened. For example, to tween mc.x from 100 and mc.y from 200, fromVars would look like this: {x:100, y:200}. + * @param toVars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 0 to 100 and mc.y from 0 to 200 and then call myFunction, do this: TweenMax.fromTo(mc, 1, {x:0, y:0}, {x:100, y:200, onComplete:myFunction}); + * @return TweenMax instance + * @see #to() + * @see #from() + * @see #staggerFromTo() + * @see com.greensock.TimelineLite#fromTo() + * @see com.greensock.TimelineLite#staggerFromTo() + */ + public static function fromTo(target:Object, duration:Number, fromVars:Object, toVars:Object):TweenMax { + toVars = _prepVars(toVars, false); + fromVars = _prepVars(fromVars, false); + toVars.startAt = fromVars; + toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); + return new TweenMax(target, duration, toVars); + } + + /** + * Tweens an array of targets to a common set of destination values, but staggers their + * start times by a specified amount of time, creating an evenly-spaced sequence with a + * surprisingly small amount of code. For example, let's say you have an array containing + * references to a bunch of text fields that you'd like to fall away and fade out in a + * staggered fashion with 0.2 seconds between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +TweenMax.staggerTo(textFields, 1, {y:"+150", ease:CubicIn.ease}, 0.2); + + *

staggerTo() simply loops through the targets array and creates + * a to() tween for each object and then returns an array containing all of + * the resulting tweens (one for each object).

+ * + *

If you can afford the slight increase in file size, it is usually better to use + * TimelineLite's staggerTo() method because it wraps the tweens in a + * TimelineLite instead of an array which makes controlling the group as a whole much + * easier. That way you could pause(), resume(), reverse(), restart() or change the timeScale + * of everything at once.

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 5th parameter).

+ * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (7th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if useFrames:true is defined in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like ease. For example, to tween x to 100 and y to 200 for mc1, mc2, and mc3, staggering their start time by 0.25 seconds and then call myFunction when they last one has finished, do this: TweenMax.staggerTo([mc1, mc2, mc3], 1, {x:100, y:200}, 0.25, myFunction}). + * @param stagger Amount of time in seconds (or frames for frames-based tweens) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: TweenMax.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+100", alpha:0}, 0.2). + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed. + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return Array of TweenMax tweens (one for each object in the targets array) + * @see #staggerFrom() + * @see #staggerFromTo() + * @see com.greensock.TimelineLite#staggerTo() + */ + public static function staggerTo(targets:Array, duration:Number, vars:Object, stagger:Number=0, onCompleteAll:Function=null, onCompleteAllParams:Array=null):Array { + vars = _prepVars(vars, false); + var a:Array = [], + l:int = targets.length, + delay:Number = vars.delay || 0, + copy:Object, + i:int, + p:String; + for (i = 0; i < l; i++) { + copy = {}; + for (p in vars) { + copy[p] = vars[p]; + } + copy.delay = delay; + if (i == l - 1) if (onCompleteAll != null) { + copy.onComplete = function():void { + if (vars.onComplete) { + vars.onComplete.apply(null, arguments); + } + onCompleteAll.apply(null, onCompleteAllParams); + }; + } + a[i] = new TweenMax(targets[i], duration, copy); + delay += stagger; + } + return a; + } + + /** + * Tweens an array of targets from a common set of destination values (using the current + * values as the destination), but staggers their start times by a specified amount of time, + * creating an evenly-spaced sequence with a surprisingly small amount of code. For example, + * let's say you have an array containing references to a bunch of text fields that you'd + * like to drop into place while fading in, all in a staggered fashion with 0.2 seconds + * between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +TweenMax.staggerFrom(textFields, 1, {y:"+150"}, 0.2); + + *

staggerFrom() simply loops through the targets array and creates + * a from() tween for each object and then returns an array containing all of + * the resulting tweens (one for each object).

+ * + *

If you can afford the slight increase in file size, it is usually better to use + * TimelineLite's staggerFrom() method because it wraps the tweens in a + * TimelineLite instead of an array which makes controlling the group as a whole much + * easier. That way you could pause(), resume(), reverse(), restart() or change the timeScale + * of everything at once.

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 5th parameter).

+ * + *

By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins.

+ * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (7th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if useFrames:true is defined in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like ease. For example, to tween x to 100 and y to 200 for mc1, mc2, and mc3, staggering their start time by 0.25 seconds and then call myFunction when they last one has finished, do this: TweenMax.staggerTo([mc1, mc2, mc3], 1, {x:100, y:200}, 0.25, myFunction}). + * @param stagger Amount of time in seconds (or frames for frames-based tweens) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: TweenMax.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+100", alpha:0}, 0.2). + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return An array of TweenMax instances (one for each object in the targets array) + * @see #staggerTo() + * @see #staggerFromTo() + * @see com.greensock.TimelineLite#staggerFrom() + */ + public static function staggerFrom(targets:Array, duration:Number, vars:Object, stagger:Number=0, onCompleteAll:Function=null, onCompleteAllParams:Array=null):Array { + vars = _prepVars(vars, true); + vars.runBackwards = true; + if (vars.immediateRender != false) { + vars.immediateRender = true; + } + return staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams); + } + + /** + * Tweens an array of targets from and to a common set of values, but staggers their + * start times by a specified amount of time, creating an evenly-spaced sequence with a + * surprisingly small amount of code. For example, let's say you have an array containing + * references to a bunch of text fields that you'd like to fade from alpha:1 to alpha:0 in a + * staggered fashion with 0.2 seconds between each tween's start time: + * + * +var textFields = [tf1, tf2, tf3, tf4, tf5]; +TweenMax.staggerFromTo(textFields, 1, {alpha:1}, {alpha:0}, 0.2); + + *

staggerFromTo() simply loops through the targets array and creates + * a fromTo() tween for each object and then returns an array containing all of + * the resulting tweens (one for each object).

+ * + *

If you can afford the slight increase in file size, it is usually better to use + * TimelineLite's staggerFromTo() method because it wraps the tweens in a + * TimelineLite instead of an array which makes controlling the group as a whole much + * easier. That way you could pause(), resume(), reverse(), restart() or change the timeScale + * of everything at once.

+ * + *

Note that if you define an onComplete (or any callback for that matter) + * in the vars parameter, it will be called for each tween rather than the whole + * sequence. This can be very useful, but if you want to call a function after the entire + * sequence of tweens has completed, use the onCompleteAll parameter (the 6th parameter).

+ * + *

By default, immediateRender is true in + * staggerFromTo() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. This is done for convenience because it is + * often the preferred behavior when setting things up on the screen to animate into place, but + * you can override this behavior by passing immediateRender:false in the + * fromVars or toVars parameter so that it will wait to render + * the starting values until the tweens actually begin (often the desired behavior when inserting + * into TimelineLite or TimelineMax instances).

+ * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (8th) parameter for onCompleteAllScope.

+ * + * @param targets An array of target objects whose properties should be affected + * @param duration Duration in seconds (or frames if useFrames:true is defined in the vars parameter) + * @param fromVars An object defining the starting value for each property that should be tweened. For example, to tween x from 100 and y from 200, fromVars would look like this: {x:100, y:200}. + * @param toVars An object defining the end value for each property that should be tweened as well as any special properties like ease. For example, to tween x from 0 to 100 and y from 0 to 200, staggering the start times by 0.2 seconds and then call myFunction when they all complete, do this: TweenMax.staggerFromTo([mc1, mc2, mc3], 1, {x:0, y:0}, {x:100, y:200}, 0.2, myFunction}); + * @param stagger Amount of time in seconds (or frames if the timeline is frames-based) to stagger the start time of each tween. For example, you might want to have 5 objects move down 100 pixels while fading out, and stagger the start times by 0.2 seconds - you could do: TweenMax.staggerTo([mc1, mc2, mc3, mc4, mc5], 1, {y:"+100", alpha:0}, 0.2). + * @param onCompleteAll A function to call as soon as the entire sequence of tweens has completed + * @param onCompleteAllParams An array of parameters to pass the onCompleteAll method. + * @return An array of TweenMax instances (one for each object in the targets array) + * @see #staggerTo() + * @see #staggerFrom() + * @see com.greensock.TimelineLite#staggerFromTo() + */ + public static function staggerFromTo(targets:Array, duration:Number, fromVars:Object, toVars:Object, stagger:Number=0, onCompleteAll:Function=null, onCompleteAllParams:Array=null):Array { + toVars = _prepVars(toVars, false); + fromVars = _prepVars(fromVars, false); + toVars.startAt = fromVars; + toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); + return staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams); + } + + /** @private [deprecated] - included here as an alias for backward compatibility **/ + public static var allTo:Function = staggerTo; + + /** @private [deprecated] - included here as an alias for backward compatibility **/ + public static var allFrom:Function = staggerFrom; + + /** @private [deprecated] - included here as an alias for backward compatibility **/ + public static var allFromTo:Function = staggerFromTo; + + /** + * Provides a simple way to call a function after a set amount of time (or frames). You can + * optionally pass any number of parameters to the function too. + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions the 4th parameter is scope, bumping useFrames + * back to the 5th parameter:

+ * + *

TweenMax.delayedCall(delay, callback, params, scope, useFrames) [JavaScript and AS2 only]

+ * + * +//calls myFunction after 1 second and passes 2 parameters: +TweenMax.delayedCall(1, myFunction, ["param1", 2]); + +function myFunction(param1, param2) { + //do stuff +} + + * + * @param delay Delay in seconds (or frames if useFrames is true) before the function should be called + * @param callback Function to call + * @param params An Array of parameters to pass the function (optional). + * @param useFrames If the delay should be measured in frames instead of seconds, set useFrames to true (default is false) + * @return TweenMax instance + */ + public static function delayedCall(delay:Number, callback:Function, params:Array=null, useFrames:Boolean=false):TweenMax { + return new TweenMax(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onReverseComplete:callback, onReverseCompleteParams:params, immediateRender:false, useFrames:useFrames, overwrite:0}); + } + + /** + * Immediately sets properties of the target accordingly - essentially a zero-duration to() tween with a more + * intuitive name. So the following lines produce identical results: + * + * +TweenMax.set(myObject, {x:100, y:50, alpha:0}); +TweenMax.to(myObject, 0, {x:100, y:50, alpha:0}); + + * + *

And of course you can use an array to set the properties of multiple targets at the same time, like:

+ * + * +TweenMax.set([obj1, obj2, obj3], {x:100, y:50, alpha:0}); + + * + * @param target Target object (or array of objects) whose properties will be affected. + * @param vars An object defining the value for each property that should be set. For example, to set mc.x to 100 and mc.y to 200, do this: TweenMax.set(mc, {x:100, y:200}); + * @return A TweenMax instance (with a duration of 0) which can optionally be inserted into a TimelineLite/Max instance (although it's typically more concise to just use the timeline's set() method). + */ + public static function set(target:Object, vars:Object):TweenMax { + return new TweenMax(target, 0, vars); + } + + /** + * Reports whether or not a particular object is actively tweening. If a tween + * is paused, is completed, or hasn't started yet, it isn't considered active. + * + * @param target Target object whose tweens you're checking + * @return Boolean value indicating whether or not any active tweens were found + */ + public static function isTweening(target:Object):Boolean { + return (TweenLite.getTweensOf(target, true).length > 0); + } + + /** + * Returns an array containing all tweens (and optionally timelines too, excluding the root timelines). + * If your goal is to affect all of the tweens/timelines/delayedCalls (like to pause() them + * or reverse() or alter their timeScale), you might want to consider using the + * static TimelineLite.exportRoot() method instead because it provides a single instance + * that you can use to control everything. + * + * @param includeTimelines If true, TimelineLite and TimelineMax instances will also be included. + * @return Array of tweens/timelines + * @see com.greensock.TimelineLite#exportRoot() + */ + public static function getAllTweens(includeTimelines:Boolean=false):Array { + var a:Array = _getChildrenOf(_rootTimeline, includeTimelines); + return a.concat( _getChildrenOf(_rootFramesTimeline, includeTimelines) ); + } + + /** @private **/ + protected static function _getChildrenOf(timeline:SimpleTimeline, includeTimelines:Boolean):Array { + if (timeline == null) { + return []; + } + var a:Array = [], + cnt:int = 0, + tween:Animation = timeline._first; + while (tween) { + if (tween is TweenLite) { + a[cnt++] = tween; + } else { + if (includeTimelines) { + a[cnt++] = tween; + } + a = a.concat(_getChildrenOf(SimpleTimeline(tween), includeTimelines)); + cnt = a.length; + } + tween = tween._next; + } + return a; + } + + /** + * Kills all tweens and/or delayedCalls/callbacks, and/or timelines, optionally forcing them to + * completion first. The various parameters provide a way to specify exactly which types you want + * to kill + * + * +//kill everything +TweenMax.killAll(); + +//kill only tweens, but not delayedCalls or timelines +TweenMax.killAll(false, true, false, false); + +//kill only delayedCalls +TweenMax.killAll(false, false, true, false); + + * + * @param complete Determines whether or not the tweens/delayedCalls/timelines should be forced to completion before being killed. + * @param tweens If true, all tweens will be killed (TweenLite and TweenMax instances) + * @param delayedCalls If true, all delayedCalls will be killed. TimelineMax callbacks are treated the same as delayedCalls. + * @param timelines If true, all TimelineLite and TimelineMax instances will be killed. + */ + public static function killAll(complete:Boolean=false, tweens:Boolean=true, delayedCalls:Boolean=true, timelines:Boolean=true):void { + var a:Array = getAllTweens(timelines), + l:int = a.length, + isDC:Boolean, + allTrue:Boolean = (tweens && delayedCalls && timelines), + tween:Animation, i:int; + for (i = 0; i < l; i++) { + tween = a[i]; + if (allTrue || (tween is SimpleTimeline) || ((isDC = (TweenLite(tween).target == TweenLite(tween).vars.onComplete)) && delayedCalls) || (tweens && !isDC)) { + if (complete) { + tween.totalTime(tween._reversed ? 0 : tween.totalDuration()); + } else { + tween._enabled(false, false); + } + } + } + } + + /** + * [AS3/AS2 only] + * Kills all tweens of the children of a particular MovieClip/DisplayObjectContainer, optionally forcing them to completion first. + * + * @param parent The parent MovieClip/DisplayObjectContainer whose children's tweens should be killed. + * @param complete If true, the tweens will be forced to completion before being killed. + */ + public static function killChildTweensOf(parent:DisplayObjectContainer, complete:Boolean=false):void { + var a:Array = getAllTweens(false), + l:int = a.length, i:int; + for (i = 0; i < l; i++) { + if (_containsChildOf(parent, a[i].target)) { + if (complete) { + a[i].totalTime(a[i].totalDuration()); + } else { + a[i]._enabled(false, false); + } + } + } + } + + /** @private **/ + private static function _containsChildOf(parent:DisplayObjectContainer, obj:Object):Boolean { + var i:int, curParent:DisplayObjectContainer; + if (obj is Array) { + i = obj.length; + while (--i > -1) { + if (_containsChildOf(parent, obj[i])) { + return true; + } + } + } else if (obj is DisplayObject) { + curParent = obj.parent; + while (curParent) { + if (curParent == parent) { + return true; + } + curParent = curParent.parent; + } + } + return false; + } + + /** + * [deprecated] Pauses all tweens and/or delayedCalls/callbacks and/or timelines. This literally + * changes the paused state of all affected tweens/delayedCalls/timelines, but a + * more flexible way to globally control things is to use the TimelineLite.exportRoot() method + * which essentially wraps all of the tweens/timelines/delayedCalls on the root timeline into a + * TimelineLite instance so that you can pause(), resume(), or even reverse() + * or alter the timeScale without affecting animations that you create after the export. + * This also avoids having to alter the paused state of every individual + * tween/delayedCall/timeline - controlling the TimelineLite that contains the exported animations + * delivers the same effect visually, but does so in a more elegant and flexible way. + * + * @param tweens If true, all tweens will be paused. + * @param delayedCalls If true, all delayedCalls will be paused. timeline callbacks are treated the same as delayedCalls. + * @param timelines If true, all TimelineLite and TimelineMax instances will be paused (at least the ones who haven't finished and been removed from their parent timeline) + * + * @see com.greensock.TimelineLite#exportRoot() + */ + public static function pauseAll(tweens:Boolean=true, delayedCalls:Boolean=true, timelines:Boolean=true):void { + _changePause(true, tweens, delayedCalls, timelines); + } + + /** + * [deprecated] Resumes all paused tweens and/or delayedCalls/callbacks and/or timelines. This literally + * changes the paused state of all affected tweens/delayedCalls/timelines, but a + * more flexible way to globally control things is to use the TimelineLite.exportRoot() method + * which essentially wraps all of the tweens/timelines/delayedCalls on the root timeline into a + * TimelineLite instance so that you can pause(), resume(), or even reverse() + * or alter the timeScale without affecting animations that you create after the export. + * This also avoids having to alter the paused state of every individual + * tween/delayedCall/timeline - controlling the TimelineLite that contains the exported animations + * delivers the same effect visually, but does so in a more elegant and flexible way. + * + * @param tweens If true, all tweens will be resumed. + * @param delayedCalls If true, all delayedCalls will be resumed. timeline callbacks are treated the same as delayedCalls. + * @param timelines If true, all TimelineLite and TimelineMax instances will be resumed (at least the ones who haven't finished and been removed from their parent timeline) + * @see com.greensock.TimelineLite#exportRoot() + */ + public static function resumeAll(tweens:Boolean=true, delayedCalls:Boolean=true, timelines:Boolean=true):void { + _changePause(false, tweens, delayedCalls, timelines); + } + + /** + * @private + * Changes the paused state of all tweens and/or delayedCalls/callbacks + * + * @param pause Desired paused state + * @param tweens If true, all tweens will be affected. + * @param delayedCalls If true, all delayedCalls will be affected. TimelineMax callbacks are treated the same as delayedCalls. + * @param timelines If true, all TimelineLite and TimelineMax instances will be affected (at least the ones who haven't finished and been removed from their parent timeline) + */ + private static function _changePause(pause:Boolean, tweens:Boolean=true, delayedCalls:Boolean=false, timelines:Boolean=true):void { + var a:Array = getAllTweens(timelines), + isDC:Boolean, + tween:Animation, + allTrue:Boolean = (tweens && delayedCalls && timelines), + i:int = a.length; + while (--i > -1) { + tween = a[i]; + isDC = (tween is TweenLite && TweenLite(tween).target == tween.vars.onComplete); + if (allTrue || (tween is SimpleTimeline) || (isDC && delayedCalls) || (tweens && !isDC)) { + tween.paused(pause); + } + } + } + + +//---- GETTERS / SETTERS ---------------------------------------------------------------------------------------------------------- + + + /** + * Gets or sets the tween's progress which is a value between 0 and 1 indicating the position + * of the virtual playhead (excluding repeats) where 0 is at the beginning, 0.5 is halfway complete, + * and 1 is complete. If the tween has a non-zero repeat defined, progress + * and totalProgress will be different because progress doesn't include any + * repeats or repeatDelays whereas totalProgress does. For example, if a TweenMax instance + * is set to repeat once, at the end of the first cycle totalProgress would only be 0.5 + * whereas progress would be 1. If you watched both properties over the course of the entire + * animation, you'd see progress go from 0 to 1 twice (once for each cycle) in the + * same time it takes the totalProgress to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTween.progress(0.5).play();

+ * + * +var progress = myTween.progress(); //gets current progress +myTween.progress( 0.25 ); //sets progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #totalProgress() + * @see #seek() + * @see #time() + * @see #totalTime() + **/ + override public function progress(value:Number=NaN, suppressEvents:Boolean=false):* { + return (!arguments.length) ? _time / duration() : totalTime( duration() * ((_yoyo && (_cycle & 1) !== 0) ? 1 - value : value) + (_cycle * (_duration + _repeatDelay)), suppressEvents); + } + + /** + * Gets or sets the tween's totalProgress which is a value between 0 and 1 indicating the position + * of the virtual playhead (including repeats) where 0 is at the beginning, 0.5 is halfway complete, + * and 1 is complete. If the tween has a non-zero repeat defined, progress + * and totalProgress will be different because progress doesn't include + * any repeats or repeatDelays whereas totalProgress does. For example, if a TweenMax + * instance is set to repeat once, at the end of the first cycle totalProgress would + * only be 0.5 whereas progress would be 1. If you watched both properties over the + * course of the entire animation, you'd see progress go from 0 to 1 twice (once for + * each cycle) in the same time it takes the totalProgress to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTween.totalProgress(0.5).play();

+ * + * +var progress = myTween.totalProgress(); //gets total progress +myTween.totalProgress( 0.25 ); //sets total progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #progress() + * @see #seek() + * @see #time() + * @see #totalTime() + **/ + override public function totalProgress(value:Number=NaN, suppressEvents:Boolean=false):* { + return (!arguments.length) ? _totalTime / totalDuration() : totalTime( totalDuration() * value, suppressEvents); + } + + /** + * Gets or sets the local position of the playhead (essentially the current time), not + * including any repeats or repeatDelays. If the tween has a non-zero repeat, its time + * goes back to zero upon repeating even though the totalTime continues forward linearly + * (or if yoyo is true, the time alternates between moving forward + * and backward). time never exceeds the duration whereas the totalTime reflects + * the overall time including any repeats and repeatDelays. + * + *

For example, if a TweenMax instance has a duration of 2 and a repeat of 3, + * totalTime will go from 0 to 8 during the course of the tween (plays once then + * repeats 3 times, making 4 total cycles) whereas time would go from 0 to 2 a + * total of 4 times.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var currentTime = myTween.time(); //gets current time +myTween.time(2); //sets time, jumping to new value just like seek(). + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position defined in the value parameter. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #seek() + * @see #play() + * @see #reverse() + * @see #pause() + * @see #totalTime() + **/ + override public function time(value:Number=NaN, suppressEvents:Boolean=false):* { + if (!arguments.length) { + return _time; + } + if (_dirty) { + totalDuration(); + } + if (value > _duration) { + value = _duration; + } + if (_yoyo && (_cycle & 1) !== 0) { + value = (_duration - value) + (_cycle * (_duration + _repeatDelay)); + } else if (_repeat != 0) { + value += _cycle * (_duration + _repeatDelay); + } + return totalTime(value, suppressEvents); + } + + /** @inheritDoc **/ + override public function duration(value:Number=NaN):* { + if (!arguments.length) { + return this._duration; //don't set _dirty = false because there could be repeats that haven't been factored into the _totalDuration yet. Otherwise, if you create a repeated TweenMax and then immediately check its duration(), it would cache the value and the totalDuration would not be correct, thus repeats wouldn't take effect. + } + return super.duration(value); + } + + /** + * Gets or sets the total duration of the tween in seconds (or frames for frames-based tweens) + * including any repeats or repeatDelays. duration, by contrast, does + * NOT include repeats and repeatDelays. For example, if the tween has a + * duration of 10, a repeat of 1 and a repeatDelay of 2, + * the totalDuration would be 22. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var total = myTween.totalDuration(); //gets total duration +myTween.totalDuration(10); //sets the total duration + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #duration() + * @see #timeScale() + **/ + override public function totalDuration(value:Number=NaN):* { + if (!arguments.length) { + if (_dirty) { + //instead of Infinity, we use 999999999999 so that we can accommodate reverses + _totalDuration = (_repeat == -1) ? 999999999999 : _duration * (_repeat + 1) + (_repeatDelay * _repeat); + _dirty = false; + } + return _totalDuration; + } + return (_repeat == -1) ? this : duration( (value - (_repeat * _repeatDelay)) / (_repeat + 1) ); + } + + /** + * Gets or sets the number of times that the tween should repeat after its first iteration. For example, + * if repeat is 1, the tween will play a total of twice (the initial play + * plus 1 repeat). To repeat indefinitely, use -1. repeat should always be an integer. + * + *

To cause the repeats to alternate between forward and backward, set yoyo to + * true. To add a time gap between repeats, use repeatDelay. You can + * set the initial repeat value via the vars parameter, like:

+ * + *

+ * TweenMax.to(mc, 1, {x:100, repeat:2}); + *

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTween.repeat(2).yoyo(true).play();

+ * + * +var repeat = myTween.repeat(); //gets current repeat value +myTween.repeat(2); //sets repeat to 2 + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeatDelay() + * @see #yoyo() + **/ + public function repeat(value:int=0):* { + if (!arguments.length) { + return _repeat; + } + _repeat = value; + return _uncache(true); + } + + /** + * Gets or sets the amount of time in seconds (or frames for frames-based tweens) between repeats. + * For example, if repeat is 2 and repeatDelay is 1, the tween will + * play initially, then wait for 1 second before it repeats, then play again, then wait 1 second + * again before doing its final repeat. You can set the initial repeatDelay value + * via the vars parameter, like: + * + *

+ * TweenMax.to(mc, 1, {x:100, repeat:2, repeatDelay:1}); + *

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myTween.repeat(2).yoyo(true).repeatDelay(0.5).play();

+ * + * +var repeatDelay = myTween.repeatDelay(); //gets current repeatDelay value +myTween.repeatDelay(2); //sets repeatDelay to 2 + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeat() + * @see #yoyo() + **/ + public function repeatDelay(value:Number=NaN):* { + if (!arguments.length) { + return _repeatDelay; + } + _repeatDelay = value; + return _uncache(true); + } + + /** + * Gets or sets the tween's yoyo state, where true causes + * the tween to go back and forth, alternating backward and forward on each + * repeat. yoyo works in conjunction with repeat, + * where repeat controls how many times the tween repeats, and yoyo + * controls whether or not each repeat alternates direction. So in order to make a tween yoyo, + * you must set its repeat to a non-zero value. + * Yoyo-ing, has no affect on the tween's "reversed" property. For example, + * if repeat is 2 and yoyo is false, it will look like: + * start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But if yoyo is true, + * it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end. + * + *

You can set the yoyo property initially by passing yoyo:true + * in the vars parameter, like: TweenMax.to(mc, 1, {x:100, repeat:1, yoyo:true});

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.yoyo(true).repeat(3).timeScale(2).play(0.5);

+ * + * +var yoyo = myAnimation.yoyo(); //gets current yoyo state +myAnimation.yoyo( true ); //sets yoyo to true + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #repeat() + * @see #repeatDelay() + **/ + public function yoyo(value:Boolean=false):* { + if (!arguments.length) { + return _yoyo; + } + _yoyo = value; + return this; + } + + /** @private [deprecated] Multiplier describing the speed of the root timelines where 1 is normal speed, 0.5 is half-speed, 2 is double speed, etc. The lowest globalTimeScale possible is 0.0001. Deprecated in favor of TimelineLite.exportRoot() **/ + public static function globalTimeScale(value:Number=NaN):Number { + if (!arguments.length) { + return (_rootTimeline == null) ? 1 : _rootTimeline._timeScale; + } + value = value || 0.0001; //can't allow zero because it'll throw the math off + if (_rootTimeline == null) { + TweenLite.to({}, 0, {}); //forces initialization in case globalTimeScale is set before any tweens are created. + } + var tl:SimpleTimeline = _rootTimeline, + t:Number = (getTimer() / 1000); + tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value); + tl = _rootFramesTimeline; + t = _rootFrame; + tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value); + _rootFramesTimeline._timeScale = _rootTimeline._timeScale = value; + return value; + } + + + } +} + diff --git a/src/com/greensock/TweenNano.as b/src/com/greensock/TweenNano.as new file mode 100644 index 0000000..2556a8d --- /dev/null +++ b/src/com/greensock/TweenNano.as @@ -0,0 +1,662 @@ +/** + * VERSION: 12.0.0 + * DATE: 2013-01-21 + * AS3 (AS2 is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock { + import flash.display.Shape; + import flash.events.Event; + import flash.utils.getTimer; +/** + * [AS3/AS2 only] TweenNano is a super-lightweight (2k in AS3 and 2.6k in AS2) version of TweenLite + * and is only recommended for situations where you absolutely cannot afford the extra 4.7k that the normal + * TweenLite engine would cost and your project doesn't require any plugins. Normally, it is much better to + * use TweenLite because of the additional flexibility it provides via plugins and its compatibility with + * TimelineLite and TimelineMax. TweenNano can do everything TweenLite can do with the following exceptions: + *
    + *
  • No Plugins - One of the great things about TweenLite is that you can activate + * plugins in order to add features (like autoAlpha, tint, blurFilter, etc.). TweenNano, however, + * doesn't work with plugins.
  • + * + *
  • Incompatible with TimelineLite and TimelineMax - Complex sequencing and management + * of groups of tweens is much easier with TimelineLite and TimelineMax, but TweenNano instances cannot + * be inserted into TimelineLite or TimelineMax instances.
  • + * + * + *
  • Limited overwrite modes - By default, TweenNano doesn't overwrite any tweens + * but you can pass overwrite:"all" in the vars parameter to have it kill + * all tweens of the same target immediately. TweenLite, however, offers much more robust overwrite + * management, recognizing advanced modes like "auto" (which only overwrites individual + * tweening properties that overlap), "concurrent", "allOnStart", + * and "preexisting". See TweenLite's documentation for details.
  • + * + *
  • Fewer methods and properties TweenNano instances aren't meant to be altered + * on-the-fly, so they don't have methods like pause(), resume(), reverse(), seek(), restart(), etc. + * The essentials are covered, though, like to(), from(), delayedCall(), killTweensOf(), + * and kill().
  • + *
+ * + * + *

USAGE

+ *

The most common type of tween is a to() tween which allows you + * to define the destination values:

+ * + *

+ * TweenNano.to(myObject, 2, {x:100, y:200}); + *

+ * + *

The above code will tween myObject.x from whatever it currently is to 100 and + * myObject.y property to 200 over the course of 2 seconds. Notice the x and y values are + * defined inside a generic object (between curly braces). Put as many properties there as you want.

+ * + *

Tweens begin immediately.

+ * + *

The target can also be an array of objects. For example, the following tween will + * tween the alpha property to 0.5 and y property to 100 for obj1, obj2, and obj3:

+ * + *

+ * TweenNano.to([obj1, obj2, obj3], 1, {alpha:0.5, y:100}); + *

+ * + *

You can also use a from() tween if you want to define the + * starting values instead of the ending values so that the target tweens from + * the defined values to wherever they currently are.

+ * + *

Although the to() and from() static methods + * are popular because they're quick and can avoid some garbage collection hassles, you can also + * use the more object-oriented syntax like this:

+ * + *

+ * var tween = new TweenNano(myObject, 2, {x:100, y:200}); + *

+ * + *

or even:

+ * + *

+ * var tween = TweenNano.to(myObject, 2, {x:100, y:200}); + *

+ * + * + * + *

EXAMPLES:

+ * + *

Please see http://www.greensock.com/tweennano/ for examples, tutorials, and interactive demos.

+ * + * + *

SPECIAL PROPERTIES:

+ *

Typically the vars parameter is used to define ending values for tweening + * properties of the target (or beginning values for from() tweens) + * like {x:100, y:200, alpha:0}, but the following optional special properties + * serve other purposes:

+ * + *
    + *
  • delay : Number - + * Amount of delay in seconds (or frames for frames-based tweens) before the tween should begin.
  • + * + *
  • ease : Ease (or Function) - + * You can choose from various eases to control the rate of change during + * the animation, giving it a specific "feel". For example, ElasticOut.ease + * or StrongInOut.ease. TweenNano works with not only the easing equations + * in the com.greensock.easing package, but also standard easing equation that uses the + * typical 4 parameters (time, start, change, duration) like Adobe's + * fl.motion.easing eases. The default is QuadOut.ease. + * For linear animation, use the GreenSock Linear.ease ease.
  • + * + *
  • onComplete : Function - + * A function that should be called when the tween has completed
  • + * + *
  • onCompleteParams : Array - + * An Array of parameters to pass the onComplete function. For example, + * TweenNano.to(mc, 1, {x:100, onComplete:myFunction, onCompleteParams:[mc, "param2"]});
  • + * + *
  • useFrames : Boolean - + * If useFrames is true, the tweens's timing will be + * based on frames instead of seconds. This causes both its duration + * and delay to be based on frames.
  • + * + *
  • immediateRender : Boolean - + * Normally when you create a tween, it begins rendering on the very next frame (update cycle) + * unless you specify a delay. However, if you prefer to force the tween to + * render immediately when it is created, set immediateRender to true. + * Or to prevent a from() from rendering immediately, set immediateRender + * to false.
  • + * + *
  • onUpdate : Function - + * A function that should be called every time the tween updates + * (on every frame while the tween is active)
  • + * + *
  • onUpdateParams : Array - + * An Array of parameters to pass the onUpdate function. For example, + * TweenNano.to(mc, 1, {x:100, onUpdate:myFunction, onUpdateParams:[mc, "param2"]});
  • + * + *
  • overwrite : String - + * Controls how (and if) other tweens of the same target are overwritten. + * By default, no tweens are overwritten, but if you'd like to immediately overwrite + * other tweens of the same target, use overwrite:"all"
  • + *
+ * + * + * NOTES:

+ *
    + *
  • The base TweenNano class adds about 2k to your Flash file.
  • + * + *
  • Passing values as Strings and a preceding "+=" or "-=" will make the tween relative to the + * current value. For example, if you do TweenNano.to(mc, 2, {x:"-=20"}); it'll + * tween mc.x to the left 20 pixels. {x:"+=20"} would move it to the right.
  • + * + *
  • You can change the TweenNano.defaultEase if you prefer something other + * than QuadOut.ease.
  • + * + *
  • Kill all tweens of a particular object anytime with TweenNano.killTweensOf(myObject);
  • + * + *
  • You can kill all delayedCalls to a particular function using TweenNano.killTweensOf(myFunction);
  • + * + *
  • Use the TweenNano.from() method to animate things into place. For example, + * if you have things set up on the stage in the spot where they should end up, and you + * just want to animate them into place, you can pass in the beginning x and/or y and/or + * alpha (or whatever properties you want).
  • + * + *
  • If you find this class useful, please consider joining Club GreenSock + * which not only helps to sustain ongoing development, but also gets you bonus plugins, classes + * and other benefits that are ONLY available to members. Learn more at + * http://www.greensock.com/club/
  • + *
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenNano { + /** @private **/ + protected static var _time:Number; + /** @private **/ + protected static var _frame:uint; + + /** + * The object that dispatches a "tick" event each time the engine updates, making it easy for + * you to add your own listener(s) to run custom logic after each update (great for game developers). + * Add as many listeners as you want. The basic syntax is the same for all versions (AS2, AS3, and Javascript): + * + *

Basic example (AS2, AS3, and Javascript):

+//add listener +TweenNano.ticker.addEventListener("tick", myFunction); + +function myFunction(event) { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +TweenNano.ticker.removeEventListener("tick", myFunction); + + * + *

Due to differences in the core languages (and to maximize efficiency), the advanced syntax is slightly different + * for the AS3 version compared to AS2 and Javascript. The parameters beyond the first 2 in the addEventListener() + * method are outlined below:

+ * + *

Javascript and AS2

+ *

addEventListener(type, callback, scope, useParam, priority)

+ *

Parameters: + *

    + *
  1. type : String - type of listener, should always be "tick"
  2. + *
  3. callback : Function - the function to call when the event occurs
  4. + *
  5. scope : Object - binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in Javascript and AS2 because scope isn't generally maintained.
  6. + *
  7. useParam : Boolean - if true, an event object will be generated and fed to the callback each time the event occurs. The event is a generic object and has two properties: type (always "tick") and target which refers to the ticker instance. The default for useParam is false because it improves performance.
  8. + *
  9. priority : Integer - influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
  10. + *
+ *

+ * + *

Advanced example (Javascript and AS2):

+//add listener that requests an event object parameter, binds scope to the current scope (this), and sets priority to 1 so that it is called before any other listeners that had a priority lower than 1... +TweenNano.ticker.addEventListener("tick", myFunction, this, true, 1); + +function myFunction(event) { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +TweenNano.ticker.removeEventListener("tick", myFunction); + + * + *

AS3

+ *

The AS3 version uses the standard EventDispatcher.addEventListener() syntax which + * basically allows you to define a priority and whether or not to use weak references (see Adobe's + * docs for details).

+ * + *

Advanced example [AS3 only]:

+import flash.events.Event; + +//add listener with weak reference (standard syntax - notice the 5th parameter is true) +TweenNano.ticker.addEventListener("tick", myFunction, false, 0, true); + +function myFunction(event:Event):void { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +TweenNano.ticker.removeEventListener("tick", myFunction); + + **/ + public static var ticker:Shape = new Shape(); + + /** Provides An easy way to change the default easing equation. Choose from any of the GreenSock eases in the com.greensock.easing package or any standard easing function like the ones in Adobe's fl.motion.easing package. @default QuadOut.ease **/ + public static var defaultEase:Object = function (t:Number, b:Number, c:Number, d:Number):Number { + return -1 * (t /= d) * (t - 2); + } + /** @private **/ + protected static var _reservedProps:Object; + /** @private **/ + protected static var _tickEvent:Event = new Event("tick"); + /** @private **/ + protected static var _first:TweenNano; + /** @private **/ + protected static var _last:TweenNano; + + /** @private Duration of the tween in seconds (or in frames if "useFrames" is true). **/ + public var _duration:Number; + /** Stores variables (things like "alpha", "y" or whatever we're tweening, as well as special properties like "onComplete"). **/ + public var vars:Object; + /** @private Start time in seconds (or frames for frames-based tweens) **/ + public var _startTime:Number; + /** Target object whose properties this tween affects. This can be ANY object or even an array. **/ + public var target:Object; + /** @private Flagged for garbage collection **/ + public var _gc:Boolean; + /** @private Indicates that frames should be used instead of seconds for timing purposes. So if useFrames is true and the tween's duration is 10, it would mean that the tween should take 10 frames to complete, not 10 seconds. **/ + public var _useFrames:Boolean; + /** @private result of _ease(this.time, 0, 1, this.duration). Usually between 0 and 1, but not always (like with Elastic.easeOut). **/ + public var ratio:Number = 0; + + /** @private Easing method to use which determines how the values animate over time. Examples are Elastic.easeOut and Strong.easeIn. Many are found in the fl.motion.easing package or com.greensock.easing. **/ + protected var _ease:Function; + /** @private **/ + protected var _rawEase:Object; + /** @private Indicates whether or not init() has been called (where all the tween property start/end value information is recorded) **/ + protected var _initted:Boolean; + + /** @private **/ + protected var _firstPT:Object; + /** @private **/ + public var _next:TweenNano; + /** @private **/ + public var _prev:TweenNano; + /** @private **/ + public var _targets:Array; + + /** + * Constructor + * + * @param target Target object (or array of objects) whose properties this tween affects + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: new TweenNano(mc, 1, {x:100, y:200, onComplete:myFunction}). + */ + public function TweenNano(target:Object, duration:Number, vars:Object) { + if (!_reservedProps) { + _reservedProps = {ease:1, delay:1, useFrames:1, overwrite:1, onComplete:1, onCompleteParams:1, runBackwards:1, immediateRender:1, onUpdate:1, onUpdateParams:1, startAt:1}; + _time = getTimer() / 1000; + _frame = 0; + ticker.addEventListener(Event.ENTER_FRAME, _updateRoot, false, 0, true); + } + this.vars = vars; + _duration = duration; + this.target = target; + if (target is Array && typeof(target[0]) === "object") { + _targets = target.concat(); + } + _rawEase = this.vars.ease || defaultEase; + _ease = (typeof(_rawEase) == "function") ? _rawEase as Function : _rawEase.getRatio; + _useFrames = Boolean(vars.useFrames == true); + _startTime = (_useFrames ? _frame : _time) + (this.vars.delay || 0); + + if (this.vars.overwrite == "all" || int(this.vars.overwrite) == 1) { + killTweensOf(this.target); + } + + _prev = _last; + if (_last) { + _last._next = this; + } else { + _first = this; + } + _last = this; + + if (this.vars.immediateRender == true || (duration == 0 && this.vars.delay == 0 && this.vars.immediateRender != false)) { + _render(0); + } + } + + /** @private Initializes the property tweens, determining their start values and amount of change. **/ + public function _init():void { + if (vars.startAt) { + vars.startAt.immediateRender = true; + TweenNano.to(target, 0, vars.startAt); + } + var i:int, pt:Object; + if (_targets != null) { + i = _targets.length; + while (--i > -1) { + _initProps(_targets[i]); + } + } else { + _initProps(target); + } + if (vars.runBackwards) { + pt = _firstPT; + while (pt) { + pt.s += pt.c; + pt.c = -pt.c; + pt = pt._next; + } + } + _initted = true; + } + + /** @private **/ + protected function _initProps(target:*):void { + if (target != null) { + for (var p:String in vars) { + if (!(p in _reservedProps)) { + _firstPT = {_next:_firstPT, t:target, p:p, f:(typeof(target[p]) === "function")}; + _firstPT.s = (!_firstPT.f) ? Number(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ](); + _firstPT.c = (typeof(vars[p]) === "number") ? Number(vars[p]) - _firstPT.s : (typeof(vars[p]) === "string" && vars[p].charAt(1) === "=") ? int(vars[p].charAt(0)+"1") * Number(vars[p].substr(2)) : Number(vars[p]) || 0; + if (_firstPT._next) { + _firstPT._next._prev = _firstPT; + } + } + } + } + } + + /** + * @private + * Renders the tween at a particular time (or frame number for frames-based tweens) + * WITHOUT changing its _startTime, meaning if the tween is in progress when you call + * _render(), it will not adjust the tween's timing to continue from the new time. + * The time is based simply on the overall duration. For example, if a tween's duration + * is 3, _render(1.5) would render it at the halfway finished point. + * + * @param time time (or frame number for frames-based tweens) to render. + */ + public function _render(time:Number):void { + if (!_initted) { + _init(); + } + if (time >= _duration) { + time = _duration; + this.ratio = (_ease != _rawEase && _rawEase._calcEnd) ? _ease.call(_rawEase, 1) : 1; + } else if (time <= 0) { + this.ratio = (_ease != _rawEase && _rawEase._calcEnd) ? _ease.call(_rawEase, 0) : 0; + } else { + this.ratio = (_ease == _rawEase) ? _ease(time, 0, 1, _duration) : _ease.call(_rawEase, time / _duration); + } + var pt:Object = _firstPT; + while (pt) { + if (pt.f) { + pt.t[pt.p](pt.c * ratio + pt.s); + } else { + pt.t[pt.p] = pt.c * ratio + pt.s; + } + pt = pt._next; + } + if (vars.onUpdate) { + vars.onUpdate.apply(null, vars.onUpdateParams); + } + if (time == _duration) { + kill(); + if (vars.onComplete) { + vars.onComplete.apply(null, vars.onCompleteParams); + } + } + } + + /** + * Kills the tween, stopping it immediately. You can optionally define a particular target + * to isolate (or an array of targets) which is only useful in tweens whose target is + * an array. For example, let's say we have a tween like this: + * + *

+ * var tween = TweenNan.to([mc1, mc2, mc3], 2, {x:100}); + *

+ * + *

Later, we could kill only the mc2 portion of the tween like this:

+ * + *

+ * tween.kill(mc2); + *

+ * + *

To kill the entire tween, simply omit the target parameter, like tween.kill()

+ * + * @param target [optional] To kill only aspects of the animation related to a particular target (or targets), reference it here. It can be an array or a single object. For example, to kill only parts having to do with myObject, do kill(myObject) or to kill only parts having to do with myObject1 and myObject2, do kill([myObject1, myObject2]). If no target is defined, ALL targets will be affected. + **/ + public function kill(target:*=null):void { + var i:int, pt:Object = _firstPT; + target = target || _targets || this.target; + if (target is Array && typeof(target[0]) === "object") { + i = target.length; + while (--i > -1) { + kill(target[i]); + } + return; + } else if (_targets != null) { + i = _targets.length; + while (--i > -1) { + if (target == _targets[i]) { + _targets.splice(i, 1); + } + } + while (pt) { + if (pt.t == target) { + if (pt._next) { + pt._next._prev = pt._prev; + } + if (pt._prev) { + pt._prev._next = pt._next; + } else { + _firstPT = pt._next; + } + } + pt = pt._next; + } + } + if (_targets == null || _targets.length == 0) { + _gc = true; + if (_prev) { + _prev._next = _next; + } else if (this == _first) { + _first = _next; + } + if (_next) { + _next._prev = _prev; + } else if (this == _last) { + _last = _prev; + } + _next = _prev = null; + } + } + + +//---- STATIC FUNCTIONS ------------------------------------------------------------------------- + + /** + * Static method for creating a TweenNano instance that animates to the specified destination values + * (from the current values). The following lines of code all produce identical results: + * + * +TweenNano.to(mc, 1, {x:100}); +var myTween = new TweenNano(mc, 1, {x:100}); +var myTween = TweenNano.to(mc, 1, {x:100}); + + * + *

Each line above will tween the "x" property of the mc object + * to a value of 100 over the coarse of 1 second. They each use a slightly different syntax, + * all of which are valid. If you don't need to store a reference of the tween, just use the + * static TweenNano.to( ) call.

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the x property of mc1, mc2, and mc3 to a value of 100 simultaneously:

+ * + * +TweenNano.to([mc1, mc2, mc3], 1, {x:100}); + + *

Even though 3 objects are animating, there is still only one tween created.

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenNano.to(mc, 1, {x:100, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical to() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the end value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x to 100 and mc.y to 200 and then call myFunction, do this: TweenNano.to(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenNano instance + * @see com.greensock.TimelineLite#to() + * @see #from() + */ + public static function to(target:Object, duration:Number, vars:Object):TweenNano { + return new TweenNano(target, duration, vars); + } + + /** + * Static method for creating a TweenNano instance that tweens backwards - + * you define the BEGINNING values and the current values are used + * as the destination values which is great for doing things like animating objects + * onto the screen because you can set them up initially the way you want them to look + * at the end of the tween and then animate in from elsewhere. + * + *

NOTE: By default, immediateRender is true in + * from() tweens, meaning that they immediately render their starting state + * regardless of any delay that is specified. You can override this behavior by passing + * immediateRender:false in the vars parameter so that it will + * wait to render until the tween actually begins. To illustrate the default behavior, the + * following code will immediately set the alpha of mc + * to 0 and then wait 2 seconds before tweening the alpha back to 1 over + * the course of 1.5 seconds:

+ * + *

+ * TweenNano.from(mc, 1.5, {alpha:0, delay:2}); + *

+ * + *

Since the target parameter can also be an array of objects, the following + * code will tween the alpha property of mc1, mc2, and mc3 from a value of 0 simultaneously:

+ * + * +TweenNano.from([mc1, mc2, mc3], 1.5, {alpha:0}); + + *

Even though 3 objects are animating, there is still only one tween created.

+ * + *

For simple sequencing, you can use the delay special property + * (like TweenNano.from(mc, 1, {alpha:0, delay:0.5})), + * but it is highly recommended that you consider using TimelineLite (or TimelineMax) + * for all but the simplest sequencing tasks. It has an identical from() method + * that allows you to append tweens one-after-the-other and then control the entire sequence + * as a whole. You can even have the tweens overlap as much as you want.

+ * + * @param target Target object (or array of objects) whose properties this tween affects. + * @param duration Duration in seconds (or frames if useFrames:true is set in the vars parameter) + * @param vars An object defining the starting value for each property that should be tweened as well as any special properties like onComplete, ease, etc. For example, to tween mc.x from 100 and mc.y from 200 and then call myFunction, do this: TweenNano.from(mc, 1, {x:100, y:200, onComplete:myFunction}); + * @return TweenNano instance + * @see #to() + * @see com.greensock.TimelineLite#from() + * @see com.greensock.TimelineLite#staggerFrom() + */ + public static function from(target:Object, duration:Number, vars:Object):TweenNano { + vars.runBackwards = true; + if (!("immediateRender" in vars)) { + vars.immediateRender = true; + } + return new TweenNano(target, duration, vars); + } + + /** + * Provides a simple way to call a function after a set amount of time (or frames). You can + * optionally pass any number of parameters to the function too. + * + *

Javascript and AS2 note: - Due to the way Javascript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the Javascript and AS2 + * versions the 4th parameter is scope, bumping useFrames + * back to the 5th parameter:

+ * + *

TweenNano.delayedCall(delay, callback, params, scope, useFrames) [Javascript and AS2 only]

+ * + * +//calls myFunction after 1 second and passes 2 parameters: +TweenNano.delayedCall(1, myFunction, ["param1", 2]); + +function myFunction(param1, param2) { + //do stuff +} + + * + * @param delay Delay in seconds (or frames if useFrames is true) before the function should be called + * @param callback Function to call + * @param params An Array of parameters to pass the function (optional). + * @param useFrames If the delay should be measured in frames instead of seconds, set useFrames to true (default is false) + * @return TweenNano instance + * @see com.greensock.TimelineLite#call() + */ + public static function delayedCall(delay:Number, callback:Function, params:Array=null, useFrames:Boolean=false):TweenNano { + return new TweenNano(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, useFrames:useFrames}); + } + + /** + * @private + * Updates active tweens and inits those whose startTime precedes the current _time/_frame. + * + * @param e ENTER_FRAME Event + */ + public static function _updateRoot(e:Event=null):void { + _frame += 1; + _time = getTimer() * 0.001; + var tween:TweenNano = _first, + next:TweenNano, + t:Number; + while (tween) { + next = tween._next; + t = (tween._useFrames) ? _frame : _time; + if (t >= tween._startTime && !tween._gc) { + tween._render(t - tween._startTime); + } + tween = next; + } + ticker.dispatchEvent(_tickEvent); + } + + /** + * Kills all the tweens of a particular object or the delayedCalls to a particular function. + * If, for example, you want to kill all tweens of myObject, you'd do this: + * + *

+ * TweenNano.killTweensOf(myObject); + *

+ * + *

To kill all the delayedCalls that were created like TweenNano.delayedCall(5, myFunction);, + * you can simply call TweenNano.killTweensOf(myFunction); because delayedCalls + * are simply tweens that have their target and onComplete set to + * the same function (as well as a delay of course).

+ * + *

killTweensOf() affects tweens that haven't begun yet too. If, for example, + * a tween of myObject has a delay of 5 seconds and + * TweenNano.killTweensOf(mc) is called 2 seconds after the tween was created, + * it will still be killed even though it hasn't started yet.

+ * + * @param target Object whose tweens should be killed immediately + **/ + public static function killTweensOf(target:Object):void { + var t:TweenNano = _first, + next:TweenNano; + while (t) { + next = t._next; + if (t.target == target) { + t.kill(); + } else if (t._targets != null) { + t.kill(target); + } + t = next; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/changelog.txt b/src/com/greensock/changelog.txt new file mode 100644 index 0000000..5b968fb --- /dev/null +++ b/src/com/greensock/changelog.txt @@ -0,0 +1,137 @@ +CHANGE LOG : GREENSOCK TWEENING PLATFORM +---------------------------------------- + +2013-01-09 +--------------------------------------------- +Moved change log to https://github.com/greensock/GreenSock-AS3/commits/master + +2012-12-24 +--------------------------------------------- + - Added ability for TransformAroundPointPlugin to consistently apply transform changes according to the local coordinate system (with pointIsLocal:true) even if the target's position is manually changed during the course of the tween. + +2012-11-26 +--------------------------------------------- + - Removed requirement that the target of a Physics2DPlugin tween be a DisplayObject (so Starling can work) + - Fixed minor bugs in TimelineLite and TimelineMax + +2012-10-26 +--------------------------------------------- + - Added functionality to append() and all convenience methods of TimelineLite/Max so that the "offset" parameter can now be a string to indicate a label. If the label isn't found, one will be created and appended to the timeline for convenience. + +2012-09-17 +--------------------------------------------- + - Fixed issue that caused TweenMax.killAll() to kill the tweens in the reverse order that they were created. + +2012-08-31 +--------------------------------------------- + - Fixed issue that caused "{self}" references in a TimelineLite.staggerTo() or staggerFrom() or staggerFromTo() to always point at the first tween instead of each individual one. + +2012-08-29 +--------------------------------------------- + - Fixed issue in RoundPropsPlugin that could cause an error in rare situations + - Added OnCompleteRenderPlugin and the associated method in TweenMaxVars and TweenLiteVars + +2012-08-24 +--------------------------------------------- + - Fixed issue that could throw an error if a relative tween is used that begins with "+=-" or "-=-" (very rare, but could be caused if you dynamically populate a variable like TweenLite.to(mc, 1, {x:"+=" + myVariable}) and myVariable is a negative number. + +2012-07-28 +--------------------------------------------- +TintPlugin 12.01 + - Fixed issue that could cause an alpha tween to be ignored if it is running concurrently with a tint tween. + +2012-07-21 +--------------------------------------------- +BezierPlugin 12.1 + - Completely revamped BezierPlugin and added many features like support for quadratic, cubic, soft, thru, and thruFast types plus a completely new algorithm for plotting Beziers through supplied values, speed improvements, more accurate autoRotation, a new timeResolution feature that eliminates varying speed along the Bezier, and lots more. + +2012-06-30 +--------------------------------------------- +TimelineLite beta 5.7 + - Added logic to TimelineLite/Max so that if you insert/append a child that extends the duration and the timeline had already finished, it will adjust its startTime and resume playback (as long as it wasn't paused of course) + +2012-06-19 +---------------------------------------------- +TweenLite beta 5.6 +VisiblePlugin 12.1 + - Fixed issue that could cause zero-duration tweens not to render correctly initially and/or be removed from their timeline prematurely. This affected startAt as well. + - Fixed issue with VisiblePlugin that could cause zero-duration tweens not to render correctly. + +2012-05-29 +---------------------------------------------- +TweenLite beta 5.5 +BezierPlugin 12.0 + - Fixed issue that could cause an error if you used onCompleteListener, onStartListener, onRepeatListener, onReverseCompleteListener in AS3. + - Removed trace() from BezierPlugin + +2012-05-24 +---------------------------------------------- +TweenLite beta 5.4 + - - Fixed issue that could cause a tween to be overwritten if it is nested inside a TimelineLite/Max that's nested inside another and both are back-to-back and tween the identical value (very rare). + +2012-05-10 +---------------------------------------------- +TweenLite beta 5.3 +TweenMax beta 5.3 + - Added static "ticker" property to TweenLite and TweenMax to make it easier to access rather than going through "Animation" class. + +2012-05-01 +---------------------------------------------- +TimelineMax beta 5.2 +TweenLite beta 5.2 +TweenMax beta 5.2 + - Added set() method to TweenMax and TweenLite + - Added getLabelsArray() method to TimelineMax + +2012-04-10 +---------------------------------------------- + - Changed TweenLite and TweenMax's behavior so that if their duration and delay are both 0, they will default to immediateRender:true. + +2012-04-02 +---------------------------------------------- +TweenLite beta 5.1 +TweenMax beta 5.1 +TweenNano beta 5.1 +Ease beta 1.1 +SlowMo beta 1.1 + - Added a new "endcapMode" parameter to SlowMo ease to make it much easier to sync things like alpha tweens or blur tweens with positional SlowMo tweens (see ASDocs for details) + +2012-03-27 +---------------------------------------------- +TweenLite, TweenMax, TimelineLite, TimelineMax beta 5 + - Fixed issue that could cause a zero-duration tween not to render correctly if it was at the very beginning or end of a TimelineMax that repeats. + +2012-03-23 +---------------------------------------------- +[all classes] + - Changed goto() to seek() because "goto" is a reserved keyword in Javascript (for future versions). + - Changed name of OutIn ease to SlowMo to make it more intuitive/memorable. + - Added Power0, Power1, Power2, Power3, and Power4 eases which are identical to their less intuitively named Linear, Quad, Cubic, Quart, and Quint/Strong counterparts. + - Removed some redundant eases like QuadIn, QuadOut, QuadInOut, CubicIn, CubicOut, CubicInOut, etc. because it seemed wiser to keep the standard way of referencing eases like Quad.easeIn rather than QuadIn.ease. + - Changed some property names of the Ease class which were for internal use. + +2012-03-08 +---------------------------------------------- +TweenLite beta 4.1 + - Fixed issue that could cause a zero-duration tween to incorrectly overwrite a tween that starts at exactly the same time in a nested TimelineLite/Max (of the same object). Rare, but possible. + +2012-02-24 +---------------------------------------------- +TweenLite beta 4 +TimelineLite beta 4 +Animation beta 4 + - Fixed issue that caused a from() tween that is initially paused not to render immediately + - Added ability to self-reference the tween/timeline in the onCompleteParams, onUpdateParams, onStartParams, onReverseCompleteParams, and onRepeatParams array using "{self}". + +2012-02-23 +---------------------------------------------- +TimelineLite beta 1.2 +TweenPlugin beta 3 +[many plugins were updated too] + - Added an extra "baseTimeOrLabel" parameter to all of the timeline convenience methods (see ASDocs for details) + - Added a _roundProps() method and removed the _round property from TweenPlugin to improve the handling of rounding. + +2012-02-21 +---------------------------------------------- +TimelineLite beta 1.1 + - Changed the parameters of the TimelineLite.exportRoot() so that you can pass in extra special properties like onUpdate, onComplete, etc. See the ASDocs for details. diff --git a/src/com/greensock/core/Animation.as b/src/com/greensock/core/Animation.as new file mode 100644 index 0000000..2f27870 --- /dev/null +++ b/src/com/greensock/core/Animation.as @@ -0,0 +1,1095 @@ +/** + * VERSION: 12.1.1 + * DATE: 2013-12-07 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.core { + import flash.display.Shape; + import flash.events.Event; + import flash.utils.getTimer; +/** + * Base class for all TweenLite, TweenMax, TimelineLite, and TimelineMax classes, providing + * core methods/properties/functionality, but there is no reason to create an instance of this + * class directly. It can be very useful, however, as a data type in AS3/AS2 for methods/properties that + * can contain tweens or timelines. For example, maybe you build an animateIn() and + * animateOut() method for many of your own custom classes, and they each return an + * Animation instance which could be a tween or a timeline: + * + * +function animateIn():Animation { + return TweenLite.to(this, 1, {scaleX:1, scaleY:1, autoAlpha:1}); +} + +function animateOut():Animation { + var tl:TimelineLite = new TimelineLite(); + tl.to(this, 1, {scaleX:0.5, scaleY:0.5}); + tl.to(this, 0.5, {autoAlpha:0}, "-=0.25"); + return tl; +} + +var anim:Animation = animateIn(); + +//now we can control the animation with the common methods: +anim.pause(); +anim.play(); +anim.reverse(); + +//or somewhere else, we could build a sequence like this: +var tl:TimelineLite = new TimelineLite(); +tl.add( animateIn() ); +tl.add( animateOut(), 3); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class Animation { + /** @private **/ + public static const version:String = "12.1.1"; + + /** + * The object that dispatches a "tick" event each time the engine updates, making it easy for + * you to add your own listener(s) to run custom logic after each update (great for game developers). + * Add as many listeners as you want. The basic syntax is the same for all versions (AS2, AS3, and JavaScript): + * + *

Basic example (AS2, AS3, and JavaScript):

+//add listener +Animation.ticker.addEventListener("tick", myFunction); + +function myFunction(event) { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +Animation.ticker.removeEventListener("tick", myFunction); + + * + *

Due to differences in the core languages (and to maximize efficiency), the advanced syntax is slightly different + * for the AS3 version compared to AS2 and JavaScript. The parameters beyond the first 2 in the addEventListener() + * method are outlined below:

+ * + *

JavaScript and AS2

+ *

addEventListener(type, callback, scope, useParam, priority)

+ *

Parameters: + *

    + *
  1. type : String - type of listener, should always be "tick"
  2. + *
  3. callback : Function - the function to call when the event occurs
  4. + *
  5. scope : Object - binds the scope to a particular object (scope is basically what "this" refers to in your function). This can be very useful in JavaScript and AS2 because scope isn't generally maintained.
  6. + *
  7. useParam : Boolean - if true, an event object will be generated and fed to the callback each time the event occurs. The event is a generic object and has two properties: type (always "tick") and target which refers to the ticker instance. The default for useParam is false because it improves performance.
  8. + *
  9. priority : Integer - influences the order in which the listeners are called. Listeners with lower priorities are called after ones with higher priorities.
  10. + *
+ *

+ * + *

In JavaScript, the Animation object/class is located at com.greensock.core.Animation - it is not added to the global namespace in order to avoid polluting it (developers rarely directly access the Animation class)

+ * + *

Advanced example (JavaScript and AS2):

+//add listener that requests an event object parameter, binds scope to the current scope (this), and sets priority to 1 so that it is called before any other listeners that had a priority lower than 1... +Animation.ticker.addEventListener("tick", myFunction, this, true, 1); + +function myFunction(event) { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +Animation.ticker.removeEventListener("tick", myFunction); + + * + *

AS3

+ *

The AS3 version uses the standard EventDispatcher.addEventListener() syntax which + * basically allows you to define a priority and whether or not to use weak references (see Adobe's + * docs for details).

+ * + *

Advanced example [AS3 only]:

+import flash.events.Event; + +//add listener with weak reference (standard syntax - notice the 5th parameter is true) +Animation.ticker.addEventListener("tick", myFunction, false, 0, true); + +function myFunction(event:Event):void { + //executes on every tick after the core engine updates +} + +//to remove the listener later... +Animation.ticker.removeEventListener("tick", myFunction); + + **/ + public static var ticker:Shape = new Shape(); + /** @private root timeline on which all time-based tweens/timelines are initially placed (_rootFramesTimeline is for frames-based tweens/timelines where useFrames:true is defined in the constructor's vars parameter). **/ + public static var _rootTimeline:SimpleTimeline; + /** @private root timeline on which all frames-based tweens/timelines are initially placed (_rootTimeline is for time-based tweens/timelines). A frames-based animation is one that has useFrames:true defined in the constructor's vars parameter or it is placed into a parent timeline that is frames-based (the parent timeline always defines the timing mode). **/ + public static var _rootFramesTimeline:SimpleTimeline; + /** @private Each time the root timelines are updated, _rootFrame is incremented in order to keep track of how many frames have been rendered. **/ + protected static var _rootFrame:Number = -1; + /** @private We reuse this event instance for better memory management rather than recreating a new instance on every frame. **/ + protected static var _tickEvent:Event = new Event("tick"); + /** @private **/ + protected static var _tinyNum:Number = 0.0000000001; + + /** @private The onUpdate callback (if one is defined). Checking an instance property is faster than looking it up in the vars object on every render. This is purely a speed optimization **/ + protected var _onUpdate:Function; + + /** @private Delay in seconds (or frames for frames-based tweens/timelines) **/ + public var _delay:Number; + /** @private Primarily used for zero-duration tweens to determine the direction/momentum of time in its parent timeline which controls whether the starting or ending values should be rendered. See the render() method for usage (which is slightly different in tweens versus timelines) **/ + public var _rawPrevTime:Number; + /** @private Indicates whether or not the tween is currently active (typically when the parent timeline's playhead is between the start and end time of this animation). Makes conditional logic faster in the rendering queue of the parent timeline because if a tween is active, it'll always get rendered and we can flip _acitve to false when it completes. **/ + public var _active:Boolean; + /** @private Flagged for garbage collection (indicates the tween has been disabled, but keep in mind that a tween can be re-enabled later too!) **/ + public var _gc:Boolean; + /** @private Indicates whether or not the animation has been initialized (for tweens, this is when all the tweening properties get analyzed and their start/end values recorded, etc.) **/ + public var _initted:Boolean; + /** @private The time at which the animation begins, according to its parent timeline's time. For example, if the tween starts at exactly 3 seconds into the timeline on which it is placed, startTime would be 3. **/ + public var _startTime:Number; + /** @private The local position of the playhead (essentially the current time). If the animation has a non-zero repeat (only available on TweenMax and TimelineMax instances), its time goes back to zero upon repeating even though the totalTime continues forward linearly (or if it yoyo is true, the time alternates between moving forward and backward). time never exceeds the duration whereas the totalTime reflects the overall time including any repeats and repeatDelays. For example, if a TweenMax instance has a duration of 2 and a repeat of 3, totalTime will go from 0 to 8 during the course of the tween (plays once then repeats 3 times, making 4 total cycles) whereas time will go from 0 to 2 a total of 4 times. **/ + public var _time:Number; + /** @private The overall position of the playhead including any repeats and repeatDelays (which are only available in TweenMax and TimelineMax). For example, if a TweenMax instance has a duration of 2 and a repeat of 3, totalTime will go from 0 to 8 during the course of the tween (plays once then repeats 3 times, making 4 total cycles) whereas time will go from 0 to 2 a total of 4 times. **/ + public var _totalTime:Number; + /** @private Duration of the animation, not including any repeats or repeatDelays (which are only available in TweenMax and TimelineMax). For example, if a TweenMax instance has a duration of 2 and a repeat of 3, its totalDuration would be 8 (one standard play plus 3 repeats equals 4 total cycles). **/ + public var _duration:Number; + /** @private Total duration of the animation including any repeats or repeatDelays (which are only available in TweenMax and TimelineMax). For example, if a TweenMax instance has a duration of 2 and a repeat of 3, its totalDuration would be 8 (one standard play plus 3 repeats equals 4 total cycles). **/ + public var _totalDuration:Number; + /** @private Records the parent timeline's rawTime when the animation is paused (so that we can place it at the appropriate time when it is unpaused). NaN when the animation isn't paused. **/ + public var _pauseTime:Number; + /** @private Factor that's used to scale the time in the animation where 1 = normal speed, 0.5 = half speed, 2 = double speed, etc. For example, if a tween's duration is 2 but its timeScale is 0.5, it will take 4 seconds to finish. If you nest that tween in a TimelineLite that has a timeScale of 0.5 as well, it will take 8 seconds to finish. You can even tween another tween's (or timeline's) timeScale to gradually slow it down or speed it up. **/ + public var _timeScale:Number; + /** @private Indicates whether or not the animation is reversed. **/ + public var _reversed:Boolean; + /** @private The most recent parent timeline (only null for the _rootTimeline and _rootFramesTimeline). The timeline property (no "_" prefix) is null whenever the animation is removed from its parent timeline. We use this internally in slightly different ways. We need to always maintain a reference to the last parent timeline so that if the animation is re-enabled, we know where to put it. "_gc" is different in that a Animation could be eligible for gc yet not removed from its timeline, like when a TimelineLite completes for example. It makes things much faster to enable again if/when necessary, like if the TimelineLite gets restarted. **/ + public var _timeline:SimpleTimeline; + /** @private If true, the _duration or _totalDuration may need refreshing. For example, if a TimelineLite's child had a change in duration or startTime, it could affect the parent timeline's duration but we don't want to always make the update immediately because there may be many more changes made before the timeline actually need to be rendered again, so this helps improve performance. If the _dirty is false, we can skip the method call and quickly read from the _duration and/or _totalDuration. **/ + public var _dirty:Boolean; + /** @private Provides a quick way to check whether or not a animation is currently paused (skipping the paused() method call). **/ + public var _paused:Boolean; + /** @private Next Animation in the linked list. **/ + public var _next:Animation; + /** @private Previous Animation in the linked list. **/ + public var _prev:Animation; + + /** The vars object passed into the constructor which stores configuration variables like onComplete, onUpdate, etc. as well as tweening properties like opacity, x, y or whatever. **/ + public var vars:Object; + /** [Read-only] Parent timeline. Every animation is placed onto a timeline (the root timeline by default) and can only have one parent. An instance cannot exist in multiple timelines at once. **/ + public var timeline:SimpleTimeline; + /** A place to store any data you want (initially populated with vars.data if it exists). **/ + public var data:*; + + /** + * Constructor + * + * @param duration duration in seconds (or frames for frames-based tweens) + * @param vars configuration variables (for example, {x:100, y:0, opacity:0.5, onComplete:myFunction}) + */ + public function Animation(duration:Number=0, vars:Object=null) { + this.vars = vars || {}; + if (this.vars._isGSVars) { + this.vars = this.vars.vars; + } + _duration = _totalDuration = duration || 0; + _delay = Number(this.vars.delay) || 0; + _timeScale = 1; + _totalTime = _time = 0; + data = this.vars.data; + _rawPrevTime = -1; + + if (_rootTimeline == null) { + if (_rootFrame == -1) { + _rootFrame = 0; + _rootFramesTimeline = new SimpleTimeline(); + _rootTimeline = new SimpleTimeline(); + _rootTimeline._startTime = getTimer() / 1000; + _rootFramesTimeline._startTime = 0; + _rootTimeline._active = _rootFramesTimeline._active = true; + ticker.addEventListener("enterFrame", _updateRoot, false, 0, true); + } else { + return; + } + } + + var tl:SimpleTimeline = (this.vars.useFrames) ? _rootFramesTimeline : _rootTimeline; + tl.add(this, tl._time); + + _reversed = (this.vars.reversed == true); + if (this.vars.paused) { + paused(true); + } + } + + + /** + * Begins playing forward, optionally from a specific time (by default playback begins from + * wherever the playhead currently is). This also ensures that the instance is neither paused + * nor reversed. + * + *

If you define a "from" time (the first parameter, which could also be a label for TimelineLite + * or TimelineMax instances), the playhead moves there immediately and if there are any + * events/callbacks inbetween where the playhead was and the new time, they will not be triggered + * because by default suppressEvents (the 2nd parameter) is true. + * Think of it like picking the needle up on a record player and moving it to a new position + * before placing it back on the record. If, however, you do not want the events/callbacks suppressed + * during that initial move, simply set the suppressEvents parameter to false.

+ * + * +//begins playing from wherever the playhead currently is: +myAnimation.play(); + +//begins playing from exactly 2-seconds into the animation: +myAnimation.play(2); + +//begins playing from exactly 2-seconds into the animation but doesn't suppress events during the initial move: +myAnimation.play(2, false); + + * + * @param from The time (or label for TimelineLite/TimelineMax instances) from which the animation should begin playing (if none is defined, it will begin playing from wherever the playhead currently is). + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the from parameter. + * @return self (makes chaining easier) + */ + public function play(from:*=null, suppressEvents:Boolean=true):* { + if (from != null) { + seek(from, suppressEvents); + } + reversed(false); + return paused(false); + } + + /** + * Pauses the instance, optionally jumping to a specific time. + * + *

If you define a time to jump to (the first parameter, which could also be a label for TimelineLite + * or TimelineMax instances), the playhead moves there immediately and if there are any + * events/callbacks inbetween where the playhead was and the new time, they will not be triggered + * because by default suppressEvents (the 2nd parameter) is true. + * Think of it like picking the needle up on a record player and moving it to a new position + * before placing it back on the record. If, however, you do not want the events/callbacks suppressed + * during that initial move, simply set the suppressEvents parameter to false.

+ * + * + //pauses wherever the playhead currently is: + myAnimation.pause(); + + //jumps to exactly 2-seconds into the animation and then pauses: + myAnimation.pause(2); + + //jumps to exactly 2-seconds into the animation and pauses but doesn't suppress events during the initial move: + myAnimation.pause(2, false); + + * + * @param atTime The time (or label for TimelineLite/TimelineMax instances) that the instance should jump to before pausing (if none is defined, it will pause wherever the playhead is currently located). + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the atTime parameter. + * @return self (makes chaining easier) + */ + public function pause(atTime:*=null, suppressEvents:Boolean=true):* { + if (atTime != null) { + seek(atTime, suppressEvents); + } + return paused(true); + } + + /** + * Resumes playing without altering direction (forward or reversed), optionally jumping to a specific time first. + * + *

If you define a time to jump to (the first parameter, which could also be a label for TimelineLite + * or TimelineMax instances), the playhead moves there immediately and if there are any + * events/callbacks inbetween where the playhead was and the new time, they will not be triggered + * because by default suppressEvents (the 2nd parameter) is true. + * Think of it like picking the needle up on a record player and moving it to a new position + * before placing it back on the record. If, however, you do not want the events/callbacks suppressed + * during that initial move, simply set the suppressEvents parameter to false.

+ * + * + //resumes from wherever the playhead currently is: + myAnimation.resume(); + + //jumps to exactly 2-seconds into the animation and then resumes playback: + myAnimation.resume(2); + + //jumps to exactly 2-seconds into the animation and resumes playbck but doesn't suppress events during the initial move: + myAnimation.resume(2, false); + + * + * @param from The time (or label for TimelineLite/TimelineMax instances) that the instance should jump to before resuming playback (if none is defined, it will resume wherever the playhead is currently located). + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the from parameter. + * @return self (makes chaining easier) + */ + public function resume(from:*=null, suppressEvents:Boolean=true):* { + if (from != null) { + seek(from, suppressEvents); + } + return paused(false); + } + + /** + * Jumps to a specific time without affecting whether or not the instance is paused or reversed. + * + *

If there are any events/callbacks inbetween where the playhead was and the new time, + * they will not be triggered because by default suppressEvents (the 2nd parameter) + * is true. Think of it like picking the needle up on a record player and moving it + * to a new position before placing it back on the record. If, however, you do not want the + * events/callbacks suppressed during that initial move, simply set the suppressEvents + * parameter to false.

+ * + * + //jumps to exactly 2 seconds + myAnimation.seek(2); + + //jumps to exactly 2 seconds but doesn't suppress events during the initial move: + myAnimation.seek(2, false); + + * + * @param time The time (or label for TimelineLite/TimelineMax instances) to go to. + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the time parameter. + * @return self (makes chaining easier) + */ + public function seek(time:*, suppressEvents:Boolean=true):* { + return totalTime(Number(time), suppressEvents); + } + + /** + * Restarts and begins playing forward from the beginning. + * + * + //restarts, not including any delay that was defined + myAnimation.restart(); + + //restarts, including any delay, and doesn't suppress events during the initial move back to time:0 + myAnimation.restart(true, false); + + * + * @param includeDelay Determines whether or not the delay (if any) is honored when restarting. For example, if a tween has a delay of 1 second, like new TweenLite(mc, 2, {x:100, delay:1}); and then later restart() is called, it will begin immediately, but restart(true) will cause the delay to be honored so that it won't begin for another 1 second. + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the time parameter. + * @return self (makes chaining easier) + */ + public function restart(includeDelay:Boolean=false, suppressEvents:Boolean=true):* { + reversed(false); + paused(false); + return totalTime((includeDelay ? -_delay : 0), suppressEvents, true); + } + + /** + * Reverses playback so that all aspects of the animation are oriented backwards including, for example, + * a tween's ease. This will cause the instance's time and totalTime to move + * back towards zero as well. You can optionally define a specific time to jump to before reversing + * (by default it begins playing in reverse from wherever the playhead currently is). + * Calling reverse() also ensures that the instance is neither paused nor reversed. + * + *

To jump to the very end of the animation and play in reverse from there, use 0 as the + * "from" parameter, like reverse(0).

+ * + *

To check whether or not the instance is reversed, use the reversed() method, like + * if (myAnimation.reversed()) {...}

+ * + *

If you define a "from" time (the first parameter, which could also be a label for TimelineLite + * or TimelineMax instances), the playhead moves there immediately and if there are any + * events/callbacks inbetween where the playhead was and the new time, they will not be triggered + * because by default suppressEvents (the 2nd parameter) is true. + * Think of it like picking the needle up on a record player and moving it to a new position + * before placing it back on the record. If, however, you do not want the events/callbacks suppressed + * during that initial move, simply set the suppressEvents parameter to false.

+ * + * + //reverses playback from wherever the playhead currently is: + myAnimation.reverse(); + + //reverses playback from exactly 2 seconds into the animation: + myAnimation.reverse(2); + + //reverses playback from exactly 2 seconds into the animation but doesn't suppress events during the initial move: + myAnimation.reverse(2, false); + + //reverses playback from the very END of the animation: + myAnimation.reverse(0); + + //reverses playback starting from exactly 1 second before the end of the animation: + myAnimation.reverse(-1); + + //flips the orientation (if it's forward, it will go backward, if it is backward, it will go forward): + if (myAnimation.reversed()) { + myAnimation.play(); + } else { + myAnimation.reverse(); + } + + //flips the orientation using the reversed() method instead (shorter version of the code above): + myAnimation.reversed( !myAnimation.reversed() ); + + * + * @param from The time (or label for TimelineLite/TimelineMax instances) from which the animation should begin playing in reverse (if none is defined, it will begin playing from wherever the playhead currently is). To begin at the very end of the animation, use 0. Negative numbers are relative to the end of the animation, so -1 would be 1 second from the end. + * @param suppressEvents If true (the default), no events or callbacks will be triggered when the playhead moves to the new position defined in the from parameter. + * @return self (makes chaining easier) + */ + public function reverse(from:*=null, suppressEvents:Boolean=true):* { + if (from != null) { + seek((from || totalDuration()), suppressEvents); + } + reversed(true); + return paused(false); + } + + /** + * @private + * Renders the animation at a particular time (or frame number for frames-based tweens). + * The time is based simply on the overall duration. For example, if an animations's duration + * is 3, render(1.5) would render it as halfway finished. + * + * @param time time (or frame number for frames-based animations) to render. If a negative value is used, it will act like 0. If the value exceeds the totalDuration, it will act like the totalDuration. + * @param suppressEvents If true, no events or callbacks will be triggered for this render (like onComplete, onUpdate, onReverseComplete, etc.) + * @param force Normally the animation will skip rendering if the time matches the _totalTime (to improve performance), but if force is true, it forces a render. This is primarily used internally for tweens with durations of zero in TimelineLite/Max instances. + */ + public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + + } + + + /** + * Clears any initialization data (like starting/ending values in tweens) which can be useful if, for example, + * you want to restart a tween without reverting to any previously recorded starting values. When you invalidate() + * an animation, it will be re-initialized the next time it renders and its vars object will be re-parsed. + * The timing of the animation (duration, startTime, delay) will not be affected. + * + *

Another example would be if you have a TweenMax(mc, 1, {x:100, y:100}) that ran when mc.x and mc.y + * were initially at 0, but now mc.x and mc.y are 200 and you want them tween to 100 again, you could simply + * invalidate() the tween and restart() it. Without invalidating first, restarting it + * would cause the values jump back to 0 immediately (where they started when the tween originally began). + * When you invalidate a TimelineLite/TimelineMax, it automatically invalidates all of its children.

+ * @return self (makes chaining easier) + **/ + public function invalidate():* { + return this; + } + + /** + * Indicates whether or not the animation is currently active (meaning the virtual playhead is actively moving across + * this instance's time span and it is not paused, nor are any of its ancestor timelines). + * So for example, if a tween is in the middle of tweening, it's active, but after it is finished (or before + * it begins), it is not active. If it is paused or if it is placed inside of a timeline that's paused + * (or if any of its ancestor timelines are paused), isActive() will return false. If the + * playhead is directly on top of the animation's start time (even if it hasn't rendered quite yet), that counts + * as "active". + * + *

You may also check the progress() or totalProgress(), but those don't take into consideration + * the paused state or the position of the parent timeline's playhead.

+ * + * @see #progress() + * @see #totalProgress() + **/ + public function isActive():Boolean { + var tl:SimpleTimeline = _timeline, //the 2 root timelines won't have a _timeline; they're always active. + rawTime:Number; + return ((tl == null) || (!_gc && !_paused && tl.isActive() && (rawTime = tl.rawTime()) >= _startTime && rawTime < _startTime + totalDuration() / _timeScale)); + } + + /** + * @private + * If an animation is enabled, it is eligible to be rendered (unless it is paused). Disabling it + * essentially removes it from its parent timeline and stops protecting it from garbage collection. + * + * @param enabled Enabled state of the animation + * @param ignoreTimeline By default, the tween/timeline will remove itself from its parent timeline when it is disabled and add itself when it is enabled, but this parameter allows you to skip that behavior. + * @return Boolean value indicating whether or not important properties may have changed when the animation was enabled/disabled. For example, when a MotionBlurPlugin is disabled, it swaps out a BitmapData for the target and may alter the opacity. We need to know this in order to determine whether or not a new tween that is overwriting this one should be re-initialized with the changed properties. + **/ + public function _enabled(enabled:Boolean, ignoreTimeline:Boolean=false):Boolean { + _gc = !enabled; //note: it is possible for _gc to be true and timeline not to be null in situations where a parent TimelineLite/Max has completed and is removed - the developer might hold a reference to that timeline and later restart() it or something. + _active = Boolean(enabled && !_paused && _totalTime > 0 && _totalTime < _totalDuration); + if (!ignoreTimeline) { + if (enabled && timeline == null) { + _timeline.add(this, _startTime - _delay); + } else if (!enabled && timeline != null) { + _timeline._remove(this, true); + } + } + + return false; + } + + /** @private Same as kill() except that it returns a Boolean that indicates whether or not important properties may have changed when the animation was killed. For example, when a MotionBlurPlugin is disabled, it swaps out a BitmapData for the target and may alter the opacity. We need to know this in order to determine whether or not a new tween that is overwriting this one should be re-initialized with the changed properties. **/ + public function _kill(vars:Object=null, target:Object=null):Boolean { + return _enabled(false, false); + } + + /** + * Kills the animation entirely or in part depending on the parameters. Simply calling kill() + * (omitting the parameters) will immediately stop the animation and release it for garbage collection. + * To kill only particular tweening properties of the animation, use the first parameter which should + * be a generic object with enumerable properties corresponding to those that should be killed, + * like {x:true, y:true}. The second parameter allows you to define a target + * (or array of targets) to affect. + * + *

Note: the values assigned to each property of the vars parameter object don't + * matter - the sole purpose of the object is for iteration over the named properties. In other + * words, {x:true, y:true} would produce the same results as {x:false, y:false}.

+ * + * + //kill the entire animation: + myAnimation.kill(); + + //kill only the "x" and "y" properties of the animation (all targets): + myAnimation.kill({x:true, y:true}); + + //kill all parts of the animation related to the target "myObject" (if the tween has multiple targets, the others will not be affected): + myAnimation.kill(null, myObject); + + //kill only the "x" and "y" properties of animations of the target "myObject": + myAnimation.kill({x:true, y:true}, myObject); + + //kill only the "opacity" properties of animations of the targets "myObject1" and "myObject2": + myAnimation.kill({opacity:true}, [myObject1, myObject2]); + + * + * @param vars To kill only specific properties, use a generic object containing enumerable properties corresponding to the ones that should be killed, like {x:true, y:true}. The values assigned to each property of the object don't matter - the sole purpose of the object is for iteration over the named properties (in this case, x and y). If no object (or null) is defined, ALL properties will be killed. + * @param target To kill only aspects of the animation related to a particular target (or targets), reference it here. For example, to kill only parts having to do with myObject, do kill(null, myObject) or to kill only parts having to do with myObject1 and myObject2, do kill(null, [myObject1, myObject2]). If no target is defined, ALL targets will be affected. + * @return self (makes chaining easier) + **/ + public function kill(vars:Object=null, target:Object=null):* { + _kill(vars, target); + return this; + } + + /** + * @private + * Sets the _dirty property of all anscestor timelines (and optionally this instance too). Setting + * the _dirty property to true forces any necessary recalculation of its _duration and + * _totalDuration properties and sorts the affected timelines' children animations so that they're in the proper order + * next time the duration or totalDuration is requested. We don't just recalculate them + * immediately because it can be much faster to do it this way. + * + * @param includeSelf indicates whether or not this tween's _dirty property should be affected. + * @return self (makes chaining easier) + */ + protected function _uncache(includeSelf:Boolean):* { + var tween:Animation = includeSelf ? this : timeline; + while (tween) { + tween._dirty = true; + tween = tween.timeline; + } + return this; + } + + /** @private This method gets called on every frame and is responsible for rendering/updating the root timelines. If you want to unhook the engine from its ticker, you could do Animation.ticker.removeEventListener("enterFrame", _updateRoot) and then call it yourself whenever you want to update. **/ + public static function _updateRoot(event:Event=null):void { + _rootFrame++; + _rootTimeline.render((getTimer() / 1000 - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); + _rootFramesTimeline.render((_rootFrame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false); + ticker.dispatchEvent(_tickEvent); + } + + /** @private **/ + protected function _swapSelfInParams(params:Array):Array { + var i:int = params.length, + copy:Array = params.concat(); + while (--i > -1) { + if (params[i] === "{self}") { + copy[i] = this; + } + } + return copy; + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------ + + + + /** + * Gets or sets an event callback like "onComplete", "onUpdate", "onStart", "onReverseComplete" + * or "onRepeat" (onRepeat only applies to TweenMax or TimelineMax instances) + * along with any parameters that should be passed to that callback. This is the same as defining + * the values directly in the constructor's vars parameter initially, so the following + * two lines are functionally equivalent: + * + * +//the following two lines produce IDENTICAL results: +var myAnimation = new TweenLite(mc, 1, {x:100, onComplete:myFunction, onCompleteParams:["param1","param2"]}); +myAnimation.eventCallback("onComplete", myFunction, ["param1","param2"]); + + *

The benefit of using eventCallback() is that it allows you to set callbacks + * even after the animation instance has been created and it also allows you to inspect the + * callback references or even delete them on-the-fly (use null to delete the + * event callback).

+ * + * +//deletes the onUpdate +myAnimation.eventCallback("onUpdate", null); + + * + *

IMPORTANT:Animation instances can only have one callback associated with each + * event type (one onComplete, one onUpdate, one onStart, etc.). + * So setting a new value will overwrite the old one. All of the values populate the vars + * object too which was originally passed into the constructor (think of that like a storage place for + * configuration data).

+ * + *

This method serves as both a getter and setter. Omitting all but the first parameter returns + * the current value (getter), whereas defining more than the first parameter sets the value (setter) + * and returns the instance itself for easier chaining, like + * myAnimation.eventCallback("onComplete", completeHandler).eventCallback("onUpdate", updateHandler, ["param1","{self}"]).play(1);

+ * + * +var currentOnComplete = myAnimation.eventCallback("onComplete"); //gets current onComplete +myAnimation.eventCallback("onComplete", myFunction); //sets the onComplete + + * + *

JavaScript and AS2 note: - Due to the way JavaScript and AS2 don't + * maintain scope (what "this" refers to, or the context) in function calls, + * it can be useful to define the scope specifically. Therefore, in the JavaScript and AS2 + * versions accept an extra (4th) parameter for scope.

+ * + * @param type The type of event callback, like "onComplete", "onUpdate", "onStart" or "onRepeat". This is case-sensitive. + * @param callback The function that should be called when the event occurs. + * @param params An array of parameters to pass the callback. Use "{self}" to refer to the animation instance itself. Example: ["param1","{self}"] + * @return Omitting the all but the first parameter returns the current value (getter), whereas defining more than the first parameter sets the callback (setter) and returns the instance itself for easier chaining. + */ + public function eventCallback(type:String, callback:Function=null, params:Array=null):* { + if (type == null) { + return null; + } else if (type.substr(0,2) == "on") { + if (arguments.length == 1) { + return vars[type]; + } + if (callback == null) { + delete vars[type]; + } else { + vars[type] = callback; + vars[type + "Params"] = ((params is Array) && params.join("").indexOf("{self}") !== -1) ? _swapSelfInParams(params) : params; + } + if (type == "onUpdate") { + _onUpdate = callback; + } + } + return this; + } + + + /** + * Gets or sets the animation's initial delay which is the length of time in seconds + * (or frames for frames-based tweens) before the animation should begin. + * A tween's starting values are not recorded until after the delay has expired (except in + * from() tweens which render immediately by default unless immediateRender:false + * is set in the vars parameter). An animation's delay is unaffected + * by its timeScale, so if you were to change timeScale from 1 to 10, + * for example, it wouldn't cause the delay to grow tenfold. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.delay(2).timeScale(0.5).play(1);

+ * + * + var currentDelay = myAnimation.delay(); //gets current delay + myAnimation.delay(2); //sets delay + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + **/ + public function delay(value:Number=NaN):* { + if (!arguments.length) { + return _delay; + } + if (_timeline.smoothChildTiming) { + startTime( _startTime + value - _delay ); + } + _delay = value; + return this; + } + + /** + * Gets or sets the animation's duration, not including any repeats or repeatDelays + * (which are only available in TweenMax and TimelineMax). For example, if a TweenMax instance has + * a duration of 2 and a repeat of 3, its totalDuration + * would be 8 (one standard play plus 3 repeats equals 4 total cycles). + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.duration(2).delay(0.5).play(1);

+ * + * + var currentDuration = myAnimation.duration(); //gets current duration + myAnimation.duration(2); //sets duration + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #totalDuration() + * @see #timeScale() + **/ + public function duration(value:Number=NaN):* { + if (!arguments.length) { + _dirty = false; + return _duration; + } + _duration = _totalDuration = value; + _uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration. + if (_timeline.smoothChildTiming) if (_time > 0) if (_time < _duration) if (value != 0) { + totalTime(_totalTime * (value / _duration), true); + } + return this; + } + + /** + * Gets or sets the animation's total duration including + * any repeats or repeatDelays (which are only available in TweenMax and TimelineMax). + * For example, if a TweenMax instance has a duration of 2 and a repeat of 3, + * its totalDuration would be 8 (one standard play plus 3 repeats equals 4 total cycles). + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.totalDuration(2).delay(0.5).play(1);

+ * + * + var ctd = myAnimation.totalDuration(); //gets current total duration + myAnimation.totalDuration(2); //sets total duration + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #duration() + * @see #timeScale() + **/ + public function totalDuration(value:Number=NaN):* { + _dirty = false; + return (!arguments.length) ? _totalDuration : duration(value); + } + + /** + * Gets or sets the local position of the playhead (essentially the current time), + * described in seconds (or frames for frames-based animations) which + * will never be less than 0 or greater than the animation's duration. + * For example, if the duration is 10 and you were to watch the + * time during the course of the animation, you'd see it go from 0 + * at the beginning to 10 at the end. Setting time to 5 would cause the + * animation to jump to its midway point (because it's half of the duration). + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var currentTime = myAnimation.time(); //gets current time +myAnimation.time(2); //sets time, jumping to new value just like seek(). + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position defined in the value parameter. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #seek() + * @see #play() + * @see #reverse() + * @see #pause() + * @see #totalTime() + **/ + public function time(value:Number=NaN, suppressEvents:Boolean=false):* { + if (!arguments.length) { + return _time; + } + if (_dirty) { + totalDuration(); + } + if (value > _duration) { + value = _duration; + } + return totalTime(value, suppressEvents); + } + + /** + * Gets or sets the position of the playhead according to the totalDuration + * which includes any repeats and repeatDelays (only available + * in TweenMax and TimelineMax). For example, if a TweenMax instance has a + * duration of 2 and a repeat of 3, totalTime + * will go from 0 to 8 during the course of the tween (plays once then repeats 3 times, + * making 4 total cycles) whereas time will go from 0 to 2 a total of 4 times. + * If you added a repeatDelay of 1, that would make the totalTime + * go from 0 to 11 over the course of the tween. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + *

totalTime will never exceed the totalDuration, nor will it be + * less than 0 (values will be clipped appropriately). Negative values will be interpreted from + * the END of the animation. For example, -2 would be 2 seconds before the end. If the + * animation's totalDuration is 6 and you do myAnimation.totalTime(-2), + * it will jump to a totalTime of 4.

+ * + * + var tt = myAnimation.totalTime(); //gets total time + myAnimation.totalTime(2); //sets total time, jumping to new value just like seek(). + + * + * @param time Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. Negative values will be interpreted from the END of the animation. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position defined in the time parameter. + * @param uncapped By default, the time will be capped at totalDuration and if a negative number is used, it will be measured from the END of the animation, but if uncapped is true, the time won't be adjusted at all (negatives will be allowed, as will values that exceed totalDuration). + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #time() + * @see #seek() + * @see #play() + * @see #reverse() + * @see #pause() + **/ + public function totalTime(time:Number=NaN, suppressEvents:Boolean=false, uncapped:Boolean=false):* { + if (!arguments.length) { + return _totalTime; + } + if (_timeline) { + if (time < 0 && !uncapped) { + time += totalDuration(); + } + if (_timeline.smoothChildTiming) { + if (_dirty) { + totalDuration(); + } + if (time > _totalDuration && !uncapped) { + time = _totalDuration; + } + var tl:SimpleTimeline = _timeline; + _startTime = (_paused ? _pauseTime : tl._time) - ((!_reversed ? time : _totalDuration - time) / _timeScale); + if (!_timeline._dirty) { //for performance improvement. If the parent's cache is already dirty, it already took care of marking the anscestors as dirty too, so skip the function call here. + _uncache(false); + } + //in case any of the ancestor timelines had completed but should now be enabled, we should reset their totalTime() which will also ensure that they're lined up properly and enabled. Skip for animations that are on the root (wasteful). Example: a TimelineLite.exportRoot() is performed when there's a paused tween on the root, the export will not complete until that tween is unpaused, but imagine a child gets restarted later, after all [unpaused] tweens have completed. The startTime of that child would get pushed out, but one of the ancestors may have completed. + if (tl._timeline != null) { + while (tl._timeline) { + if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) { + tl.totalTime(tl._totalTime, true); + } + tl = tl._timeline; + } + } + } + if (_gc) { + _enabled(true, false); + } + if (_totalTime != time || _duration === 0) { + render(time, suppressEvents, false); + } + } + return this; + } + + /** + * Gets or sets the animations's progress which is a value between 0 and 1 indicating the position + * of the virtual playhead (excluding repeats) where 0 is at the beginning, 0.5 is at the halfway point, + * and 1 is at the end (complete). If the animation has a non-zero repeat defined (only available in TweenMax and TimelineMax), + * progress() and totalProgress() will be different because progress() doesn't include the + * repeat or repeatDelay whereas totalProgress() does. For example, if a TimelineMax instance + * is set to repeat once, at the end of the first cycle totalProgress() would only be 0.5 + * whereas progress() would be 1. If you watched both properties over the course of the entire + * animation, you'd see progress() go from 0 to 1 twice (once for each cycle) in the + * same time it takes the totalProgress() to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.progress(0.5).play();

+ * + * +var progress = myAnimation.progress(); //gets current progress +myAnimation.progress(0.25); //sets progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #seek() + * @see #time() + * @see #totalTime() + * @see #totalProgress() + **/ + public function progress(value:Number=NaN, suppressEvents:Boolean=false):* { + return (!arguments.length) ? _time / duration() : totalTime(duration() * value, suppressEvents); + } + + /** + * Gets or sets the animation's total progress which is a value between 0 and 1 indicating the position + * of the virtual playhead (including repeats) where 0 is at the beginning, 0.5 is + * at the halfway point, and 1 is at the end (complete). If the animation has a non-zero repeat defined (only available in TweenMax and TimelineMax), + * progress() and totalProgress() will be different because progress() + * doesn't include the repeat or repeatDelay whereas totalProgress() does. For example, + * if a TimelineMax instance is set to repeat once, at the end of the first cycle totalProgress() + * would only be 0.5 whereas progress would be 1. If you watched both properties over the + * course of the entire animation, you'd see progress go from 0 to 1 twice (once for + * each cycle) in the same time it takes the totalProgress() to go from 0 to 1 once. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.totalProgress(0.5).play();

+ * + * +var progress = myAnimation.totalProgress(); //gets total progress +myAnimation.totalProgress(0.25); //sets total progress to one quarter finished + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @param suppressEvents If true, no events or callbacks will be triggered when the playhead moves to the new position. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #progress() + * @see #seek() + * @see #time() + * @see #totalTime() + **/ + public function totalProgress(value:Number=NaN, suppressEvents:Boolean=false):* { + return (!arguments.length) ? _time / duration() : totalTime(duration() * value, suppressEvents); + } + + /** + * Gets or sets the time at which the animation begins on its parent timeline (after any delay + * that was defined). For example, if a tween starts at exactly 3 seconds into the timeline + * on which it is placed, the tween's startTime would be 3. + * + *

The startTime may be automatically adjusted to make the timing appear + * seamless if the parent timeline's smoothChildTiming property is true + * and a timing-dependent change is made on-the-fly, like reverse() is called or + * timeScale() is changed, etc. See the documentation for the smoothChildTiming + * property of timelines for more details.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var start = myAnimation.startTime(); //gets current start time +myAnimation.startTime(2); //sets the start time + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + **/ + public function startTime(value:Number=NaN):* { + if (!arguments.length) { + return _startTime; + } + if (value != _startTime) { + _startTime = value; + if (timeline) if (timeline._sortChildren) { + timeline.add(this, value - _delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct. + } + } + return this; + } + + /** + * Factor that's used to scale time in the animation where 1 = normal speed (the default), + * 0.5 = half speed, 2 = double speed, etc. For example, if an animation's duration + * is 2 but its timeScale is 0.5, it will take 4 seconds to finish. If you nest that + * animation in a timeline whose timeScale is 0.5 as well, it would take 8 seconds + * to finish. You can even tween the timeScale to gradually slow it down or speed it up. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.timeScale(2).play(1);

+ * + * +var currentTimeScale = myAnimation.timeScale(); //gets current timeScale +myAnimation.timeScale( 0.5 ); //sets timeScale to half-speed + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #duration() + **/ + public function timeScale(value:Number=NaN):* { + if (!arguments.length) { + return _timeScale; + } + value = value || 0.000001; //can't allow zero because it'll throw the math off + if (_timeline && _timeline.smoothChildTiming) { + var t:Number = (_pauseTime || _pauseTime == 0) ? _pauseTime : _timeline._totalTime; + _startTime = t - ((t - _startTime) * _timeScale / value); + } + _timeScale = value; + return _uncache(false); + } + + /** + * Gets or sets the animation's reversed state which indicates whether or not the animation + * should be played backwards. This value is not affected by yoyo repeats + * (TweenMax and TimelineMax only) and it does not take into account the reversed state of + * anscestor timelines. So for example, a tween that is not reversed might appear reversed + * if its parent timeline (or any ancenstor timeline) is reversed. + * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining.

+ * + * +var rev = myAnimation.reversed(); //gets current orientation +myAnimation.reversed( true ); //sets the orientation to reversed +myAnimation.reversed( !myAnimation.reversed() ); //toggles the orientation + + * + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #reverse() + * @see #play() + **/ + public function reversed(value:Boolean=false):* { + if (!arguments.length) { + return _reversed; + } + if (value != _reversed) { + _reversed = value; + totalTime(((_timeline && !_timeline.smoothChildTiming) ? totalDuration() - _totalTime : _totalTime), true); + } + return this; + } + + /** + * Gets or sets the animation's paused state which indicates whether or not the animation + * is currently paused. This does not take into account anscestor timelines. So for example, + * a tween that is not paused might appear paused if its parent timeline (or any ancenstor + * timeline) is paused. Pausing an animation doesn't remove it from its parent timeline, + * but it does cause it not to be factored into the parent timeline's + * duration/totalDuration. When an animation completes, it does + * NOT alter its paused state. + * + *

In most cases, it is easiest to use the pause() method to pause + * the animation, and resume() to resume it. But to check the current + * state, you must use the paused() method. It can also be useful for + * toggling like myAnimation.paused( !myAnimation.paused() );

+ * + *

You can set the paused state initially by passing paused:true + * in the vars parameter.

+ * + *

This method serves as both a getter and setter. Omitting the parameter returns the current + * value (getter), whereas defining the parameter sets the value (setter) and returns the instance + * itself for easier chaining, like myAnimation.paused(true).delay(2).timeScale(0.5);

+ * + * + var paused = myAnimation.paused(); //gets current paused state + myAnimation.paused( true ); //sets paused state to true (just like pause()) + myAnimation.paused( !myAnimation.paused() ); //toggles the paused state + + * @param value Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * @return Omitting the parameter returns the current value (getter), whereas defining the parameter sets the value (setter) and returns the instance itself for easier chaining. + * + * @see #pause() + * @see #resume() + * @see #play() + **/ + public function paused(value:Boolean=false):* { + if (!arguments.length) { + return _paused; + } + if (value != _paused) if (_timeline) { + var raw:Number = _timeline.rawTime(), + elapsed:Number = raw - _pauseTime; + if (!value && _timeline.smoothChildTiming) { + _startTime += elapsed; + _uncache(false); + } + _pauseTime = value ? raw : NaN; + _paused = value; + _active = (!value && _totalTime > 0 && _totalTime < _totalDuration); + if (!value && elapsed != 0 && _initted && duration() !== 0) { + render((_timeline.smoothChildTiming ? _totalTime : (raw - _startTime) / _timeScale), true, true); //in case the target's properties changed via some other tween or manual update by the user, we should force a render. + } + } + if (_gc && !value) { + _enabled(true, false); + } + return this; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/core/PropTween.as b/src/com/greensock/core/PropTween.as new file mode 100644 index 0000000..a728289 --- /dev/null +++ b/src/com/greensock/core/PropTween.as @@ -0,0 +1,67 @@ +/** + * VERSION: 12.0.0 + * DATE: 2012-02-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.core { +/** + * @private + * Stores information about an individual property tween. There is no reason to use this class directly - TweenLite, TweenMax, and some plugins use it internally. + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + final public class PropTween { + /** Target object **/ + public var t:Object; + /** Name of the property that is being tweened on the target (for plugins, this is always "setRatio", but the actual property name of the orignal target is stored in the "n" property of the PropTween instance) **/ + public var p:String; + /** Starting value **/ + public var s:Number; + /** Amount to change (basically, the difference between the starting value and ending value) **/ + public var c:Number; + /** Indicates whether or not the target's property that is being tweened is a function (true) or not (false). If it's a function, it must be set with t.p(value) rather than t.p = value. **/ + public var f:Boolean; + /** Priority in the rendering queue. The lower the value the later it will be tweened. The default value is 0, but some plugins must be rendered later (or earlier). **/ + public var pr:int; + /** Indicates whether or not the target is a TweenPlugin. **/ + public var pg:Boolean; + /** The name associated with the original target property. Typically this is the same as PropTween.p but for TweenPlugin tweens it is often different. For example an autoAlpha tween would create a PropTween of the AutoAlphaPlugin instance and p would be "setRatio", but n would be "autoAlpha". **/ + public var n:String; + /** If true, the property should be rounded. **/ + public var r:Boolean; + /** Next PropTween in the linked list **/ + public var _next:PropTween; + /** Previous PropTween in the linked list **/ + public var _prev:PropTween; + + /** + * Constructor + * + * @param target Target object + * @param property Name of the property that is being tweened on the target (for plugins, this is always "setRatio", but the actual property name of the orignal target is stored in the "n" property of the PropTween instance) + * @param start Starting value + * @param change Amount to change (basically, the difference between the starting value and ending value) + * @param name The name associated with the original target property. Typically this is the same as PropTween.p but for TweenPlugin tweens it is often different. For example an autoAlpha tween would create a PropTween of the AutoAlphaPlugin instance and p would be "setRatio", but n would be "autoAlpha". + * @param isPlugin Indicates whether or not the target is a TweenPlugin. + * @param nextNode Next PropTween in the linked list + * @param priority Priority in the rendering queue. The lower the value the later it will be tweened. The default value is 0, but some plugins must be rendered later (or earlier). + */ + public function PropTween(target:Object, property:String, start:Number, change:Number, name:String, isPlugin:Boolean, next:PropTween=null, priority:int=0) { + this.t = target; + this.p = property; + this.s = start; + this.c = change; + this.n = name; + this.f = (target[property] is Function); + this.pg = isPlugin; + if (next) { + next._prev = this; + this._next = next; + } + this.pr = priority; + } + } +} \ No newline at end of file diff --git a/src/com/greensock/core/SimpleTimeline.as b/src/com/greensock/core/SimpleTimeline.as new file mode 100644 index 0000000..a1e80e8 --- /dev/null +++ b/src/com/greensock/core/SimpleTimeline.as @@ -0,0 +1,193 @@ +/** + * VERSION: 12.0.4 + * DATE: 2014-07-08 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.core { +/** + * SimpleTimeline is the base class for TimelineLite and TimelineMax, providing the + * most basic timeline functionality and it is used for the root timelines in TweenLite but is only + * intended for internal use in the GreenSock tweening platform. It is meant to be very fast and lightweight. + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SimpleTimeline extends Animation { + + /** If true, child tweens/timelines will be removed as soon as they complete. (false by default except on the root timeline(s)) **/ + public var autoRemoveChildren:Boolean; + + /** + * Controls whether or not child tweens/timelines are repositioned automatically (changing their startTime) + * in order to maintain smooth playback when properties are changed on-the-fly. For example, imagine that + * the timeline's playhead is on a child tween that is 75% complete, moving mc.x from 0 to 100 and then + * that tween's reverse() method is called. If smoothChildTiming is false + * (the default except for the root timelines), the tween would flip in place, keeping its startTime + * consistent. Therefore the playhead of the timeline would now be at the tween's 25% completion point instead + * of 75%. Remember, the timeline's playhead position and direction are unaffected by child tween/timeline changes. + * mc.x would jump from 75 to 25, but the tween's position in the timeline would remain consistent. + * However, if smoothChildTiming is true, that child tween's startTime would + * be adjusted so that the timeline's playhead intersects with the same spot on the tween (75% complete) as it had + * immediately before reverse() was called, thus playback appears perfectly smooth. mc.x would + * still be 75 and it would continue from there as the playhead moves on, but since the tween is reversed now + * mc.x will travel back towards 0 instead of 100. Ultimately it's a decision between prioritizing smooth + * on-the-fly playback (true) or consistent position(s) of child tweens/timelines (false). + * + *

Some examples of on-the-fly changes to child tweens/timelines that could cause their startTime + * to change when smoothChildTiming is true are: reversed, timeScale, progress, + * totalProgress, time, totalTime, delay, pause, resume, duration, and totalDuration.

+ **/ + public var smoothChildTiming:Boolean; + + /** @private If true, children are sorted in order of their startTime when inserted (improves rendering accuracy in certain situations) **/ + public var _sortChildren:Boolean; + + /** @private first child in the linked list **/ + public var _first:Animation; + + /** @private last child in the linked list **/ + public var _last:Animation; + + /** + * Constructor + * + * @param vars Object containing configuration variables like onComplete, onUpdate, onStart, data, etc. + */ + public function SimpleTimeline(vars:Object=null) { + super(0, vars); + this.autoRemoveChildren = this.smoothChildTiming = true; + } + + /** + * @private + * [Deprecated in favor of add()] + * Inserts a TweenLite, TweenMax, TimelineLite, or TimelineMax instance into the timeline at a specific time. + * In classes like TimelineLite and TimelineMax that override this method, it allows things like callbacks, + * labels, and arrays of tweens/timelines/callbacks/labels to be inserted too. They also allow the time to + * be defined in terms of either a numeric time or a label (String). + * + * @param child TweenLite, TweenMax, TimelineLite, or TimelineMax instance to insert + * @param position The time in seconds (or frames for frames-based timelines) at which the tween/timeline should be inserted. For example, myTimeline.insert(myTween, 3) would insert myTween 3 seconds into the timeline. + * @return this timeline instance (useful for chaining like myTimeline.insert(...).insert(...)) + */ + public function insert(child:*, position:*=0):* { + return add(child, position || 0); + } + + /** + * Adds a TweenLite, TweenMax, TimelineLite, or TimelineMax instance to the timeline at a specific time. + * In classes like TimelineLite and TimelineMax that override this method, it allows things like callbacks, + * labels, and arrays of tweens/timelines/callbacks/labels to be inserted too. They also allow the position to + * be defined in terms of either a numeric time or a label (String). + * + * @param child TweenLite, TweenMax, TimelineLite, or TimelineMax instance to insert + * @param position The position at which the tween/timeline should be inserted which can be expressed as a number (for an absolute time as seconds or frames for frames-based timelines) or a string, using "+=" or "-=" prefix to indicate a relative value (relative to the END of the timeline). For example, myTimeline.insert(myTween, 3) would insert myTween 3 seconds into the timeline. + * @param align Determines how the tweens/timelines/callbacks/labels will be aligned in relation to each other before getting inserted. Options are: "sequence" (aligns them one-after-the-other in a sequence), "start" (aligns the start times of all of the objects (ignoring delays)), and "normal" (aligns the start times of all the tweens (honoring delays)). The default is "normal". + * @param stagger Staggers the inserted objects by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the "align" parameter is set to "start", the second one will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is "sequence", there would be 0.5 seconds added between each tween. Default is 0. + * @return this timeline instance (useful for chaining like myTimeline.add(...).add(...)) + */ + public function add(child:*, position:*="+=0", align:String="normal", stagger:Number=0):* { + child._startTime = Number(position || 0) + child._delay; + if (child._paused) if (this != child._timeline) { //we only adjust the _pauseTime if it wasn't in this timeline already. Remember, sometimes a tween will be inserted again into the same timeline when its startTime is changed so that the tweens in the TimelineLite/Max are re-ordered properly in the linked list (so everything renders in the proper order). + child._pauseTime = child._startTime + ((rawTime() - child._startTime) / child._timeScale); + } + if (child.timeline) { + child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one. + } + child.timeline = child._timeline = this; + if (child._gc) { + child._enabled(true, true); + } + + var prevTween:Animation = _last; + if (_sortChildren) { + var st:Number = child._startTime; + while (prevTween && prevTween._startTime > st) { + prevTween = prevTween._prev; + } + } + if (prevTween) { + child._next = prevTween._next; + prevTween._next = Animation(child); + } else { + child._next = _first; + _first = Animation(child); + } + if (child._next) { + child._next._prev = child; + } else { + _last = Animation(child); + } + child._prev = prevTween; + + if (_timeline) { + _uncache(true); + } + + return this; + } + + /** @private **/ + public function _remove(tween:Animation, skipDisable:Boolean=false):* { + if (tween.timeline == this) { + if (!skipDisable) { + tween._enabled(false, true); + } + + if (tween._prev) { + tween._prev._next = tween._next; + } else if (_first === tween) { + _first = tween._next; + } + if (tween._next) { + tween._next._prev = tween._prev; + } else if (_last === tween) { + _last = tween._prev; + } + tween._next = tween._prev = tween.timeline = null; + + if (_timeline) { + _uncache(true); + } + } + return this; + } + + /** @inheretDoc **/ + override public function render(time:Number, suppressEvents:Boolean=false, force:Boolean=false):void { + var tween:Animation = _first, next:Animation; + _totalTime = _time = _rawPrevTime = time; + while (tween) { + next = tween._next; //record it here because the value could change after rendering... + if (tween._active || (time >= tween._startTime && !tween._paused)) { + if (!tween._reversed) { + tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); + } else { + tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); + } + } + tween = next; + } + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------------ + + /** + * @private + * Reports the totalTime of the timeline without capping the number at the totalDuration (max) and zero (minimum) + * which can be useful when unpausing tweens/timelines. Imagine a case where a paused tween is in a timeline that has already + * reached the end, but then the tween gets unpaused - it needs a way to place itself accurately in time AFTER what was + * previously the timeline's end time. In a SimpleTimeline, rawTime is always the same as _totalTime, + * but in TimelineLite and TimelineMax, it can be different. + * + * @return The totalTime of the timeline without capping the number at the totalDuration (max) and zero (minimum) + */ + public function rawTime():Number { + return _totalTime; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/data/TweenLiteVars.as b/src/com/greensock/data/TweenLiteVars.as new file mode 100644 index 0000000..9405159 --- /dev/null +++ b/src/com/greensock/data/TweenLiteVars.as @@ -0,0 +1,1204 @@ +/** + * VERSION: 12.01 + * DATE: 2012-09-10 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/tweenvars/ + **/ +package com.greensock.data { + import com.greensock.TweenLite; + import com.greensock.motionPaths.MotionPath; + + import flash.display.Stage; + import flash.geom.Point; +/** + * [AS3 only] There are 3 primary benefits of using a TweenLiteVars instance to define your TweenLite's "vars" parameter: + *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available.
  2. + *
  3. It allows you to code using strict data typing which can improve debugging.
  4. + *
  5. It will trace() a warning if you forgot to activate a particular plugin. For example, if you define an autoAlpha value in a TweenLiteVars instance but you didn't activate() the plugin, you'll see a trace() output when you test/compile the file (an Error isn't thrown because in some very rare circumstances it can be perfectly legitimate to avoid activating the plugin)
  6. + *
+ * + *

The down side, of course, is that the code is more verbose and TweenLiteVars adds about 5kb to your published swf.

+ * + *

USAGE:

+ *

Note that each method returns the TweenLiteVars object, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without TweenLiteVars:

+ *

TweenLite.to(mc, 1, {x:300, y:100, tint:0xFF0000, onComplete:myFunction, onCompleteParams:[mc]})

+ * + *

With TweenLiteVars

+ *

TweenLite.to(mc, 1, new TweenLiteVars().move(300, 100).tint(0xFF0000).onComplete(myFunction, [mc]));

+ * + *

You can use the prop() method to set individual generic properties (like "myCustomProperty" or "rotationY") or you can + * pass a generic Object into the constructor to make it a bit more concise, like this:

+ * + *

TweenLite.to(mc, 1, new TweenLiteVars({myCustomProperty:300, rotationY:100}).tint(0xFF0000).onComplete(myFunction, [mc]));

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that TweenLiteVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * TweenLite.to(mc, 1, new TweenLiteVars({x:300, y:100}).tint(0xFF0000).onComplete(myFunction, [mc]).vars);
  • + *
  • This class adds about 6kb to your published SWF (not including TweenLite or any plugins).
  • + *
  • Using TweenLiteVars is completely optional. If you prefer the shorter generic object synatax, feel + * free to use it. The purpose of this utility is simply to enable code hinting and to allow for strict datatyping.
  • + *
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenLiteVars { + /** @private **/ + public static const version:Number = 12.0; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like added (copied) to this TweenLiteVars instance. This is particularly useful for generic properties that don't have a corresponding method for setting the values (although you can use it for properties that do have corresponding methods too). For example, to tween the x and y properties of a DisplayObject, new TweenLiteVars({x:300, y:0}) + */ + public function TweenLiteVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*, requirePlugin:Boolean=false):TweenLiteVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + if (requirePlugin && !(property in TweenLite._plugins)) { + trace("WARNING: you must activate() the " + property + " plugin in order for the feature to work in TweenLite. See http://www.greensock.com/tweenlite/#plugins for details."); + } + return this; + } + + /** + * Adds a dynamic property for tweening and allows you to indicate whether the value is relative or not. + * For example, to tween "x" to 50 less than whatever it currently is: + * + *

prop("x", -50, true);

+ * + * @param property Property name + * @param value Numeric end value (or beginning value for from() tweens) + * @param relative If true, the value will be interpreted as relative to the target's current value. For example, if mc.x is currently 300 and you do prop("x", 200, true), the end value will be 500. + */ + public function prop(property:String, value:Number, relative:Boolean=false):TweenLiteVars { + return _set(property, (!relative) ? value : (value < 0) ? "-=" + (-value) : "+=" + value); + } + + +//---- BUILT-IN SPECIAL PROPERTIES (NO PLUGIN ACTIVATION REQUIRED) -------------------------------------------------------------- + + /** Any generic data that you'd like associated with your tween. **/ + public function data(data:*):TweenLiteVars { + return _set("data", data); + } + + /** The number of seconds (or frames for frames-based tweens) to delay before the tween begins. **/ + public function delay(delay:Number):TweenLiteVars { + return _set("delay", delay); + } + + /** + * Controls the rate of change. Use any standard easing equation like ElasticOut.ease. The Default is QuadOut.ease. + * + * @param ease An ease (i.e. com.greensock.easing.ElasticOut.ease) The default is QuadOut.ease. + * @param easeParams An Array of extra parameter values to feed the easing equation (beyond the standard 4). This can be useful with easing equations like Elastic that accept extra parameters like the amplitude and period. Most easing equations, however, don't accept extra parameters so you won't need to pass in any easeParams. + **/ + public function ease(ease:*, easeParams:Array=null):TweenLiteVars { + _set("easeParams", easeParams); + return _set("ease", ease); + } + + /** + * Normally when you create a tween, it begins rendering on the very next frame (when + * the Flash Player dispatches an ENTER_FRAME event) unless you specify a delay. + * This allows you to insert tweens into timelines and perform other actions that may affect + * its timing. However, if you prefer to force the tween to render immediately when it is + * created, set immediateRender to true. from() tweens + * render immediately by default, so to prevent that behavior, set immediateRender + * to false. + **/ + public function immediateRender(value:Boolean):TweenLiteVars { + return _set("immediateRender", value, false); + } + + /** + * A function that should be called when the tween has completed. + * + * @param func A function that should be called when the tween has completed. + * @param params An Array of parameters to pass the onComplete function + **/ + public function onComplete(func:Function, params:Array=null):TweenLiteVars { + _set("onCompleteParams", params); + return _set("onComplete", func); + } + + /** + * A function that should be called after the tween has completed and rendered its final state to the stage (waits for the next ENTER_FRAME event is dispatched after the tween finishes). Target must be a DisplayObject. + * + * @param func A function that should be called after the tween has completed and rendered its final state to the stage (waits for the next ENTER_FRAME event is dispatched after the tween finishes). + * @param params An Array of parameters to pass the onCompleteRender function + **/ + public function onCompleteRender(func:Function, params:Array=null):TweenLiteVars { + _set("onCompleteRenderParams", params); + return _set("onCompleteRender", func, true); + } + + /** + * A function that should be called when the tween begins (when its time() is at 0 + * and changes to some other value which can happen more than once if the tween is restarted multiple times). + * + * @param func A function that should be called when the tween begins. + * @param params An Array of parameters to pass the onStart function. + **/ + public function onStart(func:Function, params:Array=null):TweenLiteVars { + _set("onStartParams", params); + return _set("onStart", func); + } + + /** + * A function to call whenever the tweening values are updated (on every frame during the time the tween is active). + * + * @param func A function to call whenever the tweening values are updated. + * @param params An Array of parameters to pass the onUpdate function + **/ + public function onUpdate(func:Function, params:Array=null):TweenLiteVars { + _set("onUpdateParams", params); + return _set("onUpdate", func); + } + + /** + * A function that should be called when the tween has reached its starting point again after having been reversed. + * + * @param func A function that should be called when the tween has reached its starting point again after having been reversed. + * @param params An Array of parameters to pass the onReverseComplete function + **/ + public function onReverseComplete(func:Function, params:Array=null):TweenLiteVars { + _set("onReverseCompleteParams", params); + return _set("onReverseComplete", func); + } + + /** + * Controls how (and if) other tweens of the same target are overwritten. + * There are several modes to choose from, but "auto" is the default (although + * you can change the default mode using the TweenLite.defaultOverwrite property): + *
    + *
  • "none" - no overwriting will occur.
  • + * + *
  • "all" - immediately overwrites all existing + * tweens of the same target even if they haven't started yet or don't have + * conflicting properties.
  • + * + *
  • "auto" - when the tween renders for the first time, it will analyze + * tweens of the same target that are currently active/running and only overwrite + * individual tweening properties that overlap/conflict. Tweens that haven't begun + * yet are ignored. For example, if another active tween is found that is tweening + * 3 properties, only 1 of which it shares in common with the new tween, the other + * 2 properties will be left alone. Only the conflicting property gets overwritten/killed. + * This is the default mode and typically the most intuitive for developers.
  • + * + *
  • "concurrent" - when the tween renders for the first time, it kills + * only the active (in-progress) tweens of the same target regardless of whether + * or not they contain conflicting properties. Like a mix of "all" + * and "auto". Good for situations where you only want one tween + * controling the target at a time.
  • + * + *
  • "allOnStart" - Identical to "all" but waits to run + * the overwrite logic until the tween begins (after any delay). Kills + * tweens of the same target even if they don't contain conflicting properties + * or haven't started yet.
  • + * + *
  • "preexisting" - when the tween renders for the first time, it kills + * only the tweens of the same target that existed BEFORE this tween was created + * regardless of their scheduled start times. So, for example, if you create a tween + * with a delay of 10 and then a tween with a delay of 1 and then a tween with a + * delay of 2 (all of the same target), the 2nd tween would overwrite the first + * but not the second even though scheduling might seem to dictate otherwise. + * "preexisting" only cares about the order in which the instances + * were actually created. This can be useful when the order in which your code runs + * plays a critical role.
  • + *
+ **/ + public function overwrite(value:String):TweenLiteVars { + return _set("overwrite", value, false); + } + + /** Controls the paused state of the tween - if true, the tween will be paused initially. **/ + public function paused(value:Boolean):TweenLiteVars { + return _set("paused", value, false); + } + + /** When true, the tween will flip the start and end values which is exactly what TweenLite.from() does. **/ + public function runBackwards(value:Boolean):TweenLiteVars { + return _set("runBackwards", value, false); + } + + /** + * If useFrames is set to true, the tweens's timing mode will be based on frames. + * Otherwise, it will be based on seconds/time. NOTE: a tween's timing mode is always + * determined by its parent timeline. + **/ + public function useFrames(value:Boolean):TweenLiteVars { + return _set("useFrames", value, false); + } + + +//---- COMMON CONVENIENCE PROPERTIES (NO PLUGIN REQUIRED) ------------------------------------------------------------------- + + /** Tweens the "x" and "y" properties of the target **/ + public function move(x:Number, y:Number, relative:Boolean=false):TweenLiteVars { + prop("x", x, relative); + return prop("y", y, relative); + } + + /** Tweens the "scaleX" and "scaleY" properties of the target **/ + public function scale(value:Number, relative:Boolean=false):TweenLiteVars { + prop("scaleX", value, relative); + return prop("scaleY", value, relative); + } + + /** Tweens the "rotation" property of the target **/ + public function rotation(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("rotation", value, relative); + } + + /** Tweens the "scaleX" property of the target **/ + public function scaleX(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("scaleX", value, relative); + } + + /** Tweens the "scaleY" property of the target **/ + public function scaleY(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("scaleY", value, relative); + } + + /** Tweens the "width" property of the target **/ + public function width(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("width", value, relative); + } + + /** Tweens the "height" property of the target **/ + public function height(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("height", value, relative); + } + + /** Tweens the "x" property of the target **/ + public function x(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("x", value, relative); + } + + /** Tweens the "y" property of the target **/ + public function y(value:Number, relative:Boolean=false):TweenLiteVars { + return prop("y", value, relative); + } + + +//---- PLUGIN REQUIRED ------------------------------------------------------------------------------------------- + + /** Same as changing the "alpha" property but with the additional feature of toggling the "visible" property to false whenever alpha is 0, thus improving rendering performance in the Flash Player. **/ + public function autoAlpha(alpha:Number):TweenLiteVars { + return _set("autoAlpha", alpha, true); + } + + /** + * Tweens a BevelFilter + * + * @param distance The offset distance of the bevel. + * @param angle The angle of the bevel. + * @param highlightColor The highlight color of the bevel. + * @param highlightAlpha The alpha transparency value of the highlight color. + * @param shadowColor The shadow color of the bevel. + * @param shadowAlpha The alpha transparency value of the shadow color. + * @param blurX The amount of horizontal blur, in pixels. + * @param blurY The amount of vertical blur, in pixels. + * @param strength The strength of the imprint or spread. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new BevelFilter will be added to the target even if a BevelFilter is already in its filters array. + * @param index Allows you to target a particular BevelFilter if there are multiple BevelFilters in the target's filters array - simply define the index value corresponding to the BevelFilter's position in the filters array. + * @return The TweenLiteVars instance + */ + public function bevelFilter(distance:Number=4, angle:Number=45, highlightColor:uint=0xFFFFFF, highlightAlpha:Number=0.5, shadowColor:uint=0x000000, shadowAlpha:Number=0.5, blurX:Number=4, blurY:Number=4, strength:Number=1, quality:int=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenLiteVars { + var filter:Object = {distance:distance, angle:angle, highlightColor:highlightColor, highlightAlpha:highlightAlpha, shadowColor:shadowColor, shadowAlpha:shadowAlpha, blurX:blurX, blurY:blurY, strength:strength, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("bevelFilter", filter, true); + } + + /** + * Bezier tweening allows you to tween in a non-linear way. For example, you may want to tween + * a MovieClip's position from the origin (0,0) 500 pixels to the right (500,0) but curve downwards + * through the middle of the tween. Simply pass as many objects in the bezier Array as you'd like, + * one for each "control point" (see documentation on Flash's curveTo() drawing method for more + * about how control points work). + * + *

Keep in mind that you can bezier tween ANY properties, not just x/y.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.BezierPlugin; +TweenPlugin.activate([BezierPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 3, new TweenLiteVars().bezier([{x:250, y:50}, {x:500, y:0}])); //makes my_mc travel through 250,50 and end up at 500,0. + + * + * @param values An array of objects with key/value pairs that define the bezier points like [{x:250, y:50}, {x:500, y:0}] + * @see #bezierThrough() + **/ + public function bezier(values:Array):TweenLiteVars { + return _set("bezier", values, true); + } + + /** + * Identical to bezier except that instead of passing Bezier control point values, you pass values through + * which the Bezier values should move. This can be more intuitive than using control points. + * + * @param values An array of objects with key/value pairs that define the bezier points like [{x:250, y:50}, {x:500, y:0}] + * @see #bezier() + **/ + public function bezierThrough(values:Array):TweenLiteVars { + return _set("bezierThrough", values, true); + } + + /** + * Tweens a BlurFilter + * + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param quality The number of times to perform the blur. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new BlurFilter will be added to the target even if a BlurFilter is already in its filters array. + * @param index Allows you to target a particular BlurFilter if there are multiple BlurFilters in the target's filters array - simply define the index value corresponding to the BlurFilter's position in the filters array. + * @return The TweenLiteVars instance + */ + public function blurFilter(blurX:Number, blurY:Number, quality:int=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenLiteVars { + var filter:Object = {blurX:blurX, blurY:blurY, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("blurFilter", filter, true); + } + + /** + * Tweens an object along a CirclePath2D motion path in any direction (clockwise, counter-clockwise, or shortest). + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.~~; +import com.greensock.motionPaths.~~ +TweenPlugin.activate([CirclePath2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +var circle:CirclePath2D = new CirclePath2D(150, 150, 100); +TweenLite.to(mc, 2, new TweenLiteVars().circlePath2D(circle, 90, 270, false, Direction.CLOCKWISE, 2)); + + * + * @param path The CirclePath2D instance to follow (com.greensock.motionPaths.CirclePath2D) + * @param startAngle The position at which the target should begin its rotation (described in degrees unless useRadians is true in which case it is described in radians). For example, to begin at the top of the circle, use 270 or -90 as the startAngle. + * @param endAngle The position at which the target should end its rotation (described in degrees unless useRadians is true in which case it is described in radians). For example, to end at the bottom of the circle, use 90 as the endAngle. + * @param autoRotate When autoRotate is true, the target will automatically be rotated so that it is oriented to the angle of the path. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param direction The direction in which the target should travel around the path. Options are Direction.CLOCKWISE ("clockwise"), Direction.COUNTER_CLOCKWISE ("counterClockwise"), or Direction.SHORTEST ("shortest"). + * @param extraRevolutions If instead of going directly to the endAngle, you want the target to travel one or more extra revolutions around the path before going to the endAngle, define that number of revolutions here. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + * @param useRadians If you prefer to define values in radians instead of degrees, set useRadians to true. + * @return The TweenLiteVars instance + */ + public function circlePath2D(path:MotionPath, startAngle:Number, endAngle:Number, autoRotate:Boolean=false, direction:String="clockwise", extraRevolutions:uint=0, rotationOffset:Number=0, useRadians:Boolean=false):TweenLiteVars { + return _set("circlePath2D", {path:path, startAngle:startAngle, endAngle:endAngle, autoRotate:autoRotate, direction:direction, extraRevolutions:extraRevolutions, rotationOffset:rotationOffset, useRadians:useRadians}, true); + } + + /** + * ColorMatrixFilter tweening offers an easy way to tween a DisplayObject's saturation, hue, contrast, + * brightness, and colorization. + * + *

HINT: If you'd like to match the ColorMatrixFilter values you created in the Flash IDE on a particular object, + * you can get its matrix like this:

+ * + * +import flash.display.DisplayObject; +import flash.filters.ColorMatrixFilter; + +function getColorMatrix(mc:DisplayObject):Array { + var f:Array = mc.filters, i:uint; + for (i = 0; i < f.length; i++) { + if (f[i] is ColorMatrixFilter) { + return f[i].matrix; + } + } + return null; +} + +var myOriginalMatrix:Array = getColorMatrix(my_mc); //store it so you can tween back to it anytime + + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ColorMatrixFilterPlugin; +TweenPlugin.activate([ColorMatrixFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().colorMatrixFilter(0xFF0000)); + + * + * @param colorize The color to use for the colorizing effect - colorizing a DisplayObject makes it look as though you're seeing it through a colored piece of glass whereas tinting it makes every pixel exactly that color. You can control the amount of colorization using the "amount" parameter where 1 is full strength, 0.5 is half-strength, and 0 has no colorization effect. + * @param amount A number between 0 and 1 that determines the potency of the colorize effect. This parameter is ignored if the colorize parameter is left at its default value of 0xFFFFFF. + * @param saturation A number indicating the saturation where 1 is normal saturation, 0 makes the target look grayscale, and 2 would be double the normal saturation. + * @param contrast A number indicating the contrast where 1 is normal contrast, 0 is no contrast, and 2 is double the normal contrast, etc. + * @param brightness A number indicating the brightness where 1 is normal brightness, 0 is much darker than normal, and 2 is twice the normal brightness, etc. + * @param hue An angle-like number between 0 and 360 indicating the change in hue. Think of it as degrees, so 180 would be rotating the hue to be exactly opposite as normal, 360 would be the same as 0, etc. + * @param threshold A number from 0 to 255 that controls the threshold of where the pixels turn white or black (leave as -1 to avoid any threshold effect whatsoever). + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new ColorMatrixFilter will be added to the target even if a ColorMatrixFilter is already in its filters array. + * @param index Allows you to target a particular ColorMatrixFilter if there are multiple ColorMatrixFilters in the target's filters array - simply define the index value corresponding to the ColorMatrixFilter's position in the filters array. + * @return The TweenLiteVars instance + */ + public function colorMatrixFilter(colorize:uint=0xFFFFFF, amount:Number=1, saturation:Number=1, contrast:Number=1, brightness:Number=1, hue:Number=0, threshold:Number=-1, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenLiteVars { + var filter:Object = {saturation:saturation, contrast:contrast, brightness:brightness, hue:hue, addFilter:addFilter, remove:remove}; + if (colorize != 0xFFFFFF) { + filter.colorize = colorize; + filter.amount = amount; + } + if (threshold > -1) { + filter.threshold = threshold; + } + if (index > -1) { + filter.index = index; + } + return _set("colorMatrixFilter", filter, true); + } + + /** + * Tweens ColorTransform properties of a DisplayObject to do advanced effects like overexposing, altering + * the brightness or setting the percent/amount of tint. + * + * @param tint The color value for a ColorTransform object. + * @param tintAmount A numeric value between 0 and 1 indicating the potency of the tint. For example, if tint is 0xFF0000 and tintAmount is 0.5, the target would be tinted halfway to red. + * @param exposure A numeric value between 0 and 2 where 1 is normal exposure, 0, is completely underexposed, and 2 is completely overexposed. Overexposing an object is different then changing the brightness - it seems to almost bleach the image and looks more dynamic and interesting (subjectively speaking). + * @param brightness A numeric value between 0 and 2 where 1 is normal brightness, 0 is completely dark/black, and 2 is completely bright/white + * @param redMultiplier A decimal value that is multiplied with the red channel value. + * @param greenMultiplier A decimal value that is multiplied with the green channel value. + * @param blueMultiplier A decimal value that is multiplied with the blue channel value. + * @param alphaMultiplier A decimal value that is multiplied with the alpha transparency channel value. + * @param redOffset A number from -255 to 255 that is added to the red channel value after it has been multiplied by the redMultiplier value. + * @param greenOffset A number from -255 to 255 that is added to the green channel value after it has been multiplied by the greenMultiplier value. + * @param blueOffset A number from -255 to 255 that is added to the blue channel value after it has been multiplied by the blueMultiplier value. + * @param alphaOffset A number from -255 to 255 that is added to the alpha transparency channel value after it has been multiplied by the alphaMultiplier value. + * @return The TweenLiteVars instance + */ + public function colorTransform(tint:Number=NaN, tintAmount:Number=NaN, exposure:Number=NaN, brightness:Number=NaN, redMultiplier:Number=NaN, greenMultiplier:Number=NaN, blueMultiplier:Number=NaN, alphaMultiplier:Number=NaN, redOffset:Number=NaN, greenOffset:Number=NaN, blueOffset:Number=NaN, alphaOffset:Number=NaN):TweenLiteVars { + var values:Object = {tint:tint, tintAmount:isNaN(tint) ? NaN : tintAmount, exposure:exposure, brightness:brightness, redMultiplier:redMultiplier, greenMultiplier:greenMultiplier, blueMultiplier:blueMultiplier, alphaMultiplier:alphaMultiplier, redOffset:redOffset, greenOffset:greenOffset, blueOffset:blueOffset, alphaOffset:alphaOffset}; + for (var p:String in values) { + if (isNaN(values[p])) { + delete values[p]; + } + } + return _set("colorTransform", values, true); + } + + /** + * Tweens a DropShadowFilter. + * + * @param distance The offset distance for the shadow, in pixels. + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param alpha The alpha transparency value for the shadow color. + * @param angle The angle of the shadow. + * @param color The color of the shadow. + * @param strength The strength of the imprint or spread. + * @param inner Indicates whether or not the shadow is an inner shadow. + * @param knockout Applies a knockout effect (true), which effectively makes the object's fill transparent and reveals the background color of the document. + * @param hideObject Indicates whether or not the object is hidden. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new DropShadowFilter will be added to the target even if a DropShadowFilter is already in its filters array. + * @param index Allows you to target a particular DropShadowFilter if there are multiple DropShadowFilters in the target's filters array - simply define the index value corresponding to the DropShadowFilter's position in the filters array. + * @return The TweenLiteVars instance + */ + public function dropShadowFilter(distance:Number=4, blurX:Number=4, blurY:Number=4, alpha:Number=1, angle:Number=45, color:uint=0x000000, strength:Number=2, inner:Boolean=false, knockout:Boolean=false, hideObject:Boolean=false, quality:uint=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenLiteVars { + var filter:Object = {distance:distance, blurX:blurX, blurY:blurY, alpha:alpha, angle:angle, color:color, strength:strength, inner:inner, knockout:knockout, hideObject:hideObject, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("dropShadowFilter", filter, true); + } + + /** + * If you'd like to tween something to a destination value that may change at any time, + * dynamicProps allows you to simply associate a function with a property so that + * every time the tween is rendered, it calls that function to get the new destination value + * for the associated property. For example, if you want a MovieClip to tween to wherever the + * mouse happens to be, you could do: + * + * +TweenLite.to(mc, 3, new TweenLiteVars().dynamicProps({x:getMouseX, y:getMouseY})); +function getMouseX():Number { + return this.mouseX; +} +function getMouseY():Number { + return this.mouseY; +} + + * + *

Of course you can get as complex as you want inside your custom function, as long as + * it returns the destination value, TweenLite/Max will take care of adjusting things + * on the fly.

+ * + *

You can optionally pass any number of parameters to functions using the "params" + * parameter like so:

+ * + * +TweenLite.to(mc, 3, new TweenLiteVars().dynamicProps({x:myFunction, y:myFunction}, {x:[mc2, "x"], y:[mc2, "y"]})); +function myFunction(object:MovieClip, propName:String):Number { + return object[propName]; +} + + * + *

DynamicPropsPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. + * Visit http://www.greensock.com/club/ to sign up or get + * more details.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.~~; +TweenPlugin.activate([DynamicPropsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(my_mc, 3, new TweenLiteVars().dynamicProps({x:getMouseX, y:getMouseY})); + +function getMouseX():Number { + return this.mouseX; +} +function getMouseY():Number { + return this.mouseY; +} + + * @param props An object containing properties that are named cooresponding to the properties of the target that should be affected, and the value should point to the function that handles returning the appropriate value, like {x:getMouseX, y:getMouseY}. + * @param params An object containing properties that are named corresponding to the properties of the target that should be affected, and the value should be an array of parameters that are passed to the corresponding function, like {x:[mc, "param2"], y:[mc, "param2"]} + * @return self + **/ + public function dynamicProps(props:Object, params:Object=null):TweenLiteVars { + if (params != null) { + props.params = params; + } + return _set("dynamicProps", props, true); + } + + /** An Array containing numeric end values of the target Array. Keep in mind that the target of the tween must be an Array with at least the same length as the endArray. **/ + public function endArray(values:Array):TweenLiteVars { + return _set("endArray", values, true); + } + + /** + * Tweens a MovieClip to a particular frame. + * + *

USAGE:

+ * + * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.FramePlugin; +TweenPlugin.activate([FramePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().frame(125)); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline + * (as "stream") will not be played. Doing so would be impossible because the tween might speed up or slow + * down the MovieClip to any degree.

+ * + * @param value The frame to which the MovieClip should be tweened (or if relative is true, this value would represent the number of frames to travel from the current frame) + * @param relative If true, the frame value will be interpreted as relative to the current frame. So for example, if the MovieClip is at frame 5 currently and frame(10, true) is used, the MovieClip will tween 10 frames and end up on frame 15. + **/ + public function frame(value:int, relative:Boolean=false):TweenLiteVars { + return _set("frame", (relative) ? String(value) : value, true); + } + + /** + * Tweens a MovieClip backward to a particular frame number, wrapping it if/when it reaches the beginning + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 15, a normal frame tween would go forward from 10 to 15, but a frameBackward + * would go from 10 to 1 (the beginning) and wrap to the end and continue tweening from 20 to 15. + **/ + public function frameBackward(frame:int):TweenLiteVars { + return _set("frameBackward", frame, true); + } + + /** + * Tweens a MovieClip forward to a particular frame number, wrapping it if/when it reaches the end + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 5, a normal frame tween would go backwards from 10 to 5, but a frameForward + * would go from 10 to 20 (the end) and wrap to the beginning and continue tweening from 1 to 5. + **/ + public function frameForward(frame:int):TweenLiteVars { + return _set("frameForward", frame, true); + } + + /** Tweens a MovieClip to a particular frame. **/ + public function frameLabel(label:String):TweenLiteVars { + return _set("frameLabel", label, true); + } + + + /** + * Tweens a GlowFilter + * + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param color The color of the glow. + * @param alpha The alpha transparency value for the color. + * @param strength The strength of the imprint or spread. + * @param inner Specifies whether the glow is an inner glow. + * @param knockout Specifies whether the object has a knockout effect. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new GlowFilter will be added to the target even if a GlowFilter is already in its filters array. + * @param index Allows you to target a particular GlowFilter if there are multiple GlowFilters in the target's filters array - simply define the index value corresponding to the GlowFilter's position in the filters array. + * @return The TweenLiteVars instance + */ + public function glowFilter(blurX:Number=10, blurY:Number=10, color:uint=0xFFFFFF, alpha:Number=1, strength:Number=2, inner:Boolean=false, knockout:Boolean=false, quality:uint=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenLiteVars { + var filter:Object = {blurX:blurX, blurY:blurY, color:color, alpha:alpha, strength:strength, inner:inner, knockout:knockout, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("glowFilter", filter, true); + } + + /** + * Although hex colors are technically numbers, if you try to tween them conventionally, + * you'll notice that they don't tween smoothly. To tween them properly, the red, green, and + * blue components must be extracted and tweened independently. The HexColorsPlugin makes it easy. + * To tween a property of your object that's a hex color to another hex color, just pass a hexColors + * Object with properties named the same as your object's hex color properties. For example, + * if myObject has a "myHexColor" property that you'd like to tween to red (0xFF0000) over the + * course of 2 seconds, you'd do: + * + *

+ * TweenMax.to(myObject, 2, new TweenLiteVars().hexColors({myHexColor:0xFF0000})); + *

+ * + *

You can pass in any number of hexColor properties.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.HexColorsPlugin; +TweenPlugin.activate([HexColorsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myObject, 2, new TweenLiteVars().hexColors({myHexColor:0xFF0000})); + + *

Or if you just want to tween a color and apply it somewhere on every frame, you could do:

+ * +var myColor:Object = {hex:0xFF0000}; +TweenLite.to(myColor, 2, new TweenLiteVars().hexColors({hex:0x0000FF}).onUpdate(applyColor)); +function applyColor():void { + mc.graphics.clear(); + mc.graphics.beginFill(myColor.hex, 1); + mc.graphics.drawRect(0, 0, 100, 100); + mc.graphics.endFill(); +} + + * + **/ + public function hexColors(values:Object):TweenLiteVars { + return _set("hexColors", values, true); + } + + + /** + * MotionBlurPlugin provides an easy way to apply a directional blur to a DisplayObject based on its velocity + * and angle of movement in 2D (x/y). This creates a much more realistic effect than a standard BlurFilter for + * several reasons: + *
    + *
  1. A regular BlurFilter is limited to blurring horizontally and/or vertically whereas the motionBlur + * gets applied at the angle at which the object is moving.
  2. + * + *
  3. A BlurFilter tween has static start/end values whereas a motionBlur tween dynamically adjusts the + * values on-the-fly during the tween based on the velocity of the object. So if you use a Strong.easeInOut + * for example, the strength of the blur will start out low, then increase as the object moves faster, and + * reduce again towards the end of the tween.
  4. + *
+ * + *

motionBlur even works on bezier/bezierThrough tweens!

+ * + *

To accomplish the effect, MotionBlurPlugin creates a Bitmap that it places over the original object, changing + * alpha of the original to [almost] zero during the course of the tween. The original DisplayObject still follows the + * course of the tween, so MouseEvents are properly dispatched. You shouldn't notice any loss of interactivity. + * The DisplayObject can also have animated contents - MotionBlurPlugin automatically updates on every frame. + * Be aware, however, that as with most filter effects, MotionBlurPlugin is somewhat CPU-intensive, so it is not + * recommended that you tween large quantities of objects simultaneously. You can activate fastMode + * to significantly speed up rendering if the object's contents and size/color doesn't need to change during the + * course of the tween.

+ * + * @param strength Determines the strength of the blur. The default is 1. For a more powerful blur, increase the number. Or reduce it to make the effect more subtle. + * @param fastMode Setting fastMode to true will significantly improve rendering performance but it is only appropriate for situations when the target object's contents, size, color, filters, etc. do not need to change during the course of the tween. It works by essentially taking a BitmapData snapshot of the target object at the beginning of the tween and then reuses that throughout the tween, blurring it appropriately. The default value for fastMode is false. + * @param quality The lower the quality, the less CPU-intensive the effect will be. Options are 1, 2, or 3. The default is 2. + * @param padding padding controls the amount of space around the edges of the target object that is included in the BitmapData capture (the default is 10 pixels). If the target object has filters applied to it like a GlowFilter or DropShadowFilter that extend beyond the bounds of the object itself, you might need to increase the padding to accommodate the filters. + * @return The TweenLiteVars instance + */ + public function motionBlur(strength:Number=1, fastMode:Boolean=false, quality:int=2, padding:int=10):TweenLiteVars { + return _set("motionBlur", {strength:strength, fastMode:fastMode, quality:quality, padding:padding}, true); + } + + /** + * A common effect that designers/developers want is for a MovieClip/Sprite to orient itself in the direction of + * a Bezier path (alter its rotation). orientToBezier makes it easy. In order to alter a rotation property accurately, + * TweenLite/Max needs 4 pieces of information: + *
    + *
  1. Position property 1 (typically "x")
  2. + *
  3. Position property 2 (typically "y")
  4. + *
  5. Rotational property (typically "rotation")
  6. + *
  7. Number of degrees to add (optional - makes it easy to orient your MovieClip/Sprite properly)
  8. + *
+ * + *

The orientToBezier property should be an Array containing one Array for each set of these values. + * For maximum flexibility, you can pass in any number of Arrays inside the container Array, one for + * each rotational property. This can be convenient when working in 3D because you can rotate on multiple axis. + * If you're doing a standard 2D x/y tween on a bezier, you can simply pass in a boolean value of true and + * TweenMax will use a typical setup, [["x", "y", "rotation", 0]]. + * Hint: Don't forget the container Array (notice the double outer brackets)

+ * + *

To use the default value ([["x", "y", "rotation", 0]]), you can simply leave the values parameter as null.

+ */ + public function orientToBezier(values:Object=null):TweenLiteVars { + return _set("orientToBezier", (values == null) ? true : values, false); + } + + + /** + * Provides simple physics functionality for tweening a DisplayObject's x and y coordinates based on a + * combination of velocity, angle, gravity, acceleration, accelerationAngle, and/or friction. It is not intended + * to replace a full-blown physics engine and does not offer collision detection, but serves + * as a way to easily create interesting physics-based effects with the GreenSock tweening platform. Parameters + * are not intended to be dynamically updateable, but one unique convenience is that everything is reverseable. + * So if you spawn a bunch of particle tweens, for example, and throw them into a TimelineLite, you could + * simply call reverse() on the timeline to watch the particles retrace their steps right back to the beginning. + * Keep in mind that any easing equation you define for your tween will be completely ignored for these properties. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.Physics2DPlugin; +TweenPlugin.activate([Physics2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 2, new TweenLiteVars().physics2D(300, -60, 400)); + + * + *

Physics2DPlugin is a Club GreenSock membership benefit. You must have a valid membership to use this class + * without violating the terms of use. Visit http://www.greensock.com/club/ to sign up or get more details.

+ * + * @param velocity The initial velocity of the object measured in pixels per time unit (usually seconds, but for tweens where useFrames is true, it would be measured in frames). The default is zero. + * @param angle The initial angle (in degrees) at which the object is traveling. Only pertinent when a velocity is defined. For example, if the object should start out traveling at -60 degrees (towards the upper right), the angle would be -60. The default is zero. + * @param acceleration The amount of acceleration applied to the object, measured in pixels per time unit (usually seconds, but for tweens where useFrames is true, it would be measured in frames). To apply the acceleration in a specific direction that is different than the angle, use the accelerationAngle property. + * @param accelerationAngle The angle at which acceleration is applied (if any), measured in degrees. So if, for example, you want the object to accelerate towards the left side of the screen, you'd use an accelerationAngle of 180. + * @param friction A value between 0 and 1 where 0 is no friction, 0.08 is a small amount of friction, and 1 will completely prevent any movement. This is not meant to be precise or scientific in any way, but rather serves as an easy way to apply a friction-like physics effect to your tween. Generally it is best to experiment with this number a bit. Also note that friction requires more processing than physics tweens without any friction. + * @return The TweenLiteVars instance + * @see #physicsProps() + */ + public function physics2D(velocity:Number, angle:Number, acceleration:Number=0, accelerationAngle:Number=90, friction:Number=0):TweenLiteVars { + return _set("physics2D", {velocity:velocity, angle:angle, acceleration:acceleration, accelerationAngle:accelerationAngle, friction:friction}, true); + } + + /** + * Sometimes you want to tween a property (or several) but you don't have a specific end value in mind - instead, + * you'd rather describe the movement in terms of physics concepts, like velocity, acceleration, + * and/or friction. physicsProps allows you to tween any numeric property of any object based + * on these concepts. Keep in mind that any easing equation you define for your tween will be completely + * ignored for these properties. Instead, the physics parameters will determine the movement/easing. + * These parameters, by the way, are not intended to be dynamically updateable, but one unique convenience + * is that everything is reverseable. So if you create several physics-based tweens, for example, and + * throw them into a TimelineLite, you could simply call reverse() on the timeline to watch the objects + * retrace their steps right back to the beginning. Here are the parameters you can define (note that + * friction and acceleration are both completely optional): + *
    + *
  • velocity : Number - the initial velocity of the object measured in units per time + * unit (usually seconds, but for tweens where useFrames is true, it would + * be measured in frames). The default is zero.
  • + *
  • acceleration : Number [optional] - the amount of acceleration applied to the object, measured + * in units per time unit (usually seconds, but for tweens where useFrames + * is true, it would be measured in frames). The default is zero.
  • + *
  • friction : Number [optional] - a value between 0 and 1 where 0 is no friction, 0.08 is a small amount of + * friction, and 1 will completely prevent any movement. This is not meant to be precise or + * scientific in any way, but rather serves as an easy way to apply a friction-like + * physics effect to your tween. Generally it is best to experiment with this number a bit. + * Also note that friction requires more processing than physics tweens without any friction.
  • + *
+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.PhysicsPropsPlugin; +TweenPlugin.activate([PhysicsPropsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 2, new TweenLiteVars().physicsProps({ + x:{velocity:100, acceleration:200}, + y:{velocity:-200, friction:0.1} + } +)); + + * + *

PhysicsPropsPlugin is a Club GreenSock membership benefit. You must have a valid membership to use this class + * without violating the terms of use. Visit http://www.greensock.com/club/ to sign up or get more details.

+ * + * @see #physics2D() + **/ + public function physicsProps(values:Object):TweenLiteVars { + return _set("physicsProps", values, true); + } + + /** An object with properties that correspond to the quaternion properties of the target object. For example, if your my3DObject has "orientation" and "childOrientation" properties that contain quaternions, and you'd like to tween them both, you'd do: {orientation:myTargetQuaternion1, childOrientation:myTargetQuaternion2}. Quaternions must have the following properties: x, y, z, and w. **/ + public function quaternions(values:Object):TweenLiteVars { + return _set("quaternions", values, true); + } + + /** Removes the tint of a DisplayObject over time. **/ + public function removeTint(remove:Boolean=true):TweenLiteVars { + return _set("removeTint", remove, true); + } + + /** + * Tweens the scrollRect property of a DisplayObject. You can define any (or all) of the following properties: + * + *
    + *
  • x : Number
  • + *
  • y : Number
  • + *
  • width : Number
  • + *
  • height : Number
  • + *
  • top : Number
  • + *
  • bottom : Number
  • + *
  • left : Number
  • + *
  • right : Number
  • + *
+ *
+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ScrollRectPlugin; +TweenPlugin.activate([ScrollRectPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().scrollRect({x:50, y:300, width:100, height:100})); + + **/ + public function scrollRect(props:Object):TweenLiteVars { + return _set("scrollRect", props, true); + } + + /** + * Some components require resizing with setSize() instead of standard tweens of width/height in + * order to scale properly. The SetSizePlugin accommodates this easily. You can define the width, + * height, or both. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.SetSizePlugin; +TweenPlugin.activate([SetSizePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myComponent, 1, new TweenLiteVars().setSize(200, 30)); + + **/ + public function setSize(width:Number=NaN, height:Number=NaN):TweenLiteVars { + var values:Object = {}; + if (!isNaN(width)) { + values.width = width; + } + if (!isNaN(height)) { + values.height = height; + } + return _set("setSize", values, true); + } + + /** + * To tween any rotation property of the target object in the shortest direction, use "shortRotation" + * For example, if myObject.rotation is currently 170 degrees and you want to tween it to + * -170 degrees, a normal rotation tween would travel a total of 340 degrees in the counter-clockwise + * direction, but if you use shortRotation, it would travel 20 degrees in the clockwise direction instead. + * You can define any number of rotation properties in the shortRotation object which makes 3D tweening + * easier, like: + * + *

+ * TweenLite.to(mc, 2, new TweenLiteVars().shortRotation({rotationX:-170, rotationY:35, rotationZ:200})); + *

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ShortRotationPlugin; +TweenPlugin.activate([ShortRotationPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().shortRotation({rotation:-170})); + +//or for a 3D tween with multiple rotation values... +TweenLite.to(mc, 1, new TweenLiteVars().shortRotation({rotationX:-170, rotationY:35, rotationZ:10})); + + **/ + public function shortRotation(values:Object):TweenLiteVars { + if (typeof(values) == "number") { + values = {rotation:values}; + } + return _set("shortRotation", values, true); + } + + + /** + * Tweens properties of an object's soundTransform property (like the volume, pan, leftToRight, etc. + * of a MovieClip/SoundChannel/NetStream). + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.SoundTransformPlugin; +TweenPlugin.activate([SoundTransformPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().soundTransform(0.2, 0.5)); + + * + * @param volume The volume, ranging from 0 (silent) to 1 (full volume). + * @param pan The left-to-right panning of the sound, ranging from -1 (full pan left) to 1 (full pan right). + * @param leftToLeft A value, from 0 (none) to 1 (all), specifying how much of the left input is played in the left speaker. + * @param leftToRight A value, from 0 (none) to 1 (all), specifying how much of the left input is played in the right speaker. + * @param rightToLeft A value, from 0 (none) to 1 (all), specifying how much of the right input is played in the left speaker. + * @param rightToRight A value, from 0 (none) to 1 (all), specifying how much of the right input is played in the right speaker. + * @return The TweenLiteVars instance + */ + public function soundTransform(volume:Number=1, pan:Number=0, leftToLeft:Number=1, leftToRight:Number=0, rightToLeft:Number=0, rightToRight:Number=1):TweenLiteVars { + return _set("soundTransform", {volume:volume, pan:pan, leftToLeft:leftToLeft, leftToRight:leftToRight, rightToLeft:rightToLeft, rightToRight:rightToRight}, true); + } + + /** + * Sets the stage's quality to a particular value during a tween and another value after + * the tween which can be useful for improving rendering performance in the Flash Player while things are animating. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.StageQualityPlugin; +import flash.display.StageQuality; +TweenPlugin.activate([StageQualityPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().prop("x", 100).stageQuality(this.stage, StageQuality.LOW, StageQuality.HIGH)); + + * + * @param stage A reference to the stage + * @param during The stage quality that should be used during the tween + * @param after The stage quality that should be set after the tween completes + * @return The TweenLiteVars instance + */ + public function stageQuality(stage:Stage, during:String="medium", after:String=null):TweenLiteVars { + if (after == null) { + after = stage.quality; + } + return _set("stageQuality", {stage:stage, during:during, after:after}, true); + } + + /** + * Allows you to define an initial velocity at which a property (or multiple properties) will start tweening, + * as well as [optional] maximum and/or minimum end values and then it will calculate the appropriate landing + * position and plot a smooth course to it based on the easing equation you define (Quad.easeOut by default, + * as set in TweenLite). This is perfect for flick-scrolling or animating things as though they are being thrown. + * + *

In its simplest form, you can pass just the initial velocity for each property like this: + * {x:500, y:-300}

+ * + *

In the above example, x will animate at 500 pixels per second initially and + * y will animate at -300 pixels per second. Both will decelerate smoothly + * until they come to rest based on the tween's duration.

+ * + *

To impose maximum and minimum boundaries on the end values, use the nested object syntax + * with the max and min special properties like this: + * {x:{velocity:500, max:1024, min:0}, y:{velocity:-300, max:720, min:0}}; + *

+ * + *

Notice the nesting of the objects ({}). The max and min values refer + * to the range for the final resting position (coordinates in this case), NOT the velocity. + * So x would always land between 0 and 1024 in this case, and y + * would always land between 0 and 720. If you want the target object to land on a specific value + * rather than within a range, simply set max and min to identical values. + * Also notice that you must define a velocity value for each property in the object syntax.

+ * + *

throwProps isn't just for tweening x and y coordinates. It works with any numeric + * property, so you could use it for spinning the rotation of an object as well. Or the + * scaleX/scaleY properties. Maybe the user drags to spin a wheel and + * lets go and you want it to continue increasing the rotation at that velocity, + * decelerating smoothly until it stops.

+ * + *

ThrowPropsPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ **/ + public function throwProps(props:Object):TweenLiteVars { + return _set("throwProps", props, true); + } + + /** + * To change a DisplayObject's tint, set this to the hex value of the color you'd like the DisplayObject + * to end up at (or begin at if you're using TweenLite.from()). An example hex value would be 0xFF0000. + * If you'd like to remove the tint from a DisplayObject, use the removeTint special property. + * @see #removeTint() + * @see #colorMatrixFilter() + * @see #colorTransform() + **/ + public function tint(color:uint):TweenLiteVars { + return _set("tint", color, true); + } + + /** + * Normally, all transformations (scale, rotation, and position) are based on the DisplayObject's registration + * point (most often its upper left corner), but TransformAroundCenter allows you to make the transformations + * occur around the DisplayObject's center. + * + *

If you define an x or y value in the transformAroundCenter object, it will correspond to the center which + * makes it easy to position (as opposed to having to figure out where the original registration point + * should tween to). If you prefer to define the x/y in relation to the original registration point, do so outside + * the transformAroundCenter object, like:

+ * + *

+ * TweenLite.to(mc, 3, new TweenLiteVars().prop("x", 50).prop("y", 40).transformAroundCenter({scale:0.5, rotation:30})); + *

+ * + *

TransformAroundCenterPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.TransformAroundCenterPlugin; +TweenPlugin.activate([TransformAroundCenterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().transformAroundCenter({scale:1.5, rotation:150})); + + * @see #transformAroundPoint() + **/ + public function transformAroundCenter(props:Object):TweenLiteVars { + return _set("transformAroundCenter", props, true); + } + + /** + * Normally, all transformations (scale, rotation, and position) are based on the DisplayObject's registration + * point (most often its upper left corner), but TransformAroundPoint allows you to define ANY point around which + * transformations will occur during the tween. For example, you may have a dynamically-loaded image that you + * want to scale from its center or rotate around a particular point on the stage. + * + *

If you define an x or y value in the transformAroundPoint object, it will correspond to the custom registration + * point which makes it easy to position (as opposed to having to figure out where the original registration point + * should tween to). If you prefer to define the x/y in relation to the original registration point, do so outside + * the transformAroundPoint object, like:

+ * + *

+ * TweenLite.to(mc, 3, new TweenLiteVars().prop("x", 50).prop("y", 40).transformAroundPoint(new Point(200, 300), {scale:0.5, rotation:30})); + *

+ * + *

TransformAroundPointPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.TransformAroundPointPlugin; +TweenPlugin.activate([TransformAroundPointPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().transformAroundPoint(new Point(100, 300), {scaleX:2, scaleY:1.5, rotation:150})); + + * @see #transformAroundCenter() + **/ + public function transformAroundPoint(point:Point, props:Object):TweenLiteVars { + props.point = point; + return _set("transformAroundPoint", props, true); + } + + /** + * transformMatrix tweens a DisplayObject's transform.matrix values directly either using + * the standard matrix properties (a, b, c, d, tx, and ty) or common properties + * like x, y, scaleX, scaleY, skewX, skewY, rotation and even shortRotation. + * To skew without adjusting scale visually, use skewX2 and skewY2 instead of skewX and skewY. + * + * + *

transformMatrix tween will affect all of the DisplayObject's transform properties, so do not use + * it in conjunction with regular x/y/scaleX/scaleY/rotation tweens concurrently.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.data.TweenLiteVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.TransformMatrixPlugin; +TweenPlugin.activate([TransformMatrixPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, new TweenLiteVars().transformMatrix({x:50, y:300, scaleX:2, scaleY:2})); + +//-OR- + +TweenLite.to(mc, 1, new TweenLiteVars().transformMatrix({tx:50, ty:300, a:2, d:2})); + + **/ + public function transformMatrix(properties:Object):TweenLiteVars { + return _set("transformMatrix", properties, true); + } + + /** Sets a DisplayObject's "visible" property at the end of the tween. **/ + public function visible(value:Boolean):TweenLiteVars { + return _set("visible", value, true); + } + + /** Changes the volume of any object that has a soundTransform property (MovieClip, SoundChannel, NetStream, etc.) **/ + public function volume(volume:Number):TweenLiteVars { + return _set("volume", volume, true); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------------------------------------------- + + /** The generic object populated by all of the method calls in the TweenLiteVars instance. This is the raw data that gets passed to the tween. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get _isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/data/TweenMaxVars.as b/src/com/greensock/data/TweenMaxVars.as new file mode 100644 index 0000000..45fac00 --- /dev/null +++ b/src/com/greensock/data/TweenMaxVars.as @@ -0,0 +1,1285 @@ +/** + * VERSION: 12.01 + * DATE: 2012-09-11 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/tweenvars/ + **/ +package com.greensock.data { + import com.greensock.TweenLite; + import com.greensock.TweenMax; + import com.greensock.motionPaths.MotionPath; + + import flash.display.Stage; + import flash.geom.Point; +/** + * [AS3 only] There are 3 primary benefits of using a TweenMaxVars instance to define your TweenMax's "vars" parameter: + *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available.
  2. + *
  3. It allows you to code using strict data typing which can improve debugging.
  4. + *
  5. It will trace() a warning if you forgot to activate a particular plugin. For example, if you define an autoAlpha value in a TweenMaxVars instance but you didn't activate() the plugin, you'll see a trace() output when you test/compile the file (an Error isn't thrown because in some very rare circumstances it can be perfectly legitimate to avoid activating the plugin)
  6. + *
+ * + *

The down side, of course, is that the code is more verbose and TweenMaxVars adds about 6kb to your published swf.

+ * + *

USAGE:

+ *

Note that each method returns the TweenMaxVars object, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without TweenMaxVars:

+ *

TweenMax.to(mc, 1, {x:300, y:100, tint:0xFF0000, onComplete:myFunction, onCompleteParams:[mc]})

+ * + *

With TweenMaxVars

+ *

TweenMax.to(mc, 1, new TweenMaxVars().move(300, 100).tint(0xFF0000).onComplete(myFunction, [mc]));

+ * + *

You can use the prop() method to set individual generic properties (like "myCustomProperty" or "rotationY") or you can + * pass a generic Object into the constructor to make it a bit more concise, like this:

+ * + *

TweenMax.to(mc, 1, new TweenMaxVars({myCustomProperty:300, rotationY:100}).tint(0xFF0000).onComplete(myFunction, [mc]));

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that TweenMaxVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * TweenMax.to(mc, 1, new TweenMaxVars({x:300, y:100}).tint(0xFF0000).onComplete(myFunction, [mc]).vars);
  • + *
  • This class adds about 6kb to your published SWF (not including TweenMax or any plugins).
  • + *
  • Using TweenMaxVars is completely optional. If you prefer the shorter generic object synatax, feel + * free to use it. The purpose of this utility is simply to enable code hinting and to allow for strict datatyping.
  • + *
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenMaxVars { + /** @private **/ + public static const version:String = "12.0.0"; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like added (copied) to this TweenMaxVars instance. This is particularly useful for generic properties that don't have a corresponding method for setting the values (although you can use it for properties that do have corresponding methods too). For example, to tween the x and y properties of a DisplayObject, new TweenMaxVars({x:300, y:0}) + */ + public function TweenMaxVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*, requirePlugin:Boolean=false):TweenMaxVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + if (requirePlugin && !(property in TweenLite._plugins)) { + trace("WARNING: you must activate() the " + property + " plugin in order for the feature to work in TweenMax. See http://www.greensock.com/tweenlite/#plugins for details."); + } + return this; + } + + /** + * Adds a dynamic property for tweening and allows you to indicate whether the value is relative or not. + * For example, to tween "x" to 50 less than whatever it currently is: + * + *

prop("x", -50, true);

+ * + * @param property Property name + * @param value Numeric end value (or beginning value for from() tweens) + * @param relative If true, the value will be interpreted as relative to the target's current value. For example, if mc.x is currently 300 and you do prop("x", 200, true), the end value will be 500. + */ + public function prop(property:String, value:Number, relative:Boolean=false):TweenMaxVars { + return _set(property, (!relative) ? value : (value < 0) ? "-=" + (-value) : "+=" + value); + } + + +//---- BUILT-IN SPECIAL PROPERTIES (NO PLUGIN ACTIVATION REQUIRED) -------------------------------------------------------------- + + /** Any generic data that you'd like associated with your tween. **/ + public function data(data:*):TweenMaxVars { + return _set("data", data); + } + + /** The number of seconds (or frames for frames-based tweens) to delay before the tween begins. **/ + public function delay(delay:Number):TweenMaxVars { + return _set("delay", delay); + } + + /** + * Controls the rate of change. Use any standard easing equation like ElasticOut.ease. The Default is QuadOut.ease. + * + * @param ease An ease (i.e. com.greensock.easing.ElasticOut.ease) The default is QuadOut.ease. + * @param easeParams An Array of extra parameter values to feed the easing equation (beyond the standard 4). This can be useful with easing equations like Elastic that accept extra parameters like the amplitude and period. Most easing equations, however, don't accept extra parameters so you won't need to pass in any easeParams. + **/ + public function ease(ease:*, easeParams:Array=null):TweenMaxVars { + _set("easeParams", easeParams); + return _set("ease", ease); + } + + /** + * Normally when you create a tween, it begins rendering on the very next frame (when + * the Flash Player dispatches an ENTER_FRAME event) unless you specify a delay. + * This allows you to insert tweens into timelines and perform other actions that may affect + * its timing. However, if you prefer to force the tween to render immediately when it is + * created, set immediateRender to true. from() tweens + * render immediately by default, so to prevent that behavior, set immediateRender + * to false. + **/ + public function immediateRender(value:Boolean):TweenMaxVars { + return _set("immediateRender", value, false); + } + + /** + * A function that should be called when the tween has completed. + * + * @param func A function that should be called when the tween has completed. + * @param params An Array of parameters to pass the onComplete function + **/ + public function onComplete(func:Function, params:Array=null):TweenMaxVars { + _set("onCompleteParams", params); + return _set("onComplete", func); + } + + /** + * A function that should be called after the tween has completed and rendered its final state to the stage (waits for the next ENTER_FRAME event is dispatched after the tween finishes). Target must be a DisplayObject. + * + * @param func A function that should be called after the tween has completed and rendered its final state to the stage (waits for the next ENTER_FRAME event is dispatched after the tween finishes). + * @param params An Array of parameters to pass the onCompleteRender function + **/ + public function onCompleteRender(func:Function, params:Array=null):TweenMaxVars { + _set("onCompleteRenderParams", params); + return _set("onCompleteRender", func, true); + } + + /** A function to which the TweenMax instance should dispatch a TweenEvent when it completes. This is the same as doing myTween.addEventListener(TweenEvent.COMPLETE, myFunction); **/ + public function onCompleteListener(func:Function):TweenMaxVars { + return _set("onCompleteListener", func); + } + + /** + * A function that should be called when the tween begins (when its time() is at 0 + * and changes to some other value which can happen more than once if the tween is restarted multiple times). + * + * @param func A function that should be called when the tween begins. + * @param params An Array of parameters to pass the onStart function. + **/ + public function onStart(func:Function, params:Array=null):TweenMaxVars { + _set("onStartParams", params); + return _set("onStart", func); + } + + /** A function to which the TweenMax instance should dispatch a TweenEvent when it begins. This is the same as doing myTween.addEventListener(TweenEvent.START, myFunction); **/ + public function onStartListener(func:Function):TweenMaxVars { + return _set("onStartListener", func) + } + + /** + * A function to call whenever the tweening values are updated (on every frame during the time the tween is active). + * + * @param func A function to call whenever the tweening values are updated. + * @param params An Array of parameters to pass the onUpdate function + **/ + public function onUpdate(func:Function, params:Array=null):TweenMaxVars { + _set("onUpdateParams", params); + return _set("onUpdate", func); + } + + /** A function to which the TweenMax instance should dispatch a TweenEvent every time it updates values. This is the same as doing myTween.addEventListener(TweenEvent.UPDATE, myFunction); **/ + public function onUpdateListener(func:Function):TweenMaxVars { + return _set("onUpdateListener", func); + } + + /** + * A function that should be called every time the tween repeats + * + * @param func A function that should be called every time the tween repeats + * @param params An Array of parameters to pass the onRepeat function + **/ + public function onRepeat(func:Function, params:Array=null):TweenMaxVars { + _set("onRepeatParams", params); + return _set("onRepeat", func); + } + + /** + * A function that should be called when the tween has reached its starting point again after having been reversed. + * + * @param func A function that should be called when the tween has reached its starting point again after having been reversed. + * @param params An Array of parameters to pass the onReverseComplete function + **/ + public function onReverseComplete(func:Function, params:Array=null):TweenMaxVars { + _set("onReverseCompleteParams", params); + return _set("onReverseComplete", func); + } + + /** A function to which the TweenMax instance should dispatch a TweenEvent when it has reached its starting point again after having been reversed **/ + public function onReverseCompleteListener(func:Function):TweenMaxVars { + return _set("onReverseCompleteListener", func); + } + + /** + * Controls how (and if) other tweens of the same target are overwritten. + * There are several modes to choose from, but "auto" is the default (although + * you can change the default mode using the TweenLite.defaultOverwrite property): + *
    + *
  • "none" - no overwriting will occur.
  • + * + *
  • "all" - immediately overwrites all existing + * tweens of the same target even if they haven't started yet or don't have + * conflicting properties.
  • + * + *
  • "auto" - when the tween renders for the first time, it will analyze + * tweens of the same target that are currently active/running and only overwrite + * individual tweening properties that overlap/conflict. Tweens that haven't begun + * yet are ignored. For example, if another active tween is found that is tweening + * 3 properties, only 1 of which it shares in common with the new tween, the other + * 2 properties will be left alone. Only the conflicting property gets overwritten/killed. + * This is the default mode and typically the most intuitive for developers.
  • + * + *
  • "concurrent" - when the tween renders for the first time, it kills + * only the active (in-progress) tweens of the same target regardless of whether + * or not they contain conflicting properties. Like a mix of "all" + * and "auto". Good for situations where you only want one tween + * controling the target at a time.
  • + * + *
  • "allOnStart" - Identical to "all" but waits to run + * the overwrite logic until the tween begins (after any delay). Kills + * tweens of the same target even if they don't contain conflicting properties + * or haven't started yet.
  • + * + *
  • "preexisting" - when the tween renders for the first time, it kills + * only the tweens of the same target that existed BEFORE this tween was created + * regardless of their scheduled start times. So, for example, if you create a tween + * with a delay of 10 and then a tween with a delay of 1 and then a tween with a + * delay of 2 (all of the same target), the 2nd tween would overwrite the first + * but not the second even though scheduling might seem to dictate otherwise. + * "preexisting" only cares about the order in which the instances + * were actually created. This can be useful when the order in which your code runs + * plays a critical role.
  • + *
+ **/ + public function overwrite(value:String):TweenMaxVars { + return _set("overwrite", value, false); + } + + /** Controls the paused state of the tween - if true, the tween will be paused initially. **/ + public function paused(value:Boolean):TweenMaxVars { + return _set("paused", value, false); + } + + /** Number of times that the tween should repeat (to repeat indefinitely, use -1). **/ + public function repeat(value:int):TweenMaxVars { + return _set("repeat", value); + } + + /** Amount of time in seconds (or frames for frames-based tween) between repeats. **/ + public function repeatDelay(value:Number):TweenMaxVars { + return _set("repeatDelay", value); + } + + /** + * If true, the tween will be reversed initially. This does not swap the starting/ending + * values in the tween - it literally changes its orientation/direction. Imagine the playhead + * moving backwards instead of forwards. This does NOT force it to the very end and start + * playing backwards. It simply affects the orientation of the tween, so if reversed is set to + * true initially, it will appear not to play because it is already at the beginning. To cause it to + * play backwards from the end, set reversed to true and then set the currentProgress + * property to 1 immediately after creating the tween (or set the currentTime to the duration). + **/ + public function reversed(value:Boolean):TweenMaxVars { + return _set("reversed", value); + } + + /** When true, the tween will flip the start and end values which is exactly what TweenMax.from() does. **/ + public function runBackwards(value:Boolean):TweenMaxVars { + return _set("runBackwards", value, false); + } + + /** Multiplier affecting the speed of the timeline where 1 is normal speed, 0.5 is half-speed, 2 is double speed, etc. **/ + public function timeScale(value:Number):TweenMaxVars { + return _set("timeScale", value, false); + } + + /** + * If useFrames is set to true, the tweens's timing mode will be based on frames. + * Otherwise, it will be based on seconds/time. NOTE: a tween's timing mode is always + * determined by its parent timeline. + **/ + public function useFrames(value:Boolean):TweenMaxVars { + return _set("useFrames", value, false); + } + + /** Allows you to define the starting values for properties. It is the same as defining the "from" parameter in a TweenMax.fromTo() call. **/ + public function startAt(vars:TweenMaxVars):TweenMaxVars { + return _set("startAt", vars.vars); + } + + /** + * Works in conjunction with the repeat property, determining the behavior of each + * cycle. When yoyo is true, the tween will go back and forth, appearing to reverse + * every other cycle (this has no affect on the "reversed" property though). So if repeat is + * 2 and yoyo is false, it will look like: start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But + * if repeat is 2 and yoyo is true, it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end. + **/ + public function yoyo(value:Boolean):TweenMaxVars { + return _set("yoyo", value); + } + + //---- COMMON CONVENIENCE PROPERTIES (NO PLUGIN REQUIRED) ------------------------------------------------------------------- + + /** Tweens the "x" and "y" properties of the target **/ + public function move(x:Number, y:Number, relative:Boolean=false):TweenMaxVars { + prop("x", x, relative); + return prop("y", y, relative); + } + + /** Tweens the "scaleX" and "scaleY" properties of the target **/ + public function scale(value:Number, relative:Boolean=false):TweenMaxVars { + prop("scaleX", value, relative); + return prop("scaleY", value, relative); + } + + /** Tweens the "rotation" property of the target **/ + public function rotation(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("rotation", value, relative); + } + + /** Tweens the "scaleX" property of the target **/ + public function scaleX(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("scaleX", value, relative); + } + + /** Tweens the "scaleY" property of the target **/ + public function scaleY(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("scaleY", value, relative); + } + + /** Tweens the "width" property of the target **/ + public function width(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("width", value, relative); + } + + /** Tweens the "height" property of the target **/ + public function height(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("height", value, relative); + } + + /** Tweens the "x" property of the target **/ + public function x(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("x", value, relative); + } + + /** Tweens the "y" property of the target **/ + public function y(value:Number, relative:Boolean=false):TweenMaxVars { + return prop("y", value, relative); + } + + + +//---- PLUGIN REQUIRED ------------------------------------------------------------------------------------------- + + /** Same as changing the "alpha" property but with the additional feature of toggling the "visible" property to false whenever alpha is 0, thus improving rendering performance in the Flash Player. **/ + public function autoAlpha(alpha:Number):TweenMaxVars { + return _set("autoAlpha", alpha, true); + } + + /** + * Tweens a BevelFilter + * + * @param distance The offset distance of the bevel. + * @param angle The angle of the bevel. + * @param highlightColor The highlight color of the bevel. + * @param highlightAlpha The alpha transparency value of the highlight color. + * @param shadowColor The shadow color of the bevel. + * @param shadowAlpha The alpha transparency value of the shadow color. + * @param blurX The amount of horizontal blur, in pixels. + * @param blurY The amount of vertical blur, in pixels. + * @param strength The strength of the imprint or spread. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new BevelFilter will be added to the target even if a BevelFilter is already in its filters array. + * @param index Allows you to target a particular BevelFilter if there are multiple BevelFilters in the target's filters array - simply define the index value corresponding to the BevelFilter's position in the filters array. + * @return The TweenMaxVars instance + */ + public function bevelFilter(distance:Number=4, angle:Number=45, highlightColor:uint=0xFFFFFF, highlightAlpha:Number=0.5, shadowColor:uint=0x000000, shadowAlpha:Number=0.5, blurX:Number=4, blurY:Number=4, strength:Number=1, quality:int=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenMaxVars { + var filter:Object = {distance:distance, angle:angle, highlightColor:highlightColor, highlightAlpha:highlightAlpha, shadowColor:shadowColor, shadowAlpha:shadowAlpha, blurX:blurX, blurY:blurY, strength:strength, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("bevelFilter", filter, true); + } + + /** + * Bezier tweening allows you to tween in a non-linear way. For example, you may want to tween + * a MovieClip's position from the origin (0,0) 500 pixels to the right (500,0) but curve downwards + * through the middle of the tween. Simply pass as many objects in the bezier Array as you'd like, + * one for each "control point" (see documentation on Flash's curveTo() drawing method for more + * about how control points work). + * + *

Keep in mind that you can bezier tween ANY properties, not just x/y.

+ * + *

USAGE:

+ * +import com.greensock.TweenMax; +import com.greensock.data.TweenMaxVars; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.BezierPlugin; +TweenPlugin.activate([BezierPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenMax.to(mc, 3, new TweenMaxVars().bezier([{x:250, y:50}, {x:500, y:0}])); //makes my_mc travel through 250,50 and end up at 500,0. + + * + * @param values An array of objects with key/value pairs that define the bezier points like [{x:250, y:50}, {x:500, y:0}] + * @see #bezierThrough() + **/ + public function bezier(values:Array):TweenMaxVars { + return _set("bezier", values, true); + } + + /** + * Identical to bezier except that instead of passing Bezier control point values, you pass values through + * which the Bezier values should move. This can be more intuitive than using control points. + * + * @param values An array of objects with key/value pairs that define the bezier points like [{x:250, y:50}, {x:500, y:0}] + * @see #bezier() + **/ + public function bezierThrough(values:Array):TweenMaxVars { + return _set("bezierThrough", values, true); + } + + /** + * Tweens a BlurFilter + * + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param quality The number of times to perform the blur. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new BlurFilter will be added to the target even if a BlurFilter is already in its filters array. + * @param index Allows you to target a particular BlurFilter if there are multiple BlurFilters in the target's filters array - simply define the index value corresponding to the BlurFilter's position in the filters array. + * @return The TweenMaxVars instance + */ + public function blurFilter(blurX:Number, blurY:Number, quality:int=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenMaxVars { + var filter:Object = {blurX:blurX, blurY:blurY, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("blurFilter", filter, true); + } + + /** + * Tweens an object along a CirclePath2D motion path in any direction (clockwise, counter-clockwise, or shortest). + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.~~; + import com.greensock.motionPaths.~~ + TweenPlugin.activate([CirclePath2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + var circle:CirclePath2D = new CirclePath2D(150, 150, 100); + TweenMax.to(mc, 2, new TweenMaxVars().circlePath2D(circle, 90, 270, false, Direction.CLOCKWISE, 2)); + + * + * @param path The CirclePath2D instance to follow (com.greensock.motionPaths.CirclePath2D) + * @param startAngle The position at which the target should begin its rotation (described in degrees unless useRadians is true in which case it is described in radians). For example, to begin at the top of the circle, use 270 or -90 as the startAngle. + * @param endAngle The position at which the target should end its rotation (described in degrees unless useRadians is true in which case it is described in radians). For example, to end at the bottom of the circle, use 90 as the endAngle. + * @param autoRotate When autoRotate is true, the target will automatically be rotated so that it is oriented to the angle of the path. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param direction The direction in which the target should travel around the path. Options are Direction.CLOCKWISE ("clockwise"), Direction.COUNTER_CLOCKWISE ("counterClockwise"), or Direction.SHORTEST ("shortest"). + * @param extraRevolutions If instead of going directly to the endAngle, you want the target to travel one or more extra revolutions around the path before going to the endAngle, define that number of revolutions here. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + * @param useRadians If you prefer to define values in radians instead of degrees, set useRadians to true. + * @return The TweenMaxVars instance + */ + public function circlePath2D(path:MotionPath, startAngle:Number, endAngle:Number, autoRotate:Boolean=false, direction:String="clockwise", extraRevolutions:uint=0, rotationOffset:Number=0, useRadians:Boolean=false):TweenMaxVars { + return _set("circlePath2D", {path:path, startAngle:startAngle, endAngle:endAngle, autoRotate:autoRotate, direction:direction, extraRevolutions:extraRevolutions, rotationOffset:rotationOffset, useRadians:useRadians}, true); + } + + /** + * ColorMatrixFilter tweening offers an easy way to tween a DisplayObject's saturation, hue, contrast, + * brightness, and colorization. + * + *

HINT: If you'd like to match the ColorMatrixFilter values you created in the Flash IDE on a particular object, + * you can get its matrix like this:

+ * + * + import flash.display.DisplayObject; + import flash.filters.ColorMatrixFilter; + + function getColorMatrix(mc:DisplayObject):Array { + var f:Array = mc.filters, i:uint; + for (i = 0; i < f.length; i++) { + if (f[i] is ColorMatrixFilter) { + return f[i].matrix; + } + } + return null; + } + + var myOriginalMatrix:Array = getColorMatrix(my_mc); //store it so you can tween back to it anytime + + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.ColorMatrixFilterPlugin; + TweenPlugin.activate([ColorMatrixFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().colorMatrixFilter(0xFF0000)); + + * + * @param colorize The color to use for the colorizing effect - colorizing a DisplayObject makes it look as though you're seeing it through a colored piece of glass whereas tinting it makes every pixel exactly that color. You can control the amount of colorization using the "amount" parameter where 1 is full strength, 0.5 is half-strength, and 0 has no colorization effect. + * @param amount A number between 0 and 1 that determines the potency of the colorize effect. This parameter is ignored if the colorize parameter is left at its default value of 0xFFFFFF. + * @param saturation A number indicating the saturation where 1 is normal saturation, 0 makes the target look grayscale, and 2 would be double the normal saturation. + * @param contrast A number indicating the contrast where 1 is normal contrast, 0 is no contrast, and 2 is double the normal contrast, etc. + * @param brightness A number indicating the brightness where 1 is normal brightness, 0 is much darker than normal, and 2 is twice the normal brightness, etc. + * @param hue An angle-like number between 0 and 360 indicating the change in hue. Think of it as degrees, so 180 would be rotating the hue to be exactly opposite as normal, 360 would be the same as 0, etc. + * @param threshold A number from 0 to 255 that controls the threshold of where the pixels turn white or black (leave as -1 to avoid any threshold effect whatsoever). + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new ColorMatrixFilter will be added to the target even if a ColorMatrixFilter is already in its filters array. + * @param index Allows you to target a particular ColorMatrixFilter if there are multiple ColorMatrixFilters in the target's filters array - simply define the index value corresponding to the ColorMatrixFilter's position in the filters array. + * @return The TweenMaxVars instance + */ + public function colorMatrixFilter(colorize:uint=0xFFFFFF, amount:Number=1, saturation:Number=1, contrast:Number=1, brightness:Number=1, hue:Number=0, threshold:Number=-1, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenMaxVars { + var filter:Object = {saturation:saturation, contrast:contrast, brightness:brightness, hue:hue, addFilter:addFilter, remove:remove}; + if (colorize != 0xFFFFFF) { + filter.colorize = colorize; + filter.amount = amount; + } + if (threshold > -1) { + filter.threshold = threshold; + } + if (index > -1) { + filter.index = index; + } + return _set("colorMatrixFilter", filter, true); + } + + /** + * Tweens ColorTransform properties of a DisplayObject to do advanced effects like overexposing, altering + * the brightness or setting the percent/amount of tint. + * + * @param tint The color value for a ColorTransform object. + * @param tintAmount A numeric value between 0 and 1 indicating the potency of the tint. For example, if tint is 0xFF0000 and tintAmount is 0.5, the target would be tinted halfway to red. + * @param exposure A numeric value between 0 and 2 where 1 is normal exposure, 0, is completely underexposed, and 2 is completely overexposed. Overexposing an object is different then changing the brightness - it seems to almost bleach the image and looks more dynamic and interesting (subjectively speaking). + * @param brightness A numeric value between 0 and 2 where 1 is normal brightness, 0 is completely dark/black, and 2 is completely bright/white + * @param redMultiplier A decimal value that is multiplied with the red channel value. + * @param greenMultiplier A decimal value that is multiplied with the green channel value. + * @param blueMultiplier A decimal value that is multiplied with the blue channel value. + * @param alphaMultiplier A decimal value that is multiplied with the alpha transparency channel value. + * @param redOffset A number from -255 to 255 that is added to the red channel value after it has been multiplied by the redMultiplier value. + * @param greenOffset A number from -255 to 255 that is added to the green channel value after it has been multiplied by the greenMultiplier value. + * @param blueOffset A number from -255 to 255 that is added to the blue channel value after it has been multiplied by the blueMultiplier value. + * @param alphaOffset A number from -255 to 255 that is added to the alpha transparency channel value after it has been multiplied by the alphaMultiplier value. + * @return The TweenMaxVars instance + */ + public function colorTransform(tint:Number=NaN, tintAmount:Number=NaN, exposure:Number=NaN, brightness:Number=NaN, redMultiplier:Number=NaN, greenMultiplier:Number=NaN, blueMultiplier:Number=NaN, alphaMultiplier:Number=NaN, redOffset:Number=NaN, greenOffset:Number=NaN, blueOffset:Number=NaN, alphaOffset:Number=NaN):TweenMaxVars { + var values:Object = {tint:tint, tintAmount:isNaN(tint) ? NaN : tintAmount, exposure:exposure, brightness:brightness, redMultiplier:redMultiplier, greenMultiplier:greenMultiplier, blueMultiplier:blueMultiplier, alphaMultiplier:alphaMultiplier, redOffset:redOffset, greenOffset:greenOffset, blueOffset:blueOffset, alphaOffset:alphaOffset}; + for (var p:String in values) { + if (isNaN(values[p])) { + delete values[p]; + } + } + return _set("colorTransform", values, true); + } + + /** + * Tweens a DropShadowFilter. + * + * @param distance The offset distance for the shadow, in pixels. + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param alpha The alpha transparency value for the shadow color. + * @param angle The angle of the shadow. + * @param color The color of the shadow. + * @param strength The strength of the imprint or spread. + * @param inner Indicates whether or not the shadow is an inner shadow. + * @param knockout Applies a knockout effect (true), which effectively makes the object's fill transparent and reveals the background color of the document. + * @param hideObject Indicates whether or not the object is hidden. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new DropShadowFilter will be added to the target even if a DropShadowFilter is already in its filters array. + * @param index Allows you to target a particular DropShadowFilter if there are multiple DropShadowFilters in the target's filters array - simply define the index value corresponding to the DropShadowFilter's position in the filters array. + * @return The TweenMaxVars instance + */ + public function dropShadowFilter(distance:Number=4, blurX:Number=4, blurY:Number=4, alpha:Number=1, angle:Number=45, color:uint=0x000000, strength:Number=2, inner:Boolean=false, knockout:Boolean=false, hideObject:Boolean=false, quality:uint=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenMaxVars { + var filter:Object = {distance:distance, blurX:blurX, blurY:blurY, alpha:alpha, angle:angle, color:color, strength:strength, inner:inner, knockout:knockout, hideObject:hideObject, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("dropShadowFilter", filter, true); + } + + /** + * If you'd like to tween something to a destination value that may change at any time, + * dynamicProps allows you to simply associate a function with a property so that + * every time the tween is rendered, it calls that function to get the new destination value + * for the associated property. For example, if you want a MovieClip to tween to wherever the + * mouse happens to be, you could do: + * + * + TweenMax.to(mc, 3, new TweenMaxVars().dynamicProps({x:getMouseX, y:getMouseY})); + function getMouseX():Number { + return this.mouseX; + } + function getMouseY():Number { + return this.mouseY; + } + + * + *

Of course you can get as complex as you want inside your custom function, as long as + * it returns the destination value, TweenLite/Max will take care of adjusting things + * on the fly.

+ * + *

You can optionally pass any number of parameters to functions using the "params" + * parameter like so:

+ * + * + TweenMax.to(mc, 3, new TweenMaxVars().dynamicProps({x:myFunction, y:myFunction}, {x:[mc2, "x"], y:[mc2, "y"]})); + function myFunction(object:MovieClip, propName:String):Number { + return object[propName]; + } + + * + *

DynamicPropsPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. + * Visit http://www.greensock.com/club/ to sign up or get + * more details.

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.~~; + TweenPlugin.activate([DynamicPropsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(my_mc, 3, new TweenMaxVars().dynamicProps({x:getMouseX, y:getMouseY})); + + function getMouseX():Number { + return this.mouseX; + } + function getMouseY():Number { + return this.mouseY; + } + + * @param props An object containing properties that are named cooresponding to the properties of the target that should be affected, and the value should point to the function that handles returning the appropriate value, like {x:getMouseX, y:getMouseY}. + * @param params An object containing properties that are named corresponding to the properties of the target that should be affected, and the value should be an array of parameters that are passed to the corresponding function, like {x:[mc, "param2"], y:[mc, "param2"]} + * @return self + **/ + public function dynamicProps(props:Object, params:Object=null):TweenMaxVars { + if (params != null) { + props.params = params; + } + return _set("dynamicProps", props, true); + } + + /** An Array containing numeric end values of the target Array. Keep in mind that the target of the tween must be an Array with at least the same length as the endArray. **/ + public function endArray(values:Array):TweenMaxVars { + return _set("endArray", values, true); + } + + /** + * Tweens a MovieClip to a particular frame. + * + *

USAGE:

+ * + * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.FramePlugin; + TweenPlugin.activate([FramePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().frame(125)); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline + * (as "stream") will not be played. Doing so would be impossible because the tween might speed up or slow + * down the MovieClip to any degree.

+ * + * @param value The frame to which the MovieClip should be tweened (or if relative is true, this value would represent the number of frames to travel from the current frame) + * @param relative If true, the frame value will be interpreted as relative to the current frame. So for example, if the MovieClip is at frame 5 currently and frame(10, true) is used, the MovieClip will tween 10 frames and end up on frame 15. + **/ + public function frame(value:int, relative:Boolean=false):TweenMaxVars { + return _set("frame", (relative) ? String(value) : value, true); + } + + /** + * Tweens a MovieClip backward to a particular frame number, wrapping it if/when it reaches the beginning + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 15, a normal frame tween would go forward from 10 to 15, but a frameBackward + * would go from 10 to 1 (the beginning) and wrap to the end and continue tweening from 20 to 15. + **/ + public function frameBackward(frame:int):TweenMaxVars { + return _set("frameBackward", frame, true); + } + + /** + * Tweens a MovieClip forward to a particular frame number, wrapping it if/when it reaches the end + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 5, a normal frame tween would go backwards from 10 to 5, but a frameForward + * would go from 10 to 20 (the end) and wrap to the beginning and continue tweening from 1 to 5. + **/ + public function frameForward(frame:int):TweenMaxVars { + return _set("frameForward", frame, true); + } + + /** Tweens a MovieClip to a particular frame. **/ + public function frameLabel(label:String):TweenMaxVars { + return _set("frameLabel", label, true); + } + + + /** + * Tweens a GlowFilter + * + * @param blurX The amount of horizontal blur. + * @param blurY The amount of vertical blur. + * @param color The color of the glow. + * @param alpha The alpha transparency value for the color. + * @param strength The strength of the imprint or spread. + * @param inner Specifies whether the glow is an inner glow. + * @param knockout Specifies whether the object has a knockout effect. + * @param quality The number of times to apply the filter. + * @param remove If true, the filter will be removed as soon as the tween completes + * @param addFilter If true, a new GlowFilter will be added to the target even if a GlowFilter is already in its filters array. + * @param index Allows you to target a particular GlowFilter if there are multiple GlowFilters in the target's filters array - simply define the index value corresponding to the GlowFilter's position in the filters array. + * @return The TweenMaxVars instance + */ + public function glowFilter(blurX:Number=10, blurY:Number=10, color:uint=0xFFFFFF, alpha:Number=1, strength:Number=2, inner:Boolean=false, knockout:Boolean=false, quality:uint=2, remove:Boolean=false, addFilter:Boolean=false, index:int=-1):TweenMaxVars { + var filter:Object = {blurX:blurX, blurY:blurY, color:color, alpha:alpha, strength:strength, inner:inner, knockout:knockout, quality:quality, addFilter:addFilter, remove:remove}; + if (index > -1) { + filter.index = index; + } + return _set("glowFilter", filter, true); + } + + /** + * Although hex colors are technically numbers, if you try to tween them conventionally, + * you'll notice that they don't tween smoothly. To tween them properly, the red, green, and + * blue components must be extracted and tweened independently. The HexColorsPlugin makes it easy. + * To tween a property of your object that's a hex color to another hex color, just pass a hexColors + * Object with properties named the same as your object's hex color properties. For example, + * if myObject has a "myHexColor" property that you'd like to tween to red (0xFF0000) over the + * course of 2 seconds, you'd do: + * + *

+ * TweenMax.to(myObject, 2, new TweenMaxVars().hexColors({myHexColor:0xFF0000})); + *

+ * + *

You can pass in any number of hexColor properties.

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.HexColorsPlugin; + TweenPlugin.activate([HexColorsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(myObject, 2, new TweenMaxVars().hexColors({myHexColor:0xFF0000})); + + *

Or if you just want to tween a color and apply it somewhere on every frame, you could do:

+ * + var myColor:Object = {hex:0xFF0000}; + TweenMax.to(myColor, 2, new TweenMaxVars().hexColors({hex:0x0000FF}).onUpdate(applyColor)); + function applyColor():void { + mc.graphics.clear(); + mc.graphics.beginFill(myColor.hex, 1); + mc.graphics.drawRect(0, 0, 100, 100); + mc.graphics.endFill(); + } + + * + **/ + public function hexColors(values:Object):TweenMaxVars { + return _set("hexColors", values, true); + } + + + /** + * MotionBlurPlugin provides an easy way to apply a directional blur to a DisplayObject based on its velocity + * and angle of movement in 2D (x/y). This creates a much more realistic effect than a standard BlurFilter for + * several reasons: + *
    + *
  1. A regular BlurFilter is limited to blurring horizontally and/or vertically whereas the motionBlur + * gets applied at the angle at which the object is moving.
  2. + * + *
  3. A BlurFilter tween has static start/end values whereas a motionBlur tween dynamically adjusts the + * values on-the-fly during the tween based on the velocity of the object. So if you use a Strong.easeInOut + * for example, the strength of the blur will start out low, then increase as the object moves faster, and + * reduce again towards the end of the tween.
  4. + *
+ * + *

motionBlur even works on bezier/bezierThrough tweens!

+ * + *

To accomplish the effect, MotionBlurPlugin creates a Bitmap that it places over the original object, changing + * alpha of the original to [almost] zero during the course of the tween. The original DisplayObject still follows the + * course of the tween, so MouseEvents are properly dispatched. You shouldn't notice any loss of interactivity. + * The DisplayObject can also have animated contents - MotionBlurPlugin automatically updates on every frame. + * Be aware, however, that as with most filter effects, MotionBlurPlugin is somewhat CPU-intensive, so it is not + * recommended that you tween large quantities of objects simultaneously. You can activate fastMode + * to significantly speed up rendering if the object's contents and size/color doesn't need to change during the + * course of the tween.

+ * + * @param strength Determines the strength of the blur. The default is 1. For a more powerful blur, increase the number. Or reduce it to make the effect more subtle. + * @param fastMode Setting fastMode to true will significantly improve rendering performance but it is only appropriate for situations when the target object's contents, size, color, filters, etc. do not need to change during the course of the tween. It works by essentially taking a BitmapData snapshot of the target object at the beginning of the tween and then reuses that throughout the tween, blurring it appropriately. The default value for fastMode is false. + * @param quality The lower the quality, the less CPU-intensive the effect will be. Options are 1, 2, or 3. The default is 2. + * @param padding padding controls the amount of space around the edges of the target object that is included in the BitmapData capture (the default is 10 pixels). If the target object has filters applied to it like a GlowFilter or DropShadowFilter that extend beyond the bounds of the object itself, you might need to increase the padding to accommodate the filters. + * @return The TweenMaxVars instance + */ + public function motionBlur(strength:Number=1, fastMode:Boolean=false, quality:int=2, padding:int=10):TweenMaxVars { + return _set("motionBlur", {strength:strength, fastMode:fastMode, quality:quality, padding:padding}, true); + } + + /** + * A common effect that designers/developers want is for a MovieClip/Sprite to orient itself in the direction of + * a Bezier path (alter its rotation). orientToBezier makes it easy. In order to alter a rotation property accurately, + * TweenLite/Max needs 4 pieces of information: + *
    + *
  1. Position property 1 (typically "x")
  2. + *
  3. Position property 2 (typically "y")
  4. + *
  5. Rotational property (typically "rotation")
  6. + *
  7. Number of degrees to add (optional - makes it easy to orient your MovieClip/Sprite properly)
  8. + *
+ * + *

The orientToBezier property should be an Array containing one Array for each set of these values. + * For maximum flexibility, you can pass in any number of Arrays inside the container Array, one for + * each rotational property. This can be convenient when working in 3D because you can rotate on multiple axis. + * If you're doing a standard 2D x/y tween on a bezier, you can simply pass in a boolean value of true and + * TweenMax will use a typical setup, [["x", "y", "rotation", 0]]. + * Hint: Don't forget the container Array (notice the double outer brackets)

+ * + *

To use the default value ([["x", "y", "rotation", 0]]), you can simply leave the values parameter as null.

+ */ + public function orientToBezier(values:Object=null):TweenMaxVars { + return _set("orientToBezier", (values == null) ? true : values, false); + } + + + /** + * Provides simple physics functionality for tweening a DisplayObject's x and y coordinates based on a + * combination of velocity, angle, gravity, acceleration, accelerationAngle, and/or friction. It is not intended + * to replace a full-blown physics engine and does not offer collision detection, but serves + * as a way to easily create interesting physics-based effects with the GreenSock tweening platform. Parameters + * are not intended to be dynamically updateable, but one unique convenience is that everything is reverseable. + * So if you spawn a bunch of particle tweens, for example, and throw them into a TimelineLite, you could + * simply call reverse() on the timeline to watch the particles retrace their steps right back to the beginning. + * Keep in mind that any easing equation you define for your tween will be completely ignored for these properties. + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.Physics2DPlugin; + TweenPlugin.activate([Physics2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 2, new TweenMaxVars().physics2D(300, -60, 400)); + + * + *

Physics2DPlugin is a Club GreenSock membership benefit. You must have a valid membership to use this class + * without violating the terms of use. Visit http://www.greensock.com/club/ to sign up or get more details.

+ * + * @param velocity The initial velocity of the object measured in pixels per time unit (usually seconds, but for tweens where useFrames is true, it would be measured in frames). The default is zero. + * @param angle The initial angle (in degrees) at which the object is traveling. Only pertinent when a velocity is defined. For example, if the object should start out traveling at -60 degrees (towards the upper right), the angle would be -60. The default is zero. + * @param acceleration The amount of acceleration applied to the object, measured in pixels per time unit (usually seconds, but for tweens where useFrames is true, it would be measured in frames). To apply the acceleration in a specific direction that is different than the angle, use the accelerationAngle property. + * @param accelerationAngle The angle at which acceleration is applied (if any), measured in degrees. So if, for example, you want the object to accelerate towards the left side of the screen, you'd use an accelerationAngle of 180. + * @param friction A value between 0 and 1 where 0 is no friction, 0.08 is a small amount of friction, and 1 will completely prevent any movement. This is not meant to be precise or scientific in any way, but rather serves as an easy way to apply a friction-like physics effect to your tween. Generally it is best to experiment with this number a bit. Also note that friction requires more processing than physics tweens without any friction. + * @return The TweenMaxVars instance + * @see #physicsProps() + */ + public function physics2D(velocity:Number, angle:Number, acceleration:Number=0, accelerationAngle:Number=90, friction:Number=0):TweenMaxVars { + return _set("physics2D", {velocity:velocity, angle:angle, acceleration:acceleration, accelerationAngle:accelerationAngle, friction:friction}, true); + } + + /** + * Sometimes you want to tween a property (or several) but you don't have a specific end value in mind - instead, + * you'd rather describe the movement in terms of physics concepts, like velocity, acceleration, + * and/or friction. physicsProps allows you to tween any numeric property of any object based + * on these concepts. Keep in mind that any easing equation you define for your tween will be completely + * ignored for these properties. Instead, the physics parameters will determine the movement/easing. + * These parameters, by the way, are not intended to be dynamically updateable, but one unique convenience + * is that everything is reverseable. So if you create several physics-based tweens, for example, and + * throw them into a TimelineLite, you could simply call reverse() on the timeline to watch the objects + * retrace their steps right back to the beginning. Here are the parameters you can define (note that + * friction and acceleration are both completely optional): + *
    + *
  • velocity : Number - the initial velocity of the object measured in units per time + * unit (usually seconds, but for tweens where useFrames is true, it would + * be measured in frames). The default is zero.
  • + *
  • acceleration : Number [optional] - the amount of acceleration applied to the object, measured + * in units per time unit (usually seconds, but for tweens where useFrames + * is true, it would be measured in frames). The default is zero.
  • + *
  • friction : Number [optional] - a value between 0 and 1 where 0 is no friction, 0.08 is a small amount of + * friction, and 1 will completely prevent any movement. This is not meant to be precise or + * scientific in any way, but rather serves as an easy way to apply a friction-like + * physics effect to your tween. Generally it is best to experiment with this number a bit. + * Also note that friction requires more processing than physics tweens without any friction.
  • + *
+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.PhysicsPropsPlugin; + TweenPlugin.activate([PhysicsPropsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 2, new TweenMaxVars().physicsProps({ + x:{velocity:100, acceleration:200}, + y:{velocity:-200, friction:0.1} + } + )); + + * + *

PhysicsPropsPlugin is a Club GreenSock membership benefit. You must have a valid membership to use this class + * without violating the terms of use. Visit http://www.greensock.com/club/ to sign up or get more details.

+ * + * @see #physics2D() + **/ + public function physicsProps(values:Object):TweenMaxVars { + return _set("physicsProps", values, true); + } + + /** An object with properties that correspond to the quaternion properties of the target object. For example, if your my3DObject has "orientation" and "childOrientation" properties that contain quaternions, and you'd like to tween them both, you'd do: {orientation:myTargetQuaternion1, childOrientation:myTargetQuaternion2}. Quaternions must have the following properties: x, y, z, and w. **/ + public function quaternions(values:Object):TweenMaxVars { + return _set("quaternions", values, true); + } + + /** Removes the tint of a DisplayObject over time. **/ + public function removeTint(remove:Boolean=true):TweenMaxVars { + return _set("removeTint", remove, true); + } + + /** An array of the names of properties that should be rounded to the nearest integer when tweening. For example, ["x","y"] **/ + public function roundProps(propertyNames:Array):TweenMaxVars { + return _set("roundProps", propertyNames, true); + } + + /** + * Tweens the scrollRect property of a DisplayObject. You can define any (or all) of the following properties: + * + *
    + *
  • x : Number
  • + *
  • y : Number
  • + *
  • width : Number
  • + *
  • height : Number
  • + *
  • top : Number
  • + *
  • bottom : Number
  • + *
  • left : Number
  • + *
  • right : Number
  • + *
+ *
+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.ScrollRectPlugin; + TweenPlugin.activate([ScrollRectPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().scrollRect({x:50, y:300, width:100, height:100})); + + **/ + public function scrollRect(props:Object):TweenMaxVars { + return _set("scrollRect", props, true); + } + + /** + * Some components require resizing with setSize() instead of standard tweens of width/height in + * order to scale properly. The SetSizePlugin accommodates this easily. You can define the width, + * height, or both. + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.SetSizePlugin; + TweenPlugin.activate([SetSizePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(myComponent, 1, new TweenMaxVars().setSize(200, 30)); + + **/ + public function setSize(width:Number=NaN, height:Number=NaN):TweenMaxVars { + var values:Object = {}; + if (!isNaN(width)) { + values.width = width; + } + if (!isNaN(height)) { + values.height = height; + } + return _set("setSize", values, true); + } + + /** + * To tween any rotation property of the target object in the shortest direction, use "shortRotation" + * For example, if myObject.rotation is currently 170 degrees and you want to tween it to + * -170 degrees, a normal rotation tween would travel a total of 340 degrees in the counter-clockwise + * direction, but if you use shortRotation, it would travel 20 degrees in the clockwise direction instead. + * You can define any number of rotation properties in the shortRotation object which makes 3D tweening + * easier, like: + * + *

+ * TweenMax.to(mc, 2, new TweenMaxVars().shortRotation({rotationX:-170, rotationY:35, rotationZ:200})); + *

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.ShortRotationPlugin; + TweenPlugin.activate([ShortRotationPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().shortRotation({rotation:-170})); + + //or for a 3D tween with multiple rotation values... + TweenMax.to(mc, 1, new TweenMaxVars().shortRotation({rotationX:-170, rotationY:35, rotationZ:10})); + + **/ + public function shortRotation(values:Object):TweenMaxVars { + if (typeof(values) == "number") { + values = {rotation:values}; + } + return _set("shortRotation", values, true); + } + + + /** + * Tweens properties of an object's soundTransform property (like the volume, pan, leftToRight, etc. + * of a MovieClip/SoundChannel/NetStream). + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.SoundTransformPlugin; + TweenPlugin.activate([SoundTransformPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().soundTransform(0.2, 0.5)); + + * + * @param volume The volume, ranging from 0 (silent) to 1 (full volume). + * @param pan The left-to-right panning of the sound, ranging from -1 (full pan left) to 1 (full pan right). + * @param leftToLeft A value, from 0 (none) to 1 (all), specifying how much of the left input is played in the left speaker. + * @param leftToRight A value, from 0 (none) to 1 (all), specifying how much of the left input is played in the right speaker. + * @param rightToLeft A value, from 0 (none) to 1 (all), specifying how much of the right input is played in the left speaker. + * @param rightToRight A value, from 0 (none) to 1 (all), specifying how much of the right input is played in the right speaker. + * @return The TweenMaxVars instance + */ + public function soundTransform(volume:Number=1, pan:Number=0, leftToLeft:Number=1, leftToRight:Number=0, rightToLeft:Number=0, rightToRight:Number=1):TweenMaxVars { + return _set("soundTransform", {volume:volume, pan:pan, leftToLeft:leftToLeft, leftToRight:leftToRight, rightToLeft:rightToLeft, rightToRight:rightToRight}, true); + } + + /** + * Sets the stage's quality to a particular value during a tween and another value after + * the tween which can be useful for improving rendering performance in the Flash Player while things are animating. + * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.StageQualityPlugin; + import flash.display.StageQuality; + TweenPlugin.activate([StageQualityPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().prop("x", 100).stageQuality(this.stage, StageQuality.LOW, StageQuality.HIGH)); + + * + * @param stage A reference to the stage + * @param during The stage quality that should be used during the tween + * @param after The stage quality that should be set after the tween completes + * @return The TweenMaxVars instance + */ + public function stageQuality(stage:Stage, during:String="medium", after:String=null):TweenMaxVars { + if (after == null) { + after = stage.quality; + } + return _set("stageQuality", {stage:stage, during:during, after:after}, true); + } + + /** + * Allows you to define an initial velocity at which a property (or multiple properties) will start tweening, + * as well as [optional] maximum and/or minimum end values and then it will calculate the appropriate landing + * position and plot a smooth course to it based on the easing equation you define (Quad.easeOut by default, + * as set in TweenLite). This is perfect for flick-scrolling or animating things as though they are being thrown. + * + *

In its simplest form, you can pass just the initial velocity for each property like this: + * {x:500, y:-300}

+ * + *

In the above example, x will animate at 500 pixels per second initially and + * y will animate at -300 pixels per second. Both will decelerate smoothly + * until they come to rest based on the tween's duration.

+ * + *

To impose maximum and minimum boundaries on the end values, use the nested object syntax + * with the max and min special properties like this: + * {x:{velocity:500, max:1024, min:0}, y:{velocity:-300, max:720, min:0}}; + *

+ * + *

Notice the nesting of the objects ({}). The max and min values refer + * to the range for the final resting position (coordinates in this case), NOT the velocity. + * So x would always land between 0 and 1024 in this case, and y + * would always land between 0 and 720. If you want the target object to land on a specific value + * rather than within a range, simply set max and min to identical values. + * Also notice that you must define a velocity value for each property in the object syntax.

+ * + *

throwProps isn't just for tweening x and y coordinates. It works with any numeric + * property, so you could use it for spinning the rotation of an object as well. Or the + * scaleX/scaleY properties. Maybe the user drags to spin a wheel and + * lets go and you want it to continue increasing the rotation at that velocity, + * decelerating smoothly until it stops.

+ * + *

ThrowPropsPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ **/ + public function throwProps(props:Object):TweenMaxVars { + return _set("throwProps", props, true); + } + + /** + * To change a DisplayObject's tint, set this to the hex value of the color you'd like the DisplayObject + * to end up at (or begin at if you're using TweenMax.from()). An example hex value would be 0xFF0000. + * If you'd like to remove the tint from a DisplayObject, use the removeTint special property. + * @see #removeTint() + * @see #colorMatrixFilter() + * @see #colorTransform() + **/ + public function tint(color:uint):TweenMaxVars { + return _set("tint", color, true); + } + + /** + * Normally, all transformations (scale, rotation, and position) are based on the DisplayObject's registration + * point (most often its upper left corner), but TransformAroundCenter allows you to make the transformations + * occur around the DisplayObject's center. + * + *

If you define an x or y value in the transformAroundCenter object, it will correspond to the center which + * makes it easy to position (as opposed to having to figure out where the original registration point + * should tween to). If you prefer to define the x/y in relation to the original registration point, do so outside + * the transformAroundCenter object, like:

+ * + *

+ * TweenMax.to(mc, 3, new TweenMaxVars().prop("x", 50).prop("y", 40).transformAroundCenter({scale:0.5, rotation:30})); + *

+ * + *

TransformAroundCenterPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.TransformAroundCenterPlugin; + TweenPlugin.activate([TransformAroundCenterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().transformAroundCenter({scale:1.5, rotation:150})); + + * @see #transformAroundPoint() + **/ + public function transformAroundCenter(props:Object):TweenMaxVars { + return _set("transformAroundCenter", props, true); + } + + /** + * Normally, all transformations (scale, rotation, and position) are based on the DisplayObject's registration + * point (most often its upper left corner), but TransformAroundPoint allows you to define ANY point around which + * transformations will occur during the tween. For example, you may have a dynamically-loaded image that you + * want to scale from its center or rotate around a particular point on the stage. + * + *

If you define an x or y value in the transformAroundPoint object, it will correspond to the custom registration + * point which makes it easy to position (as opposed to having to figure out where the original registration point + * should tween to). If you prefer to define the x/y in relation to the original registration point, do so outside + * the transformAroundPoint object, like:

+ * + *

+ * TweenMax.to(mc, 3, new TweenMaxVars().prop("x", 50).prop("y", 40).transformAroundPoint(new Point(200, 300), {scale:0.5, rotation:30})); + *

+ * + *

TransformAroundPointPlugin is a Club GreenSock membership benefit. + * You must have a valid membership to use this class without violating the terms of use. Visit + * http://www.greensock.com/club/ to sign up or get more details.

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.TransformAroundPointPlugin; + TweenPlugin.activate([TransformAroundPointPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().transformAroundPoint(new Point(100, 300), {scaleX:2, scaleY:1.5, rotation:150})); + + * @see #transformAroundCenter() + **/ + public function transformAroundPoint(point:Point, props:Object):TweenMaxVars { + props.point = point; + return _set("transformAroundPoint", props, true); + } + + /** + * transformMatrix tweens a DisplayObject's transform.matrix values directly either using + * the standard matrix properties (a, b, c, d, tx, and ty) or common properties + * like x, y, scaleX, scaleY, skewX, skewY, rotation and even shortRotation. + * To skew without adjusting scale visually, use skewX2 and skewY2 instead of skewX and skewY. + * + * + *

transformMatrix tween will affect all of the DisplayObject's transform properties, so do not use + * it in conjunction with regular x/y/scaleX/scaleY/rotation tweens concurrently.

+ * + *

USAGE:

+ * + import com.greensock.TweenMax; + import com.greensock.data.TweenMaxVars; + import com.greensock.plugins.TweenPlugin; + import com.greensock.plugins.TransformMatrixPlugin; + TweenPlugin.activate([TransformMatrixPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + + TweenMax.to(mc, 1, new TweenMaxVars().transformMatrix({x:50, y:300, scaleX:2, scaleY:2})); + + //-OR- + + TweenMax.to(mc, 1, new TweenMaxVars().transformMatrix({tx:50, ty:300, a:2, d:2})); + + **/ + public function transformMatrix(properties:Object):TweenMaxVars { + return _set("transformMatrix", properties, true); + } + + /** Sets a DisplayObject's "visible" property at the end of the tween. **/ + public function visible(value:Boolean):TweenMaxVars { + return _set("visible", value, true); + } + + /** Changes the volume of any object that has a soundTransform property (MovieClip, SoundChannel, NetStream, etc.) **/ + public function volume(volume:Number):TweenMaxVars { + return _set("volume", volume, true); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------------------------------------------- + + /** The generic object populated by all of the method calls in the TweenMaxVars instance. This is the raw data that gets passed to the tween. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get _isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Back.as b/src/com/greensock/easing/Back.as new file mode 100644 index 0000000..601c395 --- /dev/null +++ b/src/com/greensock/easing/Back.as @@ -0,0 +1,32 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases with an overshoot either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Back is a convenience class that congregates the 3 types of Back eases (BackIn, BackOut, + * and BackInOut) as static properties so that they can be referenced using the standard synatax, like + * Back.easeIn, Back.easeOut, and Back.easeInOut. + * + *

You can configure the amount of overshoot using the config() method, like + * TweenLite.to(obj, 1, {x:100, ease:Back.easeOut.config(3)});

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Back { + + /** Eases out with an overshoot. **/ + public static var easeOut:BackOut = new BackOut(); + + /** Eases in with an overshoot, initially dipping below the starting value before accelerating towards the end. **/ + public static var easeIn:BackIn = new BackIn(); + + /** Eases in and out with an overshoot, initially dipping below the starting value before accelerating towards the end, overshooting it and easing out. **/ + public static var easeInOut:BackInOut = new BackInOut(); + } +} diff --git a/src/com/greensock/easing/BackIn.as b/src/com/greensock/easing/BackIn.as new file mode 100644 index 0000000..3e1cea5 --- /dev/null +++ b/src/com/greensock/easing/BackIn.as @@ -0,0 +1,47 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in with an overshoot, initially dipping below the starting value before accelerating towards the end. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BackIn extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BackIn = new BackIn(); + + /** + * Constructor + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + */ + public function BackIn(overshoot:Number=1.70158) { + _p1 = overshoot; + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return p * p * ((_p1 + 1) * p - _p1); + } + + /** + * Permits customization of the ease with various parameters. + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + * @return new BackIn instance that is configured according to the parameters provided + */ + public function config(overshoot:Number=1.70158):BackIn { + return new BackIn(overshoot); + } + + } + +} diff --git a/src/com/greensock/easing/BackInOut.as b/src/com/greensock/easing/BackInOut.as new file mode 100644 index 0000000..5bd0aba --- /dev/null +++ b/src/com/greensock/easing/BackInOut.as @@ -0,0 +1,48 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in and out with an overshoot, initially dipping below the starting value before accelerating towards the end, overshooting it and easing out. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BackInOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BackInOut = new BackInOut(); + + /** + * Constructor + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + */ + public function BackInOut(overshoot:Number=1.70158) { + _p1 = overshoot; + _p2 = _p1 * 1.525; + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return ((p*=2) < 1) ? 0.5 * p * p * ((_p2 + 1) * p - _p2) : 0.5 * ((p -= 2) * p * ((_p2 + 1) * p + _p2) + 2); + } + + /** + * Permits customization of the ease with various parameters. + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + * @return new BackInOut instance that is configured according to the parameters provided + */ + public function config(overshoot:Number=1.70158):BackInOut { + return new BackInOut(overshoot); + } + + } + +} diff --git a/src/com/greensock/easing/BackOut.as b/src/com/greensock/easing/BackOut.as new file mode 100644 index 0000000..b701b95 --- /dev/null +++ b/src/com/greensock/easing/BackOut.as @@ -0,0 +1,47 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases out with an overshoot. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BackOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BackOut = new BackOut(); + + /** + * Constructor + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + */ + public function BackOut(overshoot:Number=1.70158) { + _p1 = overshoot; + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return ((p = p - 1) * p * ((_p1 + 1) * p + _p1) + 1); + } + + /** + * Permits customization of the ease with various parameters. + * + * @param overshoot affects the degree or strength of the overshoot (default: 1.70158) + * @return new BackOut instance that is configured according to the parameters provided + */ + public function config(overshoot:Number=1.70158):BackOut { + return new BackOut(overshoot); + } + + } + +} diff --git a/src/com/greensock/easing/Bounce.as b/src/com/greensock/easing/Bounce.as new file mode 100644 index 0000000..4cce5a2 --- /dev/null +++ b/src/com/greensock/easing/Bounce.as @@ -0,0 +1,29 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases, bouncing either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Bounce is a convenience class that congregates the 3 types of Bounce eases (BounceIn, BounceOut, + * and BounceInOut) as static properties so that they can be referenced using the standard synatax, like + * Bounce.easeIn, Bounce.easeOut, and Bounce.easeInOut. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Bounce { + + /** Eases out, bouncing at the end. **/ + public static var easeOut:BounceOut = new BounceOut(); + + /** Bounces slightly at first, then to a greater degree over time, accelerating as the ease progresses. **/ + public static var easeIn:BounceIn = new BounceIn(); + + /** Bounces in increasing degree towards the center of the ease, then eases out, bouncing to the end (decreasing in degree at the end). **/ + public static var easeInOut:BounceInOut = new BounceInOut(); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/BounceIn.as b/src/com/greensock/easing/BounceIn.as new file mode 100644 index 0000000..892d296 --- /dev/null +++ b/src/com/greensock/easing/BounceIn.as @@ -0,0 +1,36 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Bounces slightly at first, then to a greater degree over time, accelerating as the ease progresses. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BounceIn extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BounceIn = new BounceIn(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + if ((p = 1 - p) < 1 / 2.75) { + return 1 - (7.5625 * p * p); + } else if (p < 2 / 2.75) { + return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + .75); + } else if (p < 2.5 / 2.75) { + return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + .9375); + } else { + return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + .984375); + } + } + + } + +} diff --git a/src/com/greensock/easing/BounceInOut.as b/src/com/greensock/easing/BounceInOut.as new file mode 100644 index 0000000..a5cef9d --- /dev/null +++ b/src/com/greensock/easing/BounceInOut.as @@ -0,0 +1,44 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Bounces in increasing degree towards the center of the ease, then eases out, bouncing to the end (decreasing in degree at the end). + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BounceInOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BounceInOut = new BounceInOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + var invert:Boolean; + if (p < 0.5) { + invert = true; + p = 1 - (p * 2); + } else { + p = (p * 2) - 1; + } + if (p < 1 / 2.75) { + p = 7.5625 * p * p; + } else if (p < 2 / 2.75) { + p = 7.5625 * (p -= 1.5 / 2.75) * p + .75; + } else if (p < 2.5 / 2.75) { + p = 7.5625 * (p -= 2.25 / 2.75) * p + .9375; + } else { + p = 7.5625 * (p -= 2.625 / 2.75) * p + .984375; + } + return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5; + } + + } + +} diff --git a/src/com/greensock/easing/BounceOut.as b/src/com/greensock/easing/BounceOut.as new file mode 100644 index 0000000..ef003ad --- /dev/null +++ b/src/com/greensock/easing/BounceOut.as @@ -0,0 +1,36 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases out, bouncing at the end. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class BounceOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:BounceOut = new BounceOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + if (p < 1 / 2.75) { + return 7.5625 * p * p; + } else if (p < 2 / 2.75) { + return 7.5625 * (p -= 1.5 / 2.75) * p + .75; + } else if (p < 2.5 / 2.75) { + return 7.5625 * (p -= 2.25 / 2.75) * p + .9375; + } else { + return 7.5625 * (p -= 2.625 / 2.75) * p + .984375; + } + } + + } + +} diff --git a/src/com/greensock/easing/Circ.as b/src/com/greensock/easing/Circ.as new file mode 100644 index 0000000..b36909e --- /dev/null +++ b/src/com/greensock/easing/Circ.as @@ -0,0 +1,29 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases with an abrupt change in velocity either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Circ is a convenience class that congregates the 3 types of Circ eases (CircIn, CircOut, + * and CircInOut) as static properties so that they can be referenced using the standard synatax, like + * Circ.easeIn, Circ.easeOut, and Circ.easeInOut. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Circ { + + /** Eases out with an abrupt change in velocity. **/ + public static var easeOut:CircOut = new CircOut(); + + /** Eases in with an abrupt change in velocity. **/ + public static var easeIn:CircIn = new CircIn(); + + /** Eases in and out with an abrupt change in velocity. **/ + public static var easeInOut:CircInOut = new CircInOut(); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/CircIn.as b/src/com/greensock/easing/CircIn.as new file mode 100644 index 0000000..21bf640 --- /dev/null +++ b/src/com/greensock/easing/CircIn.as @@ -0,0 +1,27 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in with an abrupt change in velocity. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class CircIn extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:CircIn = new CircIn(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return -(Math.sqrt(1 - (p * p)) - 1); + } + + } +} diff --git a/src/com/greensock/easing/CircInOut.as b/src/com/greensock/easing/CircInOut.as new file mode 100644 index 0000000..72b44fe --- /dev/null +++ b/src/com/greensock/easing/CircInOut.as @@ -0,0 +1,28 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in and out with an abrupt change in velocity. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class CircInOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:CircInOut = new CircInOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1); + } + + } + +} diff --git a/src/com/greensock/easing/CircOut.as b/src/com/greensock/easing/CircOut.as new file mode 100644 index 0000000..9404d81 --- /dev/null +++ b/src/com/greensock/easing/CircOut.as @@ -0,0 +1,28 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases out with an abrupt change in velocity. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class CircOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:CircOut = new CircOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return Math.sqrt(1 - (p = p - 1) * p); + } + + } + +} diff --git a/src/com/greensock/easing/Cubic.as b/src/com/greensock/easing/Cubic.as new file mode 100644 index 0000000..d35a7f4 --- /dev/null +++ b/src/com/greensock/easing/Cubic.as @@ -0,0 +1,38 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 2 which is identical to the Power2 ease. The more power, the more + * exaggerated the easing effect. Using a numeric approach like Power2 instead of Cubic makes experimenting + * easier and the code reads more intuitively. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Cubic.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Cubic { + + /** Eases out with a power of 2 **/ + public static var easeOut:Ease = new Ease(null,null,1,2); + + /** Eases in with a power of 2 **/ + public static var easeIn:Ease = new Ease(null,null,2,2); + + /** eases in and then out with a power of 2 **/ + public static var easeInOut:Ease = new Ease(null,null,3,2); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Ease.as b/src/com/greensock/easing/Ease.as new file mode 100644 index 0000000..bcb5db5 --- /dev/null +++ b/src/com/greensock/easing/Ease.as @@ -0,0 +1,113 @@ +/** + * VERSION: 0.5 + * DATE: 2012-01-31 + * AS3 (AS2 version is also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Base class for all GreenSock easing equations. In its simplest form, an Ease + * is responsible for translating linear time (typically represented as a number + * between 0 and 1 where 0 is the beginning, 0.5 is halfway complete, and 1 is + * the end) into a value that has a different rate of change but still starts + * at 0 and ends at 1. In the GreenSock platform, eases are used to give + * tweens/animations the look and feel that the animator desires. For example, + * a ball rolling to a stop would decelerate over time (easeOut) rather than using + * a linear velocity. An Elastic ease could be used to make an object appear as + * though it is loosely attached somewhere and is snapping into place with loose + * (or tight) tension. + * + *

All Ease instances have a getRatio() method that is responsible + * for the translation of the progress ratio which the tween typically feeds in. + * End users almost never need to directly feed any values to or get any values from + * an Ease instance - the tweens will do that internally.

+ * + *

The base Ease class handles most of the common power-based easeIn/easeOut/eaesInOut + * calculations (like Linear, Quad, Cubic, Quart, Quint, and Strong) internally. + * You can define a separate function that uses what was considered the 4 standard + * easing parameters by Adobe and many others (time, start, change, duration) and + * Ease will serve as a proxy in order to maximize backwards compatibility and usability. + * For example, if you have a custom method that you created like this:

+ * +function myEase(t:Number, s:Number, c:Number, d:Number):Number { + return s+(t=t/d)*t*t*t*c; +} + + * You could still use that by wrapping Ease around it like this: + * +import com.greensock.~~; +import com.greensock.easing.~~; + +TweenLite.to(mc, 5, {x:600, ease:new Ease(myEase)}); + + *

In the above example, the anytime the Ease's getRatio() method is called, it + * would feed the first parameter as a ratio between 0 and 1 and the rest of the 3 parameters + * would always be 0, 1, 1. This is all done transparently by TweenLite/TweenMax, so you + * really shouldn't need to worry about this.

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + public class Ease { + /** @private **/ + protected static var _baseParams:Array = [0, 0, 1, 1]; + /** @private **/ + protected var _func:Function; + /** @private **/ + protected var _params:Array; + /** @private **/ + protected var _p1:Number; + /** @private **/ + protected var _p2:Number; + /** @private **/ + protected var _p3:Number; + /** @private integer indicating the type of ease where 1 is easeOut, 2 is easeIn, 3 is easeInOut, and 0 is none of these. **/ + public var _type:int; + /** @private power of the ease where Linear is 0, Quad is 1, Cubic is 2, Quart is 3, Quint (and Strong) is 4, etc. **/ + public var _power:int; + /** @private if true, TweenLite/Max will call setRatio() at the end and beginning of the tween instead of assuming it's 1/0. This is only useful in very rare situations like in a SlowMo ease that uses endcapMode=true which will have a 0 ratio at the end of the tween. **/ + public var _calcEnd:Boolean; + + /** + * Constructor + * + * @param func Function (if any) that should be proxied. This is completely optional and is in fact rarely used except when you have your own custom ease function that follows the standard ease parameter pattern like time, start, change, duration. + * @param extraParams If any extra parameters beyond the standard 4 (time, start, change, duration) need to be fed to the func function, define them as an array here. For example, the old Elastic.easeOut accepts 2 extra parameters in its standard equation (although the newer GreenSock version uses the more modern config() method for configuring the ease and doesn't require any extraPrams here) + * @param type Integer indicating the type of ease where 1 is easeOut, 2 is easeIn, 3 is easeInOut, and 0 is none of these. + * @param power Power of the ease where Linear is 0, Quad is 1, Cubic is 2, Quart is 3, Quint (and Strong) is 4, etc. + */ + public function Ease(func:Function=null, extraParams:Array=null, type:Number=0, power:Number=0) { + _func = func; + _params = (extraParams) ? _baseParams.concat(extraParams) : _baseParams; + _type = type; + _power = power; + } + + /** + * Translates the tween's progress ratio into the corresponding ease ratio. This is the heart of the Ease, where it does all its work. + * + * @param p progress ratio (a value between 0 and 1 indicating the progress of the tween/ease) + * @return translated number + */ + public function getRatio(p:Number):Number { + if (_func != null) { + _params[0] = p; + return _func.apply(null, _params); + } else { + var r:Number = (_type == 1) ? 1 - p : (_type == 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2; + if (_power == 1) { + r *= r; + } else if (_power == 2) { + r *= r * r; + } else if (_power == 3) { + r *= r * r * r; + } else if (_power == 4) { + r *= r * r * r * r; + } + return (_type == 1) ? 1 - r : (_type == 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2); + } + } + + } +} diff --git a/src/com/greensock/easing/EaseLookup.as b/src/com/greensock/easing/EaseLookup.as new file mode 100644 index 0000000..3852453 --- /dev/null +++ b/src/com/greensock/easing/EaseLookup.as @@ -0,0 +1,73 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * EaseLookup enables you to find the easing function associated with a particular name (String), + * like "strongEaseOut" which can be useful when loading in XML data that comes in as Strings but + * needs to be translated to native function references. + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class EaseLookup { + /** @private **/ + private static var _lookup:Object; + + /** + * Finds the easing function associated with a particular name (String), like "strongEaseOut". This can be useful when + * loading in XML data that comes in as Strings but needs to be translated to native function references. You can pass in + * the name with or without the period, and it is case insensitive, so any of the following will find the Strong.easeOut function: + * + *

EaseLookup.find("Strong.easeOut")

+ *

EaseLookup.find("strongEaseOut")

+ *

EaseLookup.find("strongeaseout")

+ * + *

You can translate strings directly when tweening, like this:

+ * + *

+ * TweenLite.to(mc, 1, {x:100, ease:EaseLookup.find(myString)}); + *

+ * + * @param name The name of the easing function, with or without the period and case insensitive (i.e. "Strong.easeOut" or "strongEaseOut") + * @return The easing function associated with the name + */ + public static function find(name:String):Ease { + if (_lookup == null) { + _lookup = {}; + + _addInOut(Back, ["back"]); + _addInOut(Bounce, ["bounce"]); + _addInOut(Circ, ["circ", "circular"]); + _addInOut(Cubic, ["cubic","power2"]); + _addInOut(Elastic, ["elastic"]); + _addInOut(Expo, ["expo", "exponential"]); + _addInOut(Power0, ["linear","power0"]); + _addInOut(Quad, ["quad", "quadratic","power1"]); + _addInOut(Quart, ["quart","quartic","power3"]); + _addInOut(Quint, ["quint", "quintic", "strong","power4"]); + _addInOut(Sine, ["sine"]); + + _lookup["linear.easenone"] = _lookup["lineareasenone"] = Linear.easeNone; + _lookup.slowmo = _lookup["slowmo.ease"] = SlowMo.ease; + } + return _lookup[name.toLowerCase()]; + } + + /** @private **/ + private static function _addInOut(easeClass:Class, names:Array):void { + var name:String, i:int = names.length; + while (--i > -1) { + name = names[i].toLowerCase(); + _lookup[name + ".easein"] = _lookup[name + "easein"] = easeClass.easeIn; + _lookup[name + ".easeout"] = _lookup[name + "easeout"] = easeClass.easeOut; + _lookup[name + ".easeinout"] = _lookup[name + "easeinout"] = easeClass.easeInOut; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Elastic.as b/src/com/greensock/easing/Elastic.as new file mode 100644 index 0000000..5fb9732 --- /dev/null +++ b/src/com/greensock/easing/Elastic.as @@ -0,0 +1,32 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases with an elastic effect either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Elastic is a convenience class that congregates the 3 types of Elastic eases (ElasticIn, ElasticOut, + * and ElasticInOut) as static properties so that they can be referenced using the standard synatax, like + * Elastic.easeIn, Elastic.easeOut, and Elastic.easeInOut. + * + *

You can configure the amplitude and period of the sine wave using the config() method, like + * TweenLite.to(obj, 1, {x:100, ease:Elastic.easeOut.config(0.5, 2)});

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Elastic { + + /** Eases using a sine wave that starts fast and then decelerates over time. **/ + public static var easeOut:ElasticOut = new ElasticOut(); + + /** Eases using a sine wave that starts slowly and then accelerates over time **/ + public static var easeIn:ElasticIn = new ElasticIn(); + + /** Eases using a sine wave that starts slowly, then accelerates and then decelerates over time. **/ + public static var easeInOut:ElasticInOut = new ElasticInOut(); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/ElasticIn.as b/src/com/greensock/easing/ElasticIn.as new file mode 100644 index 0000000..4f00cd0 --- /dev/null +++ b/src/com/greensock/easing/ElasticIn.as @@ -0,0 +1,54 @@ +/** + * VERSION: 1.1 + * DATE: 2012-07-27 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases using a sine wave that starts slowly and then accelerates over time. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ElasticIn extends Ease { + + /** @private **/ + private static const _2PI:Number = Math.PI * 2; + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ElasticIn = new ElasticIn(); + + /** + * Constructor + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 1. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency where a lower value produces more cycles). Default is 0.3. + */ + public function ElasticIn(amplitude:Number=1, period:Number=0.3) { + _p1 = amplitude || 1; + _p2 = period || 0.3; + _p3 = _p2 / _2PI * (Math.asin(1 / _p1) || 0); + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return -(_p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - _p3) * _2PI / _p2 )); + } + + /** + * Permits customization of the ease with various parameters. + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 1. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency where a lower value produces more cycles). Default is 0.3. + * @return new ElasticIn instance that is configured according to the parameters provided + */ + public function config(amplitude:Number=1, period:Number=0.3):ElasticIn { + return new ElasticIn(amplitude, period); + } + + } + +} diff --git a/src/com/greensock/easing/ElasticInOut.as b/src/com/greensock/easing/ElasticInOut.as new file mode 100644 index 0000000..d057de3 --- /dev/null +++ b/src/com/greensock/easing/ElasticInOut.as @@ -0,0 +1,54 @@ +/** + * VERSION: 1.1 + * DATE: 2012-07-27 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases using a sine wave that starts slowly, then accelerates and then decelerates over time. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ElasticInOut extends Ease { + + /** @private **/ + private static const _2PI:Number = Math.PI * 2; + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ElasticInOut = new ElasticInOut(); + + /** + * Constructor + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 0. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency). Default is 0. + */ + public function ElasticInOut(amplitude:Number=1, period:Number=0.3) { + _p1 = amplitude || 1; + _p2 = period || 0.45; + _p3 = _p2 / _2PI * (Math.asin(1 / _p1) || 0); + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return ((p*=2) < 1) ? -.5 * (_p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - _p3) * _2PI / _p2)) : _p1 * Math.pow(2, -10 *(p -= 1)) * Math.sin( (p - _p3) * _2PI / _p2 ) *.5 + 1; + } + + /** + * Permits customization of the ease with various parameters. + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 1. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency where a lower value produces more cycles). Default is 0.3. + * @return new ElasticInOut instance that is configured according to the parameters provided + */ + public function config(amplitude:Number=1, period:Number=0.3):ElasticInOut { + return new ElasticInOut(amplitude, period); + } + + } + +} diff --git a/src/com/greensock/easing/ElasticOut.as b/src/com/greensock/easing/ElasticOut.as new file mode 100644 index 0000000..a1ab077 --- /dev/null +++ b/src/com/greensock/easing/ElasticOut.as @@ -0,0 +1,54 @@ +/** + * VERSION: 1.1 + * DATE: 2012-07-27 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases using a sine wave that starts fast and then decelerates over time. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ElasticOut extends Ease { + + /** @private **/ + private static const _2PI:Number = Math.PI * 2; + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ElasticOut = new ElasticOut(); + + /** + * Constructor + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 1. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency where a lower value produces more cycles). Default is 0.3. + */ + public function ElasticOut(amplitude:Number=1, period:Number=0.3) { + _p1 = amplitude || 1; + _p2 = period || 0.3; + _p3 = _p2 / _2PI * (Math.asin(1 / _p1) || 0); + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return _p1 * Math.pow(2, -10 * p) * Math.sin( (p - _p3) * _2PI / _p2 ) + 1; + } + + /** + * Permits customization of the ease with various parameters. + * + * @param amplitude the amplitude of the sine wave (how exaggerated its movement is). Default is 1. + * @param period the period of the sine wave (how far apart its waves are spaced, like its frequency where a lower value produces more cycles). Default is 0.3. + * @return new ElasticOut instance that is configured according to the parameters provided + */ + public function config(amplitude:Number=1, period:Number=0.3):ElasticOut { + return new ElasticOut(amplitude, period); + } + + } + +} diff --git a/src/com/greensock/easing/Expo.as b/src/com/greensock/easing/Expo.as new file mode 100644 index 0000000..ac2b468 --- /dev/null +++ b/src/com/greensock/easing/Expo.as @@ -0,0 +1,41 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases in a strong fashion either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Expo is a convenience class that congregates the 3 types of Expo eases (ExpoIn, ExpoOut, + * and ExpoInOut) as static properties so that they can be referenced using the standard synatax, like + * Expo.easeIn, Expo.easeOut, and Expo.easeInOut. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Expo { + + /** + * Eases out in a strong fashion starting out fast and then decelerating. Produces an effect similar to the + * popular "Zeno's paradox" style of scripted easing, where each interval of time decreases the remaining + * distance by a constant proportion. + **/ + public static var easeOut:ExpoOut = new ExpoOut(); + + /** + * Eases in a strong fashion starting out slowly and then accelerating. Produces an effect similar to the + * popular "Zeno's paradox" style of scripted easing, where each interval of time decreases the remaining + * distance by a constant proportion. + **/ + public static var easeIn:ExpoIn = new ExpoIn(); + + /** + * Eases in a strong fashion starting out slowly and then accelerating, then decelerating at the end. + * Produces an effect similar to the popular "Zeno's paradox" style of scripted easing, where each + * interval of time decreases the remaining distance by a constant proportion. + **/ + public static var easeInOut:ExpoInOut = new ExpoInOut(); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/ExpoIn.as b/src/com/greensock/easing/ExpoIn.as new file mode 100644 index 0000000..c56a4e6 --- /dev/null +++ b/src/com/greensock/easing/ExpoIn.as @@ -0,0 +1,30 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in a strong fashion starting out slowly and then accelerating. Produces an effect similar to the + * popular "Zeno's paradox" style of scripted easing, where each interval of time decreases the remaining + * distance by a constant proportion. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ExpoIn extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ExpoIn = new ExpoIn(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return Math.pow(2, 10 * (p - 1)) - 0.001; + } + + } + +} diff --git a/src/com/greensock/easing/ExpoInOut.as b/src/com/greensock/easing/ExpoInOut.as new file mode 100644 index 0000000..64dabfc --- /dev/null +++ b/src/com/greensock/easing/ExpoInOut.as @@ -0,0 +1,30 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in a strong fashion starting out slowly and then accelerating, then decelerating at the end. + * Produces an effect similar to the popular "Zeno's paradox" style of scripted easing, where each + * interval of time decreases the remaining distance by a constant proportion. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ExpoInOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ExpoInOut = new ExpoInOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return ((p*=2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); + } + + } + +} diff --git a/src/com/greensock/easing/ExpoOut.as b/src/com/greensock/easing/ExpoOut.as new file mode 100644 index 0000000..b8ea588 --- /dev/null +++ b/src/com/greensock/easing/ExpoOut.as @@ -0,0 +1,30 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases out in a strong fashion starting out fast and then decelerating. Produces an effect similar to the + * popular "Zeno's paradox" style of scripted easing, where each interval of time decreases the remaining + * distance by a constant proportion. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class ExpoOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:ExpoOut = new ExpoOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return 1 - Math.pow(2, -10 * p); + } + + } + +} diff --git a/src/com/greensock/easing/Linear.as b/src/com/greensock/easing/Linear.as new file mode 100644 index 0000000..95894f1 --- /dev/null +++ b/src/com/greensock/easing/Linear.as @@ -0,0 +1,47 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Linear ease with no acceleration or deceleration. Linear is identical to Power0. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Linear.easeNone}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Linear extends Ease { + + /** Linear ease with no acceleration or deceleration (for backwards compatibility) **/ + public static var easeNone:Linear = new Linear(); + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:Linear = easeNone; + + /** Linear ease with no acceleration or deceleration **/ + public static var easeIn:Linear = easeNone; + + /** Linear ease with no acceleration or deceleration **/ + public static var easeOut:Linear = easeNone; + + /** Linear ease with no acceleration or deceleration **/ + public static var easeInOut:Linear = easeNone; + + /** Constructor **/ + public function Linear() { + super(null, null, 1, 0); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Power0.as b/src/com/greensock/easing/Power0.as new file mode 100644 index 0000000..1a47a22 --- /dev/null +++ b/src/com/greensock/easing/Power0.as @@ -0,0 +1,40 @@ +/** + * VERSION: 1.0 + * DATE: 2013-03-13 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 0 which is identical to Linear but with a more intuitive name. The more power, the more + * exaggerated the easing effect. So Power0 actually has no power at all, providing a linear transition. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Power0.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Power0 { + + /** Eases out with a power of 0 (linear). Power0.easeIn, Power0.easeOut, and Power0.easeInOut are all identical because there is no power - they're all linear but use the common naming convention for ease of use. **/ + public static var easeOut:Ease = new Ease(null,null,1,0); + + /** Eases in with a power of 0 (linear). Power0.easeIn, Power0.easeOut, and Power0.easeInOut are all identical because there is no power - they're all linear but use the common naming convention for ease of use. **/ + public static var easeIn:Ease = new Ease(null,null,2,0); + + /** eases in and then out with a power of 0 (linear). Power0.easeIn, Power0.easeOut, and Power0.easeInOut are all identical because there is no power - they're all linear but use the common naming convention for ease of use. **/ + public static var easeInOut:Ease = new Ease(null,null,3,0); + + /** Eases out with a power of 0 (linear). Power0.easeNone, Power0.easeIn, Power0.easeOut, and Power0.easeInOut are all identical because there is no power - they're all linear but use the common naming convention for ease of use. **/ + public static var easeNone:Ease = new Ease(null,null,1,0); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Power1.as b/src/com/greensock/easing/Power1.as new file mode 100644 index 0000000..ef0dd80 --- /dev/null +++ b/src/com/greensock/easing/Power1.as @@ -0,0 +1,37 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 1 which is identical to Quad but with a more intuitive name. The more power, the more + * exaggerated the easing effect. Using a numeric approach like this in the name makes experimenting easier. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) + * are accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Power1.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Power1 { + + /** Eases out with a power of 1 **/ + public static var easeOut:Ease = new Ease(null,null,1,1); + + /** Eases in with a power of 1 **/ + public static var easeIn:Ease = new Ease(null,null,2,1); + + /** Eases in and then out with a power of 1 **/ + public static var easeInOut:Ease = new Ease(null,null,3,1); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Power2.as b/src/com/greensock/easing/Power2.as new file mode 100644 index 0000000..fcdb9be --- /dev/null +++ b/src/com/greensock/easing/Power2.as @@ -0,0 +1,37 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 2 which is identical to Cubic but with a more intuitive name. The more power, the more + * exaggerated the easing effect. Using a numeric approach like this in the name makes experimenting easier. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Power2.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Power2 { + + /** Eases out with a power of 2 **/ + public static var easeOut:Ease = new Ease(null,null,1,2); + + /** Eases in with a power of 2 **/ + public static var easeIn:Ease = new Ease(null,null,2,2); + + /** Eases in and then out with a power of 2 **/ + public static var easeInOut:Ease = new Ease(null,null,3,2); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Power3.as b/src/com/greensock/easing/Power3.as new file mode 100644 index 0000000..375d8ee --- /dev/null +++ b/src/com/greensock/easing/Power3.as @@ -0,0 +1,37 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 3 which is identical to Quart but with a more intuitive name. The more power, the more + * exaggerated the easing effect. Using a numeric approach like this in the name makes experimenting easier. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) + * are accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Power3.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Power3 { + + /** Eases out with a power of 3 **/ + public static var easeOut:Ease = new Ease(null,null,1,3); + + /** Eases in with a power of 3 **/ + public static var easeIn:Ease = new Ease(null,null,2,3); + + /** Eases in and then out with a power of 3 **/ + public static var easeInOut:Ease = new Ease(null,null,3,3); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Power4.as b/src/com/greensock/easing/Power4.as new file mode 100644 index 0000000..4d35830 --- /dev/null +++ b/src/com/greensock/easing/Power4.as @@ -0,0 +1,38 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 4 which is identical to Quint and Strong but with a more intuitive name. + * The more power, the more exaggerated the easing effect. Using a numeric approach like this in the name + * makes experimenting easier. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) + * are accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Power4.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Power4 { + + /** Eases out with a power of 4 **/ + public static var easeOut:Ease = new Ease(null,null,1,4); + + /** Eases in with a power of 4 **/ + public static var easeIn:Ease = new Ease(null,null,2,4); + + /** Eases in and then out with a power of 4 **/ + public static var easeInOut:Ease = new Ease(null,null,3,4); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Quad.as b/src/com/greensock/easing/Quad.as new file mode 100644 index 0000000..5fc2fc9 --- /dev/null +++ b/src/com/greensock/easing/Quad.as @@ -0,0 +1,38 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 1 which is identical to the Power1 ease. The more power, the more + * exaggerated the easing effect. Using a numeric approach like Power1 instead of Quad makes experimenting + * easier and the code reads more intuitively. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Quad.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Quad { + + /** Eases out with a power of 1. **/ + public static var easeOut:Ease = new Ease(null,null,1,1); + + /** Eases in with a power of 1. **/ + public static var easeIn:Ease = new Ease(null,null,2,1); + + /** Eases in and then out with a power of 1. **/ + public static var easeInOut:Ease = new Ease(null,null,3,1); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Quart.as b/src/com/greensock/easing/Quart.as new file mode 100644 index 0000000..6ec0f80 --- /dev/null +++ b/src/com/greensock/easing/Quart.as @@ -0,0 +1,38 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 2 which is identical to the Power2 ease. The more power, the more + * exaggerated the easing effect. Using a numeric approach like Power2 instead of Quart makes experimenting + * easier and the code reads more intuitively. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Quart.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Quart { + + /** Eases out with a power of 3. **/ + public static var easeOut:Ease = new Ease(null,null,1,3); + + /** Eases in with a power of 3. **/ + public static var easeIn:Ease = new Ease(null,null,2,3); + + /** Eases in and then out with a power of 3. **/ + public static var easeInOut:Ease = new Ease(null,null,3,3); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Quint.as b/src/com/greensock/easing/Quint.as new file mode 100644 index 0000000..44b28ab --- /dev/null +++ b/src/com/greensock/easing/Quint.as @@ -0,0 +1,38 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 4 which is identical to the Power4 ease. The more power, the more + * exaggerated the easing effect. Using a numeric approach like Power4 instead of Quint makes experimenting + * easier and the code reads more intuitively. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Quint.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Quint { + + /** Eases out with a power of 4 **/ + public static var easeOut:Ease = new Ease(null,null,1,4); + + /** Eases in with a power of 4 **/ + public static var easeIn:Ease = new Ease(null,null,2,4); + + /** Eases in and then out with a power of 4 **/ + public static var easeInOut:Ease = new Ease(null,null,3,4); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/RoughEase.as b/src/com/greensock/easing/RoughEase.as new file mode 100644 index 0000000..a75e335 --- /dev/null +++ b/src/com/greensock/easing/RoughEase.as @@ -0,0 +1,289 @@ +/** + * VERSION: 12.0.5 + * DATE: 2013-03-27 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/roughease/ + **/ +package com.greensock.easing { + import com.greensock.easing.core.EasePoint; +/** + * Most easing equations give a smooth, gradual transition between the start and end values, but RoughEase provides + * an easy way to get a rough, jagged effect instead, or you can also get an evenly-spaced back-and-forth movement + * if you prefer. Configure the RoughEase by passing an object to the constructor or config() method with any + * of the following properties (all are optional): + * + *
    + *
  • template : Ease - an ease that should be used as a template, like a general guide. + * The RoughEase will plot points that wander from that template. You can use this to influence + * the general shape of the RoughEase. (Default: Linear.easeNone)
  • + * + *
  • strength : Number - controls how far from the template ease the points are allowed to wander + * (a small number like 0.1 keeps it very close to the template ease whereas a larger number like 5 creates + * much bigger variations). (Default: 1)
  • + * + *
  • points : Number - the number of points to be plotted along the ease, making it jerk more or less + * frequently. (Default: 20)
  • + * + *
  • clamp : Boolean - setting clamp to true will prevent points from + * exceeding the end value or dropping below the starting value. For example, if you're tweening the x + * property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if + * clamp is true, but if it is false, x could potentially jump + * above 100 or below 0 at some point during the tween (it would always end at 100 though in this example) + * (Default: false).
  • + * + *
  • taper : String ("in" | "out" | "both" | "none") - to make the strength of the + * roughness taper towards the end or beginning or both, use "out", "in", + * or "both" respectively. (Default: "none")
  • + * + *
  • randomize : Boolean - by default, the placement of points will be randomized (creating the roughness) + * but you can set randomize to false to make the points zig-zag evenly across the ease. + * Using this in conjunction with a taper value can create a nice effect. (Default: true)
  • + *
+ * + *

For a visual example and more details, check out http://www.greensock.com/roughease/.

+ * + *

Example code

+ * +import com.greensock.TweenLite; +import com.greensock.easing.~~; + +//use the default values +TweenLite.from(mc, 3, {alpha:0, ease:RoughEase.ease}); + +//or customize the configuration +TweenLite.to(mc, 3, {y:300, ease:RoughEase.ease.config({strength:3, points:50, template:Strong.easeInOut, taper:"both", randomize:false}) }); + +//or create a RoughEase that we can pass in to multiple tweens later +var rough:RoughEase = new RoughEase({strength:3, points:50, template:Strong.easeInOut, taper:"both", randomize:false}); +TweenLite.to(mc, 3, {y:300, ease:rough}); +TweenLite.to(mc2, 5, {x:500, ease:rough}); + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class RoughEase extends Ease { + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:RoughEase = new RoughEase(); + /** @private **/ + private static var _lookup:Object = {}; //keeps track of all named instances so we can find them in byName(). + /** @private **/ + private static var _count:int = 0; + + /** @private **/ + private var _name:String; + /** @private **/ + private var _first:EasePoint; + /** @private **/ + private var _prev:EasePoint; + + /** + * Constructor + * + * @param vars a generic object with any of the following properties (all are completely optional): + *
    + *
  • template : Ease - an ease that should be used as a template, like a general guide. + * The RoughEase will plot points that wander from that template. You can use this to influence + * the general shape of the RoughEase. (Default: Linear.easeNone)
  • + * + *
  • strength : Number - controls how far from the template ease the points are allowed to wander + * (a small number like 0.1 keeps it very close to the template ease whereas a larger number like 5 creates + * much bigger variations). (Default: 1)
  • + * + *
  • points : Number - the number of points to be plotted along the ease, making it jerk more or less + * frequently. (Default: 20)
  • + * + *
  • clamp : Boolean - setting clamp to true will prevent points from + * exceeding the end value or dropping below the starting value. For example, if you're tweening the x + * property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if + * clamp is true, but if it is false, x could potentially jump + * above 100 or below 0 at some point during the tween (it would always end at 100 though in this example) + * (Default: false).
  • + * + *
  • taper : String ("in" | "out" | "both" | "none") - to make the strength of the + * roughness taper towards the end or beginning or both, use "out", "in", + * or "both" respectively. (Default: "none")
  • + * + *
  • randomize : Boolean - by default, the placement of points will be randomized (creating the roughness) + * but you can set randomize to false to make the points zig-zag evenly across the ease. + * Using this in conjunction with a taper value can create a nice effect. (Default: true)
  • + *
+ */ + public function RoughEase(vars:*=null, ...args) { + if (typeof(vars) !== "object" || vars == null) { + vars = {strength:vars, points:args[0], clamp:args[1], template:args[2], taper:args[3], randomize:args[4], name:args[5]}; + } + if (vars.name) { + _name = vars.name; + _lookup[vars.name] = this; + } else { + _name = "roughEase" + (_count++); + } + var taper:String = vars.taper || "none", + a:Array = [], + cnt:int = 0, + points:int = int(vars.points) || 20, + i:int = points, + randomize:Boolean = (vars.randomize !== false), + clamp:Boolean = (vars.clamp === true), + template:Ease = (vars.template is Ease) ? vars.template : null, + strength:Number = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4, + x:Number, y:Number, bump:Number, invX:Number, obj:Object; + while (--i > -1) { + x = randomize ? Math.random() : (1 / points) * i; + y = (template != null) ? template.getRatio(x) : x; + if (taper === "none") { + bump = strength; + } else if (taper === "out") { + invX = 1 - x; + bump = invX * invX * strength; + } else if (taper === "in") { + bump = x * x * strength; + } else if (x < 0.5) { //"both" (start) + invX = x * 2; + bump = invX * invX * 0.5 * strength; + } else { //"both" (end) + invX = (1 - x) * 2; + bump = invX * invX * 0.5 * strength; + } + if (randomize) { + y += (Math.random() * bump) - (bump * 0.5); + } else if (i % 2) { + y += bump * 0.5; + } else { + y -= bump * 0.5; + } + if (clamp) { + if (y > 1) { + y = 1; + } else if (y < 0) { + y = 0; + } + } + a[cnt++] = {x:x, y:y}; + } + a.sortOn("x", Array.NUMERIC); + + _first = new EasePoint(1, 1, null); + i = points; + while (--i > -1) { + obj = a[i]; + _first = new EasePoint(obj.x, obj.y, _first); + } + + _first = _prev = new EasePoint(0, 0, (_first.time !== 0) ? _first : _first.next); + } + + /** + * @private + * DEPRECATED + * This static function provides a quick way to create a RoughEase and immediately reference its ease function + * in a tween, like:

+ * + * TweenLite.from(mc, 2, {alpha:0, ease:RoughEase.create(1.5, 15)});
+ *
+ * + * @param strength amount of variance from the templateEase (Linear.easeNone by default) that each random point can be placed. A low number like 0.1 will hug very closely to the templateEase whereas a larger number like 2 will allow the values to wander further away from the templateEase. + * @param points quantity of random points to plot in the ease. A larger number will cause more (and quicker) flickering. + * @param clamp If true, the ease will prevent random points from exceeding the end value or dropping below the starting value. For example, if you're tweening the x property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if restrictMaxAndMin is true, but if it is false, a x could potentially jump above 100 or below 0 at some point during the tween (it would always end at 100 though). + * @param templateEase an easing equation that should be used as a template or guide. Then random points are plotted at a certain distance away from the templateEase (based on the strength parameter). The default is Linear.easeNone. + * @param taper to make the strength of the roughness taper towards the end or beginning or both, use "out", "in", or "both" respectively here (default is "none"). + * @param randomize to randomize the placement of the points, set randomize to true (otherwise the points will zig-zag evenly across the ease) + * @param name a name to associate with the ease so that you can use RoughEase.byName() to look it up later. Of course you should always make sure you use a unique name for each ease (if you leave it blank, a name will automatically be generated). + * @return easing function + */ + public static function create(strength:Number=1, points:uint=20, clamp:Boolean=false, templateEase:Ease=null, taper:String="none", randomize:Boolean=true, name:String=""):Ease { + return new RoughEase(strength, points, clamp, templateEase, taper, randomize, name); + } + + /** + * @private + * DEPRECATED + * Provides a quick way to look up a RoughEase by its name. + * + * @param name the name of the RoughEase + * @return the RoughEase associated with the name + */ + public static function byName(name:String):Ease { + return _lookup[name]; + } + + /** + * Translates the tween's progress ratio into the corresponding ease ratio. This is the heart of the Ease, where it does all its work. + * + * @param p progress ratio (a value between 0 and 1 indicating the progress of the tween/ease) + * @return translated number + */ + override public function getRatio(p:Number):Number { + var pnt:EasePoint = _prev; + if (p > _prev.time) { + while (pnt.next && p >= pnt.time) { + pnt = pnt.next; + } + pnt = pnt.prev; + } else { + while (pnt.prev && p <= pnt.time) { + pnt = pnt.prev; + } + } + _prev = pnt; + return (pnt.value + ((p - pnt.time) / pnt.gap) * pnt.change); + } + + /** @private [DEPRECATED] Disposes the RoughEase so that it is no longer stored for easy lookups by name with byName(), releasing it for garbage collection. **/ + public function dispose():void { + delete _lookup[_name]; + } + + /** @private [DEPRECATED] name of the RoughEase instance **/ + public function get name():String { + return _name; + } + + /** @private [DEPRECATED] name of the RoughEase instance **/ + public function set name(value:String):void { + delete _lookup[_name]; + _name = value; + _lookup[_name] = this; + } + + /** + * Permits customization of the ease with various parameters. + * + * @param vars a generic object with any of the following properties (all are completely optional): + *
    + *
  • template : Ease - an ease that should be used as a template, like a general guide. + * The RoughEase will plot points that wander from that template. You can use this to influence + * the general shape of the RoughEase. (Default: Linear.easeNone)
  • + * + *
  • strength : Number - controls how far from the template ease the points are allowed to wander + * (a small number like 0.1 keeps it very close to the template ease whereas a larger number like 5 creates + * much bigger variations). (Default: 1)
  • + * + *
  • points : Number - the number of points to be plotted along the ease, making it jerk more or less + * frequently. (Default: 20)
  • + * + *
  • clamp : Boolean - setting clamp to true will prevent points from + * exceeding the end value or dropping below the starting value. For example, if you're tweening the x + * property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if + * clamp is true, but if it is false, x could potentially jump + * above 100 or below 0 at some point during the tween (it would always end at 100 though in this example) + * (Default: false).
  • + * + *
  • taper : String ("in" | "out" | "both" | "none") - to make the strength of the + * roughness taper towards the end or beginning or both, use "out", "in", + * or "both" respectively. (Default: "none")
  • + * + *
  • randomize : Boolean - by default, the placement of points will be randomized (creating the roughness) + * but you can set randomize to false to make the points zig-zag evenly across the ease. + * Using this in conjunction with a taper value can create a nice effect. (Default: true)
  • + *
+ * @return new RoughEase instance that is configured according to the parameters provided + */ + public function config(vars:Object=null):RoughEase { + return new RoughEase(vars); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/Sine.as b/src/com/greensock/easing/Sine.as new file mode 100644 index 0000000..d81abdf --- /dev/null +++ b/src/com/greensock/easing/Sine.as @@ -0,0 +1,29 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Eases with a relatively low power either at the beginning (easeIn), the end (easeOut), or both (easeInOut). + * Sine is a convenience class that congregates the 3 types of Sine eases (SineIn, SineOut, + * and SineInOut) as static properties so that they can be referenced using the standard synatax, like + * Sine.easeIn, Sine.easeOut, and Sine.easeInOut. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Sine { + + /** Eases out with slight deceleration. **/ + public static var easeOut:SineOut = new SineOut(); + + /** Eases in with slight acceleration. **/ + public static var easeIn:SineIn = new SineIn(); + + /** Eases in and then out with slight acceleration/deceleration. **/ + public static var easeInOut:SineInOut = new SineInOut(); + } +} \ No newline at end of file diff --git a/src/com/greensock/easing/SineIn.as b/src/com/greensock/easing/SineIn.as new file mode 100644 index 0000000..2e7ddd2 --- /dev/null +++ b/src/com/greensock/easing/SineIn.as @@ -0,0 +1,31 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in with slight acceleration. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class SineIn extends Ease { + + /** @private **/ + private static const _HALF_PI:Number = Math.PI / 2; + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:SineIn = new SineIn(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return -Math.cos(p * _HALF_PI) + 1; + } + + } + +} diff --git a/src/com/greensock/easing/SineInOut.as b/src/com/greensock/easing/SineInOut.as new file mode 100644 index 0000000..6fe08f8 --- /dev/null +++ b/src/com/greensock/easing/SineInOut.as @@ -0,0 +1,28 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases in and then out with slight acceleration/deceleration. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class SineInOut extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:SineInOut = new SineInOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return -0.5 * (Math.cos(Math.PI * p) - 1); + } + + } + +} diff --git a/src/com/greensock/easing/SineOut.as b/src/com/greensock/easing/SineOut.as new file mode 100644 index 0000000..52ce8da --- /dev/null +++ b/src/com/greensock/easing/SineOut.as @@ -0,0 +1,31 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * @private + * Eases out with slight deceleration. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class SineOut extends Ease { + + /** @private **/ + private static const _HALF_PI:Number = Math.PI / 2; + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:SineOut = new SineOut(); + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + return Math.sin(p * _HALF_PI); + } + + } + +} diff --git a/src/com/greensock/easing/SlowMo.as b/src/com/greensock/easing/SlowMo.as new file mode 100644 index 0000000..4ea35e1 --- /dev/null +++ b/src/com/greensock/easing/SlowMo.as @@ -0,0 +1,113 @@ +/** + * VERSION: 1.11 + * DATE: 2012-06-06 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { + import com.greensock.easing.Ease; +/** + * SlowMo is a configurable ease that produces a slow-motion effect that decelerates initially, then moves + * linearly for a certain portion of the ease (which you can choose) and then accelerates again at the end; + * it's great for effects like zooming text onto the screen, smoothly moving it long enough for people to + * read it, and then zooming it off the screen. Without SlowMo, animators would often try to get the same + * effect by sequencing 3 tweens, one with an easeOut, then another with a Linear.easeNone, and finally + * an easeIn but the problem was that the eases didn't smoothly transition into one another, so you'd see + * sudden shifts in velocity at the joints. SlowMo solves this problem and gives you complete control over + * how strong the eases are on each end and what portion of the movement in the middle is linear. + * + *

The first parameter, linearRatio, determines the proportion of the ease during which + * the rate of change will be linear (steady pace). This should be a number between 0 and 1. For example, + * 0.5 would be half, so the first 25% of the ease would be easing out (decelerating), then 50% would be + * linear, then the final 25% would be easing in (accelerating). If you choose 0.8, that would mean 80% + * of the ease would be linear, leaving 10% on each end to ease. The default is 0.7.

+ * + *

The second parameter, power, determines the strength of the ease at each end. + * If you define a value greater than 1, it will actually reverse the linear portion in the middle + * which can create interesting effects. The default is 0.7.

+ * + *

The third parameter, yoyoMode, provides an easy way to create companion tweens that + * sync with normal SlowMo tweens. For example, let's say you have a SlowMo tween that is zooming some + * text onto the screen and moving it linearly for a while and then zooming off, but you want to + * tween that alpha of the text at the beginning and end of the positional tween. Normally, you'd need + * to create 2 separate alpha tweens, 1 for the fade-in at the beginning and 1 for the fade-out at the + * end and you'd need to calculate their durations manually to ensure that they finish fading in + * by the time the linear motion begins and then they start fading out at the end right when the linear + * motion completes. But to make this whole process much easier, all you'd need to do is create a separate + * tween for the alpha and use the same duration but a SlowMo ease that has its yoyoMode + * parameter set to true.

+ * + * @example Example AS3 example:+import com.greensock.~~; +import com.greensock.easing.~~; + +//use the default SlowMo ease (linearRatio of 0.7 and power of 0.7) +TweenLite.to(myText, 5, {x:600, ease:SlowMo.ease}); + +//use a new SlowMo ease with 50% of the tween being linear (2.5 seconds) and a power of 0.8 +TweenLite.to(myText, 5, {x:600, ease:new SlowMo(0.5, 0.8)}); + +//this gives the exact same effect as the line above, but uses a different syntax +TweenLite.to(myText, 5, {x:600, ease:SlowMo.ease.config(0.5, 0.8)}); + +//now let's create an alpha tween that syncs with the above positional tween, fading it in at the beginning and out at the end +myText.alpha = 0; +TweenLite.to(myText, 5, {alpha:1, ease:SlowMo.ease.config(0.5, 0.8, true)}); + + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + public class SlowMo extends Ease { + + /** The default ease instance which can be reused many times in various tweens in order to conserve memory and improve performance slightly compared to creating a new instance each time. **/ + public static var ease:SlowMo = new SlowMo(); + + /** @private **/ + private var _p:Number; + + /** + * Constructor + * + * @param linearRatio the proportion of the ease during which the rate of change will be linear (steady pace). This should be a number between 0 and 1. For example, 0.5 would be half, so the first 25% of the ease would be easing out (decelerating), then 50% would be linear, then the final 25% would be easing in (accelerating). If you choose 0.8, that would mean 80% of the ease would be linear, leaving 10% on each end to ease. The default is 0.7. + * @param power The strength of the ease at each end. If you define a value above 1, it will actually reverse the linear portion in the middle which can create interesting effects. The default is 0.7. + * @param yoyoMode If true, the ease will reach its destination value mid-tween and maintain it during the entire linear mode and then go back to the original value at the end (like a yoyo of sorts). This can be very useful if, for example, you want the alpha (or some other property) of some text to fade at the front end of a SlowMo positional ease and then back down again at the end of that positional SlowMo tween. Otherwise you would need to create separate tweens for the beginning and ending fades that match up with that positional tween. Example: TweenLite.to(myText, 5, {x:600, ease:SlowMo.ease.config(0.7, 0.7, false)}); TweenLite.from(myText, 5, {alpha:0, ease:SlowMo.ease.config(0.7, 0.7, true)}); + */ + public function SlowMo(linearRatio:Number=0.7, power:Number=0.7, yoyoMode:Boolean=false) { + if (linearRatio > 1) { + linearRatio = 1; + } + _p = (linearRatio != 1) ? power : 0; + _p1 = (1 - linearRatio) / 2; + _p2 = linearRatio; + _p3 = _p1 + _p2; + _calcEnd = yoyoMode; + } + + /** @inheritDoc **/ + override public function getRatio(p:Number):Number { + var r:Number = p + (0.5 - p) * _p; + if (p < _p1) { + return _calcEnd ? 1 - ((p = 1 - (p / _p1)) * p) : r - ((p = 1 - (p / _p1)) * p * p * p * r); + } else if (p > _p3) { + return _calcEnd ? 1 - (p = (p - _p3) / _p1) * p : r + ((p - r) * (p = (p - _p3) / _p1) * p * p * p); + } + return _calcEnd ? 1 : r; + } + + /** + * Permits customization of the ease with various parameters. + * + * @param linearRatio the proportion of the ease during which the rate of change will be linear (steady pace). This should be a number between 0 and 1. For example, 0.5 would be half, so the first 25% of the ease would be easing out (decelerating), then 50% would be linear, then the final 25% would be easing in (accelerating). If you choose 0.8, that would mean 80% of the ease would be linear, leaving 10% on each end to ease. The default is 0.7. + * @param power The strength of the ease at each end. If you define a value above 1, it will actually reverse the linear portion in the middle which can create interesting effects. The default is 0.7. + * @param yoyoMode If true, the ease will reach its destination value mid-tween and maintain it during the entire linear mode and then go back to the original value at the end (like a yoyo of sorts). This can be very useful if, for example, you want the alpha (or some other property) of some text to fade at the front end of a SlowMo positional ease and then back down again at the end of that positional SlowMo tween. Otherwise you would need to create separate tweens for the beginning and ending fades that match up with that positional tween. Example: TweenLite.to(myText, 5, {x:600, ease:SlowMo.ease.config(0.7, 0.7, false)}); TweenLite.from(myText, 5, {alpha:0, ease:SlowMo.ease.config(0.7, 0.7, true)}); + * @return new SlowMo instance that is configured according to the parameters provided + */ + public function config(linearRatio:Number=0.7, power:Number=0.7, yoyoMode:Boolean=false):SlowMo { + return new SlowMo(linearRatio, power, yoyoMode); + } + + } + +} diff --git a/src/com/greensock/easing/SteppedEase.as b/src/com/greensock/easing/SteppedEase.as new file mode 100644 index 0000000..6a4fa28 --- /dev/null +++ b/src/com/greensock/easing/SteppedEase.as @@ -0,0 +1,89 @@ +/** + * VERSION: 0.5 + * DATE: 2010-11-30 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/ + **/ +package com.greensock.easing { +/** + * Most easing equations give a smooth, gradual transition between the start and end values, but SteppedEase provides + * an easy way to define a specific number of steps that the transition should take. For example, if mc.x is 0 and you + * want to tween it to 100 with 5 steps (20, 40, 60, 80, and 100) over the course of 2 seconds, you'd do: + * + * +TweenLite.to(mc, 2, {x:100, ease:SteppedEase.config(5)}); + +//or create an instance directly +var steppedEase = new SteppedEase(5); +TweenLite.to(mc, 3, {y:300, ease:steppedEase}); + + * + *

Note: SteppedEase is optimized for use with the GreenSock tweenining platform, so it isn't intended to be used with other engines. + * Specifically, its easing equation always returns values between 0 and 1.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SteppedEase extends Ease { + /** @private **/ + private var _steps:int; + + /** + * Constructor + * + * @param steps Number of steps between the start and the end values. + */ + public function SteppedEase(steps:int) { + _p1 = 1 / steps; + _steps = steps + 1; + } + + /** + * @private + * Deprecated + * This static function provides a quick way to create a SteppedEase and immediately reference its ease function + * in a tween, like:

+ * + * TweenLite.to(mc, 2, {x:100, ease:SteppedEase.create(5)});
+ *
+ * + * @param steps Number of steps between the start and the end values. + * @return The easing function that can be plugged into a tween + */ + public static function create(steps:int):SteppedEase { + return new SteppedEase(steps); + } + + /** + * Translates the tween's progress ratio into the corresponding ease ratio. This is the heart of the Ease, where it does all its work. + * + * @param p progress ratio (a value between 0 and 1 indicating the progress of the tween/ease) + * @return translated number + */ + override public function getRatio(p:Number):Number { + if (p < 0) { + p = 0; + } else if (p >= 1) { + p = 0.999999999; + } + return ((_steps * p) >> 0) * _p1; + } + + /** + * Permits customization of the ease (defining a number of steps). + * + * @param steps Number of steps between the start and the end values. + * @return new SteppedEase instance that is configured according to the parameters provided + */ + public static function config(steps:int):SteppedEase { + return new SteppedEase(steps); + } + + /** @private Deprecated - Number of steps between the start and the end values. **/ + public function get steps():int { + return _steps - 1; + } + + } +} diff --git a/src/com/greensock/easing/Strong.as b/src/com/greensock/easing/Strong.as new file mode 100644 index 0000000..9ff6cc1 --- /dev/null +++ b/src/com/greensock/easing/Strong.as @@ -0,0 +1,39 @@ +/** + * VERSION: 1.0 + * DATE: 2012-03-22 + * AS3 (AS2 and JS versions are also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing { +/** + * Provides an easeIn, easeOut, and easeInOut with a power (or strength) + * of 4 which is identical to the Power4 ease. The more power, the more + * exaggerated the easing effect. Using a numeric approach like Power4 instead of Strong makes experimenting + * easier and the code reads more intuitively. + * + *

This is one of the eases that is natively accelerated in TweenLite and TweenMax. All of the + * "Power" eases and their counterparts (Linear (0), Quad (1), Cubic (2), Quart (3), Quint (4), and Strong (4)) are + * accelerated.

+ * + *

Example usage:

+ *

+ * TweenLite.to(obj, 1, {x:100, ease:Strong.easeOut}); + *

+ * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class Strong { + + /** Eases out with a power of 4 **/ + public static var easeOut:Ease = new Ease(null,null,1,4); + + /** Eases in with a power of 4 **/ + public static var easeIn:Ease = new Ease(null,null,2,4); + + /** Eases in and then out with a power of 4 **/ + public static var easeInOut:Ease = new Ease(null,null,3,4); + + } +} diff --git a/src/com/greensock/easing/core/EasePoint.as b/src/com/greensock/easing/core/EasePoint.as new file mode 100644 index 0000000..25b3544 --- /dev/null +++ b/src/com/greensock/easing/core/EasePoint.as @@ -0,0 +1,35 @@ +/** + * VERSION: 1.0.0 + * DATE: 2013-03-27 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.easing.core { +/** + * @private + * Used by RoughEase. Couldn't use an internal class due to instantiation order issues caused by referencing an EasePoint inside the RoughEase constructor when we create an "ease" public static var that's a RoughEase. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + final public class EasePoint { + public var time:Number; + public var gap:Number; + public var value:Number; + public var change:Number; + public var next:EasePoint; + public var prev:EasePoint; + + public function EasePoint(time:Number, value:Number, next:EasePoint) { + this.time = time; + this.value = value; + if (next) { + this.next = next; + next.prev = this; + this.change = next.value - value; + this.gap = next.time - time; + } + } + } +} \ No newline at end of file diff --git a/src/com/greensock/events/LoaderEvent.as b/src/com/greensock/events/LoaderEvent.as new file mode 100644 index 0000000..6fd8373 --- /dev/null +++ b/src/com/greensock/events/LoaderEvent.as @@ -0,0 +1,123 @@ +/** + * VERSION: 1.87 + * DATE: 2011-07-30 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.events { + + import flash.events.Event; +/** + * An Event dispatched by one of the loaders in the LoaderMax system. + *

+ * + * Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership. + * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderEvent extends Event { + /** Dispatched by a LoaderMax (or other loader that may dynamically recognize nested loaders like XMLLoader and SWFLoader) when one of its children begins loading. **/ + public static const CHILD_OPEN:String="childOpen"; + /** Dispatched by a LoaderMax (or other loader that may dynamically recognize nested loaders like XMLLoader and SWFLoader) when one of its children dispatches a PROGRESS Event. **/ + public static const CHILD_PROGRESS:String="childProgress"; + /** Dispatched by a LoaderMax (or other loader that may dynamically recognize nested loaders like XMLLoader and SWFLoader) when one of its children aborts its loading. This can happen when the loader fails, when cancel() is manually called, or when another loader is prioritized in the loading queue. **/ + public static const CHILD_CANCEL:String="childCancel"; + /** Dispatched by a LoaderMax (or other loader that may dynamically recognize nested loaders like XMLLoader and SWFLoader) when one of its children finishes loading. **/ + public static const CHILD_COMPLETE:String="childComplete"; + /** Dispatched by a LoaderMax (or other loader that may dynamically recognize nested loaders like XMLLoader and SWFLoader) when one of its children fails to load. **/ + public static const CHILD_FAIL:String="childFail"; + /** Dispatched when the loader begins loading, like when its load() method is called. **/ + public static const OPEN:String="open"; + /** Dispatched when the loader's bytesLoaded changes. **/ + public static const PROGRESS:String="progress"; + /** Dispatched when the loader aborts its loading. This can happen when the loader fails, when cancel() is manually called, or when another loader is prioritized in the loading queue. **/ + public static const CANCEL:String="cancel"; + /** Dispatched when the loader fails. **/ + public static const FAIL:String="fail"; + /** Dispatched when the loader initializes which means different things for different loaders. For example, a SWFLoader dispatches INIT when it downloads enough of the swf to render the first frame. When a VideoLoader receives MetaData, it dispatches its INIT event, as does an MP3Loader when it receives ID3 data. See the docs for each class for specifics. **/ + public static const INIT:String="init"; + /** Dispatched when the loader finishes loading. **/ + public static const COMPLETE:String="complete"; + /** Dispatched when the loader (or one of its children) receives an HTTP_STATUS event (see Adobe docs for specifics). **/ + public static const HTTP_STATUS:String="httpStatus"; + /** Dispatched when the loader (or one of its children) receives an HTTP_RESPONSE_STATUS event (see Adobe docs for specifics). **/ + public static const HTTP_RESPONSE_STATUS:String="httpResponseStatus"; + /** When script access is denied for a particular loader (like if an ImageLoader or SWFLoader tries loading from another domain and the crossdomain.xml file is missing or doesn't grant permission properly), a SCRIPT_ACCESS_DENIED LoaderEvent will be dispatched. **/ + public static const SCRIPT_ACCESS_DENIED:String="scriptAccessDenied"; + /** Dispatched when the loader (or one of its children) throws any error, like an IO_ERROR or SECURITY_ERROR. **/ + public static const ERROR:String="error"; + /** Dispatched when the the loader (or one of its children) encounters an IO_ERROR (typically when it cannot find the file at the specified url). **/ + public static const IO_ERROR:String="ioError"; + /** Dispatched when the loader (or one of its children) encounters a SECURITY_ERROR (see Adobe's docs for details). **/ + public static const SECURITY_ERROR:String="securityError"; + /** Dispatched when a swf that's loaded by a SWFLoader encounters an UncaughtErrorEvent which is basically any Error that gets thrown outside of a try...catch statement. This can be useful when subloading swfs from a 3rd party that may contain errors. However, UNCAUGHT_ERROR events will only be dispatched if the parent swf is published for Flash Player 10.1 or later! See SWFLoader's suppressUncaughtErrors special property if you'd like to have it automatically suppress these errors. The original UncaughtErrorEvent is stored in the LoaderEvent's data property. So, for example, if you'd like to call preventDefault() on that event, you'd do myLoaderEvent.data.preventDefault(). **/ + public static const UNCAUGHT_ERROR:String="uncaughtError"; + /** + * Dispatched when the loader unloads (which happens when either unload() or dispose(true) is called + * or if a loader is canceled while in the process of loading). This can be particularly useful to listen for in a swf that was + * subloaded by a SWFLoader so that it can get notified when the parent has requested an unload. For example, in the subloaded swf, + * you could do: + * +var curParent:DisplayObjectContainer = this.parent; +while (curParent) { + if (curParent.hasOwnProperty("rawContent") && curParent.hasOwnProperty("loader")) { + Object(curParent).loader.addEventListener("unload", dispose, false, 0, true); + } + curParent = curParent.parent; +} +function dispose(event:Event):void { + //do cleanup stuff here like removing event listeners, stopping sounds, closing NetStreams, etc... +} + + **/ + public static const UNLOAD:String="unload"; + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _ready:Boolean; + + /** For ERROR, FAIL, and CHILD_FAIL events, this text will give more details about the error or failure. **/ + public var text:String; + /** Event-related data which varies based on the type of event. For example, VideoLoader dispatches a VIDEO_CUE_POINT event containing data about the cue point. A SWFLoader dispatches an UNCAUGHT_ERROR event containing the original UncaughtErrorEvent instance. **/ + public var data:*; + + /** + * Constructor + * + * @param type Type of event + * @param target Target + * @param text Error text (if any) + */ + public function LoaderEvent(type:String, target:Object, text:String="", data:*=null){ + super(type, false, false); + _target = target; + this.text = text; + this.data = data; + } + + /** @inheritDoc **/ + public override function clone():Event{ + return new LoaderEvent(this.type, _target, this.text, this.data); + } + + /** + * The loader associated with the LoaderEvent. This may be different than the currentTarget. + * The target always refers to the originating loader, so if there is an ImageLoader nested inside + * a LoaderMax instance and you add an event listener to the LoaderMax, if the ImageLoader dispatches an error + * event, the event's target will refer to the ImageLoader whereas the currentTarget will + * refer to the LoaderMax instance that is currently processing the event. + **/ + override public function get target():Object { + if (_ready) { + return _target; + } else { + //when the event is re-dispatched, Flash's event system checks to see if the target has been set and if it has, Flash will clone() and reset the target so we need to report the target as null the first time and then on subsequent calls, report the real target. + _ready = true; + } + return null; + } + + } + +} \ No newline at end of file diff --git a/src/com/greensock/events/TweenEvent.as b/src/com/greensock/events/TweenEvent.as new file mode 100644 index 0000000..dc38a54 --- /dev/null +++ b/src/com/greensock/events/TweenEvent.as @@ -0,0 +1,29 @@ +package com.greensock.events { + import flash.events.Event; +/** + * Used for dispatching events from the GreenSock Animation Platform. + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenEvent extends Event { + /** @private **/ + public static const VERSION:Number = 12.0; + public static const START:String = "start"; + public static const UPDATE:String = "change"; + public static const COMPLETE:String = "complete"; + public static const REVERSE_COMPLETE:String = "reverseComplete"; + public static const REPEAT:String = "repeat"; + + public function TweenEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) { + super(type, bubbles, cancelable); + } + + public override function clone():Event { + return new TweenEvent(this.type, this.bubbles, this.cancelable); + } + + } + +} \ No newline at end of file diff --git a/src/com/greensock/layout/AlignMode.as b/src/com/greensock/layout/AlignMode.as new file mode 100644 index 0000000..3f9621e --- /dev/null +++ b/src/com/greensock/layout/AlignMode.as @@ -0,0 +1,31 @@ +/** + * VERSION: 1.1 + * DATE: 2011-04-26 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/ + **/ +package com.greensock.layout { +/** + * Provides constants for defining the alignment of objects. + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class AlignMode { + + /** Align with the top of the area. **/ + public static const TOP:String = "top"; + /** Align with the center of the area. **/ + public static const CENTER:String = "center"; + /** Align with the right side of the area. **/ + public static const RIGHT:String = "right"; + /** Align with the left side of the area. **/ + public static const LEFT:String = "left"; + /** Align with the bottom of the area. **/ + public static const BOTTOM:String = "bottom"; + /** No alignment **/ + public static const NONE:String = "none"; + + } +} \ No newline at end of file diff --git a/src/com/greensock/layout/AutoFitArea.as b/src/com/greensock/layout/AutoFitArea.as new file mode 100644 index 0000000..5e368c3 --- /dev/null +++ b/src/com/greensock/layout/AutoFitArea.as @@ -0,0 +1,789 @@ +/** + * VERSION: 2.54 + * DATE: 2011-04-26 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/autofitarea/ + **/ +package com.greensock.layout { + import flash.display.BitmapData; + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.display.Graphics; + import flash.display.Shape; + import flash.events.Event; + import flash.geom.ColorTransform; + import flash.geom.Matrix; + import flash.geom.Rectangle; +/** + * AutoFitArea allows you to define a rectangular area and then attach() DisplayObjects + * so that they automatically fill the area, scaling/stretching in any of the following modes: STRETCH, + * PROPORTIONAL_INSIDE, PROPORTIONAL_OUTSIDE, NONE, WIDTH_ONLY, or HEIGHT_ONLY. Horizontally + * align the attached DisplayObjects left, center, or right. Vertically align them top, center, or bottom. + * AutoFitArea extends the Shape class, so you can alter the width/height/scaleX/scaleY/x/y + * properties of the AutoFitArea and then all of the attached objects will automatically be affected. + * Attach as many DisplayObjects as you want. To make visualization easy, you can set the previewColor + * to any color and set the preview property to true in order to see the area on the stage + * (or simply use it like a regular Shape by adding it to the display list with addChild(), but the + * preview property makes it simpler because it automatically ensures that it is behind + * all of its attached DisplayObjects in the stacking order). + * + *

When you attach() a DisplayObject, you can define a minimum and maximum width and height. + * AutoFitArea doesn't require that the DisplayObject's registration point be in its upper left corner + * either. You can even set the calculateVisible parameter to true when attaching an object + * so that AutoFitArea will ignore masked areas inside the DisplayObject (this is more processor-intensive, + * so beware).

+ * + *

For scaling, AutoFitArea alters the DisplayObject's width and/or height + * properties unless it is rotated in which case it alters the DisplayObject's transform.matrix + * directly so that accurate stretching/skewing can be accomplished.

+ * + *

There is also a LiquidArea class that extends AutoFitArea and integrates with + * LiquidStage so that it automatically + * adjusts its size whenever the stage is resized. This makes it simple to create things like + * a background that proportionally fills the stage or a bar that always stretches horizontally + * to fill the stage but stays stuck to the bottom, etc.

+ * + * +import com.greensock.layout.~~; + +//create a 300x100 rectangular area at x:50, y:70 that stretches when the stage resizes (as though its top left and bottom right corners are pinned to their corresponding PinPoints on the stage) +var area:AutoFitArea = new AutoFitArea(this, 50, 70, 300, 100); + +//attach a "myImage" Sprite to the area and set its ScaleMode to PROPORTIONAL_OUTSIDE and crops the extra content that spills over the edges +area.attach(myImage, {scaleMode:ScaleMode.PROPORTIONAL_OUTSIDE, crop:true}); + +//if you'd like to preview the area visually, set preview to true (by default previewColor is red) +area.preview = true; + +//attach a CHANGE event listener to the area +area.addEventListener(Event.CHANGE, onAreaUpdate); +function onAreaUpdate(event:Event):void { + trace("updated AutoFitArea"); +} + +//to create an AutoFitArea exactly around a "myImage" DisplayObject so that it conforms its initial dimensions around the DisplayObject, use the static createAround() method: +var area:AutoFitArea = AutoFitArea.createAround(myImage); + + + * + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class AutoFitArea extends Shape { + /** @private **/ + public static const version:Number = 2.54; + + /** @private **/ + private static var _bd:BitmapData; + /** @private **/ + private static var _rect:Rectangle = new Rectangle(0, 0, 2800, 2800); + /** @private **/ + private static var _matrix:Matrix = new Matrix(); + + /** @private **/ + protected var _parent:DisplayObjectContainer; + /** @private **/ + protected var _previewColor:uint; + /** @private **/ + protected var _rootItem:AutoFitItem; + /** @private **/ + protected var _hasListener:Boolean; + /** @private **/ + protected var _preview:Boolean; + /** @private **/ + protected var _tweenMode:Boolean; + /** @private **/ + protected var _width:Number; + /** @private **/ + protected var _height:Number; + + /** + * Constructor + * + * @param parent The parent DisplayObjectContainer in which the AutoFitArea should be created. All objects that get attached must share the same parent. + * @param x x coordinate of the AutoFitArea's upper left corner + * @param y y coordinate of the AutoFitArea's upper left corner + * @param width width of the AutoFitArea + * @param height height of the AutoFitArea + * @param previewColor color of the AutoFitArea (which won't be seen unless you set preview to true or manually add it to the display list with addChild()) + */ + public function AutoFitArea(parent:DisplayObjectContainer, x:Number=0, y:Number=0, width:Number=100, height:Number=100, previewColor:uint=0xFF0000) { + super(); + super.x = x; + super.y = y; + if (parent == null) { + throw new Error("AutoFitArea parent cannot be null"); + } + _parent = parent; + _width = width; + _height = height; + _redraw(previewColor); + } + + /** + * Creates an AutoFitArea with its initial dimensions fit precisely around a target DisplayObject. It also attaches + * the target DisplayObject immediately. + * + * @param target The target DisplayObject whose position and dimensions the AutoFitArea should match initially. + * @param vars An object used for defining various optional parameters (see below for list) - this is more readable and concise than defining 11 or more normal arguments. + * For example, createAround(mc, {scaleMode:"proportionalOutside", crop:true}); instead of createAround(mc, "proportionalOutside", "center", "center", true, 0, 99999999, 0, 99999999, false, NaN, false);. + * The following optional parameters are recognized: + *
    + *
  • scaleMode : String - Determines how the target should be scaled to fit the area. Use the ScaleMode class constants: STRETCH, PROPORTIONAL_INSIDE, PROPORTIONAL_OUTSIDE, NONE, WIDTH_ONLY, or HEIGHT_ONLY
  • + *
  • hAlign : String - Horizontal alignment of the target inside the area. Use the AlignMode class constants: LEFT, CENTER, and RIGHT.
  • + *
  • vAlign : String - Vertical alignment of the target inside the area. Use the AlignMode class constants: TOP, CENTER, and BOTTOM.
  • + *
  • crop : Boolean - If true, a mask will be created and added to the display list so that the target will be cropped wherever it exceeds the bounds of the AutoFitArea.
  • + *
  • roundPosition : Boolean - To force the target's x/y position to snap to whole pixel values, set roundPosition to true (it is false by default).
  • + *
  • customBoundsTarget : DisplayObject - A DisplayObject that AutoFitArea/LiquidArea should use when measuring bounds instead of the target. For example, maybe the target contains 3 boxes arranged next to each other, left-to-right and instead of fitting ALL of those boxes into the area, you only want the center one fit into the area. In this case, you can define the customBoundsTarget as that center box so that the AutoFitArea/LiquidArea only uses it when calculating bounds. Make sure that the object is in the display list (its visible property can be set to false if you want to use an invisible object to define custom bounds).
  • + *
  • minWidth : Number - Minimum width to which the target is allowed to scale
  • + *
  • maxWidth : Number - Maximum width to which the target is allowed to scale
  • + *
  • minHeight : Number - Minimum height to which the target is allowed to scale
  • + *
  • maxHeight : Number - Maximum height to which the target is allowed to scale
  • + *
  • calculateVisible : Boolean - If true, only the visible portions of the target will be taken into account when determining its position and scale which can be useful for objects that have masks applied (otherwise, Flash reports their width/height and getBounds() values including the masked portions). Setting calculateVisible to true degrades performance, so only use it when absolutely necessary.
  • + *
  • customAspectRatio : Number - Normally if you set the scaleMode to PROPORTIONAL_INSIDE or PROPORTIONAL_OUTSIDE, its native (unscaled) dimensions will be used to determine the proportions (aspect ratio), but if you prefer to define a custom width-to-height ratio, use customAspectRatio. For example, if an item is 100 pixels wide and 50 pixels tall at its native size, the aspect ratio would be 100/50 or 2. If, however, you want it to be square (a 1-to-1 ratio), the customAspectRatio would be 1.
  • + *
  • previewColor : uint - The preview color of the AutoFitArea (default is 0xFF0000). To preview, you must set the AutoFitArea's visible property to true (it is false by default).
  • + *
+ * @return An AutoFitArea instance + */ + public static function createAround(target:DisplayObject, vars:Object=null, ...args):AutoFitArea { + if (vars == null || typeof(vars) == "string") { + //sensed old method - parse the params for backwards compatibility + vars = {scaleMode:vars || "proportionalInside", + hAlign:args[0] || "center", + vAlign:args[1] || "center", + crop:Boolean(args[2]), + minWidth:args[3] || 0, + maxWidth:(isNaN(args[4]) ? 999999999 : args[4]), + minHeight:args[5] || 0, + maxHeight:(isNaN(args[6]) ? 999999999 : args[6]), + calculateVisible:Boolean(args[8])}; + } + var boundsTarget:DisplayObject = (vars.customBoundsTarget is DisplayObject) ? vars.customBoundsTarget : target; + var previewColor:uint = isNaN(args[7]) ? (("previewColor" in vars) ? uint(vars.previewColor) : 0xFF0000) : args[7]; + var bounds:Rectangle = (vars.calculateVisible == true) ? getVisibleBounds(boundsTarget, target.parent) : boundsTarget.getBounds(target.parent); + var afa:AutoFitArea = new AutoFitArea(target.parent, bounds.x, bounds.y, bounds.width, bounds.height, previewColor); + afa.attach(target, vars); + return afa; + } + + /** + * Attaches a DisplayObject, causing it to automatically scale to fit the area in one of the + * following ScaleModes: STRETCH, PROPORTIONAL_INSIDE, PROPORTIONAL_OUTSIDE, NONE, WIDTH_ONLY, + * or HEIGHT_ONLY. Horizontally and vertically align the object within the area as well. + * When the area resizes, all attached DisplayObjects will automatically be moved/scaled accordingly. + * + * @param target The DisplayObject to attach and scale/stretch to fit within the area. + * @param vars An object used for defining various optional parameters (see below for list) - this is more readable and concise than defining 11 or more normal arguments. + * For example, attach(mc, {scaleMode:"proportionalOutside", crop:true}); instead of attach(mc, "proportionalOutside", "center", "center", true, 0, 99999999, 0, 99999999, false, NaN, false);. + * The following optional parameters are recognized: + *
    + *
  • scaleMode : String - Determines how the target should be scaled to fit the area. Use the ScaleMode class constants: STRETCH, PROPORTIONAL_INSIDE, PROPORTIONAL_OUTSIDE, NONE, WIDTH_ONLY, or HEIGHT_ONLY
  • + *
  • hAlign : String - Horizontal alignment of the target inside the area. Use the AlignMode class constants: LEFT, CENTER, and RIGHT.
  • + *
  • vAlign : String - Vertical alignment of the target inside the area. Use the AlignMode class constants: TOP, CENTER, and BOTTOM.
  • + *
  • crop : Boolean - If true, a mask will be created and added to the display list so that the target will be cropped wherever it exceeds the bounds of the AutoFitArea.
  • + *
  • roundPosition : Boolean - To force the target's x/y position to snap to whole pixel values, set roundPosition to true (it is false by default).
  • + *
  • customBoundsTarget : DisplayObject - A DisplayObject that AutoFitArea/LiquidArea should use when measuring bounds instead of the target. For example, maybe the target contains 3 boxes arranged next to each other, left-to-right and instead of fitting ALL of those boxes into the area, you only want the center one fit into the area. In this case, you can define the customBoundsTarget as that center box so that the AutoFitArea/LiquidArea only uses it when calculating bounds. Make sure that the object is in the display list (its visible property can be set to false if you want to use an invisible object to define custom bounds).
  • + *
  • minWidth : Number - Minimum width to which the target is allowed to scale
  • + *
  • maxWidth : Number - Maximum width to which the target is allowed to scale
  • + *
  • minHeight : Number - Minimum height to which the target is allowed to scale
  • + *
  • maxHeight : Number - Maximum height to which the target is allowed to scale
  • + *
  • calculateVisible : Boolean - If true, only the visible portions of the target will be taken into account when determining its position and scale which can be useful for objects that have masks applied (otherwise, Flash reports their width/height and getBounds() values including the masked portions). Setting calculateVisible to true degrades performance, so only use it when absolutely necessary.
  • + *
  • customAspectRatio : Number - Normally if you set the scaleMode to PROPORTIONAL_INSIDE or PROPORTIONAL_OUTSIDE, its native (unscaled) dimensions will be used to determine the proportions (aspect ratio), but if you prefer to define a custom width-to-height ratio, use customAspectRatio. For example, if an item is 100 pixels wide and 50 pixels tall at its native size, the aspect ratio would be 100/50 or 2. If, however, you want it to be square (a 1-to-1 ratio), the customAspectRatio would be 1.
  • + *
+ */ + public function attach(target:DisplayObject, vars:Object=null, ...args):void { + if (target.parent != _parent) { + throw new Error("The parent of the DisplayObject " + target.name + " added to AutoFitArea " + this.name + " doesn't share the same parent."); + } + if (vars == null || typeof(vars) == "string") { + //sensed old method - parse the params for backwards compatibility + vars = {scaleMode:vars || "proportionalInside", + hAlign:args[0] || "center", + vAlign:args[1] || "center", + crop:Boolean(args[2]), + minWidth:args[3] || 0, + maxWidth:(isNaN(args[4]) ? 999999999 : args[4]), + minHeight:args[5] || 0, + maxHeight:(isNaN(args[6]) ? 999999999 : args[6]), + calculateVisible:Boolean(args[7]), + customAspectRatio:Number(args[8]), + roundPosition:Boolean(args[9])}; + } + + release(target); + _rootItem = new AutoFitItem(target, vars, _rootItem); + if (vars != null && vars.crop == true) { + var shape:Shape = new Shape(); + var bounds:Rectangle = this.getBounds(this); + shape.graphics.beginFill(_previewColor, 1); + shape.graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height); + shape.graphics.endFill(); + shape.visible = false; + _parent.addChild(shape); + _rootItem.mask = shape; + target.mask = shape; + } + if (_preview) { + this.preview = true; + } + update(null); + } + + /** + * Releases control of an attached DisplayObject. + * + * @param target The DisplayObject to release + * @return if the target was found and released, this value will be true. If the target isn't attached, it will be false. + */ + public function release(target:DisplayObject):Boolean { + var item:AutoFitItem = getItem(target); + if (item == null) { + return false; + } + if (item.mask != null) { + if (item.mask.parent) { + item.mask.parent.removeChild(item.mask); + } + target.mask = null; + item.mask = null; + } + if (item.next) { + item.next.prev = item.prev; + } + if (item.prev) { + item.prev.next = item.next; + } else if (item == _rootItem) { + _rootItem = item.next; + } + item.next = item.prev = null; + item.boundsTarget = null; + item.target = null; + return true; + } + + /** + * Returns an Array of all attached DisplayObjects. + * + * @return An array of all attached objects + */ + public function getAttachedObjects():Array { + var a:Array = []; + var cnt:uint = 0; + var item:AutoFitItem = _rootItem; + while (item) { + a[cnt++] = item.target; + item = item.next; + } + return a; + } + + /** @private **/ + protected function getItem(target:DisplayObject):AutoFitItem { + var item:AutoFitItem = _rootItem; + while (item) { + if (item.target == target) { + return item; + } + item = item.next; + } + return null; + } + + + /** + * Forces the area to update, making any necessary adjustments to the scale/position of attached objects. + * @param event An optional event (which is unused internally) - this makes it possible to have an ENTER_FRAME or some other listener call this method if, for example, you want the AutoFitArea to constantly update and make any adjustments to attached objects that may have resized or been manually moved. + **/ + public function update(event:Event=null):void { + //create local variables to speed things up + var width:Number = this.width; + var height:Number = this.height; + var x:Number = this.x; + var y:Number = this.y; + var matrix:Matrix = this.transform.matrix; + + var item:AutoFitItem = _rootItem; + var w:Number, h:Number, tx:Number, ty:Number, target:DisplayObject, innerBounds:Rectangle, outerBounds:Rectangle, tRatio:Number, scaleMode:String, ratio:Number, angle:Number, sin:Number, cos:Number, m:Matrix, wScale:Number, hScale:Number, mPrev:Matrix; + while (item) { + target = item.target; + scaleMode = item.scaleMode; + + if (scaleMode != ScaleMode.NONE) { + + //if the width or height is zero, we cannot effectively scale using multiplication/division, so make sure that the target is at least 1 pixel tall/wide before proceeding. Remember, it'll get adjusted back to what it should be later. + if (scaleMode != ScaleMode.HEIGHT_ONLY && target.width == 0) { + target.width = 1; + } + if (scaleMode != ScaleMode.WIDTH_ONLY && target.height == 0) { + target.height = 1; + } + + if (item.calculateVisible) { + innerBounds = item.bounds = getVisibleBounds(item.boundsTarget, target); + outerBounds = getVisibleBounds(item.boundsTarget, _parent); + } else { + innerBounds = item.boundsTarget.getBounds(target); + outerBounds = item.boundsTarget.getBounds(_parent); + } + tRatio = (item.hasCustomRatio) ? item.aspectRatio : innerBounds.width / innerBounds.height; + + m = target.transform.matrix; + if (m.b != 0 || m.a == 0 || m.d == 0) { + //if the width/height is zero, we cannot accurately measure the angle. + if (m.a == 0 || m.d == 0) { + m = target.transform.matrix = item.matrix; + } else { + //inline operations are about 10 times faster than doing item.matrix = m.clone(); + mPrev = item.matrix; + mPrev.a = m.a; + mPrev.b = m.b; + mPrev.c = m.c; + mPrev.d = m.d; + mPrev.tx = m.tx; + mPrev.ty = m.ty; + } + angle = Math.atan2(m.b, m.a); + if (m.a < 0 && m.d >= 0) { + if (angle <= 0) { + angle += Math.PI; + } else { + angle -= Math.PI; + } + } + sin = Math.sin(angle); + if (sin < 0) { + sin = -sin; + } + cos = Math.cos(angle); + if (cos < 0) { + cos = -cos; + } + tRatio = (tRatio * cos + sin) / (tRatio * sin + cos); + } + + w = (width > item.maxWidth) ? item.maxWidth : (width < item.minWidth) ? item.minWidth : width; + h = (height > item.maxHeight) ? item.maxHeight : (height < item.minHeight) ? item.minHeight : height; + ratio = w / h; + + if ((tRatio < ratio && scaleMode == ScaleMode.PROPORTIONAL_INSIDE) || (tRatio > ratio && scaleMode == ScaleMode.PROPORTIONAL_OUTSIDE)) { + w = h * tRatio; + if (w == 0) { + h = 0; + } else if (w > item.maxWidth) { + w = item.maxWidth; + h = w / tRatio; + } else if (w < item.minWidth) { + w = item.minWidth; + h = w / tRatio; + } + } + if ((tRatio > ratio && scaleMode == ScaleMode.PROPORTIONAL_INSIDE) || (tRatio < ratio && scaleMode == ScaleMode.PROPORTIONAL_OUTSIDE)) { + h = w / tRatio; + if (h > item.maxHeight) { + h = item.maxHeight; + w = h * tRatio; + } else if (h < item.minHeight) { + h = item.minHeight; + w = h * tRatio; + } + } + + if (w != 0 && h != 0) { + wScale = w / outerBounds.width; + hScale = h / outerBounds.height; + } else { + wScale = hScale = 0; + } + + if (scaleMode != ScaleMode.HEIGHT_ONLY) { + if (item.calculateVisible) { + item.scaleVisibleWidth(wScale); + } else if (m.b != 0) { + m.a *= wScale; + m.c *= wScale; + target.transform.matrix = m; + } else { + target.width *= wScale; + } + } + if (scaleMode != ScaleMode.WIDTH_ONLY) { + if (item.calculateVisible) { + item.scaleVisibleHeight(hScale); + } else if (m.b != 0) { + m.d *= hScale; + m.b *= hScale; + target.transform.matrix = m; + } else { + target.height *= hScale; + } + } + + } + + if (item.hasDrawNow) { //some components incorrectly report getBounds() until after we drawNow() + Object(target).drawNow(); + } + + if (scaleMode != ScaleMode.NONE && innerBounds.x == 0 && innerBounds.y == 0) { //for optimization + if (scaleMode != ScaleMode.HEIGHT_ONLY) { + outerBounds.width = w; + } + if (scaleMode != ScaleMode.WIDTH_ONLY) { + outerBounds.height = h; + } + } else { + outerBounds = (item.calculateVisible) ? getVisibleBounds(item.boundsTarget, _parent) : item.boundsTarget.getBounds(_parent); + } + + tx = target.x; + ty = target.y; + if (item.hAlign == AlignMode.LEFT) { + tx += (x - outerBounds.x); + } else if (item.hAlign == AlignMode.CENTER) { + tx += (x - outerBounds.x) + ((width - outerBounds.width) * 0.5); + } else if (item.hAlign == AlignMode.RIGHT) { + tx += (x - outerBounds.x) + (width - outerBounds.width); + } + + if (item.vAlign == AlignMode.TOP) { + ty += (y - outerBounds.y); + } else if (item.vAlign == AlignMode.CENTER) { + ty += (y - outerBounds.y) + ((height - outerBounds.height) * 0.5); + } else if (item.vAlign == AlignMode.BOTTOM) { + ty += (y - outerBounds.y) + (height - outerBounds.height); + } + + if (item.roundPosition) { + tx = (tx + 0.5) >> 0; //much faster than Math.round() + ty = (ty + 0.5) >> 0; + } + + target.x = tx; + target.y = ty; + + if (item.mask) { + item.mask.transform.matrix = matrix; + } + + item = item.next; + } + + if (_hasListener) { + dispatchEvent(new Event(Event.CHANGE)); + } + } + + /** + * Enables the area's tween mode; normally, any changes to the area's transform properties like + * x, y, scaleX, scaleY, width, or height will force an immediate + * update() call but when the area is in tween mode, that automatic update() + * is suspended. This effects perfomance because if, for example, you tween the area's x, y, width, + * and height properties simultaneously, update() would get called 4 times + * each frame (once for each property) even though it only really needs to be called once after all + * properties were updated inside the tween. So to maximize performance during a tween, it is best + * to use the tween's onStart to call enableTweenMode() at the beginning + * of the tween, use the tween's onUpdate to call the area's update() method, + * and then the tween's onComplete to call disableTweenMode() like so:

+ * + * TweenLite.to(myArea, 3, {x:100, y:50, width:300, height:250, onStart:myArea.enableTweenMode, onUpdate:myArea.update, onComplete:myArea.disableTweenMode});

+ **/ + public function enableTweenMode():void { + _tweenMode = true; + } + + /** + * Disables the area's tween mode; normally, any changes to the area's transform properties like + * x, y, scaleX, scaleY, width, or height will force an immediate + * update() call but when the area is in tween mode, that automatic update() + * is suspended. This effects perfomance because if, for example, you tween the area's x, y, width, + * and height properties simultaneously, update() would get called 4 times + * each frame (once for each property) even though it only really needs to be called once after all + * properties were updated inside the tween. So to maximize performance during a tween, it is best + * to use the tween's onStart to call enableTweenMode() at the beginning + * of the tween, use the tween's onUpdate to call the area's update() method, + * and then the tween's onComplete to call disableTweenMode() like so:

+ * + * TweenLite.to(myArea, 3, {x:100, y:50, width:300, height:250, onStart:myArea.enableTweenMode, onUpdate:myArea.update, onComplete:myArea.disableTweenMode});

+ **/ + public function disableTweenMode():void { + _tweenMode = false; + } + + /** + * Allows you to add an Event.CHANGE event listener. + * + * @param type Event type (Event.CHANGE) + * @param listener Listener function + * @param useCapture useCapture + * @param priority Priority level + * @param useWeakReference Use weak references + */ + override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + _hasListener = true; + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** Destroys the instance by releasing all DisplayObjects, setting preview to false, and nulling references to the parent, ensuring that garbage collection isn't hindered. **/ + public function destroy():void { + if (_preview) { + this.preview = false; + } + var nxt:AutoFitItem; + var item:AutoFitItem = _rootItem; + while (item) { + nxt = item.next; + release(item.target); + item = nxt; + } + if (_bd != null) { + _bd.dispose(); + _bd = null; + } + _parent = null; + } + + /** @private For objects with masks, the only way to accurately report the bounds of the visible areas is to use BitmapData. **/ + protected static function getVisibleBounds(target:DisplayObject, targetCoordinateSpace:DisplayObject):Rectangle { + if (_bd == null) { + _bd = new BitmapData(2800, 2800, true, 0x00FFFFFF); + } + var msk:DisplayObject = target.mask; + target.mask = null; + _bd.fillRect(_rect, 0x00FFFFFF); + _matrix.tx = _matrix.ty = 0; + var offset:Rectangle = target.getBounds(targetCoordinateSpace); + var m:Matrix = (targetCoordinateSpace == target) ? _matrix : target.transform.matrix; + m.tx -= offset.x; + m.ty -= offset.y; + _bd.draw(target, m, null, "normal", _rect, false); + var bounds:Rectangle = _bd.getColorBoundsRect(0xFF000000, 0x00000000, false); + bounds.x += offset.x; + bounds.y += offset.y; + target.mask = msk; + return bounds; + } + + /** @private **/ + protected function _redraw(color:uint):void { + _previewColor = color; + var g:Graphics = this.graphics; + g.clear(); + g.beginFill(_previewColor, 1); + g.drawRect(0, 0, _width, _height); + g.endFill(); + } + +//---- GETTERS / SETTERS --------------------------------------------------------------------------- + + /** @inheritDoc **/ + override public function set x(value:Number):void { + super.x = value; + if (!_tweenMode) { + update(); + } + } + + /** @inheritDoc **/ + override public function set y(value:Number):void { + super.y = value; + if (!_tweenMode) { + update(); + } + } + + /** @inheritDoc **/ + override public function set width(value:Number):void { + super.width = value; + if (!_tweenMode) { + update(); + } + } + + /** @inheritDoc **/ + override public function set height(value:Number):void { + super.height = value; + if (!_tweenMode) { + update(); + } + } + + /** @inheritDoc **/ + override public function set scaleX(value:Number):void { + super.scaleX = value; + update(); + } + + /** @inheritDoc **/ + override public function set scaleY(value:Number):void { + super.scaleY = value; + update(); + } + + /** @inheritDoc **/ + override public function set rotation(value:Number):void { + trace("Warning: AutoFitArea instances should not be rotated."); + } + + /** The preview color with which the area should be filled, making it easy to visualize on the stage. You will not see this color unless you set preview to true or manually add the area to the display list with addChild(). **/ + public function get previewColor():uint { + return _previewColor; + } + public function set previewColor(value:uint):void { + _redraw(value); + } + + /** To see a visual representation of the area on the screen, set preview to true. Doing so will add the area to the display list behind any DisplayObjects that have been attached. **/ + public function get preview():Boolean { + return _preview; + } + public function set preview(value:Boolean):void { + _preview = value; + if (this.parent == _parent) { + _parent.removeChild(this); + } + if (value) { + var level:uint = (_rootItem == null) ? 0 : 999999999; + var index:uint; + var item:AutoFitItem = _rootItem; + while (item) { + if (item.target.parent == _parent) { + index = _parent.getChildIndex(item.target); + if (index < level) { + level = index; + } + } + item = item.next; + } + _parent.addChildAt(this, level); + this.visible = true; + } + } + + } +} + +import flash.display.BitmapData; +import flash.display.DisplayObject; +import flash.display.Shape; +import flash.geom.Matrix; +import flash.geom.Rectangle; + +internal class AutoFitItem { + public var target:DisplayObject; + public var scaleMode:String; + public var hAlign:String; + public var vAlign:String; + public var minWidth:Number; + public var maxWidth:Number; + public var minHeight:Number; + public var maxHeight:Number; + public var aspectRatio:Number; + public var mask:Shape; + public var matrix:Matrix; + public var hasCustomRatio:Boolean; + public var roundPosition:Boolean; + + public var next:AutoFitItem; + public var prev:AutoFitItem; + + public var calculateVisible:Boolean; + public var boundsTarget:DisplayObject; + public var bounds:Rectangle; + public var hasDrawNow:Boolean; + + /** @private **/ + public function AutoFitItem(target:DisplayObject, vars:Object, next:AutoFitItem) { + this.target = target; + if (vars == null) { + vars = {}; + } + this.scaleMode = vars.scaleMode || "proportionalInside"; + this.hAlign = vars.hAlign || "center"; + this.vAlign = vars.vAlign || "center"; + this.minWidth = Number(vars.minWidth) || 0; + this.maxWidth = isNaN(vars.maxWidth) ? 999999999 : Number(vars.maxWidth); + this.minHeight = Number(vars.minHeight) || 0; + this.maxHeight = isNaN(vars.maxHeight) ? 999999999 : Number(vars.maxHeight); + this.roundPosition = Boolean(vars.roundPosition); + this.boundsTarget = (vars.customBoundsTarget is DisplayObject) ? vars.customBoundsTarget : this.target; + this.matrix = target.transform.matrix; + this.calculateVisible = Boolean(vars.calculateVisible); + this.hasDrawNow = this.target.hasOwnProperty("drawNow"); + if (this.hasDrawNow) { + Object(this.target).drawNow(); //just to make sure we're starting with the correct values if it's a component. + } + if (!isNaN(vars.customAspectRatio)) { + this.aspectRatio = vars.customAspectRatio; + this.hasCustomRatio = true; + } + if (next) { + next.prev = this; + this.next = next; + } + } + + /** @private **/ + public function setVisibleWidth(value:Number):void { + var m:Matrix = this.target.transform.matrix; + if ((m.a == 0 && m.c == 0) || (m.d == 0 && m.b == 0)) { + m.a = this.matrix.a; + m.c = this.matrix.c; + } + var curWidth:Number = (m.a < 0) ? -m.a * this.bounds.width : m.a * this.bounds.width; + curWidth += (m.c < 0) ? -m.c * this.bounds.height : m.c * this.bounds.height; + if (curWidth != 0) { + var scale:Number = value / curWidth; + m.a *= scale; + m.c *= scale; + this.target.transform.matrix = m; + if (value != 0) { + this.matrix = m; + } + } + } + + /** @private **/ + public function setVisibleHeight(value:Number):void { + var m:Matrix = this.target.transform.matrix; + if ((m.a == 0 && m.c == 0) || (m.d == 0 && m.b == 0)) { + m.b = this.matrix.b; + m.d = this.matrix.d; + } + var curHeight:Number = (m.b < 0) ? -m.b * this.bounds.width : m.b * this.bounds.width; + curHeight += (m.d < 0) ? -m.d * this.bounds.height : m.d * this.bounds.height; + if (curHeight != 0) { + var scale:Number = value / curHeight; + m.b *= scale; + m.d *= scale; + this.target.transform.matrix = m; + if (value != 0) { + this.matrix = m; + } + } + } + + /** @private **/ + public function scaleVisibleWidth(value:Number):void { + var m:Matrix = this.target.transform.matrix; + m.a *= value; + m.c *= value; + this.target.transform.matrix = m; + if (value != 0) { + this.matrix = m; + } + } + + /** @private **/ + public function scaleVisibleHeight(value:Number):void { + var m:Matrix = this.target.transform.matrix; + m.b *= value; + m.d *= value; + this.target.transform.matrix = m; + if (value != 0) { + this.matrix = m; + } + } + +} \ No newline at end of file diff --git a/src/com/greensock/layout/ScaleMode.as b/src/com/greensock/layout/ScaleMode.as new file mode 100644 index 0000000..e2b5311 --- /dev/null +++ b/src/com/greensock/layout/ScaleMode.as @@ -0,0 +1,31 @@ +/** + * VERSION: 1.04 + * DATE: 2010-03-06 + * AS3 + * UPDATES AND DOCUMENTATION AT: http://blog.greensock.com/ + **/ + package com.greensock.layout { +/** + * Provides constants for defining how objects should scale/stretch to fit within an area (like a LiquidArea or AutoFitArea). + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ScaleMode { + + /** Stretches the object to fill the area completely in terms of both width and height. This mode does NOT concern itself with preserving the object's original aspect ratio (proportions). **/ + public static const STRETCH:String = "stretch"; + /** Stretches the object's width to fill the area horizontally, but does not affect its height **/ + public static const WIDTH_ONLY:String = "widthOnly"; + /** Stretches the object's height to fill the area vertically, but does not affect its width **/ + public static const HEIGHT_ONLY:String = "heightOnly"; + /** Scales the object proportionally to completely fill the area, allowing portions of it to exceed the bounds when its aspect ratio doesn't match the area's. For example, if the area is 100x50 and the DisplayObject is natively 200x200, it will scale it down to 100x100 meaning it will exceed the bounds of the area vertically. **/ + public static const PROPORTIONAL_OUTSIDE:String = "proportionalOutside"; + /** Scales the object proportionally to fit inside the area (its edges will never exceed the bounds of the area). For example, if the area is 100x50 and the DisplayObject is natively 200x200, it will scale it down to 50x50 meaning it will not fill the area horizontally, but it will vertically. **/ + public static const PROPORTIONAL_INSIDE:String = "proportionalInside"; + /** Does not scale the object at all **/ + public static const NONE:String = "none"; + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/BinaryDataLoader.as b/src/com/greensock/loading/BinaryDataLoader.as new file mode 100644 index 0000000..94bce1d --- /dev/null +++ b/src/com/greensock/loading/BinaryDataLoader.as @@ -0,0 +1,137 @@ +/** + * VERSION: 1.84 + * DATE: 2011-03-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import flash.events.Event; + +/** + * Loads generic binary data - identical to using a DataLoader with its "format" + * special property set to "binary". The reason for having a BinaryDataLoader + * class is to allow certain file extensions (like ".zip") to be associated with it so that the + * LoaderMax.parse() method can accurately parse URLs with those file extensions. If you do + * not plan on using the LoaderMax.parse() method, however, you could save a small amount + * of kb by simply using DataLoaders with their format set to "binary" instead of using BinaryDataLoaders. + * + *

The following are essentially the same:

+ * +new DataLoader("file.zip", {format:"binary"}); +new BinaryDataLoader("file.zip"); + + * + *

If you'd like to associate additional file extensions with BinaryDataLoader, you may use the + * LoaderMax.registerFileType() method.

+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the BinaryDataLoader constructor via its vars + * parameter which can be either a generic object or a DataLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this BinaryDataLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:BinaryDataLoader = new BinaryDataLoader("file.zip", {name:"zipFile", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * + *

Note: Using a DataLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.events.LoaderEvent; + + //create a BinaryDataLoader + var loader:BinaryDataLoader = new BinaryDataLoader("file.zip", {name:"myZipFile", requireWithRoot:this.root, estimatedBytes:6800}); + + //begin loading + loader.load(); + + //or we could parse() and array of files, creating a LoaderMax queue with loaders for each file. To do that, we'll first create the array: + var files:Array = ["files/archive.zip","images/1.jpg","files/report.pdf","swfs/child.swf"]; + + //since we want the parse() method to recognize the .pdf file as a BinaryDataLoader, we should registerFileType() first because pdf isn't one of the extensions recognized by default. + LoaderMax.registerFileType("pdf", BinaryDataLoader); + + //before we parse() the array, we need to activate() the loader types that LoaderMax should recognize (we only need to do this once) + LoaderMax.activate([BinaryDataLoader, ImageLoader, SWFLoader]); + + //now parse the files and create a LoaderMax queue + var queue:LoaderMax = LoaderMax.parse(files, {onProgress:progressHandler, onComplete:completeHandler, onChildFail:childFailHandler}); + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); + } + + function completeHandler(event:LoaderEvent):void { + trace("completed " + event.target); + } + + function childFailHandler(event:LoaderEvent):void { + trace(event.target + " failed."); + } + + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.DataLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class BinaryDataLoader extends DataLoader { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("BinaryDataLoader", BinaryDataLoader, "zip"); + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content. + * @param vars An object containing optional configuration details. For example: new BinaryDataLoader("file.zip", {name:"myZipFile", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or a DataLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this BinaryDataLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:BinaryDataLoader = new BinaryDataLoader("file.zip", {name:"myZipFile", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * @see com.greensock.loading.data.DataLoaderVars + */ + public function BinaryDataLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _loader.dataFormat = "binary"; //just to make sure it wasn't overridden if the "format" special vars property was passed into in DataLoader's constructor. + _type = "BinaryDataLoader"; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/CSSLoader.as b/src/com/greensock/loading/CSSLoader.as new file mode 100644 index 0000000..1025024 --- /dev/null +++ b/src/com/greensock/loading/CSSLoader.as @@ -0,0 +1,139 @@ +/** + * VERSION: 1.84 + * DATE: 2011-03-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import flash.events.Event; + import flash.text.StyleSheet; + + /** Dispatched when the loader's httpStatus value changes. **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] +/** + * Loads StyleSheet (CSS) data. + * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the CSSLoader constructor via its vars + * parameter which can be either a generic object or a CSSLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this CSSLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:CSSLoader = new CSSLoader("styles.css", {name:"styles", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * + *

Note: Using a CSSLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

content data type: flash.text.StyleSheet

+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.events.LoaderEvent; + import import flash.text.StyleSheet; + + //create a CSSLoader + var loader:CSSLoader = new CSSLoader("css/styles.css", {name:"myCSS", requireWithRoot:this.root, estimatedBytes:900}); + + //begin loading + loader.load(); + + //Or you could put the CSSLoader into a LoaderMax. Create one first... + var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + + //append the CSSLoader and several other loaders + queue.append( loader ); + queue.append( new SWFLoader("swf/main.swf", {name:"mainSWF", estimatedBytes:4800}) ); + queue.append( new ImageLoader("img/photo1.jpg", {name:"photo1", estimatedBytes:3500}) ); + + //start loading + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); + } + + function completeHandler(event:LoaderEvent):void { + myTextField.styleSheet = LoaderMax.getContent("myCSS"); + trace("load complete."); + } + + function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); + } + + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.CSSLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class CSSLoader extends DataLoader { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("CSSLoader", CSSLoader, "css"); + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content. + * @param vars An object containing optional configuration details. For example: new CSSLoader("css/styles.css", {name:"myCSS", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or a CSSLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this CSSLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:CSSLoader = new CSSLoader("styles.css", {name:"styles", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * @see com.greensock.loading.data.CSSLoaderVars + */ + public function CSSLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _loader.dataFormat = "text"; //just to make sure it wasn't overridden if the "format" special vars property was passed into in DataLoader's constructor. + _type = "CSSLoader"; + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + override protected function _receiveDataHandler(event:Event):void { + var style:StyleSheet = _content = new StyleSheet(); + style.parseCSS(_loader.data); + super._completeHandler(event); + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/DataLoader.as b/src/com/greensock/loading/DataLoader.as new file mode 100644 index 0000000..235c158 --- /dev/null +++ b/src/com/greensock/loading/DataLoader.as @@ -0,0 +1,181 @@ +/** + * VERSION: 1.841 + * DATE: 2013-03-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.loading.core.LoaderItem; + + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.net.URLLoader; + + /** Dispatched when the loader's httpStatus value changes. **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader experiences a SECURITY_ERROR while loading or auditing its size. **/ + [Event(name="securityError", type="com.greensock.events.LoaderEvent")] +/** + * Loads generic data which can be text (the default), binary data, or URL-encoded variables. + * + *

If the format vars property is "text", the content will be a String containing the text of the loaded file.

+ *

If the format vars property is "binary", the content will be a ByteArray object containing the raw binary data. (See also: BinaryDataLoader)

+ *

If the format vars property is "variables", the content will be a URLVariables object containing the URL-encoded variables

+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the DataLoader constructor via its vars + * parameter which can be either a generic object or a DataLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • format : String - Controls whether the downloaded data is received as text ("text"), raw binary data ("binary"), or URL-encoded variables ("variables").
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this DataLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:DataLoader = new DataLoader("text.txt", {name:"myText", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * + *

Note: Using a DataLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.events.LoaderEvent; + import flash.utils.ByteArray; + import flash.net.URLVariables; + +//create a DataLoader for loading text (the default format) +var loader:DataLoader = new DataLoader("assets/data.txt", {name:"myText", requireWithRoot:this.root, estimatedBytes:900}); + +//start loading +loader.load(); + +//Or you could put the DataLoader into a LoaderMax. Create one first... +var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + +//append the DataLoader and several other loaders +queue.append( loader ); +queue.append( new DataLoader("assets/variables.txt", {name:"myVariables", format:"variables"}) ); +queue.append( new DataLoader("assets/image1.png", {name:"myBinary", format:"binary", estimatedBytes:3500}) ); + +//start loading the LoaderMax queue +queue.load(); + +function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); +} + +function completeHandler(event:LoaderEvent):void { + var text:String = LoaderMax.getContent("myText"); + var variables:URLVariables = LoaderMax.getContent("myVariables"); + var binary:ByteArray = LoaderMax.getContent("myBinary"); + trace("complete. myText: " + text + ", myVariables.var1: " + variables.var1 + ", myBinary.length: " + binary.length); +} + +function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); +} + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.DataLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class DataLoader extends LoaderItem { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("DataLoader", DataLoader, "txt,js"); + /** @private **/ + protected var _loader:URLLoader; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content. + * @param vars An object containing optional configuration details. For example: new DataLoader("text/data.txt", {name:"data", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or a DataLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • format : String - Controls whether the downloaded data is received as text ("text"), raw binary data ("binary"), or URL-encoded variables ("variables").
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this DataLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:DataLoader = new DataLoader("text.txt", {name:"myText", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
+ * @see com.greensock.loading.data.DataLoaderVars + */ + public function DataLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _type = "DataLoader"; + _loader = new URLLoader(null); + if ("format" in this.vars) { + _loader.dataFormat = String(this.vars.format); + } + _loader.addEventListener(ProgressEvent.PROGRESS, _progressHandler, false, 0, true); + _loader.addEventListener(Event.COMPLETE, _receiveDataHandler, false, 0, true); + _loader.addEventListener("ioError", _failHandler, false, 0, true); + _loader.addEventListener("securityError", _failHandler, false, 0, true); + _loader.addEventListener("httpStatus", _httpStatusHandler, false, 0, true); + _loader.addEventListener("httpResponseStatus", _httpStatusHandler, false, 0, true); + } + + /** @private **/ + override protected function _load():void { + _prepRequest(); + _loader.load(_request); + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + if (_status == LoaderStatus.LOADING) { + try { + _loader.close(); + } catch (error:Error) { + + } + } + super._dump(scrubLevel, newStatus, suppressEvents); + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private Don't use _completeHandler so that subclasses can set _content differently and still call super._completeHandler() (otherwise setting _content in the _completeHandler would always override the _content previously set in sublcasses). **/ + protected function _receiveDataHandler(event:Event):void { + _content = _loader.data; + super._completeHandler(event); + } + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/ImageLoader.as b/src/com/greensock/loading/ImageLoader.as new file mode 100644 index 0000000..2f13c1a --- /dev/null +++ b/src/com/greensock/loading/ImageLoader.as @@ -0,0 +1,310 @@ +/** + * VERSION: 1.897 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.DisplayObjectLoader; + import com.greensock.loading.core.LoaderItem; + + import flash.display.Bitmap; + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.ProgressEvent; + +/** + * Loads an image file (png, jpg, or gif) and automatically applies smoothing by default. + * + *

The ImageLoader's content refers to a ContentDisplay (Sprite) that + * is created immediately so that you can position/scale/rotate it or add ROLL_OVER/ROLL_OUT/CLICK listeners + * before (or while) the image loads. Use the ImageLoader's content property to get the ContentDisplay + * Sprite, or use the rawContent property to get the actual Bitmap. If a container + * is defined in the vars object, the ContentDisplay will immediately be added to that container).

+ * + *

If you define a width and height, it will draw a rectangle + * in the ContentDisplay so that interactive events fire appropriately (rollovers, etc.) and width/height/bounds + * get reported accurately. This rectangle is invisible by default, but you can control its color and alpha + * with the bgColor and bgAlpha properties. When the image loads, it will be + * added to the ContentDisplay at index 0 with addChildAt() and scaled to fit the width/height + * according to the scaleMode. These are all optional features - you do not need to define a + * width or height in which case the image will load at its native size. + * See the list below for all the special properties that can be passed through the vars + * parameter but don't let the list overwhelm you - these are all optional and they are intended to make + * your job as a developer much easier.

+ * + *

[new in version 1.89:] When you load() an ImageLoader, it will automatically + * check to see if another ImageLoader exists with a matching url that has already finished + * loading. If it finds one, it will copy that BitmapData to use in its own Bitmap in order to maximize + * performance and minimize memory usage. After all, why load the file again if you've already loaded it? + * (The exception, of course, is when the ImageLoader's noCache is set to true.)

+ * + *

By default, the ImageLoader will attempt to load the image in a way that allows full script + * access. However, if a security error is thrown because the image is being loaded from another + * domain and the appropriate crossdomain.xml file isn't in place to grant access, the ImageLoader + * will automatically adjust the default LoaderContext so that it falls back to the more restricted + * mode which will have the following effect:

+ *
    + *
  • A LoaderEvent.SCRIPT_ACCESS_DENIED event will be dispatched and the scriptAccessDenied property of the ImageLoader will be set to true. You can check this value before performing any restricted operations on the content like BitmapData.draw().
  • + *
  • The ImageLoader's rawContent property will be a Loader instance instead of a Bitmap.
  • + *
  • The smoothing property will not be set to true.
  • + *
  • BitmapData operations like draw() will not be able to be performed on the image.
  • + *
+ * + *

To maximize the likelihood of your image loading without any security problems, consider taking the following steps:

+ *
    + *
  • Use a crossdomain.xml file - See Adobe's docs for details, but here is an example that grants full access (put this in a crossdomain.xml file that is at the root of the remote domain): + * <?xml version="1.0" encoding="utf-8"?> + * <cross-domain-policy> + * <allow-access-from domain="~~" /> + * </cross-domain-policy>
  • + *
  • In the embed code of any HTML wrapper, set AllowScriptAccess to "always"
  • + *
+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the ImageLoader constructor via its vars + * parameter which can be either a generic object or an ImageLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the ImageLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the ContentDisplay Sprite should be added immediately.
  • + *
  • smoothing : Boolean - When smoothing is true (the default), smoothing will be enabled for the image which typically leads to much better scaling results (otherwise the image can look crunchy/jagged). If your image is loaded from another domain where the appropriate crossdomain.xml file doesn't grant permission, Flash will not allow smoothing to be enabled (it's a security restriction).
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - If true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the loaded image will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The image will fill the width/height exactly.
    • + *
    • "proportionalInside" - The image will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The image will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the image will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the image will be adjusted to fit.
    • + *
    • "none" - No scaling of the image will occur.
    • + *
  • + *
  • hAlign : String - When a width and height is defined, the hAlign determines how the image is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The image will be centered horizontally in the area
    • + *
    • "left" - The image will be aligned with the left side of the area
    • + *
    • "right" - The image will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height is defined, the vAlign determines how the image is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The image will be centered vertically in the area
    • + *
    • "top" - The image will be aligned with the top of the area
    • + *
    • "bottom" - The image will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the image to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the image that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • context : LoaderContext - To control whether or not a policy file is checked (which is required if you're loading an image from another domain and you want to use it in BitmapData operations), define a LoaderContext object. By default, the policy file will be checked when running remotely, so make sure the appropriate crossdomain.xml file is in place. See Adobe's LoaderContext documentation for details and precautions.
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this ImageLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:ImageLoader = new ImageLoader("photo1.jpg", {name:"image1", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are called when the image has downloaded and has been placed into the ContentDisplay Sprite. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderEvent.SCRIPT_ACCESS_DENIED events which are dispatched when the image is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like smoothing or BitmapData manipulation. You can also check the loader's scriptAccessDenied property after the image has loaded. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a ImageLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

Jerky animation? If you animate the image after loading it and you notice that the movement + * is rather jerky, try setting the scaleX and/or scaleY to something other than 1, like 1.001 because there is + * a bug in Flash that forces Bitmaps to always act like their pixelSnapping is "auto" + * when their scaleX/scaleY are 1.

+ * + *

content data type: com.greensock.loading.display.ContentDisplay (a Sprite). + * When the image has finished loading, the rawContent will be added to the ContentDisplay Sprite + * at index 0 using addChildAt(). rawContent will be a flash.display.Bitmap unless + * unless script access is denied in which case it will be a flash.display.Loader (to avoid security errors).

+ * + * Example AS3 code:+ import com.greensock.~~; + import com.greensock.events.LoaderEvent; + import com.greensock.loading.~~; + + //create an ImageLoader: + var loader:ImageLoader = new ImageLoader("img/photo1.jpg", {name:"photo1", container:this, x:180, y:100, width:200, height:150, scaleMode:"proportionalInside", centerRegistration:true, onComplete:onImageLoad}); + + //begin loading + loader.load(); + + //when the image loads, fade it in from alpha:0 using TweenLite + function onImageLoad(event:LoaderEvent):void { + TweenLite.from(event.target.content, 1, {alpha:0}); + } + + //Or you could put the ImageLoader into a LoaderMax. Create one first... + var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + + //append the ImageLoader and several other loaders + queue.append( loader ); + queue.append( new XMLLoader("xml/doc.xml", {name:"xmlDoc", estimatedBytes:425}) ); + queue.append( new SWFLoader("swf/main.swf", {name:"mainClip", estimatedBytes:3000, container:this, autoPlay:false}) ); + + //start loading + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + queue.progress); + } + + function completeHandler(event:LoaderEvent):void { + trace(event.target + " is complete!"); + } + + function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); + } + + + *

NOTES / TIPS:

+ *
    + *
  • You will not see the image unless you either manually add it to the display list in your onComplete handler or simply use the container special property (see above).
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.ImageLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class ImageLoader extends DisplayObjectLoader { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("ImageLoader", ImageLoader, "jpg,jpeg,png,gif,bmp"); + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content + * @param vars An object containing optional configuration details. For example: new ImageLoader("img/photo1.jpg", {name:"photo1", container:this, x:100, y:50, alpha:0, onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or an ImageLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the ImageLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the ContentDisplay Sprite should be added immediately.
  • + *
  • smoothing : Boolean - When smoothing is true (the default), smoothing will be enabled for the image which typically leads to much better scaling results (otherwise the image can look crunchy/jagged). If your image is loaded from another domain where the appropriate crossdomain.xml file doesn't grant permission, Flash will not allow smoothing to be enabled (it's a security restriction).
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - if true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the loaded image will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The image will fill the width/height exactly.
    • + *
    • "proportionalInside" - The image will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The image will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the image will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the image will be adjusted to fit.
    • + *
    • "none" - No scaling of the image will occur.
    • + *
  • + *
  • hAlign : String - When a width and height is defined, the hAlign determines how the image is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The image will be centered horizontally in the area
    • + *
    • "left" - The image will be aligned with the left side of the area
    • + *
    • "right" - The image will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height is defined, the vAlign determines how the image is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The image will be centered vertically in the area
    • + *
    • "top" - The image will be aligned with the top of the area
    • + *
    • "bottom" - The image will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the image to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the image that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • context : LoaderContext - To control whether or not a policy file is checked (which is required if you're loading an image from another domain and you want to use it in BitmapData operations), define a LoaderContext object. By default, the policy file will be checked when running remotely, so make sure the appropriate crossdomain.xml file is in place. See Adobe's LoaderContext documentation for details and precautions.
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally)
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this ImageLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:ImageLoader = new ImageLoader("photo1.jpg", {name:"image1", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are called when the image has downloaded and has been placed into the ContentDisplay Sprite. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderEvent.SCRIPT_ACCESS_DENIED events which are dispatched when the image is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like smoothing or BitmapData manipulation. You can also check the loader's scriptAccessDenied property after the image has loaded. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.ImageLoaderVars + */ + public function ImageLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _type = "ImageLoader"; + } + + override protected function _load():void { + if (this.vars.noCache != true) { + //check to see if another ImageLoader with the same URL exists and has completed so that we can copy that BitmapData to speed things up and reduce memory usage. + var loaders:Array = _globalRootLoader.getChildren(true, true); + var loader:LoaderItem; + var i:int = loaders.length; + while (--i > -1) { + loader = loaders[i]; + if (loader.url == _url && loader != this && loader.status == LoaderStatus.COMPLETED && loader is ImageLoader && ImageLoader(loader).rawContent is Bitmap) { + _closeStream(); + _content = new Bitmap(ImageLoader(loader).rawContent.bitmapData, "auto", Boolean(this.vars.smoothing != false)); + Object(_sprite).rawContent = (_content as DisplayObject); + _initted = true; + _progressHandler(new ProgressEvent(ProgressEvent.PROGRESS, false, false, loader.bytesLoaded, loader.bytesTotal)); + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this)); + _completeHandler(null); + return; + } + } + } + super._load(); + } + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + override protected function _initHandler(event:Event):void { + _determineScriptAccess(); + if (!_scriptAccessDenied) { + _content = Bitmap(_loader.content); + _content.smoothing = Boolean(this.vars.smoothing != false); + } else { + _content = _loader; + } + super._initHandler(event); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/LoaderMax.as b/src/com/greensock/loading/LoaderMax.as new file mode 100644 index 0000000..1d77771 --- /dev/null +++ b/src/com/greensock/loading/LoaderMax.as @@ -0,0 +1,1084 @@ +/** + * VERSION: 1.941 + * DATE: 2015-01-20 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.LoaderCore; + import com.greensock.loading.core.LoaderItem; + + import flash.display.DisplayObject; + import flash.events.Event; + import flash.net.URLRequest; + import flash.system.LoaderContext; + import flash.utils.Dictionary; + + /** Dispatched when any child of the LoaderMax instance starts loading. So if a LoaderMax contains 5 loaders, the CHILD_OPEN event will be dispatched 5 times during the course of the LoaderMax's load. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="childOpen", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches a PROGRESS event. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="childProgress", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance completes. So if a LoaderMax contains 5 loaders, the CHILD_COMPLETE event will be dispatched 5 times during the course of the LoaderMax's load. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="childComplete", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance fails to load. This occurs even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="childFail", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches a CANCEL event which could occur when another child is prioritized in the queue or when the LoaderMax is canceled while loading the child. CHILD_CANCEL can be dispatched even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="childCancel", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches a SCRIPT_ACCESS_DENIED event. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="scriptAccessDenied", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches an HTTP_STATUS event. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches an IO_ERROR event. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="ioError", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any child of the LoaderMax instance dispatches a SECURITY_ERROR event. This can occur even if the LoaderMax itself isn't in the process of loading (because load() or prioritize() could have been called directly on a child loader) **/ + [Event(name="securityError", type="com.greensock.events.LoaderEvent")] +/** + * In its simplest form, a LoaderMax provides a way to group a sequence of loaders together and + * report their progress as a whole. It is essentially a queue of loaders. But there are many other + * conveniences that the LoaderMax system delivers: + *
    + *
  • Integration of loaders inside subloaded swfs - With most other systems, if you subload a swf, the loader will only concern itself with the swf file's bytes but what if that swf must subload other content like XML, images, and/or other swf files before it should be considered fully loaded? LoaderMax can elegantly handle the sub-subloads as deep as they go. You can link any loader and/or LoaderMax with a swf's root (using the requireWithRoot vars property) so that when you subload it into another Flash application, the parent SWFLoader automatically factors the nested loaders into its overall loading progress! It won't dispatch its COMPLETE event until they have finished as well.
  • + *
  • Automatic parsing of LoaderMax-related nodes inside XML - The XMLLoader class automatically looks for LoaderMax-related nodes like <LoaderMax>, <ImageLoader>, <SWFLoader>, <XMLLoader>, <VideoLoader>, <DataLoader>, <CSSLoader>, <MP3Loader>, etc. in XML files that it loads, and if any are found it will create the necessary instances and then begin loading the ones that had a load="true" attribute, automatically integrating their progress into the XMLLoader's overall progress and it won't dispatch a COMPLETE event until the XML-driven loaders have finished as well.
  • + *
  • Tight file size - Many other systems are 16-24k+ even if you're just loading text, but LoaderMax can be as little as 7k (depending on which loader types you use).
  • + *
  • A common set of properties and methods among all loaders - Every loader type (XMLLoader, SWFLoader, ImageLoader, MP3Loader, CSSLoader, VideoLoader, LoaderMax, etc.) all share common content, name, status, loadTime, paused, bytesLoaded, bytesTotal, and progress properties as well as methods like load(), pause(), resume(), prioritize(), unload(), cancel(), auditSize() and dispose() delivering a touch of polymorphism sweetness.
  • + *
  • Nest LoaderMax instances inside other LoaderMax instances as deeply as you want. - This makes complex queues simple. Need to know when the first 3 loaders have finished loading inside a 10-loader queue? Just put those 3 into their own LoaderMax that has an onComplete and nest that LoaderMax inside your main LoaderMax queue.
  • + *
  • Set a width/height for an ImageLoader, SWFLoader, or VideoLoader and when it loads, the image/swf/video will automatically scale to fit using any of the following scaleModes: "stretch", "proportionalInside", "proportionalOutside", "widthOnly", or "heightOnly". Even crop the image/swf/video with crop:true.
  • + *
  • Conveniences like auto smoothing of images, centering their registration point, noCache, setting initial x, y, scaleX, scaleY, rotation, alpha, and blendMode properties, optional autoPlay for mp3s, swfs, and videos, and more.
  • + *
  • Works around common Flash hassles/bugs - LoaderMax implements workarounds for things like garbage collection headaches with subloaded swfs, images, and NetStreams as well as problems with subloaded swfs that use TLF.
  • + *
  • Find loaders and content by name or url - Every loader has a name property which you can use to uniquely identify it. Feed a name or URL to the static LoaderMax.getLoader() or LoaderMax.getContent() methods to quickly get the associated loader or content.
  • + *
  • A single loader can belong to multiple LoaderMax instances
  • + *
  • Accurate progress reporting - For maximum performance, set an estimatedBytes for each loader or allow LoaderMax's auditSize feature to automatically preload just enough of each child loader's content to determine its bytesTotal, making progress reporting on large queues very accurate.
  • + *
  • prioritize() a loader anytime - Kick an object to the top of all LoaderMax queues to which it belongs, immediately supplanting the top spot in each one.
  • + *
  • A robust event system
  • + *
  • Define an alternateURL for any loader - If the original url fails to load, it will automatically switch to the alternateURL and try again.
  • + *
  • Set up multiple event listeners in one line - Add listeners like onComplete, onProgress, onError, etc. via the constructor like new LoaderMax({name:"mainQueue", onComplete:completeHandler, onProgress:progressHandler, onError:errorHandler});
  • + *
  • maxConnections - Set the maximum number of simultaneous connections for each LoaderMax instance (default is 2). This can speed up overall loading times.
  • + *
  • pause()/resume() - no queue loading solution would be complete without the ability to pause()/resume() anytime.
  • + *
  • Flex friendly - Simply change the LoaderMax.contentDisplayClass to FlexContentDisplay and then ImageLoaders, SWFLoaders, and VideoLoaders will return content wrapped in a UIComponent.
  • + *
+ * + * Example AS3 code:+import com.greensock.~~; +import com.greensock.loading.~~; +import com.greensock.events.LoaderEvent; +import com.greensock.loading.display.~~; + +//create a LoaderMax named "mainQueue" and set up onProgress, onComplete and onError listeners +var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + +//append several loaders +queue.append( new XMLLoader("xml/data.xml", {name:"xmlDoc", alternateURL:"http://otherserver.com/data.xml"}) ); +queue.append( new ImageLoader("img/photo1.jpg", {name:"photo1", estimatedBytes:2400, container:this, alpha:0, width:250, height:150, scaleMode:"proportionalInside"}) ); +queue.append( new SWFLoader("swf/main.swf", {name:"mainClip", estimatedBytes:3000, container:this, x:250, autoPlay:false}) ); + +//add a loader to the top of the queue using prepend() +queue.prepend( new MP3Loader("mp3/audio.mp3", {name:"audio", repeat:100, autoPlay:true}) ); + +//prioritize the loader named "photo1" +LoaderMax.prioritize("photo1"); //same as LoaderMax.getLoader("photo1").prioritize(); + +//start loading +queue.load(); + +function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); +} + +function completeHandler(event:LoaderEvent):void { + var image:ContentDisplay = LoaderMax.getContent("photo1"); + TweenLite.to(image, 1, {alpha:1, y:100}); + trace(event.target + " is complete!"); +} + +function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); +} + + * + *

LoaderMax will automatically skip over any child loaders in the queue that are already complete. By default + * it will also skip any that have failed or are paused (you can change this behavior with the skipFailed + * and skipPaused special properties). To flush the content and force a full reload, simply unload() + * first or use the flushContent parameter in load() like load(true).

+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the LoaderMax constructor via the vars + * parameter which can be either a generic object or a LoaderMaxVars object:

+ *
    + *
  • name : String - A name that is used to identify the LoaderMax instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • auditSize : Boolean - By default, when the LoaderMax begins to load it quickly loops through its children and if it finds any that don't have an estimatedBytes defined, it will briefly open a URLStream in order to attempt to determine its bytesTotal, immediately closing the URLStream once the value has been determined. This causes a brief delay initially, but greatly improves the accuracy of the progress and bytesTotal values. Set auditSize to false to prevent the LoaderMax from auditing its childrens' size (it is true by default). For maximum performance, it is best to define an estimatedBytes value for as many loaders as possible to avoid the delay caused by audits. When the LoaderMax audits an XMLLoader, it cannot recognize loaders that will be created from the XML data nor can it recognize loaders inside subloaded swf files from a SWFLoader (it would take far too long to load sufficient data for that - audits should be as fast as possible). If you do not set an appropriate estimatedSize for XMLLoaders or SWFLoaders that contain LoaderMax loaders, you'll notice that the parent LoaderMax's progress and bytesTotal change when the nested loaders are recognized (this is normal). To control the default auditSize value, use the static LoaderMax.defaultAuditSize property.
  • + *
  • maxConnections : uint - Maximum number of simultaneous connections that should be used while loading the LoaderMax queue. A higher number will generally result in faster overall load times for the group. The default is 2. This value is instance-based, not system-wide, so if you have two LoaderMax instances that both have a maxConnections value of 3 and they are both loading, there could be up to 6 connections at a time total. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define.
  • + *
  • skipFailed : Boolean - If skipFailed is true (the default), any failed loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a failed loader and the LoaderMax's status will become LoaderStatus.FAILED.
  • + *
  • skipPaused : Boolean - If skipPaused is true (the default), any paused loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a paused loader and the LoaderMax's status will become LoaderStatus.FAILED.
  • + *
  • autoLoad : Boolean - If true, the LoaderMax instance will automatically call load() whenever you insert()/append()/prepend() a new loader whose status is LoaderStatus.READY. This basically makes it easy to create a LoaderMax queue and dump stuff into it whenever you want it to load without having to check the LoaderMax's status and call load() manually if it's not already loading.
  • + *
  • loaders : Array - An array of loaders (ImageLoaders, SWFLoaders, XMLLoaders, MP3Loaders, other LoaderMax instances, etc.) that should be immediately inserted into the LoaderMax.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want this LoaderMax to be required as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:LoaderMax = new LoaderMax({name:"mainQueue", requireWithRoot:this.root});
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either an error or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader or any of its children fails (typically because of an IO_ERROR or SECURITY_ERROR). Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time one of the loader's children (or any descendant) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time one of the loader's children (or any descendant) dispatches a PROGRESS event. To listen for changes in the LoaderMax's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the LoaderMax, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time one of the loader's children (or any descendant) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on one of the loader's children (or any descendant) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time one of the loader's children (or any descendant) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderEvent.SCRIPT_ACCESS_DENIED events which are dispatched when one of the LoaderMax's children (or any descendant) is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like smoothing or BitmapData manipulation. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a LoaderMaxVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.LoaderMaxVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderMax extends LoaderCore { + /** @private **/ + public static const version:Number = 1.941; + /** The default value that will be used for the estimatedBytes on loaders that don't declare one in the vars parameter of the constructor. **/ + public static var defaultEstimatedBytes:uint = 20000; + /** Controls the default value of auditSize in LoaderMax instances (normally true). For most situations, the auditSize feature is very convenient for ensuring that the overall progress of LoaderMax instances is reported accurately, but when working with very large quantities of files that have no estimatedBytes defined, some developers prefer to turn auditSize off by default. Of course you can always override the default for individual LoaderMax instances by defining an auditSize value in the vars parameter of the constructor. **/ + public static var defaultAuditSize:Boolean = true; + /** Optionally define a default LoaderContext to use with SWFLoaders and ImageLoaders. This can be useful if you're loading a lot of swfs, for example, and don't want to pass a custom "context" in to each one. LoaderContexts are typically used to tell Flash which ApplicationDomain to load the code into and which SecurityDomain to use. See Adobe's docs for details. **/ + public static var defaultContext:LoaderContext; + /** The class used by ImageLoaders, SWFLoaders, and VideoLoaders to create the containers into which they'll dump their rawContent - by default it is the com.greensock.loading.display.ContentDisplay class but if you're using Flex, it is typically best to change this to com.greensock.loading.display.FlexContentDisplay. You only need to do this once, like + * +import com.greensock.loading.LoaderMax; +import com.greensock.loading.display.FlexContentDisplay; +LoaderMax.contentDisplayClass = FlexContentDisplay; + **/ + public static var contentDisplayClass:Class; + + /** @private **/ + protected var _loaders:Array; + /** @private **/ + protected var _activeLoaders:Dictionary; + + /** If skipFailed is true (the default), any failed loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a failed loader and the LoaderMax's status will become LoaderStatus.FAILED. Skipped loaders are also ignored when the LoaderMax determines its bytesLoaded, bytesTotal, and progress values. **/ + public var skipFailed:Boolean; + /** If skipPaused is true (the default), any paused loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a paused loader and the LoaderMax's status will become LoaderStatus.FAILED. Skipped loaders are also ignored when the LoaderMax determines its bytesLoaded, bytesTotal, and progress values. **/ + public var skipPaused:Boolean; + /** Maximum number of simultaneous connections that should be used while loading the LoaderMax queue. A higher number will generally result in faster overall load times for the group. The default is 2. This value is instance-based, not system-wide, so if you have two LoaderMax instances that both have a maxConnections value of 3 and they are both loading, there could be up to 6 connections at a time total. **/ + public var maxConnections:uint; + /** If true, the LoaderMax instance will automatically call load() whenever you insert()/append()/prepend() a new loader whose status is LoaderStatus.READY. This basically makes it easy to create a LoaderMax queue and dump stuff into it whenever you want something to load without having to check the LoaderMax's status and call load() manually if it's not already loading. **/ + public var autoLoad:Boolean; + + /** + * Constructor + * + * @param vars An object containing optional configuration details. For example: new LoaderMax({name:"queue", onComplete:completeHandler, onProgress:progressHandler, maxConnections:3}). + * + *

The following special properties can be passed into the LoaderMax constructor via the vars parameter + * which can be either a generic object or a LoaderMaxVars object:

+ *
    + *
  • name : String - A name that is used to identify the LoaderMax instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • auditSize : Boolean - By default, when the LoaderMax begins to load it quickly loops through its children and if it finds any that don't have an estimatedBytes defined, it will briefly open a URLStream in order to attempt to determine its bytesTotal, immediately closing the URLStream once the value has been determined. This causes a brief delay initially, but greatly improves the accuracy of the progress and bytesTotal values. Set auditSize to false to prevent the LoaderMax from auditing its childrens' size (it is true by default). For maximum performance, it is best to define an estimatedBytes value for as many loaders as possible to avoid the delay caused by audits. When the LoaderMax audits an XMLLoader, it cannot recognize loaders that will be created from the XML data nor can it recognize loaders inside subloaded swf files from a SWFLoader (it would take far too long to load sufficient data for that - audits should be as fast as possible). If you do not set an appropriate estimatedSize for XMLLoaders or SWFLoaders that contain LoaderMax loaders, you'll notice that the parent LoaderMax's progress and bytesTotal change when the nested loaders are recognized (this is normal). To control the default auditSize value, use the static LoaderMax.defaultAuditSize property.
  • + *
  • maxConnections : uint - Maximum number of simultaneous connections that should be used while loading the LoaderMax queue. A higher number will generally result in faster overall load times for the group. The default is 2. This value is instance-based, not system-wide, so if you have two LoaderMax instances that both have a maxConnections value of 3 and they are both loading, there could be up to 6 connections at a time total. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define.
  • + *
  • skipFailed : Boolean - If skipFailed is true (the default), any failed loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a failed loader and the LoaderMax's status will become LoaderStatus.FAILED.
  • + *
  • skipPaused : Boolean - If skipPaused is true (the default), any paused loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a paused loader and the LoaderMax's status will become LoaderStatus.FAILED.
  • + *
  • autoLoad : Boolean - If true, the LoaderMax instance will automatically call load() whenever you insert()/append()/prepend() a new loader whose status is LoaderStatus.READY. This basically makes it easy to create a LoaderMax queue and dump stuff into it whenever you want it to load without having to check the LoaderMax's status and call load() manually if it's not already loading.
  • + *
  • loaders : Array - An array of loaders (ImageLoaders, SWFLoaders, XMLLoaders, MP3Loaders, other LoaderMax instances, etc.) that should be immediately inserted into the LoaderMax.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want this LoaderMax to be required as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:LoaderMax = new LoaderMax({name:"mainQueue", requireWithRoot:this.root});
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either an error or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader or any of its children fails (typically because of an IO_ERROR or SECURITY_ERROR). Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time one of the loader's children (or any descendant) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time one of the loader's children (or any descendant) dispatches a PROGRESS event. To listen for changes in the LoaderMax's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the LoaderMax, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time one of the loader's children (or any descendant) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on one of the loader's children (or any descendant) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time one of the loader's children (or any descendant) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderEvent.SCRIPT_ACCESS_DENIED events which are dispatched when one of the LoaderMax's children (or any descendant) is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like smoothing or BitmapData manipulation. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.LoaderMaxVars + */ + public function LoaderMax(vars:Object=null) { + super(vars); + _type = "LoaderMax"; + _loaders = []; + _activeLoaders = new Dictionary(); + this.skipFailed = Boolean(this.vars.skipFailed != false); + this.skipPaused = Boolean(this.vars.skipPaused != false); + this.autoLoad = Boolean(this.vars.autoLoad == true); + this.maxConnections = ("maxConnections" in this.vars) ? uint(this.vars.maxConnections) : 2; + if (this.vars.loaders is Array) { + for (var i:int = 0; i < this.vars.loaders.length; i++) { + insert(this.vars.loaders[i], i); + } + } + } + + /** + * Analyzes a url or array of urls and attempts to automatically create the appropriate loader(s) based + * on file extension(s) in the url(s), returning either an individual loader like an ImageLoader, + * SWFLoader, XMLLoader, etc or if an array is passed in, a LoaderMax will be returned containing + * a child for each parsed url (or URLRequest) in the array. Arrays may also contain LoaderCore instances + * (not just url Strings). + *

Single loader example:

+import com.greensock.loading.~~; +import com.greensock.loading.core.~~; +import com.greensock.events.LoaderEvent; + +//activate the necessary loaders so that their file extensions can be recognized (do this once) +LoaderMax.activate([ImageLoader, SWFLoader, XMLLoader]); + +//now parse a url and create the correct type of loader (an ImageLoader in this case because the file extension is ".jpg") +var loader:LoaderCore = LoaderMax.parse("../img/photo1.jpg", {name:"parsedLoader", onComplete:completeHandler}); + +//begin loading +loader.load(); + +function completeHandler(event:LoaderEvent):void { + trace("finished loading " + event.target); +} + + *

If an array is passed to the LoaderMax.parse() method, it will create a LoaderMax instance + * and add the necessary children based on the contents of the array:

+ * Array example:+import com.greensock.loading.~~; +import com.greensock.events.LoaderEvent; + +//activate the necessary loaders so that their file extensions can be recognized (do this once) +LoaderMax.activate([ImageLoader, SWFLoader, XMLLoader, MP3Loader]); + +var urls:Array = ["img/photo1.jpg","../../xml/data.xml","swf/main.swf","http://www.greensock.com/audio/music.mp3"]; + +//now parse all of the urls, creating a LoaderMax that contains the correct type of loaders (an ImageLoader, XMLLoader, SWFLoader, and MP3Loader respectively) +var loader:LoaderMax = LoaderMax.parse(urls, {name:"mainQueue", onComplete:completeHandler}) as LoaderMax; + +//begin loading +loader.load(); + +function completeHandler(event:LoaderEvent):void { + trace("finished loading " + loader.numChildren + " loaders."); +} + + * + * @param data A String or an array of Strings (and/or LoaderCore instances and/or URLRequest instances) to parse. + * @param vars The vars object to pass the loader's constructor. If data is an array, this vars will be passed to the LoaderMax instance that gets created, and no vars object will be passed to the child loaders that get created. + * @param childrenVars The vars object that will be passed to each child loader's constructor (only applicable when the data parameter is an array which means parse() will return a LoaderMax). For example, if you parse() and array of video urls and want autoPlay set to false for all of them, you'd do LoaderMax.parse(["1.flv","2.f4v","3.mp4"], null, {autoPlay:false});. + * @return If data is an array, parse() will return a LoaderMax. Otherwise, it will return the appropriate loader based on the file extension found in the URL. In any case, the object returned will be a LoaderCore object (all LoaderMax loaders extend LoaderCore, so if you need to datatype your object use com.greensock.loading.core.LoaderCore). The return value is typed as "*" in order to avoid compiler errors when developers forget to cast ther objects like var image:ImageLoader = LoaderMax.parse("photo.jpg") as ImageLoader + */ + public static function parse(data:*, vars:Object=null, childrenVars:Object=null):* { + if (data is Array) { + var queue:LoaderMax = new LoaderMax(vars); + var l:int = data.length; + for (var i:int = 0; i < l; i++) { + queue.append(LoaderMax.parse(data[i], childrenVars)); + } + return queue; + } else if (data is String || data is URLRequest) { + var s:String = (data is String) ? data : URLRequest(data).url; + s = s.toLowerCase().split("?")[0]; + s = s.substr(s.lastIndexOf(".") + 1); + if (s in _extensions) { + return new _extensions[s](data, vars); + } + } else if (data is LoaderCore) { + return data as LoaderCore; + } + throw new Error("LoaderMax could not parse " + data + ". Don't forget to use LoaderMax.activate() to activate the necessary types of loaders."); + return null; + } + + /** @private **/ + override protected function _load():void { + _loadNext(null); + } + + /** + * Appends a loader to the end of the queue. + * + * @param loader The loader to append to the queue. It can be any loader (ImageLoader, XMLLoader, SWFLoader, MP3Loader, another LoaderMax, etc.). + * @return The loader that was appended. + * @see #prepend() + * @see #insert() + * @see #remove() + */ + public function append(loader:LoaderCore):LoaderCore { + return insert(loader, _loaders.length); + } + + /** + * Prepends a loader at the beginning of the queue (append() adds the loader to the end whereas prepend() adds it to the beginning). + * + * @param loader The loader to prepend to the queue. It can be any loader (ImageLoader, XMLLoader, SWFLoader, MP3Loader, another LoaderMax, etc.). + * @return The loader that was prepended. + * @see #append() + * @see #insert() + * @see #remove() + */ + public function prepend(loader:LoaderCore):LoaderCore { + return insert(loader, 0); + } + + /** + * Inserts a loader at a particular position in the queue. Index values are zero-based just like arrays. + * For example, if the LoaderMax has 10 loaders in it already and you want to insert a loader at the 3rd + * position (index: 2) while moving the others back in the queue (like the way splice() works + * in arrays), you'd do:

+ * + * queue.insert( new ImageLoader("img/photo.jpg"), 2);

+ * + *

When a new loader is added to the LoaderMax, the LoaderMax's status changes to LoaderStatus.READY + * unless it is paused or disposed. If the loader is already in the queue, it will be removed first.

+ * + * @param loader The loader to insert into the queue. It can be any loader (ImageLoader, XMLLoader, SWFLoader, MP3Loader, DataLoader, CSSLoader, another LoaderMax, etc.). + * @param index The index position at which the loader should be inserted, exactly like the way splice() works for arrays. Index values are 0-based, so the first position is 0, the second is 1, the third is 2, etc. + * @return The loader that was inserted + * @see #append() + * @see #prepend() + * @see #remove() + */ + public function insert(loader:LoaderCore, index:uint=999999999):LoaderCore { + if (loader == null || loader == this || _status == LoaderStatus.DISPOSED) { + return null; + } + if (this != loader.rootLoader) { + _removeLoader(loader, false); //in case it was already added. + } + if (loader.rootLoader == _globalRootLoader) { //don't remove from rootLoaders other than _globalRootLoader, otherwise subloading swfs with loaders that contain LoaderMax instances with nested loaders that have requiredWithRoot set to the associated rootLoader won't be able to be found inside that rootLoader. We could of course leave loaders in _globalRootLoader, but that we get a performance benefit from removing them (fewer event listeners getting called). + loader.rootLoader.remove(loader); + } + + if (index > _loaders.length) { + index = _loaders.length; + } + + _loaders.splice(index, 0, loader); + if (this != _globalRootLoader) { + for (var p:String in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + loader.addEventListener(_listenerTypes[p], _passThroughEvent, false, -100, true); + } + } + loader.addEventListener(LoaderEvent.PROGRESS, _progressHandler, false, -100, true); //use -1 so that if the user adds an event listener, it gets called before LoaderMax is notified. Otherwise bubbling behavior doesn't go in the proper order. + loader.addEventListener("prioritize", _prioritizeHandler, false, -100, true); + } + loader.addEventListener("dispose", _disposeHandler, false, -100, true); + _cacheIsDirty = true; + if (_status == LoaderStatus.LOADING) { + //do nothing + } else if (_status != LoaderStatus.PAUSED) { + _status = LoaderStatus.READY; + } else if (_prePauseStatus == LoaderStatus.COMPLETED) { + _prePauseStatus = LoaderStatus.READY; + } + + if (this.autoLoad && loader.status == LoaderStatus.READY) { + if (_status != LoaderStatus.LOADING) { + this.load(false); + } else { + _loadNext(null); //to ensure the maxConnections pipeline is full + } + } + + return loader; + } + + /** + * Removes a loader from the LoaderMax. + * + * @param loader The loader to remove from the LoaderMax + * @see #append() + * @see #insert() + * @see #prepend() + */ + public function remove(loader:LoaderCore):void { + _removeLoader(loader, true); + } + + /** @private **/ + protected function _removeLoader(loader:LoaderCore, rootLoaderAppend:Boolean):void { + if (loader == null) { + return; + } + if (rootLoaderAppend && this != loader.rootLoader) { + loader.rootLoader.append(loader); + } + _removeLoaderListeners(loader, true); + _loaders.splice(getChildIndex(loader), 1); + if (loader in _activeLoaders) { + delete _activeLoaders[loader]; + loader.cancel(); + if (_status == LoaderStatus.LOADING) { + _loadNext(null); + } + } + _cacheIsDirty = true; + _progressHandler(null); //has conditional logic that will only dispatch a PROGRESS event if bytesLoaded or bytesTotal has changed. + } + + /** + * Empties the LoaderMax of all its loaders and optionally disposes/unloads them. + * + * @param disposeChildren If true (the default), dispose() will be called on all loaders in the LoaderMax. + * @param unloadAllContent If true, the content of all child loaders will be unloaded. + * @see #dispose() + * @see #unload() + */ + public function empty(disposeChildren:Boolean=true, unloadAllContent:Boolean=false):void { + var i:int = _loaders.length; + while (--i > -1) { + if (disposeChildren) { + LoaderCore(_loaders[i]).dispose(unloadAllContent); + } else if (unloadAllContent) { + LoaderCore(_loaders[i]).unload(); + } else { + _removeLoader(_loaders[i], true); + } + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + if (newStatus == LoaderStatus.DISPOSED) { + _status = LoaderStatus.DISPOSED; //must set it first so that when events from children are dispatched, it doesn't trigger other unnecessary actions. + empty(true, Boolean(scrubLevel == 3)); + if (this.vars.requireWithRoot is DisplayObject) { + delete _rootLookup[this.vars.requireWithRoot]; + } + _activeLoaders = null; + } + if (scrubLevel <= 1) { + _cancelActiveLoaders(); + } + if (scrubLevel == 1) { + var i:int = _loaders.length; + while (--i > -1) { + LoaderCore(_loaders[i]).unload(); + } + } + super._dump(scrubLevel, newStatus, suppressEvents); + _cacheIsDirty = true; + } + + /** @private **/ + override protected function _calculateProgress():void { + _cachedBytesLoaded = 0; + _cachedBytesTotal = 0; + var i:int = _loaders.length; + var loader:LoaderCore, s:int; + while (--i > -1) { + loader = _loaders[i]; + s = loader.status; + if (s <= LoaderStatus.COMPLETED || (!this.skipPaused && s == LoaderStatus.PAUSED) || (!this.skipFailed && s == LoaderStatus.FAILED)) { + _cachedBytesLoaded += loader.bytesLoaded; + _cachedBytesTotal += loader.bytesTotal; + } + } + _cacheIsDirty = false; + } + + /** @private **/ + protected function _cancelActiveLoaders():void { + var i:int = _loaders.length; + var loader:LoaderCore; + while (--i > -1) { + loader = _loaders[i]; + if (loader.status == LoaderStatus.LOADING) { + delete _activeLoaders[loader]; + _removeLoaderListeners(loader, false); + loader.cancel(); + } + } + } + + /** @private **/ + protected function _removeLoaderListeners(loader:LoaderCore, all:Boolean):void { + loader.removeEventListener(LoaderEvent.COMPLETE, _loadNext); + loader.removeEventListener(LoaderEvent.CANCEL, _loadNext); + if (all) { + loader.removeEventListener(LoaderEvent.PROGRESS, _progressHandler); + loader.removeEventListener("prioritize", _prioritizeHandler); + loader.removeEventListener("dispose", _disposeHandler); + for (var p:String in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + loader.removeEventListener(_listenerTypes[p], _passThroughEvent); + } + } + } + } + + /** + * Returns and array of child loaders that currently have a particular status. For example, + * to find all loaders inside the LoaderMax instance that are actively in the process of loading:

+ * + * loader.getChildrenByStatus(LoaderStatus.LOADING, false);

+ * + * @param status Status code like LoaderStatus.READY, LoaderStatus.LOADING, LoaderStatus.COMPLETED, LoaderStatus.PAUSED, or LoaderStatus.FAILED. + * @param includeNested If true, loaders that are nested inside other loaders (like LoaderMax instances or XMLLoaders or SWFLoaders) will be returned in the array. + * @param omitLoaderMaxes If true, no LoaderMax instances will be returned in the array; only LoaderItems like ImageLoaders, XMLLoaders, SWFLoaders, MP3Loaders, etc. The default is false. + * @return An array of loaders that match the defined status. + * @see #getChildren() + * @see #getLoader() + * @see #numChildren + */ + public function getChildrenByStatus(status:int, includeNested:Boolean=false, omitLoaderMaxes:Boolean=false):Array { + var a:Array = []; + var loaders:Array = getChildren(includeNested, omitLoaderMaxes); + var l:int = loaders.length; + for (var i:int = 0; i < l; i++) { + if (LoaderCore(loaders[i]).status == status) { + a.push(loaders[i]); + } + } + return a; + } + + /** + * Returns the child that is at a particular position (zero-based index) in the queue. For example, + * myLoaderMax.getChildAt(0) would get the first loader in the queue. + * myLoaderMax.getChildAt(2) would get the 3rd loader in the queue. + * + * @param index The index of the child in the queue that should be returned. For example, myLoaderMax.getChildAt(0) would get the first loader in the queue. myLoaderMax.getChildAt(2) would get the 3rd loader. + * @return The child loader that is located at the corresponding index + * @see #getChildren() + * @see #getLoader() + * @see #getChildrenByStatus() + */ + public function getChildAt(index:int):* { + return _loaders[index]; + } + + /** + * Returns and array of all child loaders inside the LoaderMax, optionally exposing more deeply nested + * instances as well (like loaders inside a child LoaderMax instance). + * + * @param includeNested If true, loaders that are nested inside child LoaderMax, XMLLoader, or SWFLoader instances will be included in the returned array as well. The default is false. + * @param omitLoaderMaxes If true, no LoaderMax instances will be returned in the array; only LoaderItems like ImageLoaders, XMLLoaders, SWFLoaders, MP3Loaders, etc. The default is false. + * @return An array of loaders. + * @see #getChildrenByStatus() + * @see #getLoader() + * @see #numChildren + */ + public function getChildren(includeNested:Boolean=false, omitLoaderMaxes:Boolean=false):Array { + var a:Array = []; + var l:int = _loaders.length; + for (var i:int = 0; i < l; i++) { + if (!omitLoaderMaxes || !(_loaders[i] is LoaderMax)) { + a.push(_loaders[i]); + } + if (includeNested && _loaders[i].hasOwnProperty("getChildren")) { + a = a.concat(_loaders[i].getChildren(true, omitLoaderMaxes)); + } + } + return a; + } + + /** + * Immediately prepends a value to the beginning of each child loader's url. For example, + * if the "myLoaderMax" instance contains 3 ImageLoaders with the urls "image1.jpg", "image2.jpg", and "image3.jpg" + * and you'd like to add "http://www.greensock.com/images/" to the beginning of them all, you'd do:

+ * + * myLoaderMax.prependURLs("http://www.greensock.com/images/", false);

+ * + *

Now the ImageLoader urls would be "http://www.greensock.com/images/image1.jpg", "http://www.greensock.com/images/image2.jpg", + * and "http://www.greensock.com/images/image3.jpg" respectively.

+ * + *

prependURLs() permanently affects each child loader's url meaning that + * LoaderMax.getContent("image1.jpg") would not find the loader whose url + * is now "http://www.greensock.com/images/image1.jpg" (although you could simply use its name + * instead of its url to find it). It also means that if a single loader has been + * inserted into multiple LoaderMax instances, its url change affects them all.

+ * + *

prependURLs() only affects loaders that are children of the LoaderMax when + * the method is called - it does not affect loaders that are inserted later.

+ * + *

prependURLs() does NOT affect any alternateURL values that are defined + * for each child loader.

+ * + * @param value The String that should be prepended to each child loader + * @param includeNested If true, loaders nested inside child LoaderMax instances will also be affected. It is false by default. + * @see #replaceURLText() + */ + public function prependURLs(prependText:String, includeNested:Boolean=false):void { + var loaders:Array = getChildren(includeNested, true); + var i:int = loaders.length; + while (--i > -1) { + LoaderItem(loaders[i]).url = prependText + LoaderItem(loaders[i]).url; + } + } + + /** + * Immediately replaces a certain substring in each child loader's url with another string, + * making it simple to do something like change "{imageDirectory}image1.jpg" to + * "http://www.greensock.com/images/image1.jpg". For example, + * if the "myLoaderMax" instance contains 3 ImageLoaders with the urls "{imageDirectory}image1.jpg", + * "{imageDirectory}image2.jpg", and "{imageDirectory}image3.jpg" + * and you'd like to replace {imageDirectory} with http://www.greensock.com/images/ + * you'd do:

+ * + * myLoaderMax.replaceURLText("{imageDirectory}", "http://www.greensock.com/images/", false);

+ * + *

Now the ImageLoader urls would be "http://www.greensock.com/images/image1.jpg", "http://www.greensock.com/images/image2.jpg", + * and "http://www.greensock.com/images/image3.jpg" respectively.

+ * + *

replaceURLText() permanently affects each child loader's url meaning that + * LoaderMax.getContent("image1.jpg") would not find the loader whose url + * is now "http://www.greensock.com/images/image1.jpg" (although you could simply use its name + * instead of its url to find it). It also means that if a single loader has been + * inserted into multiple LoaderMax instances, its url change affects them all.

+ * + *

replaceURLText() only affects loaders that are children of the LoaderMax when + * the method is called - it does not affect loaders that are inserted later.

+ * + *

replaceURLText() does affect alternateURL values for child loaders.

+ * + * @param fromText The old String that should be replaced in each child loader. + * @param toText The new String that should replace the fromText. + * @param includeNested If true, loaders nested inside child LoaderMax instances will also be affected. It is false by default. + * @see #prependURLs() + */ + public function replaceURLText(fromText:String, toText:String, includeNested:Boolean=false):void { + var loaders:Array = getChildren(includeNested, true); + var loader:LoaderItem; + var i:int = loaders.length; + while (--i > -1) { + loader = loaders[i]; + loader.url = loader.url.split(fromText).join(toText); + if ("alternateURL" in loader.vars) { + loader.vars.alternateURL = loader.vars.alternateURL.split(fromText).join(toText); + } + } + } + + /** + * Finds a loader based on its name or url. For example:

+ * + * var loader:ImageLoader = queue.getLoader("myPhoto1");

+ * + *

Feel free to use the static LoaderMax.getLoader() method instead of the instance-based getLoader() + * method because the static one will search ALL loaders (the only exception being loaders in a different security + * sandbox, like in subloaded swfs from a different domain that didn't have a crossdomain.xml file in place granting permission).

+ * + * @param nameOrURL The name or url associated with the loader that should be found. + * @return The loader associated with the name or url. + * @see #getContent() + * @see #getChildren() + * @see #getChildrenByStatus() + */ + public function getLoader(nameOrURL:String):* { + var i:int = _loaders.length; + var loader:LoaderCore; + while (--i > -1) { + loader = _loaders[i]; + if (loader.name == nameOrURL || (loader is LoaderItem && (loader as LoaderItem).url == nameOrURL)) { + return loader; + } else if (loader.hasOwnProperty("getLoader")) { + loader = (loader as Object).getLoader(nameOrURL) as LoaderCore; + if (loader != null) { + return loader; + } + } + } + return null; + } + + /** + * Finds the content of a loader based on its name or url. For example:

+ * + * var image:Bitmap = queue.getContent("myPhoto1");

+ * + *

Feel free to use the static LoaderMax.getContent() method instead of the instance-based getContent() + * method because the static one will search ALL loaders (the only exception being loaders in a different security + * sandbox, like in subloaded swfs from a different domain that didn't have a crossdomain.xml file in place granting permission).

+ * + * @param nameOrURL The name or url associated with the loader whose content should be found. + * @return The content that was loaded by the loader which varies by the type of loader: + *
    + *
  • ImageLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the ImageLoader's rawContent (a flash.display.Bitmap unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • SWFLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the SWFLoader's rawContent (the swf's root DisplayObject unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • VideoLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the VideoLoader's rawContent (a Video object to which the NetStream was attached). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • XMLLoader - XML
  • + *
  • DataLoader + *
      + *
    • String if the DataLoader's format vars property is "text" (the default).
    • + *
    • flash.utils.ByteArray if the DataLoader's format vars property is "binary".
    • + *
    • flash.net.URLVariables if the DataLoader's format vars property is "variables".
    • + *
  • + *
  • CSSLoader - flash.text.StyleSheet
  • + *
  • MP3Loader - flash.media.Sound
  • + *
  • LoaderMax - an array containing the content objects from each of its child loaders.
  • + *
+ * @see #getLoader() + * @see #content + */ + public function getContent(nameOrURL:String):* { + var loader:LoaderCore = this.getLoader(nameOrURL); + return (loader != null) ? loader.content : null; + } + + /** + * Finds the index position of a particular loader in the LoaderMax. Index values are always zero-based, + * meaning the first position is 0, the second is 1, the third is 2, etc. + * + * @param loader The loader whose index position should be returned + * @return The index position of the loader + * @see #getChildren() + * @see #getChildrenByStatus() + */ + public function getChildIndex(loader:LoaderCore):uint { + var i:int = _loaders.length; + while (--i > -1) { + if (_loaders[i] == loader) { + return i; + } + } + return 999999999; + } + + /** @inheritDoc **/ + override public function auditSize():void { + if (!this.auditedSize) { + _auditSize(null); + } + } + + /** @private **/ + protected function _auditSize(event:Event=null):void { + if (event != null) { + event.target.removeEventListener("auditedSize", _auditSize); + event.target.removeEventListener(LoaderEvent.FAIL, _auditSize); + } + var l:uint = _loaders.length; + var maxStatus:int = (this.skipPaused) ? LoaderStatus.COMPLETED : LoaderStatus.PAUSED; + var loader:LoaderCore, found:Boolean; + for (var i:int = 0; i < l; i++) { + loader = _loaders[i]; + if (!loader.auditedSize && loader.status <= maxStatus && loader.vars.auditSize != false) { + if (!found) { + loader.addEventListener("auditedSize", _auditSize, false, -100, true); + loader.addEventListener(LoaderEvent.FAIL, _auditSize, false, -100, true); + } + found = true; + loader.auditSize(); + } + } + if (!found) { + if (_status == LoaderStatus.LOADING) { + _loadNext(null); + } + dispatchEvent(new Event("auditedSize")); + } + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _loadNext(event:Event=null):void { + if (event != null && _activeLoaders != null) { + delete _activeLoaders[event.target]; + _removeLoaderListeners(LoaderCore(event.target), false); + } + + if (_status == LoaderStatus.LOADING) { + + var audit:Boolean = ("auditSize" in this.vars) ? Boolean(this.vars.auditSize) : LoaderMax.defaultAuditSize; + if (audit && !this.auditedSize) { + _auditSize(null); + return; + } + + var loader:LoaderCore, loaders:Array = _loaders.concat(), l:int = loaders.length, activeCount:uint = 0; //use _loaders.concat() because in some rare situations, a loader's COMPLETE event might occur immediately and if autoDispose is true, the length of the array could change mid-loop causing a skip. + _calculateProgress(); + for (var i:int = 0; i < l; i++) { + loader = loaders[i]; + if (!this.skipPaused && loader.status == LoaderStatus.PAUSED) { + super._failHandler(new LoaderEvent(LoaderEvent.FAIL, this, "Did not complete LoaderMax because skipPaused was false and " + loader.toString() + " was paused."), false); + return; + + } else if (!this.skipFailed && loader.status == LoaderStatus.FAILED) { + super._failHandler(new LoaderEvent(LoaderEvent.FAIL, this, "Did not complete LoaderMax because skipFailed was false and " + loader.toString() + " failed."), false); + return; + + } else if (loader.status <= LoaderStatus.LOADING) { + activeCount++; + if (!(loader in _activeLoaders)) { + _activeLoaders[loader] = true; + loader.addEventListener(LoaderEvent.COMPLETE, _loadNext, false, -100, true); + loader.addEventListener(LoaderEvent.CANCEL, _loadNext, false, -100, true); + loader.load(false); + } + if (activeCount == this.maxConnections) { + break; + } + } + } + if (activeCount == 0 && _cachedBytesLoaded == _cachedBytesTotal) { + _completeHandler(null); + } + } + } + + /** @private **/ + override protected function _progressHandler(event:Event):void { + if (_dispatchChildProgress && event != null) { + dispatchEvent(new LoaderEvent(LoaderEvent.CHILD_PROGRESS, event.target)); + } + if (_dispatchProgress && _status != LoaderStatus.DISPOSED) { + var bl:uint = _cachedBytesLoaded; + var bt:uint = _cachedBytesTotal; + _calculateProgress(); + if (bl == 0 && _cachedBytesLoaded == 0) { + //do nothing + } else if ((_cachedBytesLoaded != _cachedBytesTotal || _status != LoaderStatus.LOADING) && (bl != _cachedBytesLoaded || bt != _cachedBytesTotal)) { //note: added _status != LoaderStatus.LOADING because it's possible for all the children to load independently (without the LoaderMax actively loading), so in those cases, the progress would never reach 1 since LoaderMax's _completeHandler() won't be called to dispatch the final PROGRESS event. + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } else { + _cacheIsDirty = true; + } + } + + /** @private **/ + protected function _disposeHandler(event:Event):void { + _removeLoader(LoaderCore(event.target), false); + } + + /** @private **/ + protected function _prioritizeHandler(event:Event):void { + var loader:LoaderCore = event.target as LoaderCore; + _loaders.splice(getChildIndex(loader), 1); + _loaders.unshift(loader); + if (_status == LoaderStatus.LOADING && loader.status <= LoaderStatus.LOADING && !(loader in _activeLoaders)) { + _cancelActiveLoaders(); + var prevMaxConnections:uint = this.maxConnections; + this.maxConnections = 1; + _loadNext(null); + this.maxConnections = prevMaxConnections; + } + } + + +//---- STATIC METHODS ---------------------------------------------------------------------------- + + /** + * Activates particular loader classes (like ImageLoader, SWFLoader, etc.) so that they can be + * recognized inside the parse() method and XMLLoader. For example, if LoaderMax.parse("image.jpg") + * is called without first activating ImageLoader (like LoaderMax.activate([ImageLoader])), + * it wouldn't properly recognize the ".jpg" extension and return the necessary ImageLoader instance. Likewise, + * without activating ImageLoader first, XMLLoader wouldn't be able to recognize <ImageLoader> + * nodes nested inside an XML file. You only need to activate() the loader classes once in your swf. + * For example:

+ * + * LoaderMax.activate([ImageLoader, SWFLoader, MP3Loader, DataLoader, CSSLoader]);

+ * + *

The reason all loaders aren't activated by default is to conserve file size.

+ * + * @param loaderClasses An array of loader classes, like [ImageLoader, SWFLoader, MP3Loader]. + */ + public static function activate(loaderClasses:Array):void { + //no need to do anything - we just want to force the classes to get compiled in the swf. Each one calls the _activateClass() method in LoaderCore on its own. + } + + /** + * By default, LoaderMax associates certain file extensions with certain types of loaders, like "jpg", "png", and "gif" + * are associated with ImageLoader and "swf" is associated with SWFLoader so that the LoaderMax.parse() method + * can recognize and create the appropriate loaders for each URL passed in. If you'd like to associate additional file + * extensions with certain loader types, you may do so with registerFileType(). For example, to associate + * "pdf" with BinaryDataLoader, you would do this:

+ * + * LoaderMax.registerFileType("pdf", BinaryDataLoader);

+ * + *

Then, if you call LoaderMax.parse("file/myFile.pdf"), it would recognize the "pdf" file extension + * as being associated with BinaryDataLoader and would return a BinaryDataLoader instance accordingly.

+ * + *

There is no reason to use registerFileType() unless you plan on utilizing the parse() + * method and need it to recognize a extensions that LoaderMax doesn't already recognize by default.

+ * + *

NOTE: Make sure you activate() the various loader types you want LoaderMax to recognize before calling parse() - see the documentation for LoaderMax.activate())

+ * + * @param extensions The extension (or comma-delimited list of extensions) that should be associated with the loader class, like "zip" or "zip,pdf". Do not include the dot in the extension. + * @param loaderClass The loader class that should be associated with the extension(s), like BinaryDataLoader. + * @see #activate() + */ + public static function registerFileType(extensions:String, loaderClass:Class):void { + _activateClass("", loaderClass, extensions); + } + + /** + * Searches ALL loaders to find one based on its name or url. For example:

+ * + * var loader:ImageLoader = LoaderMax.getLoader("myPhoto1") as ImageLoader;

+ * + * @param nameOrURL The name or url associated with the loader that should be found. + * @return The loader associated with the name or url. + */ + public static function getLoader(nameOrURL:String):* { + return (_globalRootLoader != null) ? _globalRootLoader.getLoader(nameOrURL) : null; + } + + /** + * Searches ALL loaders to find content based on its name or url. For example:

+ * + * var image:Bitmap = LoaderMax.getContent("myPhoto1");

+ * + * @param nameOrURL The name or url associated with the loader whose content should be found. + * @return The content that was loaded by the loader which varies by the type of loader: + *
    + *
  • ImageLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the ImageLoader's rawContent (a flash.display.Bitmap unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors).
  • + *
  • SWFLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the SWFLoader's rawContent (the swf's root DisplayObject unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors).
  • + *
  • VideoLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the VideoLoader's rawContent (a Video object to which the NetStream was attached).
  • + *
  • XMLLoader - XML
  • + *
  • DataLoader + *
      + *
    • String if the DataLoader's format vars property is "text" (the default).
    • + *
    • flash.utils.ByteArray if the DataLoader's format vars property is "binary".
    • + *
    • flash.net.URLVariables if the DataLoader's format vars property is "variables".
    • + *
  • + *
  • CSSLoader - flash.text.StyleSheet
  • + *
  • MP3Loader - flash.media.Sound
  • + *
  • LoaderMax - an array containing the content objects from each of its child loaders.
  • + *
+ */ + public static function getContent(nameOrURL:String):* { + return (_globalRootLoader != null) ? _globalRootLoader.getContent(nameOrURL) : null; + } + + /** + * Immediately prioritizes a loader inside any LoaderMax instances that contain it, + * forcing it to the top position in their queue and optionally calls load() + * immediately as well. If one of its parent LoaderMax instances is currently loading a + * different loader, that one will be temporarily cancelled. + * + *

By contrast, when load() is called, it doesn't change the loader's position/index + * in any LoaderMax queues. For example, if a LoaderMax is working on loading the first object in + * its queue, you can call load() on the 20th item and it will honor your request without + * changing its index in the queue. prioritize(), however, affects the position + * in the queue and optionally loads it immediately as well.

+ * + *

So even if your LoaderMax hasn't begun loading yet, you could prioritize(false) + * a loader and it will rise to the top of all LoaderMax instances to which it belongs, but not + * start loading yet. If the goal is to load something immediately, you can just use the + * load() method.

+ * + *

For example, to immediately prioritize the loader named "myPhoto1":

+ * + * LoaderMax.prioritize("myPhoto1");

+ * + * @param nameOrURL The name or url associated with the loader that should be prioritized + * @param loadNow If true (the default), the loader will start loading immediately (otherwise it is simply placed at the top the queue in any LoaderMax instances to which it belongs). + * @return The loader that was prioritized. If no loader was found, null is returned. + */ + public static function prioritize(nameOrURL:String, loadNow:Boolean=true):LoaderCore { + var loader:LoaderCore = getLoader(nameOrURL); + if (loader != null) { + loader.prioritize(loadNow); + } + return loader; + } + + override protected function _passThroughEvent(event:Event):void { + super._passThroughEvent(event); + if (!this.skipFailed && (event.type == "fail" || event.type == "childFail") && this.status == LoaderStatus.LOADING) { + super._failHandler(new LoaderEvent(LoaderEvent.FAIL, this, "Did not complete LoaderMax because skipFailed was false and " + event.target.toString() + " failed."), false); + } + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** Number of child loaders currently contained in the LoaderMax instance (does not include deeply nested loaders - only children). To get the quantity of all children including nested ones, use getChildren(true, true).length @see #getChildren() **/ + public function get numChildren():uint { + return _loaders.length; + } + + /** An array containing the content of each loader inside the LoaderMax **/ + override public function get content():* { + var a:Array = []; + var i:int = _loaders.length; + while (--i > -1) { + a[i] = LoaderCore(_loaders[i]).content; + } + return a; + } + + /** @inheritDoc **/ + override public function get status():int { + //if the status of children changed after the LoaderMax completed, we need to make adjustments to the LoaderMax's status. + if (_status == LoaderStatus.COMPLETED) { + var statusCounts:Array = [0, 0, 0, 0, 0, 0]; //store the counts of each type of status (index 0 is for READY, 1 is LOADING, 2 is COMPLETE, etc. + var i:int = _loaders.length; + while (--i > -1) { + statusCounts[LoaderCore(_loaders[i]).status]++; + } + if ((!this.skipFailed && statusCounts[4] != 0) || (!this.skipPaused && statusCounts[3] != 0)) { + _status = LoaderStatus.FAILED; + } else if (statusCounts[0] + statusCounts[1] != 0) { + _status = LoaderStatus.READY; + _cacheIsDirty = true; + } + } + return _status; + } + + /** @inheritDoc **/ + override public function get auditedSize():Boolean { + var maxStatus:int = (this.skipPaused) ? LoaderStatus.COMPLETED : LoaderStatus.PAUSED; + var i:int = _loaders.length; + while (--i > -1) { + if (!LoaderCore(_loaders[i]).auditedSize && LoaderCore(_loaders[i]).status <= maxStatus && _loaders[i].vars.auditSize != false) { + return false; + } + } + return true; + } + + /** + * An unweighted value between 0 and 1 indicating the overall loading progress of the LoaderMax - this calculation does not concern + * itself whatsoever with bytesLoaded and bytesTotal but rather the ratio of the children that are loaded + * (all having equal weight). Therefore, rawProgress is a more crude way of measuring the overall loading progress and + * isn't weighted in terms of file size the way that progress is. The only benefit of using rawProgress instead + * of progress is that there is never a risk of the value moving backwards the way it can with progress + * when child loaders have inaccurately low estimatedByte values (before LoaderMax audits the file size values). The rate at which + * rawProgress increases may slow down or speed up depending on the file size of the asset currently loading. For example, + * if a LoaderMax contains two loaders, the first for a file that's 100k and the second for a file that's 10,000k, rawProgress + * will move quickly (while loading the 100k file) until it reaches 0.5 and then slow down significantly (while loading the 10,000k file) + * until it reaches 1. + * + *

Or let's say you have a LoaderMax that contains 3 ImageLoaders: the first two must load images that are 25k each and the + * 3rd one must load an image that's 450k. After the first two ImageLoaders finish, the LoaderMax's progress property would + * report 0.1 (50k loaded out of 500k total) whereas the rawProgress would report 0.66 (2 loaders out of 3 total have completed). + * However, if you set the estimatedBytes of all of the ImageLoaders in this example to 25600 (25k) and set the LoaderMax's + * auditSize to false, the progress would read about 0.66 after the first two ImageLoaders complete + * (it still thinks they're all 25k) and then when the 3rd one starts loading and LoaderMax finds out that it's 450k, the bytesTotal + * would automatically adjust and the progress would jump backwards to 0.1 (which correctly reflects the weighted progress). + * Of course a solution would be to more accurately set the estimatedBytes and/or leave auditSize true in the + * LoaderMax, but rawProgress can be useful if those solutions are undesirable in your scenario and you need to avoid any + * backwards adjustment of a preloader progress bar or some other interface element.

+ * + * @see #progress + **/ + public function get rawProgress():Number { + var loaded:Number = 0; + var total:uint = 0; + var status:int; + var i:int = _loaders.length; + while (--i > -1) { + status = LoaderCore(_loaders[i]).status; + if (status != LoaderStatus.DISPOSED && !(status == LoaderStatus.PAUSED && this.skipPaused) && !(status == LoaderStatus.FAILED && this.skipFailed)) { + total++; + loaded += (_loaders[i] is LoaderMax) ? LoaderMax(_loaders[i]).rawProgress : LoaderCore(_loaders[i]).progress; + } + } + return (total == 0) ? 0 : loaded / total; + } + + } +} diff --git a/src/com/greensock/loading/LoaderStatus.as b/src/com/greensock/loading/LoaderStatus.as new file mode 100644 index 0000000..72b2db0 --- /dev/null +++ b/src/com/greensock/loading/LoaderStatus.as @@ -0,0 +1,32 @@ +/** + * VERSION: 1.0 + * DATE: 2010-06-16 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { +/** + * Defines status values for loaders. + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderStatus { + + /** The loader is ready to load and has not completed yet. **/ + public static const READY:int = 0; + /** The loader is actively in the process of loading. **/ + public static const LOADING:int = 1; + /** The loader has completed. **/ + public static const COMPLETED:int = 2; + /** The loader is paused. **/ + public static const PAUSED:int = 3; + /** The loader failed and did not load properly. **/ + public static const FAILED:int = 4; + /** The loader has been disposed. **/ + public static const DISPOSED:int = 5; + + } + +} \ No newline at end of file diff --git a/src/com/greensock/loading/MP3Loader.as b/src/com/greensock/loading/MP3Loader.as new file mode 100644 index 0000000..74d464d --- /dev/null +++ b/src/com/greensock/loading/MP3Loader.as @@ -0,0 +1,472 @@ +/** + * VERSION: 1.931 + * DATE: 2012-09-09 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.LoaderItem; + + import flash.display.Shape; + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.media.SoundLoaderContext; + import flash.media.SoundTransform; + +/** + * Loads an MP3 audio file and also provides convenient playback methods + * and properties like pauseSound(), playSound(), gotoSoundTime(), playProgress, volume, + * soundPaused, duration, and soundTime. An MP3Loader will dispatch useful events + * like SOUND_COMPLETE, SOUND_PAUSE, SOUND_PLAY, and PLAY_PROGRESS in addition + * to the typical loader events, making it easy to hook up your own control interface. It packs a + * surprising amount of functionality into a very small amount of kb. + * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the MP3Loader constructor via its vars + * parameter which can be either a generic object or an MP3LoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the MP3Loader instance. This name can be fed to the find() method or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • autoPlay : Boolean - By default the MP3 will begin playing immediately when enough of the file has buffered, but to prevent it from autoPlaying, set autoPlay to false.
  • + *
  • repeat : int - Number of times that the mp3 should repeat. To repeat indefinitely, use -1. Default is 0.
  • + *
  • volume : Number - A value between 0 and 1 indicating the volume at which the sound should play when the MP3Loader's controls are used to play the sound, like playSound() or when autoPlay is true (default volume is 1).
  • + *
  • initThreshold : uint - The minimum number of bytesLoaded to wait for before the LoaderEvent.INIT event is dispatched - the higher the number the more accurate the duration estimate will be when the INIT event is dispatched (the default value is 102400 which is 100k). The MP3's duration cannot be determined with 100% accuracy until it has completely loaded, but it is estimated with more and more accuracy as the file loads.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • context : SoundLoaderContext - To control things like the buffer time and whether or not a policy file is checked, define a SoundLoaderContext object. The default context is null. See Adobe's SoundLoaderContext documentation for details.
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this MP3Loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:MP3Loader = new MP3Loader("audio.mp3", {name:"audio", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for Event.INIT events which will be dispatched when the bytesLoaded exceeds the initThreshold (100k by default) and the MP3 has streamed enough of its content to identify the ID3 meta data. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a MP3LoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

content data type: flash.media.Sound

+ * + *

NOTE: To avoid garbage collection issues in the Flash player, the Sound + * object that MP3Loader employs must get recreated internally anytime the MP3Loader is unloaded or its loading + * is cancelled, so it is best to access the content after the COMPLETE + * event has been dispatched. Otherwise, if you store a reference to the MP3Loader's content + * before or during a load and it gets cancelled or unloaded for some reason, the Sound object + * won't be the one into which the MP3 is eventually loaded.

+ * + * Example AS3 code:+ import com.greensock.~~; + import com.greensock.loading.~~; + import com.greensock.events.LoaderEvent; + + //create a MP3Loader that will begin playing immediately when it loads + var sound:MP3Loader = new MP3Loader("mp3/audio.mp3", {name:"audio", autoPlay:true, repeat:3, estimatedBytes:9500}); + + //begin loading + sound.load(); + + //add a CLICK listener to a button that causes the sound to toggle its paused state. + button.addEventListener(MouseEvent.CLICK, toggleSound); + function toggleSound(event:MouseEvent):void { + sound.soundPaused = !sound.soundPaused; + } + + //or you could put the MP3Loader into a LoaderMax queue. Create one first... + var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + + //append the MP3Loader and then several other loaders + queue.append( sound ); + queue.append( new XMLLoader("xml/doc.xml", {name:"xmlDoc", estimatedBytes:425}) ); + queue.append( new ImageLoader("img/photo1.jpg", {name:"photo1", estimatedBytes:3500}) ); + + //start loading + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); + } + + function completeHandler(event:LoaderEvent):void { + trace(event.target + " is complete!"); + } + + function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); + } + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.MP3LoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class MP3Loader extends LoaderItem { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("MP3Loader", MP3Loader, "mp3"); + /** @private for ENTER_FRAME listeners **/ + private static var _shape:Shape = new Shape(); + + /** Event type constant for when the sound completes. **/ + public static const SOUND_COMPLETE:String="soundComplete"; + /** Event type constant for when the sound is paused. **/ + public static const SOUND_PAUSE:String="soundPause"; + /** Event type constant for when the sound begins or resumes playing. **/ + public static const SOUND_PLAY:String="soundPlay"; + /** Event type constant for when the playback progresses (only dispatched when the sound is playing). **/ + public static const PLAY_PROGRESS:String="playProgress"; + + /** @private **/ + protected var _sound:Sound; + /** @private **/ + protected var _context:SoundLoaderContext; + /** @private **/ + protected var _soundPaused:Boolean; + /** @private **/ + protected var _soundComplete:Boolean; + /** @private **/ + protected var _position:Number; + /** @private **/ + protected var _soundTransform:SoundTransform; + /** @private **/ + protected var _duration:Number; + /** @private Improves performance **/ + protected var _dispatchPlayProgress:Boolean; + /** @private -1 = not initted, no ID3 data, 0 = received ID3 data, 1 = fully initted **/ + protected var _initPhase:int; + /** @private **/ + protected var _repeatCount:uint; + + /** The minimum number of bytesLoaded to wait for before the LoaderEvent.INIT event is dispatched - the higher the number the more accurate the duration estimate will be when the INIT event is dispatched (the default value is 102400 which is 100k). The MP3's duration cannot be determined with 100% accuracy until it has completely loaded, but it is estimated with more and more accuracy as the file loads. **/ + public var initThreshold:uint; + /** The SoundChannel object that results from the most recent playSound() call (or when autoPlay is true in the constructor's vars parameter). Typically there isn't much reason to use this directly. Instead, use the MP3Loader's controls like playSound(), pauseSound(), gotoSoundTime(), playProgress, duration, soundTime, etc. **/ + public var channel:SoundChannel; + + /** + * Constructor. + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content + * @param vars An object containing optional configuration details. For example: new MP3Loader("mp3/audio.mp3", {name:"audio", autoPlay:true, onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or an MP3LoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the MP3Loader instance. This name can be fed to the find() method or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • autoPlay : Boolean - By default the MP3 will begin playing immediately when enough of the file has buffered, but to prevent it from autoPlaying, set autoPlay to false.
  • + *
  • repeat : int - Number of times that the mp3 should repeat. To repeat indefinitely, use -1. Default is 0.
  • + *
  • volume : Number - A value between 0 and 1 indicating the volume at which the sound should play (default is 1).
  • + *
  • initThreshold : uint - The minimum number of bytesLoaded to wait for before the LoaderEvent.INIT event is dispatched - the higher the number the more accurate the duration estimate will be when the INIT event is dispatched (the default value is 102400 which is 100k). The MP3's duration cannot be determined with 100% accuracy until it has completely loaded, but it is estimated with more and more accuracy as the file loads.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • context : SoundLoaderContext - To control things like the buffer time and whether or not a policy file is checked, define a SoundLoaderContext object. The default context is null. See Adobe's SoundLoaderContext documentation for details.
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this MP3Loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:MP3Loader = new MP3Loader("audio.mp3", {name:"audio", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for Event.INIT events which will be dispatched when the bytesLoaded exceeds the initThreshold (100k by default) and the MP3 has streamed enough of its content to identify the ID3 meta data. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.MP3LoaderVars + */ + public function MP3Loader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _type = "MP3Loader"; + _position = 0; + _duration = 0; + _soundPaused = true; + _soundTransform = new SoundTransform(("volume" in this.vars) ? this.vars.volume : 1); + this.initThreshold = ("initThreshold" in this.vars) ? uint(this.vars.initThreshold) : 102400; + _initSound(); + } + + /** @private **/ + protected function _initSound():void { + if (_sound != null) { + try { + _sound.close(); + } catch (error:Error) { + + } + _sound.removeEventListener(ProgressEvent.PROGRESS, _progressHandler); + _sound.removeEventListener(Event.COMPLETE, _completeHandler); + _sound.removeEventListener("ioError", _failHandler); + _sound.removeEventListener(Event.ID3, _id3Handler); + } + _initPhase = -1; + _sound = _content = new Sound(); + _sound.addEventListener(ProgressEvent.PROGRESS, _progressHandler, false, 0, true); + _sound.addEventListener(Event.COMPLETE, _completeHandler, false, 0, true); + _sound.addEventListener("ioError", _failHandler, false, 0, true); + _sound.addEventListener(Event.ID3, _id3Handler, false, 0, true); + } + + /** @private **/ + override protected function _load():void { + _context = (this.vars.context is SoundLoaderContext) ? this.vars.context : new SoundLoaderContext(3000); + _prepRequest(); + _soundComplete = false; + _initPhase = -1; + _position = 0; + _duration = 0; + try { + _sound.load(_request, _context); + if (this.vars.autoPlay != false) { + playSound(); + } + } catch (error:Error) { + _errorHandler(new LoaderEvent(LoaderEvent.ERROR, this, error.message)); + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + this.pauseSound(); + _initSound(); + _position = 0; + _duration = 0; + _repeatCount = 0; + _soundComplete = false; + super._dump(scrubLevel, newStatus); + _content = _sound; + } + + /** + * Plays the sound. + * + * @param event An optional Event which simply makes it easier to use the method as a handler for mouse clicks or other events. + * @return The SoundChannel object created by the play() + * + * @see #soundPaused + * @see #pauseSound() + * @see #gotoSoundTime() + * @see #soundTime + * @see #playProgress + **/ + public function playSound(event:Event=null):SoundChannel { + this.soundPaused = false; + return this.channel; + } + + /** + * Pauses playback of the sound. + * + * @param event An optional Event which simply makes it easier to use the method as a handler for mouse clicks or other events. + * + * @see #soundPaused + * @see #gotoSoundTime() + * @see #playSound() + * @see #soundTime + * @see #playProgress + **/ + public function pauseSound(event:Event=null):void { + this.soundPaused = true; + } + + /** + * Attempts to jump to a certain time in the sound. If the sound hasn't downloaded enough to get to + * the new time, it will get as close as possible. + * For example, to jump to exactly 3-seconds into the sound and play from there:

+ * + * loader.gotoSoundTime(3, true);

+ * + * @param time The time (in seconds, offset from the very beginning) at which to place the virtual playhead in the sound. + * @param forcePlay If true, the sound will resume playback immediately after seeking to the new position. + * @param resetRepeatCount If the MP3Loader has a non-zero repeat value (meaning it loops/repeats at least once), setting resetRepeatCount to true will cause it to act like this is the first time through (no repeats yet). For example, if the MP3Loader had a repeat value of 3 and it already repeated twice when gotoSoundTime() was called, it would act like it forgot that it repeated twice already. + * @see #pauseSound() + * @see #playSound() + * @see #soundTime + * @see #playProgress + **/ + public function gotoSoundTime(time:Number, forcePlay:Boolean=false, resetRepeatCount:Boolean=true):void { + if (time > _duration) { + time = _duration; + } + _position = time * 1000; + _soundComplete = false; + if (resetRepeatCount) { + _repeatCount = 0; + } + + if (!_soundPaused || forcePlay) { + _playSound(_position); + if (_soundPaused) { + _soundPaused = false; + dispatchEvent(new LoaderEvent(SOUND_PLAY, this)); + } + } + } + + /** @private **/ + protected function _playSound(position:Number):void { + if (this.channel != null) { + this.channel.removeEventListener(Event.SOUND_COMPLETE, _soundCompleteHandler); + this.channel.stop(); + } + _position = position; + this.channel = _sound.play(_position, 1, this.soundTransform); + if (this.channel != null) { //if the device doesn't have a sound card or sound capabilities, this.channel will be null! + this.channel.addEventListener(Event.SOUND_COMPLETE, _soundCompleteHandler); + _shape.addEventListener(Event.ENTER_FRAME, _enterFrameHandler, false, 0, true); + } + } + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _id3Handler(event:Event):void { + if (_sound.bytesLoaded > this.initThreshold) { + _initPhase = 1; + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this)); + } else { + _initPhase = 0; + } + } + + /** @private **/ + override protected function _progressHandler(event:Event):void { + if (_initPhase == 0 && _sound.bytesLoaded > this.initThreshold) { + _initPhase = 1; + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this)); + } + super._progressHandler(event); + } + + /** @private **/ + protected function _soundCompleteHandler(event:Event):void { + if (uint(this.vars.repeat) > _repeatCount || int(this.vars.repeat) == -1) { + _repeatCount++; + _playSound(0); + } else { + _repeatCount = 0; + _soundComplete = true; + this.soundPaused = true; + _position = _duration * 1000; + _enterFrameHandler(null); + dispatchEvent(new LoaderEvent(SOUND_COMPLETE, this)); + } + } + + /** @private **/ + protected function _enterFrameHandler(event:Event):void { + if (_dispatchPlayProgress) { + dispatchEvent(new LoaderEvent(PLAY_PROGRESS, this)); + } + } + + /** @inheritDoc **/ + override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + if (type == PLAY_PROGRESS) { + _dispatchPlayProgress = true; + } + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** @private **/ + override protected function _completeHandler(event:Event=null):void { + _duration = _sound.length / 1000; + if (_initPhase != 1) { + _initPhase = 1; + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this)); + } + super._completeHandler(event); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** The playback status of the sound: true if the sound's playback is paused, false if it isn't. **/ + public function get soundPaused():Boolean { + return _soundPaused; + } + public function set soundPaused(value:Boolean):void { + var changed:Boolean = Boolean(value != _soundPaused); + _soundPaused = value; + if (!changed) { + return; + } + if (_soundPaused) { + if (this.channel != null) { + _position = this.channel.position; + this.channel.removeEventListener(Event.SOUND_COMPLETE, _soundCompleteHandler); + _shape.removeEventListener(Event.ENTER_FRAME, _enterFrameHandler); + this.channel.stop(); + } + } else { + _playSound(_position); + if (this.channel == null) { //if the device doesn't have a sound card or sound capabilities, this.channel will be null! + return; //so that no event is dispatched + } + } + dispatchEvent(new LoaderEvent(((_soundPaused) ? SOUND_PAUSE : SOUND_PLAY), this)); + } + + /** A value between 0 and 1 describing the playback progress where 0 means the virtual playhead is at the very beginning of the sound, 0.5 means it is at the halfway point and 1 means it is at the end of the sound. **/ + public function get playProgress():Number { + return (_soundComplete) ? 1 : (this.soundTime / this.duration); + } + public function set playProgress(value:Number):void { + if (this.duration != 0) { + gotoSoundTime((value * _duration), !_soundPaused); + } + } + + /** The volume of the sound (a value between 0 and 1). **/ + public function get volume():Number { + return this.soundTransform.volume; + } + public function set volume(value:Number):void { + _soundTransform = this.soundTransform; + _soundTransform.volume = value; + if (this.channel != null) { + this.channel.soundTransform = _soundTransform; + } + } + + /** The time (in seconds) at which the virtual playhead is positioned on the sound. For example, if the virtual playhead is currently at the 3-second position (3 seconds from the beginning), this value would be 3. **/ + public function get soundTime():Number { + return (!_soundPaused && this.channel != null) ? this.channel.position / 1000 : _position / 1000; + } + public function set soundTime(value:Number):void { + gotoSoundTime(value, !_soundPaused); + } + + /** The duration (in seconds) of the sound. This value cannot be determined with 100% accuracy until the file has completely loaded, but it is estimated with more and more accuracy as the file loads. **/ + public function get duration():Number { + if (_sound.bytesLoaded < _sound.bytesTotal) { + _duration = (_sound.length / 1000) / (_sound.bytesLoaded / _sound.bytesTotal); + } + return _duration; + } + + /** The SoundTransform of the channel **/ + public function get soundTransform():SoundTransform { + return (this.channel != null) ? this.channel.soundTransform : _soundTransform; + } + public function set soundTransform(value:SoundTransform):void { + _soundTransform = value; + if (this.channel != null) { + this.channel.soundTransform = value; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/SWFLoader.as b/src/com/greensock/loading/SWFLoader.as new file mode 100644 index 0000000..09b8bee --- /dev/null +++ b/src/com/greensock/loading/SWFLoader.as @@ -0,0 +1,811 @@ +/** + * VERSION: 1.87 + * DATE: 2011-07-30 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.DisplayObjectLoader; + import com.greensock.loading.core.LoaderCore; + + import flash.display.AVM1Movie; + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.display.MovieClip; + import flash.events.Event; + import flash.media.SoundTransform; + import flash.utils.getQualifiedClassName; + import flash.utils.getTimer; + + /** Dispatched when any loader that the SWFLoader discovered in the subloaded swf dispatches an OPEN event. **/ + [Event(name="childOpen", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the SWFLoader discovered in the subloaded swf dispatches a PROGRESS event. **/ + [Event(name="childProgress", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the SWFLoader discovered in the subloaded swf dispatches a COMPLETE event. **/ + [Event(name="childComplete", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the SWFLoader discovered in the subloaded swf dispatches a FAIL event. **/ + [Event(name="childFail", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the SWFLoader discovered in the subloaded swf dispatches a CANCEL event. **/ + [Event(name="childCancel", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader is denied script access to the swf which can happen if it is loaded from another domain and there's no crossdomain.xml file in place. **/ + [Event(name="scriptAccessDenied", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader's httpStatus value changes. **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader experiences a SECURITY_ERROR while loading or auditing its size. **/ + [Event(name="securityError", type="com.greensock.events.LoaderEvent")] +/** + * Loads a swf file and automatically searches for active loaders in that swf that have + * the requireWithRoot vars property set to that swf's root. If it finds any, + * it will factor those loaders' progress into its own progress and not dispatch its + * COMPLETE event until the nested loaders have finished. + * + *

The SWFLoader's content refers to a ContentDisplay (a Sprite) that + * is created immediately so that you can position/scale/rotate it or add ROLL_OVER/ROLL_OUT/CLICK listeners + * before (or while) the swf loads. Use the SWFLoader's content property to get the ContentDisplay + * Sprite, or use the rawContent property to get the actual root of the loaded swf file itself. + * If a container is defined in the vars object, the ContentDisplay will + * immediately be added to that container).

+ * + *

If you define a width and height, it will draw a rectangle + * in the ContentDisplay so that interactive events fire appropriately (rollovers, etc.) and width/height/bounds + * get reported accurately. This rectangle is invisible by default, but you can control its color and alpha + * with the bgColor and bgAlpha properties. When the swf loads, it will be + * added to the ContentDisplay at index 0 with addChildAt() and scaled to fit the width/height according to + * the scaleMode. These are all optional features - you do not need to define a + * width or height in which case the swf will load at its native size. + * See the list below for all the special properties that can be passed through the vars + * parameter but don't let the list overwhelm you - these are all optional and they are intended to make + * your job as a developer much easier.

+ * + *

By default, the SWFLoader will attempt to load the swf in a way that allows full script + * access (same SecurityDomain and child ApplicationDomain). However, if a security error is thrown because + * the swf is being loaded from another domain and the appropriate crossdomain.xml file isn't in place + * to grant access, the SWFLoader will automatically adjust the default LoaderContext so that it falls + * back to the more restricted mode which will have the following effect:

+ *
    + *
  • A LoaderEvent.SCRIPT_ACCESS_DENIED event will be dispatched and the scriptAccessDenied property of the SWFLoader will be set to true. You can check this value before performing any restricted operations on the content like BitmapData.draw().
  • + *
  • Other LoaderMax-related loaders inside the swf will not be recognized or integrated into the SWFLoader's overall progress.
  • + *
  • A Loader instance will be added to the ContentDisplay Sprite instead of the swf's root.
  • + *
  • The getClass() and getSWFChild() methods will always return null.
  • + *
  • BitmapData operations like draw() will not be able to be performed on the swf.
  • + *
+ * + *

If the loaded swf is an AVM1Movie (built in AS1 or AS2), scriptAccessDenied will be true + * and a Loader instance will be added to the content Sprite instead of the swf's root.

+ * + *

To maximize the likelihood of your swf loading without any security problems, consider taking the following steps:

+ *
    + *
  • Use a crossdomain.xml file - See Adobe's docs for details, but here is an example that grants full access (put this in a crossdomain.xml file that is at the root of the remote domain): + * <?xml version="1.0" encoding="utf-8"?> + * <cross-domain-policy> + * <allow-access-from domain="~~" /> + * </cross-domain-policy>
  • + *
  • In the embed code of any HTML wrapper, set AllowScriptAccess to "always"
  • + *
  • If possible, in the remote swf make sure you explicitly allow script access using something like flash.system.Security.allowDomain("~~");
  • + *
+ * + *

A note about garbage collection: A lot of effort has gone into making SWFLoader solve common garbage collection + * problems related to loading and unloading swfs, but since it is impossible for SWFLoader to know all the code that will run in + * the child swf, it cannot automatically remove event listeners, stop NetStreams, sounds, etc., all of which could interfere + * with garbage collection. Therefore it is considered a best practice to [whenever possible] build each subloaded swf so that + * it has some sort of dispose() method that runs cleanup code (removes event listeners, stops sounds, closes NetStreams, etc.). + * When the swf is loaded, you can recursively inspect the chain of parents and if a ContentDisplay object is found (it will + * have a "loader" property), you can add an "unload" event listener so that your dispose() method gets called accordingly. + * For example, in the child swf you could use code like this:

+ * In the child swf:+var curParent:DisplayObjectContainer = this.parent; +while (curParent) { + if (curParent.hasOwnProperty("loader") && curParent.hasOwnProperty("rawContent")) { //ContentDisplay objects have "loader" and "rawContent" properties. The "loader" points to the SWFLoader. Technically it would be cleaner to say if (curParent is ContentDisplay) but that would force ContentDisplay and some core LoaderMax classes to get compiled into the child swf unnecessarily, so doing it this way keeps file size down. + Object(curParent).loader.addEventListener("unload", dispose, false, 0, true); + } + curParent = curParent.parent; +} +function dispose(event:Event):void { + //do cleanup stuff here like removing event listeners, stopping sounds, closing NetStreams, etc... +} + + * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the SWFLoader constructor via its vars + * parameter which can be either a generic object or an SWFLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the SWFLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods. This name is also applied to the Sprite that is created to hold the swf (The SWFLoader's content refers to this Sprite). Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the content Sprite should be added immediately.
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - if true, the registration point will be placed in the center of the ContentDisplay Sprite which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the loaded swf will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The swf will fill the width/height exactly.
    • + *
    • "proportionalInside" - The swf will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The swf will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the swf will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the swf will be adjusted to fit.
    • + *
    • "none" - No scaling of the swf will occur.
    • + *
  • + *
  • hAlign : String - When a width and height are defined, the hAlign determines how the swf is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The swf will be centered horizontally in the area
    • + *
    • "left" - The swf will be aligned with the left side of the area
    • + *
    • "right" - The swf will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height are defined, the vAlign determines how the swf is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The swf will be centered vertically in the area
    • + *
    • "top" - The swf will be aligned with the top of the area
    • + *
    • "bottom" - The swf will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the swf to be cropped within that area (by applying a scrollRect for maximum performance) based on its native size (not the bounding box of the swf's current contents). This is typically useful when the scaleMode is "proportionalOutside" or "none" or when the swf contains objects that are positioned off-stage. Any parts of the swf that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • autoPlay : Boolean - If autoPlay is true (the default), the swf will begin playing immediately when the INIT event fires. To prevent this behavior, set autoPlay to false which will also mute the swf until the SWFLoader completes. This only calls stop() on the main timeline but it does not prevent scripted animations.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • context : LoaderContext - To control things like the ApplicationDomain, SecurityDomain, and whether or not a policy file is checked, define a LoaderContext object. The default context is null when running locally and new LoaderContext(true, new ApplicationDomain(ApplicationDomain.currentDomain), SecurityDomain.currentDomain) when running remotely in order to avoid common security sandbox errors (see Adobe's LoaderContext documentation for details and precautions). Please make sure that if you load swfs from another domain that you have a crossdomain.xml file installed on that remote server that grants your swf access rights (see Adobe's docs for crossdomain.xml details). Again, if you want to impose security restrictions on the loaded swf, please define your own LoaderContext.
  • + *
  • suppressInitReparentEvents : Boolean - If true, the SWFLoader will suppress the REMOVED_FROM_STAGE and ADDED_TO_STAGE events that are normally dispatched when the subloaded swf is reparented into the ContentDisplay (this always happens in Flash when any DisplayObject that's in the display list gets reparented - SWFLoader just circumvents it by default initially to avoid common problems that could arise if the child swf is coded a certain way). For example, if your subloaded swf has this code: addEventListener(Event.REMOVED_FROM_STAGE, disposeEverything) and you set suppressInitReparentEvents to false, disposeEverything() would get called as soon as the swf inits (assuming the ContentDisplay is in the display list).
  • + *
  • integrateProgress : Boolean - By default, a SWFLoader instance will automatically look for LoaderMax loaders in the swf when it initializes. Every loader found with a requireWithRoot parameter set to that swf's root will be integrated into the SWFLoader's overall progress. The SWFLoader's COMPLETE event won't fire until all such loaders are also complete. If you prefer NOT to integrate the subloading loaders into the SWFLoader's overall progress, set integrateProgress to false.
  • + *
  • suppressUncaughtErrors : Boolean - To automatically suppress uncaught errors in the subloaded swf (errors that are thrown outside of a try...catch statement), set suppressUncaughtErrors to true, but please note that this will ONLY work if the parent swf is published to Flash Player 10.1 or later. Suppressing the UncaughtErrorEvent simply means calling its preventDefault() and stopImmediatePropagation() methods as well as preventing it from bubbling up to its parent LoaderMax/SWFLoader anscestors. If you'd rather listen for these events so that you can handle them yourself, listen for the LoaderEvent.UNCAUGHT_ERROR event. The original UncaughtErrorEvent instance will be stored in the LoaderEvent's data property.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the swf initializes and has been analyzed enough to determine the size of any nested loaders that were found inside the swf with their requireWithRoot set to that swf's root, it will adjust the bytesTotal accordingly. Setting estimatedBytes is optional, but it provides a way to avoid situations where the progress and bytesTotal values jump around as SWFLoader recognizes nested loaders in the swf and audits their size. The estimatedBytes value should include all nested loaders as well, so if your swf file itself is 2000 bytes and it has 3 nested ImageLoaders, each loading a 2000-byte image, your SWFLoader's estimatedBytes should be 8000. The more accurate the value, the more accurate the loaders' overall progress will be.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this SWFLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:SWFLoader = new SWFLoader("subload.swf", {name:"subloadSWF", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are called when the swf has streamed enough of its content to render the first frame and determine if there are any required LoaderMax-related loaders recognized. It also adds the swf to the ContentDisplay Sprite at this point. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderMax.SCRIPT_ACCESS_DENIED events which occur when the swf is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like BitmapData manipulation or integration of LoaderMax data inside the swf, etc. You can also check the scriptAccessDenied property after the swf has loaded. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onUncaughtError : Function - A handler function for LoaderEvent.UNCAUGHT_ERROR events which are dispatched when the subloaded swf encounters an UncaughtErrorEvent meaning an Error was thrown outside of a try...catch statement. This can be useful when subloading swfs from a 3rd party that may contain errors. However, UNCAUGHT_ERROR events will only be dispatched if the parent swf is published for Flash Player 10.1 or later! See SWFLoader's suppressUncaughtErrors special property if you'd like to have it automatically suppress these errors. The original UncaughtErrorEvent is stored in the LoaderEvent's data property. So, for example, if you'd like to call preventDefault() on that UncaughtErrorEvent, you'd do myLoaderEvent.data.preventDefault().
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) dispatches a PROGRESS event. To listen for changes in the SWFLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the SWFLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a SWFLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

content data type: com.greensock.loading.display.ContentDisplay (a Sprite). + * When the swf has finished loading, the rawContent will be added to the ContentDisplay + * Sprite at index 0 using addChildAt(). rawContent refers to the loaded swf's root + * unless script access is denied in which case it will be a flash.display.Loader (to avoid security errors).

+ * + * Example AS3 code:+ import com.greensock.~~; + import com.greensock.loading.~~; + + //create a SWFLoader that will add the content to the display list at position x:50, y:100 when it has loaded: + var loader:SWFLoader = new SWFLoader("swf/main.swf", {name:"mainSWF", container:this, x:50, y:100, onInit:initHandler, estimatedBytes:9500}); + + //begin loading + loader.load(); + + function initHandler(event:LoaderEvent):void { + //fade the swf in as soon as it inits + TweenLite.from(event.target.content, 1, {alpha:0}); + + //get a MovieClip named "phoneAnimation_mc" that's on the root of the subloaded swf + var mc:DisplayObject = loader.getSWFChild("phoneAnimation_mc"); + + //find the "com.greensock.TweenLite" class that's inside the subloaded swf + var tweenClass:Class = loader.getClass("com.greensock.TweenLite"); + } + + //Or you could put the SWFLoader into a LoaderMax. Create one first... + var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + + //append the SWFLoader and several other loaders + queue.append( loader ); + queue.append( new XMLLoader("xml/doc.xml", {name:"xmlDoc", estimatedBytes:425}) ); + queue.append( new ImageLoader("img/photo1.jpg", {name:"photo1", estimatedBytes:3500}) ); + + //start loading + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); + } + + function completeHandler(event:LoaderEvent):void { + trace(event.target + " is complete!"); + } + + function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); + } + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.SWFLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class SWFLoader extends DisplayObjectLoader { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("SWFLoader", SWFLoader, "swf"); + /** @private last pass-through uncaught error event. primarily for uncaughtError events - when a subloaded swf also has another swf that's subloaded by SWFLoader and that grandchild dispatches an uncaught error, we don't want to allow duplicates to travel up because both SWFLoaders (this and the child's) would be listening for uncaught errors through the _loader and sub-SWFLoader's _loader. **/ + protected var _lastPTUncaughtError:Event; + /** @private **/ + protected var _queue:LoaderMax; + /** @private When the INIT event is dispatched, we'll check to see if there's a runtime shared library like for TLF and we must do some backflips to accommodate it - _hasRSL will be toggled to true if we find one. **/ + protected var _hasRSL:Boolean; + /** @private **/ + protected var _rslAddedCount:uint; + /** @private In certain browsers, there's a bug in the Flash Player that incorrectly reports the Loader's bytesLoaded as never reaching bytesTotal even AFTER the Loader completes (only when gzip is enabled on the server). This helps us get around that bug. **/ + protected var _loaderCompleted:Boolean; + /** @private in cases where we must allow a canceled loader to continue loading until it inits (to avoid garbage collection issues), if the url is changed during the time we're in stealthMode, we must remember to load() as soon as the old/bad swf inits! This is the flag we use for that. **/ + protected var _loadOnExitStealth:Boolean; + /** @private if the Loader fails we must record that so that when _dump() is called, we know that the Loader isn't active anymore and we can safely dump it (as opposed to allowing it to continue loading until it inits which we normally must do in order to avoid garbage collection issues in Flash) **/ + protected var _loaderFailed:Boolean; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content + * @param vars An object containing optional configuration details. For example: new SWFLoader("swf/main.swf", {name:"main", container:this, x:100, y:50, alpha:0, autoPlay:false, onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or an SWFLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the SWFLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods. This name is also applied to the Sprite that is created to hold the swf (The SWFLoader's content refers to this Sprite). Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the content Sprite should be added immediately.
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - if true, the registration point will be placed in the center of the ContentDisplay Sprite which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the loaded swf will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The swf will fill the width/height exactly.
    • + *
    • "proportionalInside" - The swf will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The swf will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the swf will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the swf will be adjusted to fit.
    • + *
    • "none" - No scaling of the swf will occur.
    • + *
  • + *
  • hAlign : String - When a width and height are defined, the hAlign determines how the swf is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The swf will be centered horizontally in the area
    • + *
    • "left" - The swf will be aligned with the left side of the area
    • + *
    • "right" - The swf will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height are defined, the vAlign determines how the swf is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The swf will be centered vertically in the area
    • + *
    • "top" - The swf will be aligned with the top of the area
    • + *
    • "bottom" - The swf will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the swf to be cropped within that area (by applying a scrollRect for maximum performance) based on its native size (not the bounding box of the swf's current contents). This is typically useful when the scaleMode is "proportionalOutside" or "none" or when the swf contains objects that are positioned off-stage. Any parts of the swf that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • autoPlay : Boolean - If autoPlay is true (the default), the swf will begin playing immediately when the INIT event fires. To prevent this behavior, set autoPlay to false which will also mute the swf until the SWFLoader completes. This only calls stop() on the main timeline but it does not prevent scripted animations.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • context : LoaderContext - To control things like the ApplicationDomain, SecurityDomain, and whether or not a policy file is checked, define a LoaderContext object. The default context is null when running locally and new LoaderContext(true, new ApplicationDomain(ApplicationDomain.currentDomain), SecurityDomain.currentDomain) when running remotely in order to avoid common security sandbox errors (see Adobe's LoaderContext documentation for details and precautions). Please make sure that if you load swfs from another domain that you have a crossdomain.xml file installed on that remote server that grants your swf access rights (see Adobe's docs for crossdomain.xml details). Again, if you want to impose security restrictions on the loaded swf, please define your own LoaderContext.
  • + *
  • suppressInitReparentEvents : Boolean - If true, the SWFLoader will suppress the REMOVED_FROM_STAGE and ADDED_TO_STAGE events that are normally dispatched when the subloaded swf is reparented into the ContentDisplay (this always happens in Flash when any DisplayObject that's in the display list gets reparented - SWFLoader just circumvents it by default initially to avoid common problems that could arise if the child swf is coded a certain way). For example, if your subloaded swf has this code: addEventListener(Event.REMOVED_FROM_STAGE, disposeEverything) and you set suppressInitReparentEvents to false, disposeEverything() would get called as soon as the swf inits (assuming the ContentDisplay is in the display list).
  • + *
  • integrateProgress : Boolean - By default, a SWFLoader instance will automatically look for LoaderMax loaders in the swf when it initializes. Every loader found with a requireWithRoot parameter set to that swf's root will be integrated into the SWFLoader's overall progress. The SWFLoader's COMPLETE event won't fire until all such loaders are also complete. If you prefer NOT to integrate the subloading loaders into the SWFLoader's overall progress, set integrateProgress to false.
  • + *
  • suppressUncaughtErrors : Boolean - To automatically suppress uncaught errors in the subloaded swf (errors that are thrown outside of a try...catch statement), set suppressUncaughtErrors to true, but please note that this will ONLY work if the parent swf is published to Flash Player 10.1 or later. Suppressing the UncaughtErrorEvent simply means calling its preventDefault() and stopImmediatePropagation() methods as well as preventing it from bubbling up to its parent LoaderMax/SWFLoader anscestors. If you'd rather listen for these events so that you can handle them yourself, listen for the LoaderEvent.UNCAUGHT_ERROR event. The original UncaughtErrorEvent instance will be stored in the LoaderEvent's data property.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the swf initializes and has been analyzed enough to determine the size of any nested loaders that were found inside the swf with their requireWithRoot set to that swf's root, it will adjust the bytesTotal accordingly. Setting estimatedBytes is optional, but it provides a way to avoid situations where the progress and bytesTotal values jump around as SWFLoader recognizes nested loaders in the swf and audits their size. The estimatedBytes value should include all nested loaders as well, so if your swf file itself is 2000 bytes and it has 3 nested ImageLoaders, each loading a 2000-byte image, your SWFLoader's estimatedBytes should be 8000. The more accurate the value, the more accurate the loaders' overall progress will be.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this SWFLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:SWFLoader = new SWFLoader("subload.swf", {name:"subloadSWF", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are called when the swf has streamed enough of its content to render the first frame and determine if there are any required LoaderMax-related loaders recognized. It also adds the swf to the ContentDisplay Sprite at this point. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onScriptAccessDenied : Function - A handler function for LoaderMax.SCRIPT_ACCESS_DENIED events which occur when the swf is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like BitmapData manipulation or integration of LoaderMax data inside the swf, etc. You can also check the scriptAccessDenied property after the swf has loaded. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onUncaughtError : Function - A handler function for LoaderEvent.UNCAUGHT_ERROR events which are dispatched when the subloaded swf encounters an UncaughtErrorEvent meaning an Error was thrown outside of a try...catch statement. This can be useful when subloading swfs from a 3rd party that may contain errors. However, UNCAUGHT_ERROR events will only be dispatched if the parent swf is published for Flash Player 10.1 or later! See SWFLoader's suppressUncaughtErrors special property if you'd like to have it automatically suppress these errors. The original UncaughtErrorEvent is stored in the LoaderEvent's data property. So, for example, if you'd like to call preventDefault() on that UncaughtErrorEvent, you'd do myLoaderEvent.data.preventDefault().
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) dispatches a PROGRESS event. To listen for changes in the SWFLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the SWFLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.SWFLoaderVars + */ + public function SWFLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _preferEstimatedBytesInAudit = true; + _type = "SWFLoader"; + } + + /** @private **/ + override protected function _load():void { + if (_stealthMode) { + //it's already loading, so exit stealth mode (stealth mode is entered when the SWFLoader is canceled before the Loader has dispatched the INIT event - bugs in Flash cause gc problems if we try to close() or unload() a Loader between the time it starts loading and when INIT fires... + _stealthMode = _loadOnExitStealth; + } else if (!_initted) { + _loader.visible = false; + _sprite.addChild(_loader); //to avoid null object reference errors in code inside the child swf that may reference "stage" (we'll removeChild() as soon as it inits) + super._load(); + } else if (_queue != null) { + _changeQueueListeners(true); + _queue.load(false); + } + } + + /** @private **/ + override protected function _refreshLoader(unloadContent:Boolean=true):void { + super._refreshLoader(unloadContent); + _loaderCompleted = false; + } + + /** @private **/ + protected function _changeQueueListeners(add:Boolean):void { + if (_queue != null) { + var p:String; + if (add && this.vars.integrateProgress != false) { + for (p in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + _queue.addEventListener(_listenerTypes[p], _passThroughEvent, false, -100, true); + } + } + _queue.addEventListener(LoaderEvent.COMPLETE, _completeHandler, false, -100, true); + _queue.addEventListener(LoaderEvent.PROGRESS, _progressHandler, false, -100, true); + _queue.addEventListener(LoaderEvent.FAIL, _failHandler, false, -100, true); + } else { + _queue.removeEventListener(LoaderEvent.COMPLETE, _completeHandler); + _queue.removeEventListener(LoaderEvent.PROGRESS, _progressHandler); + _queue.removeEventListener(LoaderEvent.FAIL, _failHandler); + for (p in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + _queue.removeEventListener(_listenerTypes[p], _passThroughEvent); + } + } + } + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + _loaderCompleted = false; + //Flash will refuse to properly unload it if the INIT event hasn't been dispatched! Technically we allow it to keep loading until _initHandler() is called where we'll unload it. + if (_status == LoaderStatus.LOADING && !_initted && !_loaderFailed) { + _stealthMode = true; + super._dump(scrubLevel, newStatus, suppressEvents); + return; + } + if (_initted && !_scriptAccessDenied && scrubLevel != 2) { + _stopMovieClips(_loader.content); + if (_loader.content in _rootLookup) { + _queue = LoaderMax(_rootLookup[_loader.content]); + _changeQueueListeners(false); + if (scrubLevel == 0) { + _queue.cancel(); + } else { + delete _rootLookup[_loader.content]; + _queue.dispose( Boolean(scrubLevel != 2) ); + } + } + } + if (_stealthMode) { + try { + _loader.close(); + } catch (error:Error) { + + } + } + _loadOnExitStealth = false; + _stealthMode = _hasRSL = _loaderFailed = false; + _cacheIsDirty = true; + if (scrubLevel >= 1) { + _queue = null; + _initted = false; + super._dump(scrubLevel, newStatus, suppressEvents); + } else { + var content:* = _content; + super._dump(scrubLevel, newStatus, suppressEvents); + _content = content; //super._dump() will null "_content", but if the swf has loaded but not the _queue, we should keep the content so that if resume() is called, it just starts loading the queue. + } + } + + + /** @private **/ + protected function _stopMovieClips(obj:DisplayObject):void { + var mc:MovieClip = obj as MovieClip; + if (mc == null) { + return; + } + mc.stop(); + var i:int = mc.numChildren; + while (--i > -1) { + _stopMovieClips(mc.getChildAt(i)); + } + } + + /** @private **/ + override protected function _determineScriptAccess():void { + //don't test the BitmapData.draw() until the swf has fully loaded because it can incorrectly throw security errors in certain situations (like NetStreams that haven't started yet). + try { + var mc:DisplayObject = _loader.content; + } catch (error:Error) { + _scriptAccessDenied = true; + dispatchEvent(new LoaderEvent(LoaderEvent.SCRIPT_ACCESS_DENIED, this, error.message)); + return; + } + if (_loader.content is AVM1Movie) { + _scriptAccessDenied = true; + dispatchEvent(new LoaderEvent(LoaderEvent.SCRIPT_ACCESS_DENIED, this, "AVM1Movie denies script access")); + } + } + + /** @private **/ + override protected function _calculateProgress():void { + _cachedBytesLoaded = (_stealthMode) ? 0 : _loader.contentLoaderInfo.bytesLoaded; + if (_loader.contentLoaderInfo.bytesTotal != 0) { //otherwise if unload() was called, bytesTotal would go back down to 0. + _cachedBytesTotal = _loader.contentLoaderInfo.bytesTotal; + } + if (_cachedBytesTotal < _cachedBytesLoaded || _loaderCompleted) { + //In Chrome when the file exceeds a certain size and gzip is enabled on the server, Adobe's Loader reports bytesTotal as 0!!! + //and in Firefox, if gzip was enabled, on very small files the Loader's bytesLoaded would never quite reach the bytesTotal even after the COMPLETE event fired! + _cachedBytesTotal = _cachedBytesLoaded; + } + if (this.vars.integrateProgress == false) { + // do nothing + } else if (_queue != null && (uint(this.vars.estimatedBytes) < _cachedBytesLoaded || _queue.auditedSize)) { //make sure that estimatedBytes is prioritized until the _queue has audited its size successfully! + if (_queue.status <= LoaderStatus.COMPLETED) { + _cachedBytesLoaded += _queue.bytesLoaded; + _cachedBytesTotal += _queue.bytesTotal; + } + } else if (uint(this.vars.estimatedBytes) > _cachedBytesLoaded && (!_initted || (_queue != null && _queue.status <= LoaderStatus.COMPLETED && !_queue.auditedSize))) { + _cachedBytesTotal = uint(this.vars.estimatedBytes); + } + if ((_hasRSL && _content == null) || (!_initted && _cachedBytesLoaded == _cachedBytesTotal)) { + _cachedBytesLoaded = int(_cachedBytesLoaded * 0.99); //don't allow the progress to hit 1 yet + } + _cacheIsDirty = false; + } + + /** @private **/ + protected function _checkRequiredLoaders():void { + if (_queue == null && this.vars.integrateProgress != false && !_scriptAccessDenied && _content != null) { + _queue = _rootLookup[_content]; + if (_queue != null) { + _changeQueueListeners(true); + _queue.load(false); + _cacheIsDirty = true; + } + } + } + + /** + * Searches the loaded swf (and any of its subloaded swfs that were loaded using SWFLoader) for a particular + * class by name. For example, if the swf contains a class named "com.greensock.TweenLite", you can get a + * reference to that class like: + * + * +var tweenLite:Class = loader.getClass("com.greensock.TweenLite"); +//then you can create an instance of TweenLite like: +var tween:Object = new tweenLite(mc, 1, {x:100}); + + * + * @param className The full name of the class, like "com.greensock.TweenLite". + * @return The class associated with the className + */ + public function getClass(className:String):Class { + if (_content == null || _scriptAccessDenied) { + return null; + } + if (_content.loaderInfo.applicationDomain.hasDefinition(className)) { + return _content.loaderInfo.applicationDomain.getDefinition(className); + } else if (_queue != null) { + var result:Object; + var loaders:Array = _queue.getChildren(true, true); + var i:int = loaders.length; + while (--i > -1) { + if (loaders[i] is SWFLoader) { + result = (loaders[i] as SWFLoader).getClass(className); + if (result != null) { + return result as Class; + } + } + } + } + return null; + } + + /** + * Finds a DisplayObject that's on the root of the loaded SWF by name. For example, + * you could put a MovieClip with an instance name of "phoneAnimation_mc" on the stage (along with + * any other objects of course) and then when you load that swf you could use + * loader.getSWFChild("phoneAnimation_mc") to get that MovieClip. It would be + * similar to doing (loader.rawContent as DisplayObjectContainer).getChildByName("phoneAnimation_mc") + * but in a more concise way that doesn't require checking to see if the rawContent is null. getSWFChild() + * will return null if the content hasn't loaded yet or if scriptAccessDenied is true. + * + * @param name The name of the child DisplayObject that is located at the root of the swf. + * @return The DisplayObject with the specified name. Returns null if the content hasn't loaded yet or if scriptAccessDenied is true. + */ + public function getSWFChild(name:String):DisplayObject { + return (!_scriptAccessDenied && _content is DisplayObjectContainer) ? DisplayObjectContainer(_content).getChildByName(name) : null; + } + + /** + * @private + * Finds a particular loader inside any active LoaderMax instances that were discovered in the subloaded swf + * which had their requireWithRoot set to the swf's root. This is only useful in situations + * where the swf contains other loaders that are required. + * + * @param nameOrURL The name or url associated with the loader that should be found. + * @return The loader associated with the name or url. Returns null if none were found. + */ + public function getLoader(nameOrURL:String):* { + return (_queue != null) ? _queue.getLoader(nameOrURL) : null; + } + + /** + * @private + * Finds a particular loader's content from inside any active LoaderMax instances that were discovered in the + * subloaded swf which had their requireWithRoot set to the swf's root. This is only useful + * in situations where the swf contains other loaders that are required. + * + * @param nameOrURL The name or url associated with the loader whose content should be found. + * @return The content associated with the name or url. Returns null if none was found. + */ + public function getContent(nameOrURL:String):* { + if (nameOrURL == this.name || nameOrURL == _url) { + return this.content; + } + var loader:LoaderCore = this.getLoader(nameOrURL); + return (loader != null) ? loader.content : null; + } + + /** + * Returns and array of all LoaderMax-related loaders (if any) that were found inside the swf and + * had their requireWithRoot special vars property set to the swf's root. For example, + * if the following code was run on the first frame of the swf, it would be identified as a child + * of this SWFLoader:

+ * + * var loader:ImageLoader = new ImageLoader("1.jpg", {requireWithRoot:this.root});

+ * + *

Even if loaders are created later (not on frame 1), as long as their requireWithRoot + * points to this swf's root, the loader(s) will be considered a child of this SWFLoader and will be + * returned in the array that getChildren() creates. Beware, however, that by default + * child loaders are integrated into the SWFLoader's progress, so if the swf finishes + * loading and then a while later a loader is created inside that swf that has its requireWithRoot + * set to the swf's root, at that point the SWFLoader's progress would no longer be 1 (it would + * be less) but the SWFLoader's status remains unchanged.

+ * + *

No child loader can be found until the SWFLoader's INIT event is dispatched, meaning the first + * frame of the swf has loaded and instantiated.

+ * + * @param includeNested If true, loaders that are nested inside child LoaderMax, XMLLoader, or SWFLoader instances will be included in the returned array as well. The default is false. + * @param omitLoaderMaxes If true, no LoaderMax instances will be returned in the array; only LoaderItems like ImageLoaders, XMLLoaders, SWFLoaders, MP3Loaders, etc. The default is false. + * @return An array of loaders. + */ + public function getChildren(includeNested:Boolean=false, omitLoaderMaxes:Boolean=false):Array { + return (_queue != null) ? _queue.getChildren(includeNested, omitLoaderMaxes) : []; + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + override protected function _initHandler(event:Event):void { + //if the SWFLoader was cancelled before _initHandler() was called, Flash will refuse to properly unload it, so we allow it to continue but check the status here and _dump() if necessary. + if (_stealthMode) { + _initted = true; + var awaitingLoad:Boolean = _loadOnExitStealth; + _dump(((_status == LoaderStatus.DISPOSED) ? 3 : 1), _status, true); + if (awaitingLoad) { + _load(); + } + return; + } + + //swfs with TLF use their own funky preloader system that causes problems, so we need to work around them here... + _hasRSL = false; + try { + var tempContent:DisplayObject = _loader.content; + var className:String = getQualifiedClassName(tempContent); + if (className.substr(-13) == "__Preloader__") { + var rslPreloader:Object = tempContent["__rslPreloader"]; + if (rslPreloader != null) { + className = getQualifiedClassName(rslPreloader); + if (className == "fl.rsl::RSLPreloader") { + _hasRSL = true; + _rslAddedCount = 0; + tempContent.addEventListener(Event.ADDED, _rslAddedHandler); + } + } + } + } catch (error:Error) { + + } + if (!_hasRSL) { + _init(); + } + } + + /** @private **/ + protected function _init():void { + _determineScriptAccess(); + if (!_scriptAccessDenied) { + if (!_hasRSL) { + _content = _loader.content; + } + if (_content != null) { + if (this.vars.autoPlay == false && _content is MovieClip) { + var st:SoundTransform = _content.soundTransform; + st.volume = 0; //just make sure you can't hear any sounds as it's loading in the background. + _content.soundTransform = st; + _content.stop(); + } + _checkRequiredLoaders(); + } + if (_loader.parent == _sprite) { + if (_sprite.stage != null && this.vars.suppressInitReparentEvents == true) { + _sprite.addEventListener(Event.ADDED_TO_STAGE, _captureFirstEvent, true, 1000, true); + _loader.addEventListener(Event.REMOVED_FROM_STAGE, _captureFirstEvent, true, 1000, true); + } + _sprite.removeChild(_loader); //we only added it temporarily so that if the child swf references "stage" somewhere, it could avoid errors (as long as this SWFLoader's ContentDisplay is on the stage, like if a "container" is defined in vars) + } + + } else { + _content = _loader; + _loader.visible = true; + } + super._initHandler(null); + } + + /** @private **/ + protected function _captureFirstEvent(event:Event):void { + event.stopImmediatePropagation(); + event.currentTarget.removeEventListener(event.type, _captureFirstEvent); + } + + /** @private Works around bug - see http://kb2.adobe.com/cps/838/cpsid_83812.html **/ + protected function _rslAddedHandler(event:Event):void { + // check to ensure this was actually something added to the _loader.content + if (event.target is DisplayObject && event.currentTarget is DisplayObjectContainer && event.target.parent == event.currentTarget) { + _rslAddedCount++; + } + // the first thing added will be the loader animation swf - ignore that + if (_rslAddedCount > 1) { + event.currentTarget.removeEventListener(Event.ADDED, _rslAddedHandler); + if (_status == LoaderStatus.LOADING) { + _content = event.target; + _init(); + _calculateProgress(); + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + _completeHandler(null); + } + } + } + + /** @private **/ + override protected function _passThroughEvent(event:Event):void { + if (!(event.type == "uncaughtError" && _suppressUncaughtError(event)) && event.target != _queue) { + super._passThroughEvent(event); + } + } + + /** @private **/ + override protected function _progressHandler(event:Event):void { + if (_status == LoaderStatus.LOADING) { + if (_queue == null && _initted) { + _checkRequiredLoaders(); + } + if (_dispatchProgress) { + var bl:uint = _cachedBytesLoaded; + var bt:uint = _cachedBytesTotal; + _calculateProgress(); + if (_cachedBytesLoaded != _cachedBytesTotal && (bl != _cachedBytesLoaded || bt != _cachedBytesTotal)) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } else { + _cacheIsDirty = true; + } + } + } + + /** @private **/ + override protected function _completeHandler(event:Event=null):void { + _loaderCompleted = true; + _checkRequiredLoaders(); + _calculateProgress(); + if (this.progress == 1) { + if (!_scriptAccessDenied && this.vars.autoPlay == false && _content is MovieClip) { + var st:SoundTransform = _content.soundTransform; + st.volume = 1; + _content.soundTransform = st; + } + _changeQueueListeners(false); + super._determineScriptAccess(); //now do the BitmapData.draw() test. + super._completeHandler(event); + } + } + + /** @private **/ + override protected function _errorHandler(event:Event):void { + if (!_suppressUncaughtError(event)) { + super._errorHandler(event); + } + } + + /** @private **/ + protected function _suppressUncaughtError(event:Event):Boolean { + if (event is LoaderEvent && LoaderEvent(event).data is Event) { + event = LoaderEvent(event).data as Event; + } + if (event.type == "uncaughtError") { + if (_lastPTUncaughtError == (_lastPTUncaughtError = event)) { + return true; + } else if (this.vars.suppressUncaughtErrors == true) { + event.preventDefault(); + event.stopImmediatePropagation(); + return true; + } + } + return false; + } + + /** @private **/ + override protected function _failHandler(event:Event, dispatchError:Boolean=true):void { + if ((event.type == "ioError" || event.type == "securityError") && event.target == _loader.contentLoaderInfo) { + _loaderFailed = true; + if (_loadOnExitStealth) { //could happen if the url is set to another value between the time the SWFLoader starts loading and when it fails. + _dump(1, _status, true); + _load(); + return; + } + } + if (event.target == _queue) { + //this is a unique situation where we don't want the failure to unload the content because only one of the nested loaders failed but the swf may be perfectly good and usable. Also, we want to retain the _queue so that getChildren() works. Therefore we don't call super._failHandler(); + _status = LoaderStatus.FAILED; + _time = getTimer() - _time; + dispatchEvent(new LoaderEvent(LoaderEvent.CANCEL, this)); + dispatchEvent(new LoaderEvent(LoaderEvent.FAIL, this, this.toString() + " > " + (event as Object).text)); + return; + } + super._failHandler(event, dispatchError); + } + + +//---- GETTERS / SETTERS --------------------------------------------------------------- + + /** @private **/ + override public function set url(value:String):void { + if (_url != value) { + if (_status == LoaderStatus.LOADING && !_initted && !_loaderFailed) { + _loadOnExitStealth = true; + } + super.url = value; //will dump() too + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/SelfLoader.as b/src/com/greensock/loading/SelfLoader.as new file mode 100644 index 0000000..8be2d15 --- /dev/null +++ b/src/com/greensock/loading/SelfLoader.as @@ -0,0 +1,109 @@ +/** + * VERSION: 1.7 + * DATE: 2010-11-13 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.loading.core.LoaderItem; + + import flash.display.DisplayObject; + import flash.display.LoaderInfo; + import flash.events.Event; + import flash.events.ProgressEvent; +/** + * Tracks the loading progress of the swf in which the loader resides (basically a simple tool for tracking + * the loaderInfo's progress). SelfLoader is only useful in situations where you want to factor + * the current swf's loading progress into a LoaderMax queue or maybe display a progress bar for the current + * swf or fire an event when loading has finished. + * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the SelfLoader constructor via its vars parameter:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent(). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.events.LoaderEvent; + +//create a SelfLoader +var loader:SelfLoader = new SelfLoader(this, {name:"self", onProgress:progressHandler, onComplete:completeHandler}); + +//Or you could put the SelfLoader into a LoaderMax. Create one first... +var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + +//append the SelfLoader and several other loaders +queue.append( loader ); +queue.append( new ImageLoader("images/photo1.jpg", {name:"photo1", container:this}) ); +queue.append( new SWFLoader("swf/child.swf", {name:"child", container:this, x:100, estimatedBytes:3500}) ); + +//start loading the LoaderMax queue +queue.load(); + +function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); +} + +function completeHandler(event:LoaderEvent):void { + trace(event.target + " complete"); +} + +function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); +} + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SelfLoader extends LoaderItem { + /** @private **/ + protected var _loaderInfo:LoaderInfo; + + /** + * Constructor + * + * @param self A DisplayObject from the main swf (it will use this DisplayObject's loaderInfo to track the loading progress). + * @param vars An object containing optional configuration details. For example: new SelfLoader(this, {name:"self", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter:

+ *
    + *
  • name : String - A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent(). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ */ + public function SelfLoader(self:DisplayObject, vars:Object=null) { + super(self.loaderInfo.url, vars); + _type = "SelfLoader"; + _loaderInfo = self.loaderInfo; + _loaderInfo.addEventListener(ProgressEvent.PROGRESS, _progressHandler, false, 0, true); + _loaderInfo.addEventListener(Event.COMPLETE, _completeHandler, false, 0, true); + _cachedBytesTotal = _loaderInfo.bytesTotal; + _cachedBytesLoaded = _loaderInfo.bytesLoaded; + _status = (_cachedBytesLoaded == _cachedBytesTotal) ? LoaderStatus.COMPLETED : LoaderStatus.LOADING; + _auditedSize = true; + _content = self; + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + if (scrubLevel >= 2) { + _loaderInfo.removeEventListener(ProgressEvent.PROGRESS, _progressHandler); + _loaderInfo.removeEventListener(Event.COMPLETE, _completeHandler); + } + super._dump(scrubLevel, newStatus, suppressEvents); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/VideoLoader.as b/src/com/greensock/loading/VideoLoader.as new file mode 100644 index 0000000..c337f90 --- /dev/null +++ b/src/com/greensock/loading/VideoLoader.as @@ -0,0 +1,1366 @@ +/** + * VERSION: 1.941 + * DATE: 2015-01-20 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.LoaderItem; + import com.greensock.loading.display.ContentDisplay; + + import flash.display.Sprite; + import flash.events.Event; + import flash.events.NetStatusEvent; + import flash.events.ProgressEvent; + import flash.events.TimerEvent; + import flash.media.SoundTransform; + import flash.media.Video; + import flash.net.NetConnection; + import flash.net.NetStream; + import flash.net.URLRequest; + import flash.net.URLVariables; + import flash.utils.Timer; + import flash.utils.getTimer; + import flash.utils.setTimeout; + + /** Dispatched when the loader's httpStatus value changes. **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the netStream dispatches a NET_STATUS event. **/ + [Event(name="netStatus", type="com.greensock.events.LoaderEvent")] +/** + * Loads an FLV, F4V, or MP4 video file using a NetStream and also provides convenient playback methods + * and properties like pauseVideo(), playVideo(), gotoVideoTime(), bufferProgress, playProgress, volume, + * duration, videoPaused, metaData, and videoTime. Just like ImageLoader and SWFLoader, + * VideoLoader's content property refers to a ContentDisplay object (Sprite) that + * gets created immediately so that you can position/scale/rotate it or add ROLL_OVER/ROLL_OUT/CLICK listeners + * before (or while) the video loads. Use the VideoLoader's content property to get the ContentDisplay + * Sprite, or use the rawContent property to get the Video object that is used inside the + * ContentDisplay to display the video. If a container is defined in the vars object, + * the ContentDisplay will immediately be added to that container). + * + *

You don't need to worry about creating a NetConnection, a Video object, attaching the NetStream, or any + * of the typical hassles. VideoLoader can even scale the video into the area you specify using scaleModes + * like "stretch", "proportionalInside", "proportionalOutside", and more. A VideoLoader will + * dispatch useful events like VIDEO_COMPLETE, VIDEO_PAUSE, VIDEO_PLAY, VIDEO_BUFFER_FULL, + * VIDEO_BUFFER_EMPTY, NET_STATUS, VIDEO_CUE_POINT, and PLAY_PROGRESS in addition + * to the typical loader events, making it easy to hook up your own control interface. It packs a + * surprising amount of functionality into a very small amount of kb.

+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the VideoLoader constructor via its vars + * parameter which can be either a generic object or a VideoLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the VideoLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • bufferTime : Number - The amount of time (in seconds) that should be buffered before the video can begin playing (set autoPlay to false to pause the video initially).
  • + *
  • autoPlay : Boolean - By default, the video will begin playing as soon as it has been adequately buffered, but to prevent it from playing initially, set autoPlay to false.
  • + *
  • smoothing : Boolean - When smoothing is true (the default), smoothing will be enabled for the video which typically leads to better scaling results.
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the ContentDisplay should be added immediately.
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - if true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the video will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The video will fill the width/height exactly.
    • + *
    • "proportionalInside" - The video will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The video will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the video will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the video will be adjusted to fit.
    • + *
    • "none" - No scaling of the video will occur.
    • + *
  • + *
  • hAlign : String - When a width and height are defined, the hAlign determines how the video is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The video will be centered horizontally in the area
    • + *
    • "left" - The video will be aligned with the left side of the area
    • + *
    • "right" - The video will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height are defined, the vAlign determines how the video is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The video will be centered vertically in the area
    • + *
    • "top" - The video will be aligned with the top of the area
    • + *
    • "bottom" - The video will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the video to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the video that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • volume : Number - A value between 0 and 1 indicating the volume at which the video should play (default is 1).
  • + *
  • repeat : int - Number of times that the video should repeat. To repeat indefinitely, use -1. Default is 0.
  • + *
  • stageVideo : StageVideo - By default, the NetStream gets attached to a Video object, but if you want to use StageVideo in Flash, you can define the stageVideo property and VideoLoader will attach its NetStream to that StageVideo instance instead of the regular Video instance (which is the rawContent). Please read Adobe's docs regarding StageVideo to understand the benefits, tradeoffs and limitations.
  • + *
  • checkPolicyFile : Boolean - If true, the VideoLoader will check for a crossdomain.xml file on the remote host (only useful when loading videos from other domains - see Adobe's docs for details about NetStream's checkPolicyFile property).
  • + *
  • estimatedDuration : Number - Estimated duration of the video in seconds. VideoLoader will only use this value until it receives the necessary metaData from the video in order to accurately determine the video's duration. You do not need to specify an estimatedDuration, but doing so can help make the playProgress and some other values more accurate (until the metaData has loaded). It can also make the progress/bytesLoaded/bytesTotal more accurate when a estimatedDuration is defined, particularly in bufferMode.
  • + *
  • deblocking : int - Indicates the type of filter applied to decoded video as part of post-processing. The default value is 0, which lets the video compressor apply a deblocking filter as needed. See Adobe's flash.media.Video class docs for details.
  • + *
  • bufferMode : Boolean - When true, the loader will report its progress only in terms of the video's buffer which can be very convenient if, for example, you want to display loading progress for the video's buffer or tuck it into a LoaderMax with other loaders and allow the LoaderMax to dispatch its COMPLETE event when the buffer is full instead of waiting for the whole file to download. When bufferMode is true, the VideoLoader will dispatch its COMPLETE event when the buffer is full as opposed to waiting for the entire video to load. You can toggle the bufferMode anytime. Please read the full bufferMode property ASDoc description below for details about how it affects things like bytesTotal.
  • + *
  • autoAdjustBuffer : Boolean If the buffer becomes empty during playback and autoAdjustBuffer is true (the default), it will automatically attempt to adjust the NetStream's bufferTime based on the rate at which the video has been loading, estimating what it needs to be in order to play the rest of the video without emptying the buffer again. This can prevent the annoying problem of video playback start/stopping/starting/stopping on a system tht doesn't have enough bandwidth to adequately buffer the video. You may also set the bufferTime in the constructor's vars parameter to set the initial value.
  • + *
  • autoDetachNetStream : Boolean - If true, the NetStream will only be attached to the Video object (the rawContent) when it is in the display list (on the stage). This conserves memory but it can cause a very brief rendering delay when the content is initially added to the stage (often imperceptible). Also, if you add it to the stage when the videoTime is after its last encoded keyframe, it will render at that last keyframe.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this VideoLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:VideoLoader = new VideoLoader("myScript.php", {name:"textData", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for Event.INIT events which will be called when the video's metaData has been received and the video is placed into the ContentDisplay. The INIT event can be dispatched more than once if the NetStream receives metaData more than once (which occasionally happens, particularly with F4V files - the first time often doesn't include the cuePoints). Make sure your onInit function accepts a single parameter of type Event (flash.events.Event).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a VideoLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

Note: To avoid garbage collection issues in the Flash player, the netStream + * object that VideoLoader employs must get recreated internally anytime the VideoLoader is unloaded or its loading + * is cancelled, so if you need to directly access the netStream, it is best to do so after + * the COMPLETE event has been dispatched. Otherwise, if you store a reference to the VideoLoader's + * netStream before or during a load and it gets cancelled or unloaded for some reason, it won't reference + * the one that was used to load the video.

+ * + *

Note: There is a bug/inconsistency in Adobe's NetStream class that causes relative URLs + * to use the swf's location as the base path instead of the HTML page's location like all other loaders. Therefore, + * it would be wise to use the "base" attribute of the <OBJECT> and <EMBED> tags in the HTML to + * make sure all relative paths are consistent. See http://kb2.adobe.com/cps/041/tn_04157.html + * for details.

+ * + *

Note: In order to minimize memory usage, VideoLoader doesn't attach the NetStream to its Video + * object (the rawContent) until it is added to the display list. Therefore, if your VideoLoader's content + * isn't somewhere on the stage, the NetStream's visual content won't be fully decoded into memory (that's a good thing). + * The only time this could be of consequence is if you are trying to do a BitmapData.draw() of the VideoLoader's content + * or rawContent when it isn't on the stage. In that case, you'd just need to attach the NetStream manually before doing + * your BitmapData.draw() like myVideoLoader.rawContent.attachNetStream(myVideoLoader.netStream).

+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.loading.display.~~; + import com.greensock.~~; + import com.greensock.events.LoaderEvent; + +//create a VideoLoader +var video:VideoLoader = new VideoLoader("assets/video.flv", {name:"myVideo", container:this, width:400, height:300, scaleMode:"proportionalInside", bgColor:0x000000, autoPlay:false, volume:0, requireWithRoot:this.root, estimatedBytes:75000}); + +//start loading +video.load(); + +//add a CLICK listener to a button that causes the video to toggle its paused state. +button.addEventListener(MouseEvent.CLICK, togglePause); +function togglePause(event:MouseEvent):void { + video.videoPaused = !video.videoPaused; +} + +//or you could put the VideoLoader into a LoaderMax queue. Create one first... +var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + +//append the VideoLoader and several other loaders +queue.append( video ); +queue.append( new DataLoader("assets/data.txt", {name:"myText"}) ); +queue.append( new ImageLoader("assets/image1.png", {name:"myImage", estimatedBytes:3500}) ); + +//start loading the LoaderMax queue +queue.load(); + +function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); +} + +function completeHandler(event:LoaderEvent):void { + //play the video + video.playVideo(); + + //tween the volume up to 1 over the course of 2 seconds. + TweenLite.to(video, 2, {volume:1}); +} + +function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); +} + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.VideoLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class VideoLoader extends LoaderItem { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("VideoLoader", VideoLoader, "flv,f4v,mp4,mov"); + + /** Event type constant for when the video completes. **/ + public static const VIDEO_COMPLETE:String="videoComplete"; + /** Event type constant for when the video's buffer is full. **/ + public static const VIDEO_BUFFER_FULL:String="videoBufferFull"; + /** Event type constant for when the video's buffer is empty. **/ + public static const VIDEO_BUFFER_EMPTY:String="videoBufferEmpty"; + /** Event type constant for when the video is paused. **/ + public static const VIDEO_PAUSE:String="videoPause"; + /** Event type constant for when the video begins or resumes playing. If the buffer isn't full yet when VIDEO_PLAY is dispatched, the video will wait to visually begin playing until the buffer is full. So VIDEO_PLAY indicates when the NetStream received an instruction to play, not necessarily when it visually begins playing. **/ + public static const VIDEO_PLAY:String="videoPlay"; + /** Event type constant for when the video reaches a cue point in the playback of the NetStream. **/ + public static const VIDEO_CUE_POINT:String="videoCuePoint"; + /** Event type constant for when the playback progresses (only dispatched when the video is playing). **/ + public static const PLAY_PROGRESS:String="playProgress"; + + /** @private **/ + protected var _ns:NetStream; + /** @private **/ + protected var _nc:NetConnection; + /** @private **/ + protected var _auditNS:NetStream; + /** @private **/ + protected var _video:Video; + /** @private **/ + protected var _stageVideo:Object; //don't type as StageVideo because that would break publishing to FP9 + /** @private **/ + protected var _sound:SoundTransform; + /** @private **/ + protected var _videoPaused:Boolean; + /** @private **/ + protected var _videoComplete:Boolean; + /** @private **/ + protected var _forceTime:Number; + /** @private **/ + protected var _duration:Number; + /** @private **/ + protected var _pausePending:Boolean; + /** @private **/ + protected var _volume:Number; + /** @private **/ + protected var _sprite:Sprite; + /** @private **/ + protected var _initted:Boolean; + /** @private **/ + protected var _bufferMode:Boolean; + /** @private **/ + protected var _repeatCount:uint; + /** @private **/ + protected var _bufferFull:Boolean; + /** @private **/ + protected var _dispatchPlayProgress:Boolean; + /** @private **/ + protected var _prevTime:Number; + /** @private **/ + protected var _prevCueTime:Number; + /** @private **/ + protected var _firstCuePoint:CuePoint; + /** @private due to a bug in the NetStream class, we cannot seek() or pause() before the NetStream has dispatched a RENDER Event (or after 50ms for Flash Player 9). **/ + protected var _renderedOnce:Boolean; + /** @private primarily used for FP9 to work around a Flash bug with seek() and pause() (see the _waitForRender() method for note). **/ + protected var _renderTimer:Timer; + /** @private **/ + protected var _autoDetachNetStream:Boolean; + /** @private the first VIDEO_PLAY event shouldn't be dispatched until the NetStream's NetStatusEvent fires with the code NetStream.Play.Start gets dispatched, so we track it with this Boolean variable. Otherwise, if you create a VideoLoader with autoPlay:false and then immediately load() and playVideo(), it would dispatch the VIDEO_PLAY event twice, once for the playVideo() and once when the NetStatusEvent is received. **/ + protected var _playStarted:Boolean; + /** @private set to true as soon as the video finishes, and then is set back to false 1 ENTER_FRAME later - we use this to work around a bug in the Flash Player that causes a flicker when a seek() is called on a NetStream that just finished. **/ + protected var _finalFrame:Boolean; + + /** The metaData that was received from the video (contains information about its width, height, frame rate, etc.). See Adobe's docs for information about a NetStream's onMetaData callback. **/ + public var metaData:Object; + /** If the buffer becomes empty during playback and autoAdjustBuffer is true (the default), it will automatically attempt to adjust the NetStream's bufferTime based on the rate at which the video has been loading, estimating what it needs to be in order to play the rest of the video without emptying the buffer again. This can prevent the annoying problem of video playback start/stopping/starting/stopping on a system tht doesn't have enough bandwidth to adequately buffer the video. You may also set the bufferTime in the constructor's vars parameter to set the initial value. **/ + public var autoAdjustBuffer:Boolean; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content. + * @param vars An object containing optional configuration details. For example: new VideoLoader("video/video.flv", {name:"myVideo", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or a VideoLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the VideoLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • bufferTime : Number - The amount of time (in seconds) that should be buffered before the video can begin playing (set autoPlay to false to pause the video initially).
  • + *
  • autoPlay : Boolean - By default, the video will begin playing as soon as it has been adequately buffered, but to prevent it from playing initially, set autoPlay to false.
  • + *
  • smoothing : Boolean - When smoothing is true (the default), smoothing will be enabled for the video which typically leads to better scaling results.
  • + *
  • container : DisplayObjectContainer - A DisplayObjectContainer into which the ContentDisplay should be added immediately.
  • + *
  • width : Number - Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY).
  • + *
  • height : Number - Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY).
  • + *
  • centerRegistration : Boolean - if true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center.
  • + *
  • scaleMode : String - When a width and height are defined, the scaleMode controls how the video will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
      + *
    • "stretch" (the default) - The video will fill the width/height exactly.
    • + *
    • "proportionalInside" - The video will be scaled proportionally to fit inside the area defined by the width/height
    • + *
    • "proportionalOutside" - The video will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
    • + *
    • "widthOnly" - Only the width of the video will be adjusted to fit.
    • + *
    • "heightOnly" - Only the height of the video will be adjusted to fit.
    • + *
    • "none" - No scaling of the video will occur.
    • + *
  • + *
  • hAlign : String - When a width and height are defined, the hAlign determines how the video is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The video will be centered horizontally in the area
    • + *
    • "left" - The video will be aligned with the left side of the area
    • + *
    • "right" - The video will be aligned with the right side of the area
    • + *
  • + *
  • vAlign : String - When a width and height are defined, the vAlign determines how the video is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
      + *
    • "center" (the default) - The video will be centered vertically in the area
    • + *
    • "top" - The video will be aligned with the top of the area
    • + *
    • "bottom" - The video will be aligned with the bottom of the area
    • + *
  • + *
  • crop : Boolean - When a width and height are defined, setting crop to true will cause the video to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the video that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area.
  • + *
  • x : Number - Sets the ContentDisplay's x property (for positioning on the stage).
  • + *
  • y : Number - Sets the ContentDisplay's y property (for positioning on the stage).
  • + *
  • scaleX : Number - Sets the ContentDisplay's scaleX property.
  • + *
  • scaleY : Number - Sets the ContentDisplay's scaleY property.
  • + *
  • rotation : Number - Sets the ContentDisplay's rotation property.
  • + *
  • alpha : Number - Sets the ContentDisplay's alpha property.
  • + *
  • visible : Boolean - Sets the ContentDisplay's visible property.
  • + *
  • blendMode : String - Sets the ContentDisplay's blendMode property.
  • + *
  • bgColor : uint - When a width and height are defined, a rectangle will be drawn inside the ContentDisplay immediately in order to ease the development process. It is transparent by default, but you may define a bgAlpha if you prefer.
  • + *
  • bgAlpha : Number - Controls the alpha of the rectangle that is drawn when a width and height are defined.
  • + *
  • volume : Number - A value between 0 and 1 indicating the volume at which the video should play (default is 1).
  • + *
  • repeat : int - Number of times that the video should repeat. To repeat indefinitely, use -1. Default is 0.
  • + *
  • stageVideo : StageVideo - By default, the NetStream gets attached to a Video object, but if you want to use StageVideo in Flash, you can define the stageVideo property and VideoLoader will attach its NetStream to that StageVideo instance instead of the regular Video instance (which is the rawContent). Please read Adobe's docs regarding StageVideo to understand the benefits, tradeoffs and limitations.
  • + *
  • checkPolicyFile : Boolean - If true, the VideoLoader will check for a crossdomain.xml file on the remote host (only useful when loading videos from other domains - see Adobe's docs for details about NetStream's checkPolicyFile property).
  • + *
  • estimatedDuration : Number - Estimated duration of the video in seconds. VideoLoader will only use this value until it receives the necessary metaData from the video in order to accurately determine the video's duration. You do not need to specify an estimatedDuration, but doing so can help make the playProgress and some other values more accurate (until the metaData has loaded). It can also make the progress/bytesLoaded/bytesTotal more accurate when a estimatedDuration is defined, particularly in bufferMode.
  • + *
  • deblocking : int - Indicates the type of filter applied to decoded video as part of post-processing. The default value is 0, which lets the video compressor apply a deblocking filter as needed. See Adobe's flash.media.Video class docs for details.
  • + *
  • bufferMode : Boolean - When true, the loader will report its progress only in terms of the video's buffer which can be very convenient if, for example, you want to display loading progress for the video's buffer or tuck it into a LoaderMax with other loaders and allow the LoaderMax to dispatch its COMPLETE event when the buffer is full instead of waiting for the whole file to download. When bufferMode is true, the VideoLoader will dispatch its COMPLETE event when the buffer is full as opposed to waiting for the entire video to load. You can toggle the bufferMode anytime. Please read the full bufferMode property ASDoc description below for details about how it affects things like bytesTotal.
  • + *
  • autoAdjustBuffer : Boolean If the buffer becomes empty during playback and autoAdjustBuffer is true (the default), it will automatically attempt to adjust the NetStream's bufferTime based on the rate at which the video has been loading, estimating what it needs to be in order to play the rest of the video without emptying the buffer again. This can prevent the annoying problem of video playback start/stopping/starting/stopping on a system tht doesn't have enough bandwidth to adequately buffer the video. You may also set the bufferTime in the constructor's vars parameter to set the initial value.
  • + *
  • autoDetachNetStream : Boolean - If true, the NetStream will only be attached to the Video object (the rawContent) when it is in the display list (on the stage). This conserves memory but it can cause a very brief rendering delay when the content is initially added to the stage (often imperceptible). Also, if you add it to the stage when the videoTime is after its last encoded keyframe, it will render at that last keyframe.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader will be inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details).
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this VideoLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:VideoLoader = new VideoLoader("myScript.php", {name:"textData", requireWithRoot:this.root});
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for Event.INIT events which will be called when the video's metaData has been received and the video is placed into the ContentDisplay. The INIT event can be dispatched more than once if the NetStream receives metaData more than once (which occasionally happens, particularly with F4V files - the first time often doesn't include the cuePoints). Make sure your onInit function accepts a single parameter of type Event (flash.events.Event).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.VideoLoaderVars + */ + public function VideoLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _type = "VideoLoader"; + _nc = new NetConnection(); + _nc.connect(null); + _nc.addEventListener("asyncError", _failHandler, false, 0, true); + _nc.addEventListener("securityError", _failHandler, false, 0, true); + + _renderTimer = new Timer(80, 0); + _renderTimer.addEventListener(TimerEvent.TIMER, _renderHandler, false, 0, true); + + _video = new Video(this.vars.width || 320, this.vars.height || 240); + _video.smoothing = Boolean(this.vars.smoothing != false); + _video.deblocking = uint(this.vars.deblocking); + //the video isn't decoded into memory fully until the NetStream is attached to the Video object. We only attach it when it is in the display list (thus can be seen) in order to conserve memory. + _video.addEventListener(Event.ADDED_TO_STAGE, _videoAddedToStage, false, 0, true); + _video.addEventListener(Event.REMOVED_FROM_STAGE, _videoRemovedFromStage, false, 0, true); + + _stageVideo = this.vars.stageVideo; + + _autoDetachNetStream = Boolean(this.vars.autoDetachNetStream == true); + + _refreshNetStream(); + + _duration = isNaN(this.vars.estimatedDuration) ? 200 : Number(this.vars.estimatedDuration); //just set it to a high number so that the progress starts out low. + _bufferMode = _preferEstimatedBytesInAudit = Boolean(this.vars.bufferMode == true); + _videoPaused = _pausePending = Boolean(this.vars.autoPlay == false); + this.autoAdjustBuffer = !(this.vars.autoAdjustBuffer == false); + + this.volume = ("volume" in this.vars) ? Number(this.vars.volume) : 1; + + if (LoaderMax.contentDisplayClass is Class) { + _sprite = new LoaderMax.contentDisplayClass(this); + if (!_sprite.hasOwnProperty("rawContent")) { + throw new Error("LoaderMax.contentDisplayClass must be set to a class with a 'rawContent' property, like com.greensock.loading.display.ContentDisplay"); + } + } else { + _sprite = new ContentDisplay(this); + } + + Object(_sprite).rawContent = null; //so that the video doesn't initially show at the wrong size before the metaData is received at which point we can accurately determine the aspect ratio. + } + + /** @private **/ + protected function _refreshNetStream():void { + if (_ns != null) { + _ns.pause(); + try { + _ns.close(); + } catch (error:Error) { + + } + _sprite.removeEventListener(Event.ENTER_FRAME, _playProgressHandler); + _video.attachNetStream(null); + _video.clear(); + _ns.client = {}; + _ns.removeEventListener(NetStatusEvent.NET_STATUS, _statusHandler); + _ns.removeEventListener("ioError", _failHandler); + _ns.removeEventListener("asyncError", _failHandler); + _ns.removeEventListener(Event.RENDER, _renderHandler); + } + _prevTime = _prevCueTime = 0; + + _ns = (this.vars.netStream is NetStream) ? this.vars.netStream : new NetStream(_nc); + _ns.checkPolicyFile = Boolean(this.vars.checkPolicyFile == true); + _ns.client = {onMetaData:_metaDataHandler}; + + _ns.addEventListener(NetStatusEvent.NET_STATUS, _statusHandler, false, 0, true); + _ns.addEventListener("ioError", _failHandler, false, 0, true); + _ns.addEventListener("asyncError", _failHandler, false, 0, true); + + _ns.bufferTime = isNaN(this.vars.bufferTime) ? 5 : Number(this.vars.bufferTime); + + if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + } else if (!_autoDetachNetStream || _video.stage != null) { + _video.attachNetStream(_ns); + } + + _sound = _ns.soundTransform; + } + + /** @private **/ + override protected function _load():void { + _prepRequest(); + _repeatCount = 0; + _prevTime = _prevCueTime = 0; + _bufferFull = _playStarted = _renderedOnce = false; + this.metaData = null; + _pausePending = _videoPaused; + if (_videoPaused) { + _setForceTime(0); + _sound.volume = 0; + _ns.soundTransform = _sound; //temporarily silence the audio because in some cases, the Flash Player will begin playing it for a brief second right before the buffer is full (we can't pause until then) + } else { + this.volume = _volume; //ensures the volume is back to normal in case it had been temporarily silenced while buffering + } + _sprite.addEventListener(Event.ENTER_FRAME, _playProgressHandler); + _sprite.addEventListener(Event.ENTER_FRAME, _loadingProgressCheck); + _waitForRender(); + _videoComplete = _initted = false; + if (this.vars.noCache && (!_isLocal || _url.substr(0, 4) == "http") && _request.data != null) { + var concatChar:String = (_request.url.indexOf("?") != -1) ? "&" : "?"; + _ns.play( _request.url + concatChar + _request.data.toString() ); + } else { + _ns.play(_request.url); + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + if (_sprite == null) { + return; //already disposed! + } + _sprite.removeEventListener(Event.ENTER_FRAME, _loadingProgressCheck); + _sprite.removeEventListener(Event.ENTER_FRAME, _playProgressHandler); + _sprite.removeEventListener(Event.ENTER_FRAME, _detachNS); + _sprite.removeEventListener(Event.ENTER_FRAME, _finalFrameFinished); + _ns.removeEventListener(Event.RENDER, _renderHandler); + _renderTimer.stop(); + _forceTime = NaN; + _prevTime = _prevCueTime = 0; + _initted = false; + _renderedOnce = false; + _videoComplete = false; + this.metaData = null; + if (scrubLevel != 2) { + _refreshNetStream(); + (_sprite as Object).rawContent = null; + if (_video.parent != null) { + _video.parent.removeChild(_video); + } + } + + if (scrubLevel >= 2) { + + if (scrubLevel == 3) { + (_sprite as Object).dispose(false, false); + } + + _renderTimer.removeEventListener(TimerEvent.TIMER, _renderHandler); + _nc.removeEventListener("asyncError", _failHandler); + _nc.removeEventListener("securityError", _failHandler); + _ns.removeEventListener(NetStatusEvent.NET_STATUS, _statusHandler); + _ns.removeEventListener("ioError", _failHandler); + _ns.removeEventListener("asyncError", _failHandler); + _video.removeEventListener(Event.ADDED_TO_STAGE, _videoAddedToStage); + _video.removeEventListener(Event.REMOVED_FROM_STAGE, _videoRemovedFromStage); + _firstCuePoint = null; + + (_sprite as Object).gcProtect = (scrubLevel == 3) ? null : _ns; //we need to reference the NetStream in the ContentDisplay before forcing garbage collection, otherwise gc kills the NetStream even if it's attached to the Video and is playing on the stage! + _ns.client = {}; + _video = null; + _ns = null; + _nc.close(); + _nc = null; + _sound = null; + (_sprite as Object).loader = null; + _sprite = null; + _renderTimer = null; + } else { + _duration = isNaN(this.vars.estimatedDuration) ? 200 : Number(this.vars.estimatedDuration); //just set it to a high number so that the progress starts out low. + _videoPaused = _pausePending = Boolean(this.vars.autoPlay == false); + } + super._dump(scrubLevel, newStatus, suppressEvents); + } + + /** @private Set inside ContentDisplay's or FlexContentDisplay's "loader" setter. **/ + public function setContentDisplay(contentDisplay:Sprite):void { + _sprite = contentDisplay; + } + + /** @inheritDoc **/ + override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + if (type == PLAY_PROGRESS) { + _dispatchPlayProgress = true; + } + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** @private **/ + override protected function _calculateProgress():void { + _cachedBytesLoaded = _ns.bytesLoaded; + if (_cachedBytesLoaded > 1) { + if (_bufferMode) { + _cachedBytesTotal = _ns.bytesTotal * (_ns.bufferTime / _duration); + if (_ns.bufferLength > 0) { + _cachedBytesLoaded = (_ns.bufferLength / _ns.bufferTime) * _cachedBytesTotal; + } + } else { + _cachedBytesTotal = _ns.bytesTotal; + } + if (_cachedBytesTotal <= _cachedBytesLoaded) { + _cachedBytesTotal = ((this.metaData != null && _renderedOnce && _initted) || (getTimer() - _time >= 10000)) ? _cachedBytesLoaded : int(1.01 * _cachedBytesLoaded) + 1; //make sure the metaData has been received because if the NetStream file is cached locally sometimes the bytesLoaded == bytesTotal BEFORE the metaData arrives. Or timeout after 10 seconds. + } + if (!_auditedSize) { + _auditedSize = true; + dispatchEvent(new Event("auditedSize")); + } + } + _cacheIsDirty = false; + } + + /** + * Adds an ActionScript cue point. Cue points are only triggered when the video is playing and passes + * the cue point's position in the video (in the forwards direction - they are not triggered when you skip + * to a previous time in the video with gotoVideoTime()). + * + *

For example, to add a cue point named "coolPart" at the 5-second point of the video, do:

+ * + * +myVideoLoader.addASCuePoint(5, "coolPart", {message:"This is a cool part.", id:5}); +myVideoLoader.addEventListener(VideoLoader.VIDEO_CUE_POINT, cuePointHandler); +function cuePointHandler(event:LoaderEvent):void { + trace("hit cue point " + event.data.name + ", message: " + event.data.parameters.message); +} + + * + * @param time The time (in seconds) at which the cue point should be placed in the video. + * @param name The name of the cue point. It is acceptable to have multiple cue points with the same name. + * @param parameters An object containing any data that you want associated with the cue point. For example, {message:"descriptive text", id:5}. This data can be retrieved in the VIDEO_CUE_POINT handler via the LoaderEvent's data property like event.data.parameters + * @return The cue point that was added + * @see #removeASCuePoint() + * @see #gotoVideoCuePoint() + * @see #getCuePointTime() + */ + public function addASCuePoint(time:Number, name:String="", parameters:Object=null):Object { + var prev:CuePoint = _firstCuePoint; + if (prev != null && prev.time > time) { + prev = null; + } else { + while (prev && prev.time <= time && prev.next && prev.next.time <= time) { + prev = prev.next; + } + } + var cp:CuePoint = new CuePoint(time, name, parameters, prev); + if (prev == null) { + if (_firstCuePoint != null) { + _firstCuePoint.prev = cp; + cp.next = _firstCuePoint; + } + _firstCuePoint = cp; + } + return cp; + } + + /** + * Removes an ActionScript cue point that was added with addASCuePoint(). If multiple ActionScript + * cue points match the search criteria, only one is removed. To remove all, call this function repeatedly in a + * loop with the same parameters until it returns null. + * + * @param timeNameOrCuePoint The time, name or cue point object that should be removed. The method removes the first cue point that matches the criteria. + * @return The cue point that was removed (or null if none were found that match the criteria) + * @see #addASCuePoint() + */ + public function removeASCuePoint(timeNameOrCuePoint:*):Object { + var cp:CuePoint = _firstCuePoint; + while (cp) { + if (cp == timeNameOrCuePoint || cp.time == timeNameOrCuePoint || cp.name == timeNameOrCuePoint) { + if (cp.next) { + cp.next.prev = cp.prev; + } + if (cp.prev) { + cp.prev.next = cp.next; + } else if (cp == _firstCuePoint) { + _firstCuePoint = cp.next; + } + cp.next = cp.prev = null; + cp.gc = true; + return cp; + } + cp = cp.next; + } + return null; + } + + /** + * Finds a cue point by name and returns its corresponding time (where it is positioned in the video). + * All cue points will be included in the search (cue points embedded into the video when it was encoded + * as well as cue points that were added with addASCuePoint()). + * + * @param name The name of the cue point + * @return The cue point's time (NaN if no cue point was found with the specified name) + * @see #addASCuePoint() + * @see #gotoVideoCuePoint() + * @see #gotoVideoTime() + */ + public function getCuePointTime(name:String):Number { + if (this.metaData != null && this.metaData.cuePoints is Array) { + var i:int = this.metaData.cuePoints.length; + while (--i > -1) { + if (name == this.metaData.cuePoints[i].name) { + return Number(this.metaData.cuePoints[i].time); + } + } + } + var cp:CuePoint = _firstCuePoint; + while (cp) { + if (cp.name == name) { + return cp.time; + } + cp = cp.next; + } + return NaN; + } + + /** + * Attempts to jump to a certain cue point (either a cue point that was embedded in the + * video itself when it was encoded or a cue point that was added via addASCuePoint()). + * If the video hasn't downloaded enough to get to the cue point or if there is no keyframe at that + * point in the video, it will get as close as possible. For example, to jump to a cue point + * named "highlight1" and play from there:

+ * + * loader.gotoVideoCuePoint("highlight1", true);

+ * + * @param name The name of the cue point + * @param forcePlay If true, the video will resume playback immediately after seeking to the new position. + * @param skipCuePoints If true (the default), any cue points that are positioned between the current videoTime and the destination cue point will be ignored when moving to the new videoTime. In other words, it is like a record player that has its needle picked up, moved, and dropped into a new position rather than dragging it across the record, triggering the various cue points (if any exist there). IMPORTANT: cue points are only triggered when the time advances in the forward direction; they are never triggered when rewinding or restarting. + * @return The cue point's time (NaN if the cue point wasn't found) + * @see #gotoVideoTime() + * @see #addASCuePoint() + * @see #removeASCuePoint() + */ + public function gotoVideoCuePoint(name:String, forcePlay:Boolean=false, skipCuePoints:Boolean=true):Number { + return gotoVideoTime(getCuePointTime(name), forcePlay, skipCuePoints); + } + + /** + * Pauses playback of the video. + * + * @param event An optional Event which simply makes it easier to use the method as a handler for mouse clicks or other events. + * + * @see #videoPaused + * @see #gotoVideoTime() + * @see #playVideo() + * @see #videoTime + * @see #playProgress + **/ + public function pauseVideo(event:Event=null):void { + this.videoPaused = true; + } + + /** + * Plays the video (if the buffer isn't full yet, playback will wait until the buffer is full). + * + * @param event An optional Event which simply makes it easier to use the method as a handler for mouse clicks or other events. + * + * @see #videoPaused + * @see #pauseVideo() + * @see #gotoVideoTime() + * @see #videoTime + * @see #playProgress + **/ + public function playVideo(event:Event=null):void { + this.videoPaused = false; + } + + /** + * Sets or gets the current repeat count (how many times the video has repeated, as determined + * by the "repeat" special property that was passed into the constructor). If you pass + * a value to the function, it acts as a setter, and if you omit the parameter, it acts as a getter + * and returns the current value. For example, if the video was set to repeat 5 times and it is currently + * in the middle of its 3rd time playing, repeatCount() will return 2 because it has already + * finished playing twice completely. + * + * @param value the value that should be assigned to the current repeat count (or if you omit this parameter, the current repeat count will be returned) + * @return If the value parameter is omitted, it will return the current repeat count (how many times it has completely played and looped back to the beginning). If the function is used as a setter, the VideoLoader instance itself is returned in order to make chaining easier. + */ + public function repeatCount(value:int=0):* { + if (!arguments.length) { + return _repeatCount; + } + if (value < int(this.vars.repeat)) { + _videoComplete = false; + } + _repeatCount = value; + return this; + } + + /** + * Attempts to jump to a certain time in the video. If the video hasn't downloaded enough to get to + * the new time or if there is no keyframe at that time value, it will get as close as possible. + * For example, to jump to exactly 3-seconds into the video and play from there:

+ * + * loader.gotoVideoTime(3, true);

+ * + *

The VideoLoader's videoTime will immediately reflect the new time, but PLAY_PROGRESS + * event won't be dispatched until the NetStream's time renders at that spot (which can take a frame or so).

+ * + * @param time The time (in seconds, offset from the very beginning) at which to place the virtual playhead on the video. + * @param forcePlay If true, the video will resume playback immediately after seeking to the new position. + * @param skipCuePoints If true (the default), any cue points that are positioned between the current videoTime and the destination time (defined by the time parameter) will be ignored when moving to the new videoTime. In other words, it is like a record player that has its needle picked up, moved, and dropped into a new position rather than dragging it across the record, triggering the various cue points (if any exist there). IMPORTANT: cue points are only triggered when the time advances in the forward direction; they are never triggered when rewinding or restarting. + * @see #pauseVideo() + * @see #playVideo() + * @see #videoTime + * @see #playProgress + **/ + public function gotoVideoTime(time:Number, forcePlay:Boolean=false, skipCuePoints:Boolean=true):Number { + if (isNaN(time) || _ns == null) { + return NaN; + } else if (time > _duration) { + time = _duration; + } + var changed:Boolean = (time != this.videoTime); + if (_initted && _renderedOnce && changed && !_finalFrame) { //don't seek() until metaData has been received otherwise it can prevent it from ever being received. Also, if the NetStream hasn't rendered once and we seek(), it often completely loses its audio! + _seek(time); + } else { + _setForceTime(time); + } + _videoComplete = false; + if (changed) { + if (skipCuePoints) { + _prevCueTime = time; + } else { + _playProgressHandler(null); + } + } + if (forcePlay) { + playVideo(); + } + return time; + } + + /** Clears the video from the rawContent (the Video object). This also works around a bug in Adobe's Video class that prevents clear() from working properly in some versions of the Flash Player (https://bugs.adobe.com/jira/browse/FP-178). Note that this does not detatch the NetStream - it simply deletes the currently displayed image/frame, so you'd want to make sure the video is paused or finished before calling clearVideo(). **/ + public function clearVideo():void { + _video.smoothing = false; //a bug in Adobe's Video class causes it to not fully clear the video unless smoothing is set to false first. https://bugs.adobe.com/jira/browse/FP-178 + _video.clear(); + _video.smoothing = (this.vars.smoothing != false); + _video.clear(); //we need to call it a second time after the smoothing is changed, otherwise it doesn't work in some later versions of the player! + } + + /** @protected **/ + protected function _seek(time:Number):void { + _ns.seek(time); + _setForceTime(time); + if (_bufferFull) { + _bufferFull = false; + dispatchEvent(new LoaderEvent(VIDEO_BUFFER_EMPTY, this)); + } + } + + /** @private **/ + protected function _setForceTime(time:Number):void { + if (!(_forceTime || _forceTime == 0)) { //if _forceTime is already set, the listener was already added (we remove it after 1 frame or after the buffer fills for the first time and metaData is received (whichever takes longer) + _waitForRender(); //if, for example, after a video has finished playing, we seek(0) the video and immediately check the playProgress, it returns 1 instead of 0 because it takes a short time to render the first frame and accurately reflect the _ns.time variable. So we use a single ENTER_FRAME to help us override the _ns.time value briefly. + } + _forceTime = time; + } + + /** @private **/ + protected function _waitForRender():void { + _ns.addEventListener(Event.RENDER, _renderHandler, false, 0, true); //only works in Flash Player 10 and later + _renderTimer.reset(); + _renderTimer.start(); //backup for Flash Player 9 + } + + /** @private **/ + protected function _onBufferFull():void { + if (!_renderedOnce && !_renderTimer.running) { //in Flash Player 9, NetStream doesn't dispatch the RENDER event and the only reliable way I could find to sense when a render truly must have occured is to wait about 50 milliseconds after the buffer fills. Even waiting for an ENTER_FRAME event wouldn't work consistently (depending on the frame rate). Also, depending on the version of Flash that published the swf, the NetStream's NetStream.Buffer.Full status event may not fire (CS3 and CS4)! + _waitForRender(); + return; + } + if (_pausePending) { + if (!_initted && getTimer() - _time < 10000) { + _video.attachNetStream(null); //in some rare circumstances, the NetStream will finish buffering even before the metaData has been received. If we pause() the NetStream before the metaData arrives, it can prevent the metaData from ever arriving (bug in Flash) even after you resume(). So in this case, we allow the NetStream to continue playing so that metaData can be received, but we detach it from the Video object so that the user doesn't see the video playing. The volume is also muted, so to the user things look paused even though the NetStream is continuing to play/load. We'll re-attach the NetStream to the Video after either the metaData arrives or 10 seconds elapse. + } else if (_renderedOnce) { + _applyPendingPause(); + } + } else if (!_bufferFull) { + _bufferFull = true; + dispatchEvent(new LoaderEvent(VIDEO_BUFFER_FULL, this)); + } + } + + /** @private **/ + protected function _applyPendingPause():void { + _pausePending = false; + this.volume = _volume; //Just resets the volume to where it should be because we temporarily made it silent during the buffer. + _seek(_forceTime || 0); + if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + _ns.pause(); + } else if (!_autoDetachNetStream || _video.stage != null) { + _video.cacheAsBitmap = false; //works around an odd bug in Flash that can cause the video not to render when it is attached and paused immediately. + _video.attachNetStream(_ns); //in case it was removed + _ns.pause(); //If we pause() the NetStream when it isn't attached to the _video, a bug in Flash causes it to act like it continues playing!!! + } + } + + /** @private **/ + protected function _forceInit():void { + if (_ns.bufferTime >= _duration) { + _ns.bufferTime = uint(_duration - 1); + } + _initted = true; + if (!_bufferFull && _ns.bufferLength >= _ns.bufferTime) { + _onBufferFull(); + } + Object(_sprite).rawContent = _video; //resizes it appropriately + if (!_bufferFull && _pausePending && _renderedOnce && _video.stage != null) { + _video.attachNetStream(null); //if the NetStream is still buffering, there's a good chance that the video will appear to play briefly right before we pause it, so we detach the NetStream from the Video briefly to avoid that funky visual behavior (we attach it again as soon as it buffers). + } else if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + } else if (!_autoDetachNetStream || _video.stage != null) { + _video.attachNetStream(_ns); + } + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _metaDataHandler(info:Object):void { + if (this.metaData == null || this.metaData.cuePoints == null) { //sometimes videos will trigger the onMetaData multiple times (especially F4V files) and occassionally the last call doesn't contain cue point data! + this.metaData = info; + } + //due to buggy behavior in Flash's NetStream that sometimes causes cue point events to be triggered multiple times and/or at the wrong time, we convert embedded cue points into ActionScript cue points so that we can make everything consistent. + if (this.metaData.cuePoints) { + var a:Array = this.metaData.cuePoints, + i:int = a.length; + while (--i > -1) { + this.removeASCuePoint(a[i].name); //in case it was already added. There's buggy behavior in Flash's NetStream that causes it to sometimes receive its metaData twice! + this.addASCuePoint(a[i].time, a[i].name); + } + } + _duration = info.duration; + if ("width" in info) { + _video.width = Number(info.width); + _video.height = Number(info.height); + } + if ("framerate" in info) { + _renderTimer.delay = int(1000 / Number(info.framerate) + 1); + } + if (!_initted) { + _forceInit(); + } else { + (_sprite as Object).rawContent = _video; //on rare occasions, _metaDataHandler() is called twice by the NeStream (particularly for F4V files) and the 2nd call contains more data than the first, so just in case the width/height changed, we set the rawContent of the ContentDisplay to make sure things render according to the correct size. + } + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this, "", info)); + } + + /** @private **/ + protected function _playProgressHandler(event:Event):void { + if (!_bufferFull && !_videoComplete && (_ns.bufferLength >= _ns.bufferTime || this.duration - this.videoTime - _ns.bufferLength < 0.1)) { //remember, bufferLength could be less than bufferTime if videoTime is towards the end of the video and there's less time remaining to play than there is bufferTime. + _onBufferFull(); + } + if (_bufferFull && (_firstCuePoint || _dispatchPlayProgress)) { + var prevTime:Number = _prevTime, + prevCueTime:Number = _prevCueTime; + _prevTime = _prevCueTime = ((_forceTime || _forceTime == 0) && _ns.time <= _duration) ? _ns.time : this.videoTime; //note: isNaN(_forceTime) is much slower than !(_forceTime || _forceTime == 0) + var next:CuePoint, + cp:CuePoint = _firstCuePoint; + while (cp) { + next = cp.next; + if (cp.time > prevCueTime && cp.time <= _prevCueTime && !cp.gc) { + dispatchEvent(new LoaderEvent(VIDEO_CUE_POINT, this, "", cp)); + } + cp = next; + } + if (_dispatchPlayProgress && prevTime != _prevTime) { + dispatchEvent(new LoaderEvent(PLAY_PROGRESS, this)); + } + } + } + + /** @private **/ + protected function _statusHandler(event:NetStatusEvent):void { + var code:String = event.info.code; + if (code == "NetStream.Play.Start" && !_playStarted) { //remember, NetStream.Play.Start can be received BEFORE the buffer is full. + _playStarted = true; + if (!_pausePending) { + dispatchEvent(new LoaderEvent(VIDEO_PLAY, this)); + } + } + dispatchEvent(new LoaderEvent(NetStatusEvent.NET_STATUS, this, code, event.info)); + if (code == "NetStream.Play.Stop") { + if (_videoPaused) { + return; //Can happen when we seek() to a time in the video between the last keyframe and the end of the video file - NetStream.Play.Stop gets received even though the NetStream was paused. + } + _finalFrame = true; + _sprite.addEventListener(Event.ENTER_FRAME, _finalFrameFinished, false, 100, true); + if (this.vars.repeat == -1 || uint(this.vars.repeat) > _repeatCount) { + _repeatCount++; + dispatchEvent(new LoaderEvent(VIDEO_COMPLETE, this)); + gotoVideoTime(0, !_videoPaused, true); + } else { + _videoComplete = true; + this.videoPaused = true; + _playProgressHandler(null); + dispatchEvent(new LoaderEvent(VIDEO_COMPLETE, this)); + } + } else if (code == "NetStream.Buffer.Full") { + _onBufferFull(); + } else if (code == "NetStream.Seek.Notify") { + if (!_autoDetachNetStream && !isNaN(_forceTime)) { + _renderHandler(null); //note: do not _ns.pause() here when the NetStream isn't attached to the _video because a bug in Flash will prevent it from working (just when this NetStreamEvent occurs!) + } + //previously called _playProgressHandler(null) but a bug in NetStream often causes its time property not to report its correct (new) position yet, so we just wait to call _playProgressHandler() until the next frame. + } else if (code == "NetStream.Seek.InvalidTime" && "details" in event.info) { + _seek(event.info.details); + } else if (code == "NetStream.Buffer.Empty" && !_videoComplete) { + var videoRemaining:Number = this.duration - this.videoTime; + var prevBufferMode:Boolean = _bufferMode; + _bufferMode = false; //make sure bufferMode is false so that when we check progress, it gives us the data we need. + _cacheIsDirty = true; + var prog:Number = this.progress; + _bufferMode = prevBufferMode; + _cacheIsDirty = true; + if (prog == 1) { + //sometimes NetStream dispatches a "NetStream.Buffer.Empty" NetStatusEvent right before it finishes playing in which case we can deduce that the buffer isn't really empty. + return; + } + var loadRemaining:Number = (1 / prog) * this.loadTime; + var revisedBufferTime:Number = videoRemaining * (1 - (videoRemaining / loadRemaining)) * 0.9; //90% of the estimated time because typically you'd want the video to start playing again sooner and the 10% might be made up while it's playing anyway. + if (this.autoAdjustBuffer && loadRemaining > videoRemaining) { + _ns.bufferTime = revisedBufferTime; + } + _bufferFull = false; + dispatchEvent(new LoaderEvent(VIDEO_BUFFER_EMPTY, this)); + } else if (code == "NetStream.Play.StreamNotFound" || + code == "NetConnection.Connect.Failed" || + code == "NetStream.Play.Failed" || + code == "NetStream.Play.FileStructureInvalid" || + code == "The MP4 doesn't contain any supported tracks") { + _failHandler(new LoaderEvent(LoaderEvent.ERROR, this, code)); + } + } + + /** @private **/ + protected function _finalFrameFinished(event:Event):void { + _sprite.removeEventListener(Event.ENTER_FRAME, _finalFrameFinished); + _finalFrame = false; + if (!isNaN(_forceTime)) { + _seek(_forceTime); + } + } + + /** @private **/ + protected function _loadingProgressCheck(event:Event):void { + var bl:uint = _cachedBytesLoaded; + var bt:uint = _cachedBytesTotal; + if (!_bufferFull && _ns.bufferLength >= _ns.bufferTime) { + _onBufferFull(); + } + _calculateProgress(); + if (_cachedBytesLoaded == _cachedBytesTotal) { + _sprite.removeEventListener(Event.ENTER_FRAME, _loadingProgressCheck); + if (!_bufferFull) { + _onBufferFull(); + } + if (_initted) { + _completeHandler(event); + } else { + setTimeout(function() { + if (!_initted) { + _forceInit(); + _errorHandler(new LoaderEvent(LoaderEvent.ERROR, this, "No metaData was received.")); + } + _completeHandler(event); + }, 100); + } + + } else if (_dispatchProgress && (_cachedBytesLoaded / _cachedBytesTotal) != (bl / bt)) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } + + /** @inheritDoc + * Flash has a bug/inconsistency that causes NetStreams to load relative URLs as being relative to the swf file itself + * rather than relative to the HTML file in which it is embedded (all other loaders exhibit the opposite behavior), so + * we need to make sure the audits use NetStreams instead of URLStreams (for relative urls at least). + **/ + override public function auditSize():void { + if (_url.substr(0, 4) == "http" && _url.indexOf("://") != -1) { //if the url isn't relative, use the regular URLStream to do the audit because it's faster/more efficient. + super.auditSize(); + } else if (_auditNS == null) { + _auditNS = new NetStream(_nc); + _auditNS.bufferTime = isNaN(this.vars.bufferTime) ? 5 : Number(this.vars.bufferTime); + _auditNS.client = {onMetaData:_auditHandler, onCuePoint:_auditHandler}; + _auditNS.addEventListener(NetStatusEvent.NET_STATUS, _auditHandler, false, 0, true); + _auditNS.addEventListener("ioError", _auditHandler, false, 0, true); + _auditNS.addEventListener("asyncError", _auditHandler, false, 0, true); + _auditNS.soundTransform = new SoundTransform(0); + var request:URLRequest = new URLRequest(); + request.data = _request.data; + _setRequestURL(request, _url, (!_isLocal || _url.substr(0, 4) == "http") ? "gsCacheBusterID=" + (_cacheID++) + "&purpose=audit" : ""); + _auditNS.play(request.url); + } + } + + /** @private **/ + protected function _auditHandler(event:Event=null):void { + var type:String = (event == null) ? "" : event.type; + var code:String = (event == null || !(event is NetStatusEvent)) ? "" : NetStatusEvent(event).info.code; + if (event != null && "duration" in event) { + _duration = Object(event).duration; + } + if (_auditNS != null) { + _cachedBytesTotal = _auditNS.bytesTotal; + if (_bufferMode && _duration != 0) { + _cachedBytesTotal *= (_auditNS.bufferTime / _duration); + } + } + if (type == "ioError" || + type == "asyncError" || + code == "NetStream.Play.StreamNotFound" || + code == "NetConnection.Connect.Failed" || + code == "NetStream.Play.Failed" || + code == "NetStream.Play.FileStructureInvalid" || + code == "The MP4 doesn't contain any supported tracks") { + if (this.vars.alternateURL != undefined && this.vars.alternateURL != "" && this.vars.alternateURL != _url) { + _errorHandler(new LoaderEvent(LoaderEvent.ERROR, this, code)); + if (_status != LoaderStatus.DISPOSED) { //it is conceivable that the user disposed the loader in an onError handler + _url = this.vars.alternateURL; + _setRequestURL(_request, _url); + var request:URLRequest = new URLRequest(); + request.data = _request.data; + _setRequestURL(request, _url, (!_isLocal || _url.substr(0, 4) == "http") ? "gsCacheBusterID=" + (_cacheID++) + "&purpose=audit" : ""); + _auditNS.play(request.url); + } + return; + } else { + //note: a CANCEL event won't be dispatched because technically the loader wasn't officially loading - we were only briefly checking the bytesTotal with a NetStream. + super._failHandler(new LoaderEvent(LoaderEvent.ERROR, this, code)); + } + } + _auditedSize = true; + _closeStream(); + dispatchEvent(new Event("auditedSize")); + } + + /** @private **/ + override protected function _closeStream():void { + if (_auditNS != null) { + _auditNS.client = {}; + _auditNS.removeEventListener(NetStatusEvent.NET_STATUS, _auditHandler); + _auditNS.removeEventListener("ioError", _auditHandler); + _auditNS.removeEventListener("asyncError", _auditHandler); + _auditNS.pause(); + try { + _auditNS.close(); + } catch (error:Error) { + + } + _auditNS = null; + } else { + super._closeStream(); + } + } + + /** @private **/ + override protected function _auditStreamHandler(event:Event):void { + if (event is ProgressEvent && _bufferMode) { + (event as ProgressEvent).bytesTotal *= (_ns.bufferTime / _duration); + } + super._auditStreamHandler(event); + } + + /** @private **/ + protected function _renderHandler(event:Event):void { + _renderedOnce = true; + if (!_videoPaused || _initted) if (!_finalFrame) { //if the video hasn't initted yet and it's paused, keep reporting the _forceTime and let the _renderTimer keep calling until the condition is no longer met. + _forceTime = NaN; + _renderTimer.stop(); + _ns.removeEventListener(Event.RENDER, _renderHandler); + } + if (_pausePending) { + if (_bufferFull) { + _applyPendingPause(); + } else if (_video.stage != null) { + //if the NetStream is still buffering, there's a good chance that the video will appear to play briefly right before we pause it, so we detach the NetStream from the Video briefly to avoid that funky visual behavior (we attach it again as soon as it buffers). + //we cannot do _video.attachNetStream(null) here (within this RENDER handler) because it causes Flash Pro to crash! We must wait for an ENTER_FRAME event. + _sprite.addEventListener(Event.ENTER_FRAME, _detachNS, false, 100, true); + } + } else if (_videoPaused && _initted) { + _ns.pause(); + } + } + + /** @private see notes in _renderHandler() **/ + private function _detachNS(event:Event):void { + _sprite.removeEventListener(Event.ENTER_FRAME, _detachNS); + if (!_bufferFull && _pausePending) { + _video.attachNetStream(null); //if the NetStream is still buffering, there's a good chance that the video will appear to play briefly right before we pause it, so we detach the NetStream from the Video briefly to avoid that funky visual behavior (we attach it again as soon as it buffers). + } + } + + /** @private The video isn't decoded into memory fully until the NetStream is attached to the Video object. We only attach it when it is in the display list (thus can be seen) in order to conserve memory. **/ + protected function _videoAddedToStage(event:Event):void { + if (_autoDetachNetStream) { + if (!_pausePending) { + _seek(this.videoTime); //a bug in Flash prevents the video from rendering visually unless we seek() when we attachNetStream() + } + if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + } else { + _video.attachNetStream(_ns); + } + } + } + + /** @private **/ + protected function _videoRemovedFromStage(event:Event):void { + if (_autoDetachNetStream) { + _video.attachNetStream(null); + _video.clear(); + } + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** A ContentDisplay (a Sprite) that contains a Video object to which the NetStream is attached. This ContentDisplay Sprite can be accessed immediately; you do not need to wait for the video to load. **/ + override public function get content():* { + return _sprite; + } + + /** The Video object to which the NetStream was attached (automatically created by VideoLoader internally) **/ + public function get rawContent():Video { + return _video; + } + + /** The NetStream object used to load the video **/ + public function get netStream():NetStream { + return _ns; + } + + /** The playback status of the video: true if the video's playback is paused, false if it isn't. **/ + public function get videoPaused():Boolean { + return _videoPaused; + } + public function set videoPaused(value:Boolean):void { + var changed:Boolean = Boolean(value != _videoPaused); + _videoPaused = value; + if (_videoPaused) { + //If we're trying to pause a NetStream that hasn't even been buffered yet, we run into problems where it won't load. So we need to set the _pausePending to true and then when it's buffered, it'll pause it at the beginning. + if (!_renderedOnce) { + _setForceTime(0); + _pausePending = true; + _sound.volume = 0; //temporarily make it silent while buffering. + _ns.soundTransform = _sound; + } else { + _pausePending = false; + this.volume = _volume; //Just resets the volume to where it should be in case we temporarily made it silent during the buffer. + _ns.pause(); + } + if (changed) { + //previously, we included _sprite.removeEventListener(Event.ENTER_FRAME, _playProgressHandler) but discovered it was better to leave it running in order to work around a bug in Adobe's NetStream that causes it not to accurately report its time even when the NetStatusEvent is dispatched with the code "NetStream.Seek.Notify". Consequently, when the VideoLoader was paused and the videoProgress was changed or gotoVideoTime() was called, the PLAY_PROGRESS event would be dispatched before the NetStream.time arrived where it was supposed to be. + dispatchEvent(new LoaderEvent(VIDEO_PAUSE, this)); + } + } else { + if (_pausePending || !_bufferFull) { + if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + } else if (_video.stage != null) { + _video.attachNetStream(_ns); //in case we had to detach it while buffering and waiting for the metaData + } + //if we don't seek() first, sometimes the NetStream doesn't attach to the video properly! + //if we don't seek() first and the NetStream was previously rendered between its last keyframe and the end of the file, the "NetStream.Play.Stop" will have been called and it will refuse to continue playing even after resume() is called! + //if we seek() before the metaData has been received (_initted==true), it typically prevents it from being received at all! + //if we seek() before the NetStream has rendered once, it can lose audio completely! + if (_initted && _renderedOnce) { + _seek(this.videoTime); + } + _pausePending = false; + } + this.volume = _volume; //Just resets the volume to where it should be in case we temporarily made it silent during the buffer. + _ns.resume(); + if (changed && _playStarted) { + dispatchEvent(new LoaderEvent(VIDEO_PLAY, this)); + } + } + } + + /** A value between 0 and 1 describing the progress of the buffer (0 = not buffered at all, 0.5 = halfway buffered, and 1 = fully buffered). The buffer progress is in relation to the bufferTime which is 5 seconds by default or you can pass a custom value in through the vars parameter in the constructor like {bufferTime:20}. **/ + public function get bufferProgress():Number { + if (uint(_ns.bytesTotal) < 5) { + return 0; + } + return (_ns.bufferLength > _ns.bufferTime) ? 1 : _ns.bufferLength / _ns.bufferTime; + } + + /** A value between 0 and 1 describing the playback progress where 0 means the virtual playhead is at the very beginning of the video, 0.5 means it is at the halfway point and 1 means it is at the end of the video. **/ + public function get playProgress():Number { + //Often times the duration MetaData that gets passed in doesn't exactly reflect the duration, so after the FLV is finished playing, the time and duration wouldn't equal each other, so we'd get percentPlayed values of 99.26978. We have to use this _videoComplete variable to accurately reflect the status. + //If for example, after an FLV has finished playing, we gotoVideoTime(0) the FLV and immediately check the playProgress, it returns 1 instead of 0 because it takes a short time to render the first frame and accurately reflect the _ns.time variable. So we use an interval to help us override the _ns.time value briefly. + return (_videoComplete) ? 1 : (this.videoTime / _duration); + } + public function set playProgress(value:Number):void { + if (_duration != 0) { + gotoVideoTime((value * _duration), !_videoPaused, true); + } + } + + /** The volume of the video (a value between 0 and 1). **/ + public function get volume():Number { + return _volume; + } + public function set volume(value:Number):void { + _sound.volume = _volume = value; + _ns.soundTransform = _sound; + } + + /** The soundTransform of the NetStream associated with the VideoLoader (this gets refreshed when the VideoLoader is unloaded or reloaded). **/ + public function get soundTransform():SoundTransform { + return _sound; + } + public function set soundTransform(value:SoundTransform):void { + _ns.soundTransform = _sound = value; + } + + /** The time (in seconds) at which the virtual playhead is positioned on the video. For example, if the virtual playhead is currently at the 3-second position (3 seconds from the beginning), this value would be 3. **/ + public function get videoTime():Number { + if (_forceTime || _forceTime == 0) { + return _forceTime; + } else if (_videoComplete) { + return _duration; + } else if (_ns.time > _duration) { + return _duration * 0.995; //sometimes the NetStream reports a time that's greater than the duration so we must correct for that. + } else { + return _ns.time; + } + } + public function set videoTime(value:Number):void { + gotoVideoTime(value, !_videoPaused, true); + } + + /** The duration (in seconds) of the video. This value is only accurate AFTER the metaData has been received and the INIT event has been dispatched. **/ + public function get duration():Number { + return _duration; + } + + /** + * When bufferMode is true, the loader will report its progress only in terms of the + * video's buffer instead of its overall file loading progress which has the following effects: + *
    + *
  • The bytesTotal will be calculated based on the NetStream's duration, bufferLength, and bufferTime meaning it may fluctuate in order to accurately reflect the overall progress ratio.
  • + *
  • Its COMPLETE event will be dispatched as soon as the buffer is full, so if the VideoLoader is nested in a LoaderMax, the LoaderMax will move on to the next loader in its queue at that point. However, the VideoLoader's NetStream will continue to load in the background, using up bandwidth.
  • + *
+ * + *

This can be very convenient if, for example, you want to display loading progress based on the video's buffer + * or if you want to load a series of loaders in a LoaderMax and have it fire its COMPLETE event + * when the buffer is full (as opposed to waiting for the entire video to load).

+ **/ + public function get bufferMode():Boolean { + return _bufferMode; + } + public function set bufferMode(value:Boolean):void { + _bufferMode = value; + _preferEstimatedBytesInAudit = _bufferMode; + _calculateProgress(); + if (_cachedBytesLoaded < _cachedBytesTotal && _status == LoaderStatus.COMPLETED) { + _status = LoaderStatus.LOADING; + _sprite.addEventListener(Event.ENTER_FRAME, _loadingProgressCheck); + } + } + + /** If true (the default), the NetStream will only be attached to the Video object (the rawContent) when it is in the display list (on the stage). This conserves memory but it can cause a very brief rendering delay when the content is initially added to the stage (often imperceptible). Also, if you add it to the stage when the videoTime is after its last encoded keyframe, it will render at that last keyframe. **/ + public function get autoDetachNetStream():Boolean { + return _autoDetachNetStream; + } + public function set autoDetachNetStream(value:Boolean):void { + _autoDetachNetStream = value; + if (_autoDetachNetStream && _video.stage == null) { + _video.attachNetStream(null); + _video.clear(); + } else if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + } else { + _video.attachNetStream(_ns); + } + } + + /** By default, the NetStream gets attached to a Video object, but if you want to use StageVideo in Flash, you can define the stageVideo object and VideoLoader will attach its NetStream to that StageVideo instance instead of the regular Video instance (which is the rawContent). Please read Adobe's docs regarding StageVideo to understand the tradeoffs and limitations. Note: the data type is Object instead of StageVideo in order to make VideoLoader compatible with Flash Player 9 and 10. Otherwise, you wouldn't be able to publish to those players because StageVideo was introduced in a later version. **/ + public function get stageVideo():Object { + return _stageVideo; + } + public function set stageVideo(value:Object):void { + if (_stageVideo != value) { + _stageVideo = value; + if (_stageVideo != null) { + _stageVideo.attachNetStream(_ns); + _video.clear(); + } else { + _video.attachNetStream(_ns); + } + } + } + + } +} + +/** @private for the linked list of cue points - makes processing very fast. **/ +internal class CuePoint { + public var next:CuePoint; + public var prev:CuePoint; + public var time:Number; + public var name:String; + public var parameters:Object; + public var gc:Boolean; + + public function CuePoint(time:Number, name:String, params:Object, prev:CuePoint) { + this.time = time; + this.name = name; + this.parameters = params; + if (prev) { + this.prev = prev; + if (prev.next) { + prev.next.prev = this; + this.next = prev.next; + } + prev.next = this; + } + } + +} \ No newline at end of file diff --git a/src/com/greensock/loading/XMLLoader.as b/src/com/greensock/loading/XMLLoader.as new file mode 100644 index 0000000..e0e8f40 --- /dev/null +++ b/src/com/greensock/loading/XMLLoader.as @@ -0,0 +1,645 @@ +/** + * VERSION: 1.934 + * DATE: 2013-02-28 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.core.LoaderCore; + + import flash.events.Event; + import flash.system.ApplicationDomain; + import flash.system.LoaderContext; + import flash.system.SecurityDomain; + import flash.utils.getTimer; + + /** Dispatched when the XML finishes loading and its contents are parsed (creating any dynamic XML-driven loader instances necessary). If any dynamic loaders are created and have a load="true" attribute, they will begin loading at this point and the XMLLoader's COMPLETE will not be dispatched until the loaders have completed as well. **/ + [Event(name="init", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches an OPEN event. **/ + [Event(name="childOpen", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches a PROGRESS event. **/ + [Event(name="childProgress", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches a COMPLETE event. **/ + [Event(name="childComplete", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches a FAIL event. **/ + [Event(name="childFail", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches a CANCEL event. **/ + [Event(name="childCancel", type="com.greensock.events.LoaderEvent")] + /** Dispatched when any loader that the XMLLoader discovered in the XML dispatches a SCRIPT_ACCESS_DENIED event. **/ + [Event(name="scriptAccessDenied", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader's httpStatus value changes. **/ + [Event(name="httpStatus", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader experiences a SECURITY_ERROR which can occur when the XML file is loaded from another domain and there is no crossdomain.xml file in place granting appropriate access. **/ + [Event(name="securityError", type="com.greensock.events.LoaderEvent")] +/** + * Loads an XML file and automatically searches it for LoaderMax-related nodes like <LoaderMax>, + * <ImageLoader>, <SWFLoader>, <XMLLoader>, <DataLoader> <CSSLoader>, <MP3Loader>, + * etc.; if it finds any, it will create the necessary instances and begin loading them if they have a load="true" + * attribute. The XMLLoader's progress will automatically factor in the dynamically-created + * loaders that have the load="true" attribute and it won't dispatch its COMPLETE event + * until those loaders have completed as well (unless integrateProgress:false is passed to the constructor). + * For example, let's say the XML file contains the following XML: + * + * Example XML code:+<?xml version="1.0" encoding="iso-8859-1"?> +<data> + <widget name="myWidget1" id="10"> + <ImageLoader name="widget1" url="img/widget1.jpg" estimatedBytes="2000" /> + </widget> + <widget name="myWidget2" id="23"> + <ImageLoader name="widget2" url="img/widget2.jpg" estimatedBytes="2800" load="true" /> + </widget> + <LoaderMax name="dynamicLoaderMax" load="true" prependURLs="http://www.greensock.com/"> + <ImageLoader name="photo1" url="img/photo1.jpg" /> + <ImageLoader name="logo" url="img/corporate_logo.png" estimatedBytes="2500" /> + <SWFLoader name="mainSWF" url="swf/main.swf" autoPlay="false" estimatedBytes="15000" /> + <MP3Loader name="audio" url="mp3/intro.mp3" autoPlay="true" loops="100" /> + </LoaderMax> +</data> + + * + *

Once the XML has been loaded and parsed, the XMLLoader will recognize the 7 LoaderMax-related nodes + * (assuming you activated the various types of loaders - see the activate() method for details) + * and it will create instances dynamically. Then it will start loading the ones that had a load="true" + * attribute which in this case means all but the first loader will be loaded in the order they were defined in the XML. + * Notice the loaders nested inside the <LoaderMax> don't have load="true" but + * they will be loaded anyway because their parent LoaderMax has the load="true" attribute. + * After the XMLLoader's INIT event is dispatched, you can get any loader by name or URL with the + * LoaderMax.getLoader() method and monitor its progress or control it as you please. + * And after the XMLLoader's COMPLETE event is dispatched, you can use LoaderMax.getContent() + * to get content based on the name or URL of any of the loaders that had load="true" defined + * in the XML. For example:

+ * + * Example AS3 code:+var loader:XMLLoader = new XMLLoader("xml/doc.xml", {name:"xmlDoc", onComplete:completeHandler}); + +function completeHandler(event:LoaderEvent):void { + + //get the content from the "photo1" ImageLoader that was defined inside the XML + var photo:ContentDisplay = LoaderMax.getContent("photo1"); + + //add it to the display list + addChild(photo); + + //fade it in + TweenLite.from(photo, 1, {alpha:0}); +} + + * + *

You do not need to put loader-related nodes in your XML files. It is a convenience that is completely + * optional. XMLLoader does a great job of loading plain XML data even without the fancy automatic parsing of + * loader data.

+ * + *

You may put extra data in the LoaderMax-related nodes that you'd like associated with that particular + * loader. XMLLoader will put all of the attributes from the XML node into the vars object of + * the resulting loader as well as an extra rawXML property which will contain the raw XML + * for that node. For example, if this node is in your XML document:

+... +<VideoLoader url="video.flv" name="video1" description="Hidden dangers of steel wool" autoPlay="false"> + <links> + <link url="http://www.greensock.com" title="GreenSock" /> + <link url="http://www.google.com" title="Google" /> + </links> +</VideoLoader> +... + + * + *

Notice the "description" attribute which isn't a LoaderMax-specific property. XMLLoader will still + * put that value into the VideoLoader's vars property and create a rawXML + * property there that contains the whole XML node (including the children) so that you can easily get + * whatever data you need like this:

+ * +function completeHandler(event:LoaderEvent):void { + var video:VideoLoader = LoaderMax.getLoader("video1"); + var description:String = video.vars.description; + var xml:XML = video.vars.rawXML; + trace("first link url: " + xml.links[0].link[0].@url); //traces "first link url: http://www.greensock.com" +} + + * + *

Since XML is essentially text/string data, you cannot reference functions. Therefore it isn't possible + * to define onComplete, onInit, onError, etc. listeners inside XML. You can, however, add those via ActionScript once + * the XMLLoader has parsed the loaders. Simply use the standard addEventListener() method for that.

+ * + *

OPTIONAL VARS PROPERTIES

+ *

The following special properties can be passed into the XMLLoader constructor via its vars + * parameter which can be either a generic object or an XMLLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the XMLLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • integrateProgress : Boolean - By default, the XMLLoader will automatically look for LoaderMax-related nodes like <LoaderMax>, <ImageLoader>, <SWFLoader>, <XMLLoader>, <MP3Loader>, <DataLoader>, and <CSSLoader> inside the XML when it inits. If it finds any that have a load="true" attribute, it will begin loading them and integrate their progress into the XMLLoader's overall progress. Its COMPLETE event won't fire until all of these loaders have completed as well. If you prefer NOT to integrate the dynamically-created loader instances into the XMLLoader's overall progress, set integrateProgress to false.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the XML has been loaded and analyzed enough to determine the size of any dynamic loaders that were found in the XML data (like <ImageLoader> nodes, etc.), it will adjust the bytesTotal accordingly. Setting estimatedBytes is optional, but it provides a way to avoid situations where the progress and bytesTotal values jump around as XMLLoader recognizes nested loaders in the XML and audits their size. The estimatedBytes value should include all nested loaders as well, so if your XML file itself is 500 bytes and you have 3 <ImageLoader> tags with load="true" and each image is about 2000 bytes, your XMLLoader's estimatedBytes should be 6500. The more accurate the value, the more accurate the loaders' overall progress will be.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this XMLLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:XMLLoader = new XMLLoader("data.xml", {name:"data", requireWithRoot:this.root});
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader (and its children) will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.
  • + *
  • prependURLs : String - A String that should be prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.) as soon as the XML has been parsed. For example, if your XML has the following node: <ImageLoader url="1.jpg" /> and prependURLs is set to "../images/", then the ImageLoader's url will end up being "../images/1.jpg". prependURLs affects ALL parsed loaders in the XML. However, if you have an <XMLLoader> node inside your XML that also loads another XML doc and you'd like to recursively prepend all of the URLs in this loader's XML as well as the subloading one and all of its children, use recursivePrependURLs instead of prependURLs.
  • + *
  • maxConnections : uint - Maximum number of simultaneous connections that should be used while loading child loaders that were parsed from the XML and had their "load" attribute set to "true" (like <ImageLoader url="1.jpg" load="true" />). A higher number will generally result in faster overall load times for the group. The default is 2. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define.
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • skipFailed : Boolean - By default, XMLLoader will parse any LoaderMax-related loaders in the XML and load any that have their "load" attribute set to "true" and then if any fail to load, they will simply be skipped. But if you prefer to have the XMLLoader fail immediately if one of the parsed loaders fails to load, set skipFailed to false (it is true by default).
  • + *
  • recursivePrependURLs : String - A String that should be recursively prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.). The functionality is identical to prependURLs except that it is recursive, affecting all parsed loaders in subloaded XMLLoaders (other XML files that this one loads too). For example, if your XML has the following node: <XMLLoader url="doc2.xml" /> and recursivePrependURLs is set to "../xml/", then the nested XMLLoader's URL will end up being "../xml/doc2.xml". Since it is recursive, parsed loaders inside doc2.xml and any other XML files that it loads will all have their URLs prepended. So if you load doc1.xml which loads doc2.xml which loads doc3.xml (due to <XMLLoader> nodes discovered in each XML file), recursivePrependURLs will affect all of the parsed LoaderMax-related URLs in all 3 documents. If you'd prefer to only have the URLs affected that are in the XML file that this XMLLoader is loading, use prependURLs instead of recursivePrependURLs. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onRawLoad : Function - A handler function for XMLLoader.RAW_LOAD events which are dispatched when the loader finishes loading the XML but has NOT parsed the XML yet. This can be useful in rare situations when you want to alter the XML before it is parsed by XMLLoader (for identifying LoaderMax-related nodes like <ImageLoader>, etc.). Make sure your onRawLoad function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are dispatched when the loader finishes loading the XML file, parses its contents, and creates any dynamic XML-driven loaders. If any dynamic loaders are created and have a load="true" attribute, they will begin loading at this point and the XMLLoader's COMPLETE will not be dispatched until the loaders have completed as well. Make sure your onInit function accepts a single parameter of type Event (flash.events.Event).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML dispatches a PROGRESS event. To listen for changes in the XMLLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the XMLLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders that were defined in the XML due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * + *

Note: Using a XMLLoaderVars instance + * instead of a generic object to define your vars is a bit more verbose but provides + * code hinting and improved debugging because it enforces strict data typing. Use whichever one you prefer.

+ * + *

Note: If you don't want the fancy auto-parsing capabilities of XMLLoader, you can just use a + * DataLoader instead of XMLLoader. Then make the content into XML like: + * var xml:XML = new XML(myDataLoader.content);

+ * + *

XMLLoader recognizes a few additional attributes for dynamically-created loaders that are defined in the XML:

+ *
    + *
  • load="true | false" - If load is "true", the loader will be loaded by the XMLLoader and its progress will be integrated with the XMLLoader's overall progress.
  • + *
  • prependURLs (<LoaderMax> and <XMLLoader> nodes only) - To prepend a certain String value to the beginning of all children of a <LoaderMax> or <XMLLoader>, use prependURLs. For example, <LoaderMax name="mainQueue" prependURLs="http://www.greensock.com/images/"><ImageLoader url="image1.jpg" /></LoaderMax> would cause the ImageLoader's url to become "http://www.greensock.com/images/image1.jpg".
  • + *
  • replaceURLText (<LoaderMax> nodes only) - To replace certain substrings in all child loaders of a <LoaderMax> with other values, use replaceURLText. Separate the old value that should be replaced from the new one that should replace it with a comma (","). The list can be as long as you want. For example, <LoaderMax name="mainQueue" replaceURLText="{imageDirectory},http://www.greensock.com/images/,{language},_en"><ImageLoader url="{imageDirectory}image1{language}.jpg" /></LoaderMax> would cause the ImageLoader's url to become "http://www.greensock.com/images/image1_en.jpg".
  • + *
  • childrenVars (<LoaderMax> nodes only) - To apply a common set of special properties to all the children of a particular <LoaderMax> node, use childrenVars and define a comma-delimited list of values like <LoaderMax name="mainQueue" childrenVars="width:200,height:100,scaleMode:proportionalOutside,crop:true"><ImageLoader url="image1.jpg" /><ImageLoader url="image2.jpg" /></LoaderMax>. Values that are defined directly in one of the child nodes will override any value(s) in the childrenVars, making things very flexible. So if you want the width of all of the children to be 200 except one which should be 500, just use childrenVars="width:200" and then in the child that should be 500 pixels wide, set that in the node like <ImageLoader url="1.jpg" width="500" /> (new in version 1.88)
  • + *
  • context="child | separate | own" - Only valid for <SWFLoader> loaders. It defines the LoaderContext's ApplicationDomain (see Adobe's LoaderContext docs for details). "child" is the default.
  • + *
+ * + *

content data type: XML

+ * + * Example AS3 code:+ import com.greensock.loading.~~; + import com.greensock.loading.display.~~; + import com.greensock.events.LoaderEvent; + + //we know the XML contains ImageLoader, SWFLoader, DataLoader, and MP3Loader data, so we need to activate those classes once in the swf so that the XMLLoader can recognize them. + LoaderMax.activate([ImageLoader, SWFLoader, DataLoader, MP3Loader]); + + //create an XMLLoader + var loader:XMLLoader = new XMLLoader("xml/doc.xml", {name:"xmlDoc", requireWithRoot:this.root, estimatedBytes:1400}); + + //begin loading + loader.load(); + + //Or you could put the XMLLoader into a LoaderMax. Create one first... + var queue:LoaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler}); + + //append the XMLLoader and several other loaders + queue.append( loader ); + queue.append( new SWFLoader("swf/main.swf", {name:"mainSWF", estimatedBytes:4800}) ); + queue.append( new ImageLoader("img/photo1.jpg", {name:"photo1"}) ); + + //begin loading queue + queue.load(); + + function progressHandler(event:LoaderEvent):void { + trace("progress: " + event.target.progress); + } + + function completeHandler(event:LoaderEvent):void { + trace("load complete. XML content: " + LoaderMax.getContent("xmlDoc")); + + //Assuming there was an node in the XML, get the associated image... + var image:ContentDisplay = LoaderMax.getContent("image1"); + addChild(image); + } + + function errorHandler(event:LoaderEvent):void { + trace("error occured with " + event.target + ": " + event.text); + } + + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @see com.greensock.loading.data.XMLLoaderVars + * + * @author Jack Doyle, jack@greensock.com + */ + public class XMLLoader extends DataLoader { + /** @private **/ + private static var _classActivated:Boolean = _activateClass("XMLLoader", XMLLoader, "xml,php,jsp,asp,cfm,cfml,aspx"); + /** @private Any non-String variable types that XMLLoader should recognized in loader nodes like , , etc. **/ + protected static var _varTypes:Object = {skipFailed:true, skipPaused:true, autoLoad:false, paused:false, load:false, noCache:false, auditSize:true, maxConnections:2, autoPlay:false, autoDispose:false, smoothing:false, autoDetachNetStream:false, estimatedBytes:1, x:1, y:1, z:1, rotationX:1, rotationY:1, rotationZ:1, width:1, height:1, scaleX:1, scaleY:1, rotation:1, alpha:1, visible:true, bgColor:0, bgAlpha:0, deblocking:1, repeat:1, checkPolicyFile:false, centerRegistration:false, bufferTime:5, volume:1, bufferMode:false, estimatedDuration:200, crop:false, autoAdjustBuffer:true, suppressInitReparentEvents:true, allowMalformedURL:false}; + /** Event type constant for when the XML has loaded but has not been parsed yet. This can be useful in rare situations when you want to alter the XML before it is parsed by XMLLoader (for identifying LoaderMax-related nodes like <ImageLoader>, etc.) **/ + public static var RAW_LOAD:String = "rawLoad"; + /** @private contains only the parsed loaders that had the load="true" XML attribute. It also contains the _parsed LoaderMax which is paused, so it won't load (we put it in there for easy searching). **/ + protected var _loadingQueue:LoaderMax; + /** @private contains all the parsed loaders (, , , , etc.) but it is paused. Any loaders that have the load="true" XML attribute will be put into the _loadingQueue. _parsed is also put into the _loadingQueue for easy searching. **/ + protected var _parsed:LoaderMax; + /** @private **/ + protected var _initted:Boolean; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content. + * @param vars An object containing optional configuration details. For example: new XMLLoader("xml/data.xml", {name:"data", onComplete:completeHandler, onProgress:progressHandler}). + * + *

The following special properties can be passed into the constructor via the vars parameter + * which can be either a generic object or an XMLLoaderVars object:

+ *
    + *
  • name : String - A name that is used to identify the XMLLoader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21".
  • + *
  • integrateProgress : Boolean - By default, the XMLLoader will automatically look for LoaderMax-related nodes like <LoaderMax>, <ImageLoader>, <SWFLoader>, <XMLLoader>, <MP3Loader>, <DataLoader>, and <CSSLoader> inside the XML when it inits. If it finds any that have a load="true" attribute, it will begin loading them and integrate their progress into the XMLLoader's overall progress. Its COMPLETE event won't fire until all of these loaders have completed as well. If you prefer NOT to integrate the dynamically-created loader instances into the XMLLoader's overall progress, set integrateProgress to false.
  • + *
  • alternateURL : String - If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example).
  • + *
  • noCache : Boolean - If noCache is true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you getLoader() or getContent() by url and when you're running locally)
  • + *
  • estimatedBytes : uint - Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the XML has been loaded and analyzed enough to determine the size of any dynamic loaders that were found in the XML data (like <ImageLoader> nodes, etc.), it will adjust the bytesTotal accordingly. Setting estimatedBytes is optional, but it provides a way to avoid situations where the progress and bytesTotal values jump around as XMLLoader recognizes nested loaders in the XML and audits their size. The estimatedBytes value should include all nested loaders as well, so if your XML file itself is 500 bytes and you have 3 <ImageLoader> tags with load="true" and each image is about 2000 bytes, your XMLLoader's estimatedBytes should be 6500. The more accurate the value, the more accurate the loaders' overall progress will be.
  • + *
  • requireWithRoot : DisplayObject - LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this XMLLoader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, var loader:XMLLoader = new XMLLoader("data.xml", {name:"data", requireWithRoot:this.root});
  • + *
  • autoDispose : Boolean - When autoDispose is true, the loader (and its children) will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.
  • + *
  • prependURLs : String - A String that should be prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.) as soon as the XML has been parsed. For example, if your XML has the following node: <ImageLoader url="1.jpg" /> and prependURLs is set to "../images/", then the ImageLoader's url will end up being "../images/1.jpg". prependURLs affects ALL parsed loaders in the XML. However, if you have an <XMLLoader> node inside your XML that also loads another XML doc and you'd like to recursively prepend all of the URLs in this loader's XML as well as the subloading one and all of its children, use recursivePrependURLs instead of prependURLs.
  • + *
  • maxConnections : uint - Maximum number of simultaneous connections that should be used while loading child loaders that were parsed from the XML and had their "load" attribute set to "true" (like <ImageLoader url="1.jpg" load="true" />). A higher number will generally result in faster overall load times for the group. The default is 2. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define.
  • + *
  • allowMalformedURL : Boolean - Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true.
  • + *
  • skipFailed : Boolean - By default, XMLLoader will parse any LoaderMax-related loaders in the XML and load any that have their "load" attribute set to "true" and then if any fail to load, they will simply be skipped. But if you prefer to have the XMLLoader fail immediately if one of the parsed loaders fails to load, set skipFailed to false (it is true by default).
  • + *
  • recursivePrependURLs : String - A String that should be recursively prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.). The functionality is identical to prependURLs except that it is recursive, affecting all parsed loaders in subloaded XMLLoaders (other XML files that this one loads too). For example, if your XML has the following node: <XMLLoader url="doc2.xml" /> and recursivePrependURLs is set to "../xml/", then the nested XMLLoader's URL will end up being "../xml/doc2.xml". Since it is recursive, parsed loaders inside doc2.xml and any other XML files that it loads will all have their URLs prepended. So if you load doc1.xml which loads doc2.xml which loads doc3.xml (due to <XMLLoader> nodes discovered in each XML file), recursivePrependURLs will affect all of the parsed LoaderMax-related URLs in all 3 documents. If you'd prefer to only have the URLs affected that are in the XML file that this XMLLoader is loading, use prependURLs instead of recursivePrependURLs. + * + *

    ----EVENT HANDLER SHORTCUTS----

  • + *
  • onOpen : Function - A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onRawLoad : Function - A handler function for XMLLoader.RAW_LOAD events which are dispatched when the loader finishes loading the XML but has NOT parsed the XML yet. This can be useful in rare situations when you want to alter the XML before it is parsed by XMLLoader (for identifying LoaderMax-related nodes like <ImageLoader>, etc.). Make sure your onRawLoad function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onInit : Function - A handler function for LoaderEvent.INIT events which are dispatched when the loader finishes loading the XML file, parses its contents, and creates any dynamic XML-driven loaders. If any dynamic loaders are created and have a load="true" attribute, they will begin loading at this point and the XMLLoader's COMPLETE will not be dispatched until the loaders have completed as well. Make sure your onInit function accepts a single parameter of type Event (flash.events.Event).
  • + *
  • onProgress : Function - A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.
  • + *
  • onComplete : Function - A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onCancel : Function - A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onError : Function - A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onFail : Function - A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onIOError : Function - A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onHTTPStatus : Function - A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).
  • + *
  • onSecurityError : Function - A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildOpen : Function - A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildProgress : Function - A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML dispatches a PROGRESS event. To listen for changes in the XMLLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the XMLLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildComplete : Function - A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildCancel : Function - A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders that were defined in the XML due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
  • onChildFail : Function - A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).
  • + *
+ * @see com.greensock.loading.data.XMLLoaderVars + */ + public function XMLLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _preferEstimatedBytesInAudit = true; + _type = "XMLLoader"; + _loader.dataFormat = "text"; //just to make sure it wasn't overridden if the "format" special vars property was passed into in DataLoader's constructor. + } + + /** @private **/ + override protected function _load():void { + if (!_initted) { + _prepRequest(); + _loader.load(_request); + } else if (_loadingQueue != null) { + _changeQueueListeners(true); + _loadingQueue.load(false); + } + } + + /** @private **/ + protected function _changeQueueListeners(add:Boolean):void { + if (_loadingQueue != null) { + var p:String; + if (add && this.vars.integrateProgress != false) { + for (p in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + _loadingQueue.addEventListener(_listenerTypes[p], _passThroughEvent, false, -100, true); + } + } + _loadingQueue.addEventListener(LoaderEvent.COMPLETE, _completeHandler, false, -100, true); + _loadingQueue.addEventListener(LoaderEvent.PROGRESS, _progressHandler, false, -100, true); + _loadingQueue.addEventListener(LoaderEvent.FAIL, _failHandler, false, -100, true); + } else { + _loadingQueue.removeEventListener(LoaderEvent.COMPLETE, _completeHandler); + _loadingQueue.removeEventListener(LoaderEvent.PROGRESS, _progressHandler); + _loadingQueue.removeEventListener(LoaderEvent.FAIL, _failHandler); + for (p in _listenerTypes) { + if (p != "onProgress" && p != "onInit") { + _loadingQueue.removeEventListener(_listenerTypes[p], _passThroughEvent); + } + } + } + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + if (_loadingQueue != null) { + _changeQueueListeners(false); + if (scrubLevel == 0) { + _loadingQueue.cancel(); + } else { + _loadingQueue.dispose(Boolean(scrubLevel == 3)); + _loadingQueue = null; + } + } + if (scrubLevel >= 1) { + if (_parsed != null) { + _parsed.dispose(Boolean(scrubLevel == 3)); + _parsed = null; + } + _initted = false; + } + _cacheIsDirty = true; + var content:* = _content; + super._dump(scrubLevel, newStatus, suppressEvents); + if (scrubLevel == 0) { + _content = content; //super._dump() nulls "_content" but if the XML loaded and not the loading queue (yet), we should keep the XML content. + } + } + + /** @private **/ + override protected function _calculateProgress():void { + _cachedBytesLoaded = _loader.bytesLoaded; + if (_loader.bytesTotal != 0) { //otherwise if unload() was called, bytesTotal would go back down to 0. + _cachedBytesTotal = _loader.bytesTotal; + } + if (_cachedBytesTotal < _cachedBytesLoaded || _initted) { + //In Chrome when the XML file exceeds a certain size and gzip is enabled on the server, Adobe's URLLoader reports bytesTotal as 0!!! + //and in Firefox, if gzip was enabled, on very small files the URLLoader's bytesLoaded would never quite reach the bytesTotal even after the COMPLETE event fired! + _cachedBytesTotal = _cachedBytesLoaded; + } + var estimate:uint = uint(this.vars.estimatedBytes); + if (this.vars.integrateProgress == false) { + // do nothing + } else if (_loadingQueue != null && (uint(this.vars.estimatedBytes) < _cachedBytesLoaded || _loadingQueue.auditedSize)) { //make sure that estimatedBytes is prioritized until the _loadingQueue has audited its size successfully! + if (_loadingQueue.status <= LoaderStatus.COMPLETED) { + _cachedBytesLoaded += _loadingQueue.bytesLoaded; + _cachedBytesTotal += _loadingQueue.bytesTotal; + } + } else if (uint(this.vars.estimatedBytes) > _cachedBytesLoaded && (!_initted || (_loadingQueue != null && _loadingQueue.status <= LoaderStatus.COMPLETED && !_loadingQueue.auditedSize))) { + _cachedBytesTotal = uint(this.vars.estimatedBytes); + } + if (!_initted && _cachedBytesLoaded == _cachedBytesTotal) { + _cachedBytesLoaded = int(_cachedBytesLoaded * 0.99); //don't allow the progress to hit 1 yet + } + _cacheIsDirty = false; + } + + /** + * Finds a particular loader inside any LoaderMax instances that were discovered in the xml content. + * For example: + * + * +var xmlLoader:XMLLoader = new XMLLoader("xml/doc.xml", {name:"xmlDoc", onComplete:completeHandler}); +function completeHandler(event:Event):void { + var imgLoader:ImageLoader = xmlLoader.getLoader("imageInXML") as ImageLoader; + addChild(imgLoader.content); +} + + * + *

The static LoaderMax.getLoader() method can be used instead which searches all loaders.

+ * + * @param nameOrURL The name or url associated with the loader that should be found. + * @return The loader associated with the name or url. Returns null if none were found. + */ + public function getLoader(nameOrURL:String):* { + return (_parsed != null) ? _parsed.getLoader(nameOrURL) : null; + } + + /** + * Finds a particular loader's content from inside any loaders that were dynamically + * generated based on the xml data. For example: + * + * +var loader:XMLLoader = new XMLLoader("xml/doc.xml", {name:"xmlDoc", onComplete:completeHandler}); +function completeHandler(event:Event):void { + var subloadedImage:Bitmap = loader.getContent("imageInXML"); + addChild(subloadedImage); +} + + * + *

The static LoaderMax.getContent() method can be used instead which searches all loaders.

+ * + * @param nameOrURL The name or url associated with the loader whose content should be found. + * @return The content associated with the loader's name or url. Returns null if none were found. + * @see #content + */ + public function getContent(nameOrURL:String):* { + if (nameOrURL == this.name || nameOrURL == _url) { + return _content; + } + var loader:LoaderCore = this.getLoader(nameOrURL); + return (loader != null) ? loader.content : null; + } + + /** + * Returns and array of all LoaderMax-related loaders (if any) that were found inside the XML. + * For example, if the following XML was in the document, a child loader would be created for it + * immediately before the INIT event is dispatched:

+ * + * <ImageLoader url="1.jpg" name="image1" />

+ * + *

Don't forget to use LoaderMax.activate() to activate the types of loaders + * that you want XMLLoader to recognize (you only need to activate() them once in your swf). + * Like LoaderMax.activate([ImageLoader, SWFLoader]); to ensure that XMLLoader + * recognizes <ImageLoader> and <SWFLoader> nodes.

+ * + *

No child loader can be found until the XMLLoader's INIT event is dispatched, meaning the + * XML has been loaded and parsed.

+ * + * @param includeNested If true, loaders that are nested inside child LoaderMax, XMLLoader, or SWFLoader instances will be included in the returned array as well. The default is false. + * @param omitLoaderMaxes If true, no LoaderMax instances will be returned in the array; only LoaderItems like ImageLoaders, XMLLoaders, SWFLoaders, MP3Loaders, etc. The default is false. + * @return An array of loaders. + */ + public function getChildren(includeNested:Boolean=false, omitLoaderMaxes:Boolean=false):Array { + return (_parsed != null) ? _parsed.getChildren(includeNested, omitLoaderMaxes) : []; + } + +//---- STATIC METHODS ------------------------------------------------------------------------------------ + + /** @private **/ + protected static function _parseVars(xml:XML):Object { + var v:Object = {rawXML:xml}; + var s:String, type:String, value:String, domain:ApplicationDomain; + var list:XMLList = xml.attributes(); + for each (var attribute:XML in list) { + s = attribute.name(); + value = attribute.toString(); + if (s == "url") { + continue; + } else if (s == "context") { + v.context = new LoaderContext(true, + (value == "own") ? ApplicationDomain.currentDomain : (value == "separate") ? new ApplicationDomain() : new ApplicationDomain(ApplicationDomain.currentDomain), + (!_isLocal) ? SecurityDomain.currentDomain : null); + continue; + } + type = typeof(_varTypes[s]); + if (type == "boolean") { + v[s] = Boolean(value == "true" || value == "1"); + } else if (type == "number") { + v[s] = Number(value); + } else { + v[s] = value; + } + + } + return v; + } + + /** + * Parses an XML object and finds all activated loader types (like LoaderMax, ImageLoader, SWFLoader, DataLoader, + * CSSLoader, MP3Loader, etc.), creates the necessary instances, and appends them to the LoaderMax that is defined + * in the 2nd parameter. Don't forget to make sure you activate() the necessary loader types that you + * want XMLLoader to recognize in the XML, like:

+ * + * LoaderMax.activate([ImageLoader, SWFLoader]); //or whatever types you're using.

+ * + * @param xml The XML to parse + * @param all The LoaderMax instance to which all parsed loaders should be appended + * @param toLoad The LoaderMax instance to which ONLY parsed loaders that have a load="true" attribute defined in the XML should be appended. These loaders will also be appended to the LoaderMax defined in the all parameter. + */ + public static function parseLoaders(xml:XML, all:LoaderMax, toLoad:LoaderMax=null):void { + var node:XML; + var nodeName:String = String(xml.name()).toLowerCase(); + if (nodeName == "loadermax") { + var queue:LoaderMax = all.append(new LoaderMax(_parseVars(xml))) as LoaderMax; + if (toLoad != null && queue.vars.load) { + toLoad.append(queue); + } + + if (queue.vars.childrenVars != null && queue.vars.childrenVars.indexOf(":") != -1) { + queue.vars.childrenVars = _parseVars( new XML("") ); + } + + for each (node in xml.children()) { + parseLoaders(node, queue, toLoad); + } + + if ("replaceURLText" in queue.vars) { + var replaceText:Array = queue.vars.replaceURLText.split(","); + for (var i:int = 0; i < replaceText.length; i += 2) { + queue.replaceURLText(replaceText[i], replaceText[i+1], false); + } + } + if ("prependURLs" in queue.vars) { + queue.prependURLs(queue.vars.prependURLs, false); + } + } else { + if (nodeName in _types) { + var loaderClass:Class = _types[nodeName]; + var parsedVars:Object = _parseVars(xml); + if (typeof(all.vars.childrenVars) == "object") { + for (var p:String in all.vars.childrenVars) { + if (!(p in parsedVars)) { + parsedVars[p] = all.vars.childrenVars[p]; + } + } + } + var loader:LoaderCore = all.append(new loaderClass(xml.@url, parsedVars)); + if (toLoad != null && loader.vars.load && !all.vars.load) { + toLoad.append(loader); + } + } + + for each (node in xml.children()) { + parseLoaders(node, all, toLoad); + } + } + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + override protected function _progressHandler(event:Event):void { + if (_dispatchProgress) { + var bl:uint = _cachedBytesLoaded; + var bt:uint = _cachedBytesTotal; + _calculateProgress(); + if (_cachedBytesLoaded != _cachedBytesTotal && (bl != _cachedBytesLoaded || bt != _cachedBytesTotal)) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } else { + _cacheIsDirty = true; + } + } + + /** @private **/ + override protected function _passThroughEvent(event:Event):void { + if (event.target != _loadingQueue) { + super._passThroughEvent(event); + } + } + + /** @private **/ + override protected function _receiveDataHandler(event:Event):void { + try { + _content = new XML(_loader.data); + } catch (error:Error) { + _content = _loader.data; + _failHandler(new LoaderEvent(LoaderEvent.ERROR, this, error.message)); + return; + } + dispatchEvent(new LoaderEvent(RAW_LOAD, this, "", _content)); + _initted = true; + + _loadingQueue = new LoaderMax({name:this.name + "_Queue", maxConnections:(uint(this.vars.maxConnections) || 2), skipFailed:Boolean(this.vars.skipFailed != false), skipPaused:Boolean(this.vars.skipPaused != false)}); + _parsed = new LoaderMax({name:this.name + "_ParsedLoaders", paused:true}); + parseLoaders(_content as XML, _parsed, _loadingQueue); + if (_parsed.numChildren == 0) { + _parsed.dispose(false); + _parsed = null; + } else if ("recursivePrependURLs" in this.vars) { + _parsed.prependURLs(this.vars.recursivePrependURLs, true); + var loaders:Array = _parsed.getChildren(true, true); + var i:int = loaders.length; + while (--i > -1) { + if (loaders[i] is XMLLoader) { + loaders[i].vars.recursivePrependURLs = this.vars.recursivePrependURLs; + } + } + } else if ("prependURLs" in this.vars) { + _parsed.prependURLs(this.vars.prependURLs, true); + } + if (_loadingQueue.getChildren(true, true).length == 0) { + _loadingQueue.empty(false); + _loadingQueue.dispose(false); + _loadingQueue = null; + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this, "", _content)); + } else { + _cacheIsDirty = true; + _changeQueueListeners(true); + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this, "", _content)); + _loadingQueue.load(false); + } + + if (_loadingQueue == null || (this.vars.integrateProgress == false)) { + _completeHandler(event); + } + } + + /** @private **/ + override protected function _failHandler(event:Event, dispatchError:Boolean=true):void { + if (event.target == _loadingQueue) { + //this is a unique situation where we don't want the failure to unload the XML because only one of the nested loaders failed but the XML is perfectly good and usable. Also, we want to retain the _loadingQueue so that getChildren() works. Therefore we don't call super._failHandler(); + _status = LoaderStatus.FAILED; + _time = getTimer() - _time; + dispatchEvent(new LoaderEvent(LoaderEvent.CANCEL, this)); + dispatchEvent(new LoaderEvent(LoaderEvent.FAIL, this, this.toString() + " > " + (event as Object).text)); + } else { + super._failHandler(event, dispatchError); + } + } + + /** @private **/ + override protected function _completeHandler(event:Event=null):void { + _calculateProgress(); + if (this.progress == 1) { + _changeQueueListeners(false); + super._completeHandler(event); + } + } + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** @inheritDoc The purpose of the override is so that we can return 1 in rare cases where the XML file literally is empty (bytesTotal == 0) which is verified when _initted == true. **/ + override public function get progress():Number { + return (this.bytesTotal != 0) ? _cachedBytesLoaded / _cachedBytesTotal : (_status == LoaderStatus.COMPLETED || _initted) ? 1 : 0; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/changelog.txt b/src/com/greensock/loading/changelog.txt new file mode 100644 index 0000000..df2230c --- /dev/null +++ b/src/com/greensock/loading/changelog.txt @@ -0,0 +1,1056 @@ +CHANGE LOG : GREENSOCK LOADERMAX SYSTEM +---------------------------------------- +2013-02-21 +---------------------------------------- +XMLLoader 1.932 +LoaderItem 1.932 +LoaderMax 1.932 + - Added ability for XMLLoader to recognize "allowMalformedURL" in XML nodes + - To avoid errors when attempting to load local files, if allowMalformedURL isn't true and there are parameters in the query string, the URLRequest's method will be forced to "POST" (again, ONLY when the swf is running locally, not on the network) + +2013-01-08 +---------------------------------------- +XMLLoader 1.931 +LoaderMax 1.931 + - Fixed issue that could cause redundant childComplete events to get fired from XMLLoader if you defined load="true" inside BOTH a node and its child node. + +2012-09-05 +---------------------------------------- +VideoLoader 1.93 + - Worked around issue in Flash Player 11.x that caused a flicker of the video when gotoVideoTime() was called inside a VIDEO_COMPLETE event handler (and when the VideoLoader repeats). This was a bug in the Flash Player itself, and the solution was to wait 1 frame after when the Netstream stops before doing a seek(). VideoLoader automatically handles this internally now. + +2012-08-09 +---------------------------------------- +VideoLoader 1.921 +LoaderItem 1.921 + - Fixed issue that could prevent the BUFFER_EMPTY event from being dispatched if bufferMode is true + - Implemented logic that causes a loader's "auditedSize" property to get reset when its "url" is changed. + +2012-04-26 +---------------------------------------- +VideoLoader 1.901 + - Fixed issue that could cause an error to be thrown if you dispose() a VideoLoader the moment it finishes playing (inside an event handler that is handling the VideoLoader.VIDEO_COMPLETE event). + +2012-04-16 +---------------------------------------- +LoaderMax 1.9 +VideoLoader 1.9 + - Added "stageVideo" special property to VideoLoader to accommodate StageVideo + +2012-02-24 +---------------------------------------- +LoaderMax 1.8993 +LoaderCore 1.8993 +VideoLoader 1.8993 + - Fixed issue that caused loaders to incorrectly report their bytesLoaded as non-zero after unload() was called (after having loaded of course) + - Fixed issue that could cause a VideoLoader not to dispatch its VIDEO_BUFFER_FULL event if the video is seeked to a time that is closer to the end of the video than the bufferTime. + +2012-02-23 +---------------------------------------- +LoaderMax 1.8992 +VideoLoader 1.8992 + - Fixed issue in VideoLoader that could cause cuePoints to be triggered when gotoVideoTime() was called even though the skipCuePoints parameter was set to true. + +2012-02-15 +---------------------------------------- +LoaderMax 1.8991 +VideoLoader 1.8991 +LoaderItem 1.8991 + - Previously, VideoLoader would only dispatch one INIT event (the first time it received metaData from the NetStream). However, due to an inconsistency in Flash/NetStream, metaData is sometimes received more than once, and subsequent times carry more data. For example, the metaData may not contain cuePoints the first time, but it does the second time (this is often the case for F4V files). Therefore, VideoLoader now dispatches an INIT event every time it receives metaData. If you're looking for cuePoints, you can check the VideoLoader's metaData.cuePoints array. + - Fixed issue that could cause a loader to throw an error if it has an alternateURL defined and it fails and its dispose() method is called directly inside an onError handler (VERY uncommon) :) + +2012-01-25 +---------------------------------------- +LoaderMax 1.899 +VideoLoader 1.899 + - Fixed issue in VideoLoader that could cause an error during its auditing process (if estimatedBytes isn't defined and auditSize in the parent LoaderMax isn't set to false). + +2012-01-19 +---------------------------------------- +LoaderMax 1.898 +DisplayObjectLoader 1.898 + - Fixed issue that could cause an ImageLoader or SWFLoader to fail during the auditing process inside a LoaderMax if the URL is from another domain and there is no crossdomain.xml policy file in place. Now it simply tries again with a higher security threshold (script access will be denied of course due to Flash's security policies). + +2012-01-14 +---------------------------------------- +LoaderMax 1.897 +ImageLoader 1.897 + - Fixed issue that could cause an error if an ImageLoader with an identical url is loaded after the other has finished loading which encountered a security error (thus its rawContent was a Loader instead of a Bitmap, making it impossible to copy the BitmapData). Only happens if noCache is false. + +2012-01-10 +---------------------------------------- +LoaderMax 1.896 + - Fixed issue that could cause a null object reference and/or a loader to be skipped in a LoaderMax queue in a very rare scenario where an ImageLoader is loaded after another ImageLoader with an identical URL has finished loading (and noCache is false). + +2011-11-27 +---------------------------------------- +LoaderMax 1.895 +ContentDisplay 1.895 +FlexContentDisplay 1.895 + - Fixed issue introduced in version 1.892 that caused videos not to scale correctly when using a scaleMode other than "none" or "stretch" + +2011-11-26 +---------------------------------------- +LoaderMax 1.894 +VideoLoader 1.894 +DisplayObjectLoader 1.894 + - Fixed issue that could cause VideoLoader not to dispatch PLAY_PROGRESS events if you called gotoVideoTime() on every frame + - SWFLoaders and ImageLoaders now do not force garbage collection when dispose(false) is called (or autoDispose is set to true). + - Added a DisplayObjectLoader.defaultAutoForceGC static property that you can set to false to prevent it from forcing GC whenever a SWFLoader or ImageLoader is unloaded (or dispose(true)). + - Added "autoForceGC" special property recognition for SWFLoader and ImageLoader (default is true). + +2011-11-17 +---------------------------------------- +LoaderMax 1.893 +VideoLoader 1.893 +ContentDisplay 1.892 +FlexContentDisplay 1.892 + - Fixed issue that could cause a VIDEO_BUFFER_FULL event to be dispatched from VideoLoader after the video finishes playing + - Changed behavior of VideoLoader so that it only dispatches a PLAY_PROGRESS event after the NetStream renders its new frame (which can take a brief moment after you alter the videoProgress or gotoVideoTime()). + - Fixed an issue that caused ContentDisplay and FlexContentDisplay to use the wrong aspect ratio on the rawContent if the scaleMode is set to "stretch" and then either "proportionalInside" or "proportionalOutside". Again, it would ONLY happen after it was changed from "stretch". + - Changed behavior of VideoLoader so that it dispatches a VIDEO_BUFFER_EMPTY event whenever the playhead is moved (like with gotoVideoTime() or changing the videoProgress) which is essentially what happens with the NetStream - it empties the buffer when the playhead moves and then dispatches a VIDEO_BUFFER_FULL event when the buffer fills from the new spot. + +2011-11-03 +---------------------------------------- +LoaderMax 1.891 +XMLLoader 1.891 +VideoLoader 1.891 + - Changed the default value of VideoLoader's autoDetachNetStream from true to false because Adobe's NetStream and/or Video classes exhibit some buggy/inconsistent behavior when trying to attach the NetStream at very particular times during the load when the Video object isn't in the display list yet. In most cases, autoDetachNetStream will work great, but it shouldn't be toggled on when the content isn't in the display list yet. + - XMLLoader now recognizes the "autoDetachNetStream" property of nodes. + +2011-10-20 +---------------------------------------- +LoaderMax 1.89 +ImageLoader 1.89 + - Added new performance- and memory-enhancing behavior to ImageLoader: when you load() an ImageLoader, it will automatically check to see if another ImageLoader exists with a matching url that has already finished loading. If it finds one, it will copy that BitmapData to use in its own Bitmap in order to maximize performance and minimize memory usage. After all, why load the file again if you've already loaded it? (The exception, of course, is when the ImageLoader's noCache is set to true.) + +2011-10-06 +---------------------------------------- +LoaderItem 1.884 +LoaderMax 1.884 + - When a loader with an alternateURL defined fails to load the original url, the LoaderEvent.ERROR event gets dispatched BEFORE the url is changed to the alternateURL now (previously the event was dispatched after the url was changed). + +2011-09-13 +----------------------------------------- +LoaderMax 1.883 +VideoLoader 1.883 + - VideoLoader now correctly implements noCache:true (previously the cache-busting data wasn't added to the URL) + - Fixed an issue that caused a VideoLoader that was unloaded to not respect autoPlay:true when reloaded AFTER it was already completely finished playing the first time. + - Worked around a bug in NetStream that could cause a VideoLoader's video not to pause quickly enough when autoDetachNetStream was set to true and it wasn't in the display list and autoPlay was set to false. + - Fixed issue that could (in rare situations) cause a video to render as blank/transparent for a VERY brief moment when it was first added to the display list even though autoDetachNetStream was set to false. + +2011-08-19 +----------------------------------------- +LoaderMax 1.882 +VideoLoader 1.882 + - Fixed issue in VideoLoader that could cause a video not to display correctly if autoDetachNetStream was set to false and the video loaded in a particular way and had a particular type of encoding. + +2011-08-15 +----------------------------------------- +LoaderMax 1.881 +MP3Loader 1.881 + - Fixed issue that could cause MP3Loader to repeat from the incorrect position after it is paused during playback and then resumed (it would repeat starting from the position at which it was when playback was resumed due to a bug in Adobe's Sound class). + +2011-08-09 +----------------------------------------- +LoaderMax 1.88 +XMLLoader 1.88 + - Added the capability for XMLLoader to recognize a new "childrenVars" attribute of a node so that you can apply a common set of special properties to all its children. For example: + +2011-08-06 +------------------------------------------ +LoaderMax 1.872 +VideoLoader 1.872 + - Fixed issue that could cause a VideoLoader to dispatch its VIDEO_PLAY event twice if autoPlay was initially set to false and then load() and playVideo() were called immediately. + +2011-08-03 +------------------------------------------ +LoaderMax 1.871 +ContentDisplay 1.871 +FlexContentDisplay 1.871 + - Fixed issue that could cause a VideoLoader's content not to be added to the ContentDisplay (or FlexContentDisplay) properly if crop was set to true and autoDetatchNetStream was true. + +2011-07-30 +------------------------------------------ +LoaderMax 1.87 +SWFLoader 1.87 +LoaderCore 1.87 +DisplayObjectLoader 1.87 +LoaderEvent 1.87 +SWFLoaderVars 1.23 + - Added UNCAUGHT_ERROR event handling for SWFLoader (FP 10.1 and later only) + - Added onUncaughtError and suppressUncaughtErrors special properties to SWFLoader (FP 10.1 and later only) + - Added new methods to SWFLoaderVars for onUncaughtError and suppressUncaughtErrors + +2011-07-27 +------------------------------------------ +LoaderMax 1.86 +VideoLoader 1.86 +ContentDisplay 1.86 +FlexContentDisplay 1.86 +VideoLoaderVars 1.23 + - Added getChildAt() method to LoaderMax + - Added an "autoDetachNetStream" property to VideoLoader (and VideoLoaderVars) that allows you to choose whether or not it automatically attached/detaches the NetStream to/from the Video object in order to conserve memory (it is true by default) + - Updated VideoLoader to work around a newly discovered bug in NetStream that could cause a paused NetStream (VideoLoader) to act as though it isn't paused if it isn't attached to a Video object in the display list + - Made a small change to ContentDisplay and FlexContentDisplay so that they could be used independently (without a loader) if necessary, although it isn't generally recommended. + - Updated ASDocs + +2011-07-05 +------------------------------------------ +VideoLoader 1.856 +LoaderMax 1.856 + - Fixed issue that could cause a VideoLoader's videoTime to report as non-zero initially even though autoPlay was set to false (it's actually a bug in Adobe's NetStream that was worked around) + +2011-06-27 +------------------------------------------- +LoaderMax 1.855 +LoaderItem 1.855 + - Now if you define a URLRequest with a method of "POST" for a loader, that will be honored in the file size audit too (previously audits always used "GET" as the method). + - Now if you define a URLRequest that has a data value that isn't a URLVariables, it won't cause problems with file size audits. + +2011-06-19 +------------------------------------------- +LoaderMax 1.854 +VideoLoader 1.854 +MP3Loader 1.854 +LoaderItem 1.854 + - Previously, if the playback device didn't have sound capabilities, MP3Loader would throw an error when playSound() was called. Implemented a workaround. + - VideoLoader now delays attaching the NetStream to its Video object (the rawContent) until it is added to the stage in order to minimize memory usage. Flash doesn't fully decode the video content until it is attached, thus this technique improves memory usage especially for videos that are loading in the background (not in the display list). + - When a loader's url property is changed, the old content is now unloaded immediately (previously the loader was only canceled). + +2011-05-11 +------------------------------------------- +LoaderMax 1.853 +VideoLoader 1.853 + - Minor fix that appends "purpose=audit&gsCacheID=someRandomNumber" to the file audit URL of VideoLoader which was accidentally dropped off in version 1.852 (less than 24 hours ago) + +2011-05-10 +------------------------------------------- +LoaderMax 1.852 +VideoLoader 1.852 + - Worked around a Flash bug/inconsistency that could cause a VideoLoader's file size audit to fail (only for relative URLs, not absolute). Unlike all other loaders (URLLoader, Loader, URLStream), Adobe's NetStream loads relative URLs as though they are relative to the swf file's location rather than the HTML page in which it is embedded. Another workaround is to set the "base" attribute in the object/embed code (http://kb2.adobe.com/cps/041/tn_04157.html) + +2011-05-05 +-------------------------------------------- +LoaderMax 1.851 +VideoLoader 1.851 +ContentDisplay 1.851 +FlexContentDisplay 1.851 +SWFLoaderVars 1.22 +ImageLoaderVars 1.22 +VideoLoaderVars 1.22 + - Added ability for ContentDisplay/FlexContentDisplay to recognize 3D properties z, rotationX, rotationY, and rotationZ. + - Minor improvement to VideoLoader that prevents an extremely unlikely/rare issue when a VideoLoader is destroyed before it renders for the first time + +2011-04-26 +-------------------------------------------- +LoaderMax 1.85 +XMLLoader 1.85 + - Added new "autoLoad" feature to LoaderMax that automatically calls load() when a new loader is appended/inserted/prepended whose status is LoaderStatus.READY. + - Added ability for XMLLoader to recognize skipFailed and skipPaused special properties in its vars parameter. + +2011-04-20 +-------------------------------------------- +LoaderMax 1.843 +XMLLoader 1.843 + - Added a new "maxConnections" special property to XMLLoader that controls the number of child loaders that load simultaneously (the default is 2). + +2011-04-14 +-------------------------------------------- +LoaderMax 1.842 +VideoLoader 1.842 + - Fixed issue with VideoLoader that could cause an error if an instance is disposed almost immediately after it starts loading. + +2011-03-26 +-------------------------------------------- +LoaderMax 1.841 +VideoLoader 1.841 + - Fixed issue with VideoLoader that could cause a video not to display properly if autoPlay was set to false and the video file loaded in a very particular way/speed. + +2011-03-23 +-------------------------------------------- +LoaderMax 1.84 +XMLLoader 1.84 +LoaderItem 1.84 + - Added ability for XMLLoader to recognize a root LoaderMax-related node. Previously, the XML document's root couldn't be a (or other LoaderMax-related tag). Well, it could be but that node wouldn't be parsed. + - Added new "allowMalformedURL" Boolean special property to all loaders which accommodates URLs that are technically not valid, like if they have multiple values defined for the same variable in the query string (like ?c=S&c=SE&c=SW). By default, allowMalformedURL is false which means LoaderMax will parse the query string and translate it into a URLVariables which gets attached to the URLRequest - this avoids 2 particular bugs in Flash. It is very unlikely that you'll ever need to set allowMalformedURL:true + - A LoaderMax's rawProgress value will factor in any nested LoaderMax instances' rawProgress (previously it factored in the progress instead). It is very unlikely you'd run into situations where this matters, but it can prevent jerky progress reporting. + +2011-02-16 +-------------------------------------------- +LoaderMax 1.831 +SWFLoader 1.831 + - Very minor enhancement to SWFLoader's getClass() method that prevents an error if you try to find a class definition that doesn't exist in the subloaded swf. + +2011-02-15 +-------------------------------------------- +LoaderMax 1.83 +SWFLoader 1.83 +DisplayObjectLoader 1.83 +DataLoader 1.83 +LoaderCore 1.83 +BinaryDataLoader 1.83 + - Added a new BinaryDataLoader class that's essentially the same as using a DataLoader with its "format" special property set to "binary". The reason for having a BinaryDataLoader class is to allow certain file extensions (like ".zip") to be associated with it so that the LoaderMax.parse() method can accurately parse URLs with those file extensions. + - Added a new registerFileType() method to LoaderMax so that you can associate unrecognized file types (like "pdf") with particular loader types (like BinaryDataLoader) so that LoaderMax.parse() can correctly identify them. + - Improved the garbage collection routines in SWFLoader so that there is a greater likelihood that even sub-swfs that use non-weak listeners on the stage will be garbage collected after dispose(true) is called. + - Updated ASDocs + +2011-02-11 +-------------------------------------------- +LoaderMax 1.821 +VideoLoader 1.821 + - Worked around issue on a Mac that could cause VideoLoader to report its videoTime incorrectly if autoPlay was set to false. This could also lead to cue points that were added with addASCuePoint() not getting triggered as a result of the videoTime being reported incorrectly. + +2011-02-09 +-------------------------------------------- +LoaderMax 1.82 +VideoLoader 1.82 + - Fixed issue that could cause VideoLoader to act as though it ignored the "skipCuePoints" parameter of the gotoVideoTime() method. This would only happen when the frame rate was high enough in the swf and it was caused by problems in Adobe's NetStream class incorrectly reporting its time for a brief moment after its seek() method was called. + +2011-02-03 +-------------------------------------------- +LoaderMax 1.81 +LoaderCore 1.81 + - Fixed issue that caused the "paused" property to not be reported correctly if set directly through the "paused" setter when the loader wasn't in the process of loading. + +2011-01-21 +-------------------------------------------- +LoaderMax 1.8 +XMLLoader 1.8 +SWFLoader 1.8 +LoaderCore 1.8 + - Added new feature to XMLLoader that causes it to attach a "rawXML" property to the vars object of any loader parsed in the XML document. For example, if this node is found in the XML: , you could get the "description" data in the resulting VideoLoader's vars like this: myVideoLoader.vars.description. And the full XML node (including children) could be accessed with myVideoLoader.vars.rawXML. + - Fixed a few issues with the order of bubbling events and one particular scenario that could cause an extra FAIL event to be dispatched (two in a row). + - Fixed issue that could cause a loader with requireWithRoot not to be integrated a parent SWFLoader's progress if that loader was nested inside another LoaderMax instance in the child swf. + - Altered SWFLoader's behavior so that if the swf loads fine but a child loader inside that swf fails (one that has their requireWithRoot set to that swf's root), it will dispatch a FAIL event but it will not reset the progress to 0 or clear the subloading queue. This way, it's still possible to getChildren() on the SWFLoader. + - Fixed a problem introduced in version 1.79 (2 days ago) that caused XMLLoader not to parse the "prependURLs" attribute on nodes inside the XML. + +2011-01-20 +-------------------------------------------- +LoaderMax 1.791 +VideoLoader 1.791 +LoaderCore 1.79 + - Fixed issue that would cause a VideoLoader to not dispatch its PROGRESS and COMPLETE events if its bufferMode was initially set to true and then in its onComplete handler it was set to false. + +2011-01-19 +-------------------------------------------- +LoaderMax 1.79 +XMLLoader 1.79 +XMLLoaderVars 1.2 + - Added a new "prependURLs" special property to XMLLoader that allows you to prepend all of the URLs of LoaderMax-related loaders parsed in the XML. + - Added a new "recursivePrependURLs" special property to XMLLoader that allows you to prepend all of the URLs of LoaderMax-related loaders parsed in the XML and all of the other XML files that it loads too (infinitely deep). For example, if you load doc1.xml that has an that loads doc2.xml that has an that loads doc3.xml, recursivePrependURLs would prepend the URLs of LoaderMax-related loaders in all of those documents. + - Fixed issue that could cause an "error" or "fail" event to dispatch and bubble in a way that would flow up the chain of any anscestor LoaderMax instances BEFORE an onError/onFail handler you added directly to the loader. Basically, the order in which the handlers were called was unintuitive in that particular case. + - Exposed the getChildren() methods in SWFLoader's and XMLLoader's ASDocs + +2011-01-18 +-------------------------------------------- +LoaderMax 1.781 +SWFLoader 1.781 +LoaderCore 1.781 + - Fixed issue that could cause nested loaders discovered in a SWFLoader that had requireWithRoot set to that swf's root to prevent the parent LoaderMax from dispatching a COMPLETE or FAIL event if on of those nested loaders failed. + - Fixed issue that could cause the auditing process to stall on a LoaderMax when it had a nested LoaderMax as a child that failed during its auditing process. + +2011-01-17 +-------------------------------------------- +LoaderMax 1.78 +LoaderCore 1.78 + - Fixed issue that could cause an error if "requireWithRoot" was set to a DisplayObject that wasn't actually a root and was not in the display list + +2011-01-15 +-------------------------------------------- +LoaderMax 1.777 +LoaderItem 1.777 + - Fixed issue that could cause part of the URL query string to be lost if more than one "=" sign was used in a single value pair (extremely rare), like http://www.greensock.com/?prop1=value1=extraValue&prop2=value2 - the extraValue would be dropped in some previous versions (that's fixed now obviously). + - Fixed typo in ASDocs that mentioned LoaderStatus.COMPLETE instead of LoaderStatus.COMPLETED (missing the "D"). + +2011-01-11 +-------------------------------------------- +LoaderMax 1.776 +VideoLoader 1.776 + - Fixed issue that could cause VERY short videos whose duration is equal to or less than the bufferTime (defaults to 5 seconds) not to play correctly. This would only happen if the duration reported in the metaData was slightly inflated which sometimes happens when videos are encoded. + +2011-01-10 +-------------------------------------------- +LoaderMax 1.775 +VideoLoader 1.775 + - Fixed issue that could cause a LoaderMax instance to dispatch a PROGRESS event after being disposed + - Fixed issue in VideoLoader that could cause it to report its "progress" as 1 before the metaData has been received (very rare, and only in situations where the file is already completely cached). + +2011-01-06 +-------------------------------------------- +LoaderMax 1.773 +VideoLoader 1.773 +DisplayObjectLoader 1.773 + - Fixed an issue that could cause a LoaderMax not to dispatch its COMPLETE event if it has a VideoLoader that changes its bufferMode from true to false upon its completion while the LoaderMax is loading + - Fixed issue that would cause a LoaderMax to dispatch a PROGRESS event when a child loader is appended/inserted. + - Added a very small amount of code to DisplayObjectLoader to make it easier to use it in a standalone fashion (typically it is used only as a base class for SWFLoader and ImageLoader). Again, it isn't recommended to use DisplayObjectLoader on its own, but if you need absolute minimum file size and can do without the bells & whistles of SWFLoader and ImageLoader, you could do it. + +2010-12-28 +-------------------------------------------- +SWFLoader 1.772 +LoaderMax 1.772 +SWFLoaderVars 1.12 + - Changed the default suppressInitReparentEvents value in SWFLoader to true instead of false because some users had coded their swfs in a way that required ADDED_TO_STAGE to fire which shouldn't be necessary when the swf is run on its own or in a Loader that has already been added to the stage before it started loading, but nevertheless after mulling it over it did seem better not to suppress events unless specifically requested. + +2010-12-23 +-------------------------------------------- +SWFLoader 1.771 +LoaderMax 1.771 +DisplayObjectLoader 1.771 + - Fixed issue introduced in version 1.77 that could cause a Type Coercion failure with SecurityDomains + +2010-12-21 +-------------------------------------------- +LoaderMax 1.77 +SWFLoader 1.77 +DisplayObjectLoader 1.77 +LoaderCore 1.77 +LoaderEvent 1.77 + - Added new LoaderEvent.UNLOAD event dispatching which can be very convenient for notifying a child swf about when it should run disposal/cleanup code. For example, if you create stage event listeners or start sounds or NetStreams, etc. inside the child swf, those should be cleaned up when the SWFLoader is asked to unload (otherwise your listeners/NetStreams/Sounds might interfere with garbage collection and never go away). So in the child swf, you could run this code at start: + + var curParent:DisplayObjectContainer = this.parent; + while (curParent) { + if (curParent.hasOwnProperty("loader")) { + Object(curParent).loader.addEventListener("unload", dispose, false, 0, true); + } + curParent = curParent.parent; + } + + function dispose(event:Event):void { + //cleanup code here... + } + + - Corrected an issue that prevented the LoaderMax.defaultContext from taking effect when the swf was run locally + +2010-12-19 +-------------------------------------------- +SWFLoader 1.769 +LoaderMax 1.769 +XMLLoader 1.769 +SWFLoaderVars 1.11 + - Added a new suppressInitReparentEvents special property to SWFLoader that suppresses the ADDED_TO_STAGE and REMOVED_FROM_STAGE events that are normally dispatched when the rawContent is reparented from the internal Loader to the ContentDisplay object. By default, the value is true but you can set it to false if you prefer. See the ASDocs for more details + +2010-12-17 +--------------------------------------------- +ContentDisplay 1.768 +FlexContentDisplay 1.768 +LoaderMax 1.768 + - Fixed issue that could cause a SWFLoader's rawContent to not scale/position properly if script access was denied (like if the subloaded swf was AS2 or cross-loaded from another domain that didn't have the appropriate crossdomain.xml file in place) and a width/height was defined in the SWFLoader's vars parameter. + +2010-12-17 +--------------------------------------------- +VideoLoader 1.767 +LoaderMax 1.767 + - Added gotoVideoCuePoint() method to VideoLoader + - Added getCuePointTime() method to VideoLoader + - Minor internal optimizations in VideoLoader + +2010-12-16 +--------------------------------------------- +LoaderMax 1.766 +SWFLoader 1.766 +VideoLoader 1.765 + - Fixed issue that could allow a VideoLoader to dispatch a CUE_POINT event if the cue point is very early in the video and autoPlay was set to false. + - Changed the way SWFLoader handles the Loader internally so that it temporarily adds it to the ContentDisplay object's display list while it loads initially so that any code that references "stage" in the child swf doesn't throw null object reference errors (as long as you've added the ContentDisplay to the display list, like by defining a "container" in the vars parameter). As soon as the swf inits (and as long as no script access denied errors occur), the swf's root is added to the ContentDisplay instead. + - Fixed a small gc issue with SWFLoader - if there were sub-loaders inside the SWFLoader that had requireWithRoot set to that swf's root and then the SWFLoader was disposed, a LoaderMax instance could remain behind (it wouldn't hurt anything, but it would consume a small amount of memory) + +2010-12-15 +--------------------------------------------- +LoaderMax 1.764 +SWFLoader 1.764 + - Fixed issue that could cause a SWFLoader to not switch to its alternateURL properly when the initial url fails. + - Enhanced SWFLoader so that if you change the url between the time load() is called and when it inits or fails, that new URL will attempt to load as soon as possible (when the old url either fails or inits - we must wait for that in order to avoid garbage collection issues in Flash). + +2010-12-14 +--------------------------------------------- +LoaderMax 1.763 + - Fixed issue that could cause an error in a LoaderMax instance if dispose() is called more than once on the last child loader as it completes and autoDispose is true on the LoaderMax instance. + +2010-12-13 +--------------------------------------------- +LoaderMax 1.762 +SWFLoader 1.762 +XMLLoader 1.762 +LoaderCore 1.762 + - Fixed issue that could cause a SWFLoader's or XMLLoader's "progress" property to report incorrectly as 1 after unload() was called + - Forced a refresh of a LoaderMax instance's progress/bytesLoaded/bytesTotal values after one of its children is disposed + +2010-12-07 +--------------------------------------------- +VideoLoader 1.761 +LoaderMax 1.761 + - Fixed an issue that could cause a VideoLoader's "rawContent" to become null after unload() was called and then load() was called again on the same instance. + +2010-12-03 +--------------------------------------------- +LoaderMax 1.76 +VideoLoader 1.76 + - Worked around Flash bug that could cause VideoLoaders not to dispatch a COMPLETE event when the swf is compiled with CS3 or CS4 (very odd, I know). Apparently when you publish with CS3 or CS4, NetStreams don't consistently dispatch a NetStreamEvent for code "NetStream.Buffer.Full" and if you seek() a NetStream before about 50 millseconds after it finishes buffering (even if it has COMPLETELY loaded!) it can lose its audio, so VideoLoader works around that as well now. + +2010-12-02 +--------------------------------------------- +LoaderMax 1.75 +XMLLoader 1.75 +LoaderCore 1.75 +XMLLoaderVars 1.02 + - Added a new event to XMLLoader (RAW_LOAD) that gets dispatched when the XML has finished loading but has NOT been parsed yet so that you can alter the XML if necessary before it gets parsed for LoaderMax-related data. There is also a new onRawLoad event listener shortcut. + +2010-11-29 +--------------------------------------------- +VideoLoader 1.742 +LoaderMax 1.742 + - Very minor adjustment to VideoLoader that could help avoid a very rare scenario where it wouldn't dispatch its COMPLETE event + +2010-11-22 +--------------------------------------------- +VideoLoader 1.741 +LoaderMax 1.741 + - Fixed issue that was introduced in version 1.73 that could cause a VideoLoader to not properly dispatch PLAY_PROGRESS events after gotoVideoTime() was called or the videoTime was set to a particular value. + +2010-11-21 +--------------------------------------------- +VideoLoader 1.74 +LoaderMax 1.74 + - Fixed issue with VideoLoader not dispatching its COMPLETE event in a file that was published for Flash Player 9. This issue was introduced in version 1.73. + - Changed "cacheBusterID" URL variable to "gsCacheBusterID" in order to make it more unique and avoid a potential conflict with a particular CDN. + +2010-11-19 +--------------------------------------------- +VideoLoader 1.731 +LoaderMax 1.731 + - Fixed a minor cleanup issue in VideoLoader related to removing an event listener internally + +2010-11-18 +--------------------------------------------- +VideoLoader 1.73 +LoaderMax 1.73 + - Worked around a bug in the NetStream class that could cause a video to lose its audio if it was loaded/buffered even before the NetStream rendered a single frame. + - Streamlined a few things internally in the VideoLoader class + +2010-11-17 +--------------------------------------------- +LoaderMax 1.72 +VideoLoader 1.72 +ContentDisplay 1.72 +FlexContentDisplay 1.72 +(all vars classes) 1.01 + - Added "data" property to ContentDisplay and FlexContentDisplay to store arbitrary data (optional of course) + - Added prop() method to all vars data classes so that you can add arbitrary properties to the vars object. + - Fixed issue with VideoLoader that could cause PLAY_PROGRESS events to be dispatched even when there wasn't a change in the video time. + +2010-11-16 +--------------------------------------------- +VideoLoader 1.71 +LoaderMax 1.71 + - Fixed issue with VideoLoader that could cause cue points that were added with addASCuePoint() to be triggered when goToVideoTime() is called even if its skipCuePoints parameter was true. + +2010-11-13 +--------------------------------------------- +(all LoaderMax classes) 1.7 + - Rewrote all the vars classes (CSSLoaderVars, DataLoaderVars, ImageLoaderVars, SWFLoaderVars, LoaderMaxVars, MP3LoaderVars, VideoLoaderVars, XMLLoaderVars) to facilitate method chaining like new ImageLoaderVars().name("image1").estimatedBytes(12420).width(200).height(100). This makes it possible to do things inline, greatly reducing the number of lines necessary to accomplish the same task. + - Consolidated virtually all of the old vars-related classes like BlurFilterVars, ColorMatrixFilterVars, etc. into the TweenLiteVars and TweenMaxVars classes (so the old ones were deleted). + - Fixed an issue that could cause a subloaded swf that uses TLF to not properly recognize LoaderMax-related loaders with their requireWithRoot set to that swf's root. This was caused by the bug in Adobe's Loader. + - Updated ASDocs + +2010-11-03 +--------------------------------------------- +LoaderMax 1.652 +XMLLoader 1.652 + - Fixed issue that would cause an error to be thrown if an SWFLoader was created through XML that was loaded with XMLLoader and its "context" property was defined in the XML and the swf was running locally. + +2010-11-01 +--------------------------------------------- +LoaderMax 1.651 +VideoLoader 1.651 +ContentDisplay 1.651 +FlexContentDisplay 1.651 + - Fixed issue that could cause a video to briefly display at 320x160 before being resized to the width/height defined in the vars parameter of the constructor. + +2010-10-29 +--------------------------------------------- +LoaderMax 1.65 +LoaderCore 1.65 +LoaderItem 1.65 +DisplayObjectLoader 1.65 + - Added a static defaultContext property to LoaderMax for defining a default LoaderContext. + - Worked around a VERY strange bug in certain older versions of the Flash Player (like 10.0.12.36 in Firefox) that omitted the "?" when GET parameters are appended to the URL of a loader. This only happened when the URL contained a "?" character already to begin with. + +2010-10-23 +--------------------------------------------- +LoaderMax 1.642 +LoaderItem 1.642 + - Fixed issue that could cause alternateURL not to function properly on children of a LoaderMax that had auditSize set to true + +2010-10-21 +--------------------------------------------- +LoaderMax 1.641 +LoaderItem 1.641 + - Worked around an issue with Flash's URLRequest not correctly appending URLVariables to the end of a URL that already contained GET variables (it would neglect to add the "&" separator). (The fix that was applied in version 1.62 didn't fully resolve the issue) + +2010-10-20 +--------------------------------------------- +LoaderMax 1.64 +ContentDisplay 1.64 +FlexContentDisplay 1.64 + - Worked around bug in Flash that incorrectly reports getBounds() on objects with a scrollRect which caused ContentDisplay/FlexContentDisplay objects not to properly crop the rawContent when crop was true and any of the custom properties like fitWidth/fitHeight/crop/bgAlpha/bgColor/etc. was altered more than once. + - Fixed an issue that could cause a SWFLoader's content not to be cropped properly if crop was set to true and one of the special properties was changed more then once. + +2010-10-13 +--------------------------------------------- +LoaderMax 1.631 +SelfLoader 1.631 + - Fixed issue that could cause SelfLoader not to fire its COMPLETE event inside a LoaderMax if the SelfLoader is created AFTER the swf has already fully loaded (very unlikely, but possible) + +2010-10-08 +--------------------------------------------- +XMLLoader 1.63 +LoaderMax 1.63 + - Fixed problem that prevented XMLLoader from recognizing the "context" special attribute inside nodes. + +2010-10-07 +--------------------------------------------- +LoaderMax 1.62 +XMLLoader 1.62 +LoaderItem 1.62 +LoaderCore 1.62 + - Worked around an issue with Flash's URLRequest not correctly appending URLVariables to the end of a URL that already contained GET variables (it would neglect to add the "&" separator). This only happened on loaders that were originally created by passing a URLRequest into the contstructor that had URLVariables set as its "data" property. Well, that's the only time you'd see the symptom, but the problem appears to be in Adobe's URLRequest class and is unrelated to LoaderMax. + - Enhanced XMLLoader so that LoaderMax-related tag recognition (like , , , etc.) is no longer case sensitive. + +2010-10-05 +--------------------------------------------- +LoaderMax 1.61 +LoaderItem 1.61 + - Worked around a bug in the way some browsers (like Chrome and Firefox) occasionally use partially-loaded audit files in the cache instead of loading them normally - the workaround simply involves appending the cacheBusterID to the end of the URL (like when noCache:true is passed in) but only during the auditing process (it doesn't force the cacheBusterID to be appended to the normal/full load). + - Fixed issue that caused the cacheBusterID to remain on the url from the auditing process even on loaders that didn't have noCache set to true (introduced in v1.6) + +2010-10-02 +--------------------------------------------- +VideoLoader 1.62 +LoaderMax 1.6 +LoaderItem 1.6 +XMLLoader 1.6 + - Added "autoAdjustBuffer" property to VideoLoader (and VideoLoaderVars) that will automatically adjust the buffer if/when the initial buffer empties. It will base the new bufferTime on the rate at which the file has been loading (the user's bandwidth up to that point) so that the buffer most likely won't empty again. + - Fixed issue that could cause a VideoLoader not to receive metaData if gotoAndPlay() was called before the metaData was received or if load() was called and then immediately playVideo() was called. + - Added the dispatch of an ERROR event when the metaData timeout occurs. + - Improved the handling of multiple calls to gotoVideoTime() or playVideo() in quick succession. + +2010-10-01 +--------------------------------------------- +VideoLoader 1.61 +LoaderMax 1.53 +LoaderCore 1.53 + - Fixed issue that could cause a LoaderMax queue to not proceed with its loading if an audit is occuring on a child VideoLoader at the same time as load() is called on the VideoLoader (very rare). + - Fixed issue that could cause the VIDEO_BUFFER_FULL not to fire after the video has finished playing and is restarted + +2010-09-29 +--------------------------------------------- +VideoLoader 1.6 +LoaderMax 1.52 + - Added new addASCuePoint() and removeASCuePoint() methods to VideoLoader for dynamically adding cue points instead of embedding them in the video file. + +2010-09-28 +--------------------------------------------- +LoaderMax 1.51 +LoaderCore 1.51 +VideoLoader 1.51 + - Fixed problem that caused VideoLoader to dispatch its VIDEO_BUFFER_FULL event before the buffer was full (only when autoPlay was true) + - Prevented LoaderMax from dispatching an OPEN event when it has no children. + +2010-09-27 +--------------------------------------------- +ContentDisplay 1.5 +FlexContentDisplay 1.5 + - Simply changed the order that properties are applied to the ContentDisplay/FlexContentDisplay so that the size is determined BEFORE being added to the container (if "container" is defined). + +2010-09-22 +--------------------------------------------- +LoaderMax 1.5 +ContentDisplay 1.472 +FlexContentDisplay 1.472 + - Added a "childrenVars" parameter to LoaderMax.parse() that allows you to define a vars object that should be passed to all child loaders that are created when an array of urls is parsed. For example, if you have 20 video urls to parse() and autoPlay should be false for all of them, you'd do LoaderMax.parse(myVideoUrlArray, null, {autoPlay:false}); + - Fixed problem that caused the background rectangle drawn in ContentDisplay and FlexContentDisplay not to be redrawn properly when special properties like fitWidth/fitHeight were set more than once (the old rectangle remained - it needed a graphics.clear()) + - Updated ASDocs + +2010-09-18 +--------------------------------------------- +LoaderMax 1.471 +SWFLoader 1.47 +LoaderCore 1.47 + - Worked around a Flash bug that caused SWFLoaders to incorrectly report their loading progress in a very particular scenario + - Worked around Flash bug that cause LoaderMax loaders with their name set to exactly "0" (the Number) through the vars parameter to use the default name instead (like "loader1"). When vars.name was 0, Boolean(vars.name == "") would return true which is what caused the problem. + +2010-09-15 +--------------------------------------------- +LoaderMax 1.46 +ContentDisplay 1.46 +FlexContentDisplay 1.46 + - Fixed issue with ContentDisplay and FlexContentDisplay that could cause a Video to be displayed in the wrong proportions (this bug was introduced in version 1.43) + - Worked around bug in Flex that caused FlexContentDisplay's width and height properties to always report as 0. + +2010-09-14 +--------------------------------------------- +SWFLoader 1.4 +LoaderMax 1.45 + - Fixed issue that could cause a SWFLoader's internal Loader to not get closed if cancel() or unload() or dispose() is called before the swf has loaded enough to initialize its first frame. + +2010-09-10 +--------------------------------------------- +MP3Loader 1.4 + - Improved handing of the "duration" property so that it constantly updates its estimated value (the real duration cannot be determined with 100% accuracy until the file is fully loaded - that's just a limitatin of Flash). + - Added "initThreshold" special property to MP3Loader that allows you to specify a minimum bytesLoaded that must be reached before the INIT event gets dispatched which means you can set it to larger numbers to ensure a more accurate "duration" property when INIT is dispatched. The default is 102400 bytes (100k) + +2010-09-09 +--------------------------------------------- +LoaderMax 1.43 +ContentDisplay 1.43 +FlexContentDisplay 1.43 +VideoLoader 1.4 + - Added scaleMode, centerRegistration, hAlign, vAlign, crop, fitWidth, fitHeight, bgColor, and bgAlpha properties to ContentDisplay and FlexContentDisplay so that you can set all of those properties even AFTER creating the associated ImageLoader/SWFLoader/VideoLoader. Previously, the only way to set those properties was through the vars object of the loader. + - Fixed issue in VideoLoader that could cause very short (in duration) videos to not be sized correctly (and may be invisible) when autoPlay was set to false. + +2010-09-06 +--------------------------------------------- +LoaderMax 1.42 + - Changed data type of the return value of LoaderMax.parse() to "*" to avoid compiler errors when developers forget to cast their variable, like var image:ImageLoader = LoaderMax.parse("photo.jpg") as ImageLoader + +2010-09-05 +--------------------------------------------- +LoaderMax 1.41 + - Changed the name of the new LoaderMax "steppedProgress" property to "rawProgress" and improved its accuracy inbetween loads so that it is updated each time there's a PROGRESS event instead of only when a child loader fully completes. + +2010-09-01 +--------------------------------------------- +LoaderMax 1.4 +DisplayObjectLoader 1.31 + - Added a new "steppedProgress" property to LoaderMax that is based solely on the ratio of child loaders that have completed loading compared to those that haven't - this calculation does not concern itself whatsoever with bytesLoaded and bytesTotal. See docs for details. + - Changed the data type that the static getLoader() method returns to "*" instead of LoaderCore in order to make it easier on developers. Previously, for example, playing a VideoLoader would need to be done like (LoaderMax.getLoader("myVideoLoader") as VideoLoader).playVideo() but now it can simply be LoaderMax.getLoader("myVideoLoader").playVideo() + - Fixed minor problem with the forced garbage collection routine in SWFLoader and ImageLoader that gets called after unload() and dispose(true). + +2010-08-30 +--------------------------------------------- +LoaderMax 1.31 + - Fixed problem in LoaderMax.prependURLs() and LoaderMax.replaceURLText() that could cause an error if the includeNested parameter was set to true and there were nested child loaders. + +2010-08-12 +--------------------------------------------- +VideoLoader 1.32 + - Fixed VideoLoader's default autoPlay to be true instead of false (the documentation said the default was true but the actual behavior indicated false) + - Worked around bug in Flash that prevented metaData from being received by a NetStream (in VideoLoader) if pause() was called before the client's onMetaData callback was called. So in very rare circumstances (depending on how the flv was encoded), metaData simply wasn't received by VideoLoader, causing the INIT event to never be dispatched in that case. + +2010-08-09 +---------------------------------------------- +(all LoaderMax classes) 1.3 + - Added new com.greensock.loading.data package with classes for defining vars objects for each type of loader which enables strict data typing and code hinting. For example, XMLoaderVars, LoaderMaxVars, SWFLoaderVars, etc. + - Made all LoaderMax classes compatible with the new "vars" objects + - Changed default MP3Loader autoPlay value from false to true in order to make it consistent with VideoLoader and SWFLoader + - Prevented parsed loaders that an XMLLoader finds inside XML from having their size audited automatically when their "load" attribute was set to "false". + - Fixed missing removeEventListener() inside LoaderMax that was listening for "dispose" events which could cause an error to be thrown if autoDispose was set to true on both a LoaderMax and its child. + +2010-08-07 +---------------------------------------------- +VideoLoader 1.24 +MP3Loader 1.24 +LoaderMax 1.24 + - Added a bunch of capabilities to MP3Loader for controlling playback, like playSound(), pauseSound(), gotoSoundTime(), volume, playProgress, duration, soundTime, soundPaused, and some extra event dispatching like SOUND_PLAY, SOUND_PAUSE, SOUND_COMPLETE, and PLAY_PROGRESS + - Added dispatching of VIDEO_PROGRESS events from VideoLoader + - Worked around a bug in Adobe's NetStream class that prevented the proper loading of a video file after the loader was either canceled or unloaded (after the NetStream's close() method was called, no subsequent files could be played properly). + - Updated ASDocs + +2010-08-05 +---------------------------------------------- +SWFLoader 1.23 +XMLLoader 1.23 + - Fixed issue that could cause a SWFLoader or XMLLoader to prematurely dispatch its COMPLETE event in a very rare scenario, like if a nested loader inside a swf dynamically inserted a new (more deeply) nested loader right before it would normally dispatch its COMPLETE event. + +2010-08-04 +---------------------------------------------- +LoaderMax 1.23 + - Fixed issue that could cause a LoaderMax instance not to fire its COMPLETE event if new child loaders were added to it while it was in the process of loading its children. + +2010-07-31 +---------------------------------------------- +SWFLoader 1.22 + - Worked around bug in Firefox version of Flash Player that could cause Adobe's Loader class not to report bytesLoaded and bytesTotal properly on swfs that are a particular size and gzip is enabled on the server (VERY rare). It could prevent SWFLoader from dispatching its COMPLETE event. + +2010-07-30 +---------------------------------------------- +SWFLoader 1.21 +XMLLoader 1.21 + - Changed the default ApplicationDomain in SWFLoader to child (new ApplicationDomain(ApplicationDomain.currentDomain)) whereas previously it was same (ApplicationDomain.currentDomain) because several users were loading multiple swfs that used the same class/package which caused conflicts. Of course they could define a custom LoaderContext using the "context" special property, but the typical user doesn't understand what a LoaderContext is really, so it seemed safest to change the default to the safer "child" option. + - Updated ASDocs + +2010-07-28 +---------------------------------------------- +(all LoaderMax classes) 1.2 + - Added "defaultAuditSize" static property to LoaderMax to give developers control of the default auditSize special vars property for LoaderMax instances. + - Added "data" property to the LoaderEvent class to store variable data like for VideoLoader's VIDEO_CUE_POINT event's cue point info. + - Added conditional logic so that Security.allowDomain() isn't called from an AIR app (AIR apps will burp on Security.allowDomain()) + - Updated ASDocs + +2010-07-23 +---------------------------------------------- +LoaderMax 1.195 +SWFLoader 1.195 +XMLLoader 1.195 + - Changed the data type that getLoader() returns to "*" instead of LoaderCore in order to make it easier on developers. Previously, for example, playing a VideoLoader would need to be done like (LoaderMax.getLoader("myVideoLoader") as VideoLoader).playVideo() but now it can simply be LoaderMax.getLoader("myVideoLoader").playVideo() + +2010-07-17 +---------------------------------------------- +LoaderMax 1.194 +DisplayObjectLoader 1.194 + - Significantly reduced brief RAM usage when loading images and/or swfs. Previously, BitmapData.draw() was used to check security restrictions in loaded assets which caused a temporary spike in RAM usage even though the BitmapData object was only 1 pixel tall and 1 pixel wide. + +2010-07-15 +---------------------------------------------- +LoaderMax 1.193 +XMLLoader 1.192 + - Fixed issue that could cause a LoaderMax instance to cancel and start reloading a file unnecessarily when prioritize() is called on a child loader that has already completed loading. + - Worked around bug in Flash Player for Firefox that caused Adobe's URLLoader to incorrectly report its bytesLoaded as less than bytesTotal (only on very small files) even after its COMPLETE event had been dispatched, causing XMLLoader not to dispatch its COMPLETE event properly. + +2010-07-14 +---------------------------------------------- +VideoLoader 1.192 +LoaderMax 1.192 +DisplayObjectLoader 1.192 + - Worked around Flash bug that prevented the VIDEO_BUFFER_FULL from being dispatched in VideoLoader when autoPlay was set to false. + - Fixed issue that could cause an ImageLoader or SWFLoader not to properly cancel() (it could still load in the background in very specific rare situations) + +2010-07-09 +---------------------------------------------- +MP3Loader 1.141 + - Fixed problem where an MP3Loader's content could be null after its loading was canceled. + +2010-07-08 +---------------------------------------------- +XMLLoader 1.191 + - Fixed issue that could cause an XMLLoader not to dispatch its COMPLETE event if the XML file was completely empty (bytesTotal of 0) + - Worked around a bug in the Flash Player for Chrome that caused Adobe's URLLoader to always report a bytesTotal of zero for very large XML files that were loaded with gzip enabled on the server (it only affected very large XML files) + +2010-06-30 +---------------------------------------------- +XMLLoader 1.19 +LoaderMax 1.19 +SelfLoader 1.0 + - Worked around a bug in Flash Builder that caused it not to recognize XMLLoader for code hinting, etc. + - Added SelfLoader for tracking the current swf's loading progress + +2010-06-28 +---------------------------------------------- +VideoLoader 1.18 + - Fixed issue that could cause a video to autoPlay even if autoPlay was set to false. This only happened in rare scenarios. + +2010-06-24 +---------------------------------------------- +ContentDisplay 1.17 +FlexContentDisplay 1.17 +VideoLoader 1.17 + - Fixed crop feature that could incorrectly scale a video or an swf that had scriptAccessDenied = true. + +2010-06-23 +---------------------------------------------- +ImageLoader 1.15 +SWFLoader 1.15 +VideoLoader 1.15 +XMLLoader 1.15 +ContentDisplay 1.15 +FlexContentDisplay 1.15 + - Added a new "crop" special property for SWFLoader, ImageLoader, and VideoLoader. + - Adjusted SWFLoader so that the swf's native size is what is factored into the scaleMode and alignement rather than its getBounds(). + +2010-06-22 +---------------------------------------------- +(all LoaderMax classes) 1.14 + - Added ability to change a loader's url on the fly + - When a loader's url changes, it now resets its status to READY (unless it is paused in which case it will remain PAUSED). + - Implemented unloadAndStop() internally in SWFLoader and ImageLoader for swfs that are published to Flash Player 10 and later. This is just an added layer of protection against gc issues. + +2010-06-21 +---------------------------------------------- +LoaderCore 1.12 +LoaderItem 1.12 + - Prevented PROGRESS event from being dispatched when a loader is disposed + - Added the dispatch of an ERROR LoaderEvent when an alternateURL is defined and the initial url fails + +2010-06-18 +---------------------------------------------- +(all LoaderMax classes) 1.11 + - Added "alternateURL" property to all LoaderItems that allows you to define an alternate URL to load from if the original URL fails. + - VideoLoader now dispatches a VIDEO_PLAY event when autoPlay is true and the video initially loads and plays. + +2010-06-17 +---------------------------------------------- +(all LoaderMax classes) 1.1 + - Added new "loadTime" property to all loaders which reports the number of seconds elapsed during the load. + - Fixed issue in SWFLoader that could cause it to stall if canceled before the swf had dispatched its INIT event. + - Altered VideoLoader to better handle a situation where metaData isn't received from the video being loaded. + - When XMLLoader encounters malformed XML it will now dispatch a LoaderEvent.ERROR. The "text" property of the LoaderEvent will describe the error. + - Added automatic trace() of all errors to make debugging easier. + +2010-06-17 +---------------------------------------------- +LoaderMax 1.02 +LoaderCore 1.02 + - Improved status recognition in LoaderMax instances so that if a child was paused and the LoaderMax completed, when load() is called it will check again to see if any children were unpaused or failed and act accordingly, loading them if they're unpaused at that point. + +2010-06-16 +---------------------------------------------- +LoaderMax 1.01 +VideoLoader 1.01 + - Fixed issue that could cause a LoaderMax not to dispatch its final PROGRESS event if it's not in the process of loading and its children have independently loaded fully. + - Fixed issue that could cause a video that doesn't have autoPlay:true to briefly play audio just as its buffer is filled (very brief). + +2010-06-16 (version 1.0) +---------------------------------------------- + - Added "paused" property to VideoLoader + - Changed data type of SWFLoader's, ImageLoader's, VideoLoader's, ContentDisplay's, and FlexContentDisplay's rawContent property to * in order to reduce questions about compile-time errors when developers don't understand the concept of casting. + - Added "bufferMode" to VideoLoader + - Changed "loops" property in MP3Loader and "loop" property in VideoLoader to "repeat" to be consistent with TweenMax/TimelineMax (and with each other). + - Fixed problem that could cause a SWF to deny script access if it had NetStreams that hadn't started yet + +2010-06-04 (version 0.993) +---------------------------------------------- + - Added "flushContent" parameter to the dispose() method so that if you want to completely destroy an instance, you can dispose(true) instead of unload() and dispose(). dispose(true) also destroys the ContentDisplay associated with any ImageLoaders, SWFLoaders, or VideoLoaders (removing them from the display list if necessary). + - Eliminated error that could be thrown if you use LoaderMax.getLoader() or LoaderMax.getContent() before any loaders are created. + - Added dispose() method to ContentDisplay and FlexContentDisplay + +2010-06-12 (version 0.992) +---------------------------------------------- + - Fixed bug that could cause a SWFLoader or XMLLoader not to fire its COMPLETE event if it had an estimatedBytes defined and recognized sub-LoaderMax instances didn't have estimatedBytes defined and didn't load quickly enough. + +2010-06-10 (version 0.99) +---------------------------------------------- + - Introduced new LoaderEvent class for easier event management + - Many of the LoaderEvents essentially bubble through Loaders, so the LoaderEvent's "target" is always the originating loader and the "currentTarget" can be the LoaderMax instance. For example, if you nest an ImageLoader in a LoaderMax and listen for LoaderEvent.ERROR events on that LoaderMax and the ImageLoader throws an error, your listener will receive a LoaderEvent whose target points to the ImageLoader and currentTarget points to the LoaderMax. + - Revamped most of the event architecture in the system, making it more robust + - Introduced FAIL, CHILD_OPEN, CHILD_PROGRESS, CHILD_COMPLETE, CHILD_CANCEL, and CHILD_FAIL events. + - You can listen for CHILD_* events on XMLLoaders and SWFLoaders too in order to get information about any nested loaders that are discovered in the XML/SWF. + - Added "httpStatus" property to LoaderItems + - Changed VideoLoader's resumeVideo() to playVideo() + - Changed VideoLoader's seekVideo() to gotoVideoTime() + +2010-06-09 (version 0.98) +---------------------------------------------- + - Changed "prependChildrenURLs()" to "prependURLs()" in LoaderMax + - Added LoaderMax.parse() method for automatically creating the appropriate type of loader based on a url's file extension (you may pass an array as well in which case it will create a LoaderMax containing the necessary children) + - Added replaceURLText() method to LoaderMax + - Added ability for XMLLoader to recognize replaceURLText="" attribute + - Added "deblocking" and "loop" special properties to VideoLoader + - Eliminated unhandled error notifications + - Improved error reporting and identification of various types of loaders in trace() statements and toString(). + - Improved performance slightly + +2010-06-09 (version 0.972) +---------------------------------------------- + - Added "smoothing" property to VideoLoader (enabled by default) + - Added "duration" property to VideoLoader + +2010-06-08 (version 0.971) +---------------------------------------------- + - Fixed problem that could cause a SWFLoader or XMLLoader not to fire its COMPLETE event + +2010-06-08 (version 0.97) +---------------------------------------------- + - Added VideoLoader + - Added ContentDisplay and FlexContentDisplay classes. + - Now ImageLoaders, SWFLoaders, and VideoLoaders will all return a ContentDisplay instance (a Sprite) as their content. The nice thing about ContentDisplay instances is that they have a "loader" property that gives you a way to reference the loader object that populated it. So if you need to unload() it or reload it or find out the loading progress, etc., that's all possible. + - Added a LoaderMax.contentDisplayClass property which you can set to FlexContentDisplay in order to make LoaderMax Flex-friendly. FlexContentDisplay extends UIComponent, fulfilling the Flex requirement. + - Reworked the cancel(), unload(), and dispose() logic internally + - Reduced file size slightly + - Changed the default "auditSize" special property to true in LoaderMax instances. Previously it was false, but it seems as though it will be a very useful feature that most developers will use and it will prevent some confusion with LoaderMax instances' progress and bytesTotal values being jumpy when estimatedBytes isn't used. Remember, whenever an estimatedBytes property is defined, it will be used instead of auditing the size with a URLStream (to improve performance). + - Prevented a ProgressEvent from being fired where the bytesLoaded == bytesTotal until any parsing and/or setting of the "content" property was completed. + - Prevented redundant ProgressEvents from being dispatched from a LoaderMax. + - Minor fixes + - Updated docs + +2010-06-04 (version 0.96) +---------------------------------------------- + - Fixed issue with SWFLoader and XMLLoader that could cause the bytesLoaded to exceed bytesTotal if an estimatedBytes was defined that was too low and the swf/xml hadn't initted yet. + +2010-06-03 (version 0.95) +---------------------------------------------- + - Added getSWFChild() to SWFLoader for finding DisplayObjects at the root of subloaded swfs + - Fixed problem that could cause XMLLoader not to fire its COMPLETE event in a certain situation + - Fixed bug in prepend() that replaced the first element instead of pushing it back + +2010-06-03 (version 0.93) +---------------------------------------------- + - Fixed issue where the COMPLETE event could fire before the INIT event in an XMLLoader that found an empty node in the XML (and no other loaders) + - Fixed issue where an empty LoaderMax could report a progress of NaN instead of 0. + +2010-06-02 (version 0.92) +---------------------------------------------- + - Enhanced SWFLoader to work around an Adobe bug in swfs that have TLF content which would mislabel content and fire INIT/COMPLETE events inappropriately. + +2010-06-02 (version 0.91) +---------------------------------------------- + - Added "rawContent" property to ImageLoader and SWFLoader for retrieving raw content instead of the Sprite into which it was loaded. For example, an ImageLoader's "content" would be a Sprite whereas its rawContent would be a Bitmap (or a Loader if script access was denied). + - Removed "bitmap" property from ImageLoader because the generic "rawContent" property makes it redundant. + - Renamed CommonLoader to DisplayObjectLoader (you'd never use the class directly anyway) + +2010-06-01 (version 0.9) +---------------------------------------------- + - Added prependChildrenURLs() to LoaderMax + - Added ability for XMLLoader to recognize prependChildrenURLs attribute in XML nodes + - Eliminated redundant ProgressEvent dispatching from XMLLoaders that found LoaderMax data inside XML. + +2010-06-01 (version 0.81) +---------------------------------------------- + - Improved unload() and dispose() performance in ImageLoader and SWFLoader + - Images and swfs are now added at index 0 to the content Sprite instead of the top level + - Removed "offsetRegX" and "offsetRegY" special properties from ImageLoader and SWFLoader to reduce file size (they didn't seem super useful anyway). + - Fixed issue where a completed LoaderMax that was paused after having completed and then unload() is called would not load() again (refusing to unpause). + - Updated docs + +2010-06-01 (version 0.8) +---------------------------------------------- + - Changed behavior of ImageLoader and SWFLoader so that their "content" property always returns a Sprite which serves as a container for the remote content. This Sprite is immediately available, making development tasks easier. + - Added the ability to have images and swfs scale to fit inside the width/height defined for the ImageLoader or SWFLoader with various scaleModes like "proportionalInside", "proportionalOutside", "stretch", "widthOnly", "heightOnly", and "none" + - Added hAlign, vAlign, bgColor, bgAlpha, offsetRegX, and offsetRegY special properties for ImageLoader and SWFLoader + - Updated XMLLoader so that it recognizes the various new special properties available in ImageLoader and SWFLoader + +2010-05-29 (version 0.71) +---------------------------------------------- + - When XMLLoader finds LoaderMax-related data inside the XML, it will now audit the size of any that don't have an estimatedSize defined. + - Fixed issue that could cause auditedSize to incorrectly report false. + +2010-05-29 (version 0.7) +---------------------------------------------- + - When auditSize() is called and the URLStream fails because of an IO_ERROR, the loader will now set its status to FAILED and generate an error event which also takes it out of consideration when determining a LoaderMax's progress. + - Fixed a garbage collection problem + - Updated docs + +2010-05-28 (version 0.65) +---------------------------------------------- + - Added DataLoader class for loading generic text, binary, and URL-encoded variables + - Added CSSLoader class for loading StyleSheet information. + - Added onOpen to docs for all loaders + - Fixed error that was thrown when a swf was loaded whose root was not a MovieClip + +2010-05-27 +---------------------------------------------- + - XMLLoader now recognizes a "context" attribute inside XML-driven loaders, like . The default value is "own" and other options are "child" and "separate". + - ImageLoader and SWFLoader now recognize the "blendMode" special property so that you can set the image's/swf's blendMode as soon as it loads. + - If SWFLoader loads an AVM1Movie (AS1/AS2), it will toggle the scriptAccessDenied to true and the content will refer to the Loader object instead of the root of the swf for security purposes. + - Minor updates to docs + +2010-05-26 +---------------------------------------------- + - Added an auditSize special vars property to LoaderMax. See ASDocs for details about functionality + - Added auditSize() method and auditedSize property to all loaders + - Added logic in ImageLoader and SWFLoader to fall back to a more restricted mode in terms of script access when a security error is thrown due to a missing or malconfigured crossdomain.xml file. This improves the likelihood that the content will still load even if there was a security error. + - Added LoaderMax.SCRIPT_ACCESS_DENIED event dispatching along with an onScriptAccessDenied special vars property + - Added a new scriptAccessDenied property that you can check in order to determine whether or not you can perform operations like BitmapData.draw() on ImageLoader/SWFLoader content. + - Tweaked error handling so that unhandled errors aren't thrown. + - Added dispatching of IOErrorEvents, SecurityErrorEvents, and HTTPStatusEvents (you can set them up normally or with onIOError, onSecurityError, and onHTTPStatus special vars properties) + +2010-05-25 +---------------------------------------------- + - Changed LoaderMax's getAllLoaders() to getChildren() + - Changed LoaderMax's getLoadersByStatus() to getChildrenByStatus() + - Changed LoaderMax's getLoaderIndex() to getChildIndex() + - Added LoaderMax.defaultEstimatedBytes so that you can define any default value for new Loaders that don't have an estimatedBytes property defined in the vars object. + - Changed the default estimatedBytes to 20000 instead of 1000. + - Added the ability for ImageLoaders and SWFLoaders to recognize a "visible" property in the vars object so that you can set the object's visible property. + - Removed "loaders" property of LoaderMax + - Added getClass() method to SWFLoader + - Minor updates to docs diff --git a/src/com/greensock/loading/core/DisplayObjectLoader.as b/src/com/greensock/loading/core/DisplayObjectLoader.as new file mode 100644 index 0000000..392633b --- /dev/null +++ b/src/com/greensock/loading/core/DisplayObjectLoader.as @@ -0,0 +1,318 @@ +/** + * VERSION: 1.935 + * DATE: 2013-03-18 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.core { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.LoaderMax; + import com.greensock.loading.LoaderStatus; + import com.greensock.loading.display.ContentDisplay; + + import flash.display.DisplayObject; + import flash.display.Loader; + import flash.display.Sprite; + import flash.events.ErrorEvent; + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.net.LocalConnection; + import flash.net.URLRequest; + import flash.system.ApplicationDomain; + import flash.system.Capabilities; + import flash.system.LoaderContext; + import flash.system.Security; + import flash.system.SecurityDomain; + +/** + * Serves as the base class for SWFLoader and ImageLoader. There is no reason to use this class on its own. + * Please refer to the documentation for the other classes. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class DisplayObjectLoader extends LoaderItem { + /** By default, LoaderMax will automatically attempt to force garbage collection when a SWFLoader or ImageLoader is unloaded or cancelled but if you prefer to skip this measure, set defaultAutoForceGC to false. If garbage collection isn't forced, sometimes Flash doesn't completely unload swfs/images properly, particularly if there is audio embedded in the root timeline. **/ + public static var defaultAutoForceGC:Boolean = true; + /** @private the Sprite to which the EVENT_LISTENER was attached for forcing garbage collection after 1 frame (improves performance especially when multiple loaders are disposed at one time). **/ + protected static var _gcDispatcher:Sprite; + /** @private **/ + protected static var _gcCycles:uint = 0; + /** @private **/ + protected var _loader:Loader; + /** @private **/ + protected var _sprite:Sprite; + /** @private **/ + protected var _context:LoaderContext; + /** @private **/ + protected var _initted:Boolean; + /** @private used by SWFLoader when the loader is canceled before the SWF ever had a chance to init which causes garbage collection issues. We slip into stealthMode at that point, wait for it to init, and then cancel the _loader's loading.**/ + protected var _stealthMode:Boolean; + /** @private allows us to apply a LoaderContext to the file size audit (only if necessary - URLStream is better/faster/smaller and works great unless we run into security errors because of a missing crossdomain.xml file) **/ + protected var _fallbackAudit:Loader; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content + * @param vars An object containing optional parameters like estimatedBytes, name, autoDispose, onComplete, onProgress, onError, etc. For example, {estimatedBytes:2400, name:"myImage1", onComplete:completeHandler}. + */ + public function DisplayObjectLoader(urlOrRequest:*, vars:Object=null) { + super(urlOrRequest, vars); + _refreshLoader(false); + if (LoaderMax.contentDisplayClass is Class) { + _sprite = new LoaderMax.contentDisplayClass(this); + if (!_sprite.hasOwnProperty("rawContent")) { + throw new Error("LoaderMax.contentDisplayClass must be set to a class with a 'rawContent' property, like com.greensock.loading.display.ContentDisplay"); + } + } else { + _sprite = new ContentDisplay(this); + } + } + + /** @private Set inside ContentDisplay's or FlexContentDisplay's "loader" setter. **/ + public function setContentDisplay(contentDisplay:Sprite):void { + _sprite = contentDisplay; + } + + /** @private **/ + override protected function _load():void { + _prepRequest(); + if (this.vars.context is LoaderContext) { + _context = this.vars.context; + } else if (_context == null) { + if (LoaderMax.defaultContext != null) { + _context = LoaderMax.defaultContext; + if (_isLocal) { + _context.securityDomain = null; + } + } else if (!_isLocal) { + _context = new LoaderContext(true, new ApplicationDomain(ApplicationDomain.currentDomain), SecurityDomain.currentDomain); //avoids some security sandbox headaches that plague many users. + } + } + if (Capabilities.playerType != "Desktop") { //AIR apps will choke on Security.allowDomain() + Security.allowDomain(_url); + } + _loader.load(_request, _context); + } + + /** @inheritDoc **/ + override public function auditSize():void { + if (Capabilities.playerType != "Desktop") { //AIR apps will choke on Security.allowDomain() + Security.allowDomain(_url); + } + super.auditSize(); + } + + override protected function _closeStream():void { + _closeFallbackAudit(); + super._closeStream(); + } + + protected function _closeFallbackAudit():void { + if (_fallbackAudit != null) { + _fallbackAudit.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener(Event.COMPLETE, _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener("ioError", _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener("securityError", _auditStreamHandler, false, 0, true); + try { + _fallbackAudit.close(); + } catch (error:Error) { + + } + _fallbackAudit = null; + } + } + + /** @private **/ + override protected function _auditStreamHandler(event:Event):void { + //If a security error is thrown because of a missing crossdomain.xml file for example and the user didn't define a specific LoaderContext, we'll try again without checking the policy file, accepting the restrictions that come along with it because typically people would rather have the content show up on the screen rather than just error out (and they can always check the scriptAccessDenied property if they need to figure out whether it's safe to do BitmapData stuff on it, etc.) + if (event.type == "securityError") { + if (_fallbackAudit == null) { + _context = new LoaderContext(false); + _scriptAccessDenied = true; + dispatchEvent(new LoaderEvent(LoaderEvent.SCRIPT_ACCESS_DENIED, this, ErrorEvent(event).text)); + _errorHandler(event); + _fallbackAudit = new Loader(); //so that we can apply a LoaderContext. We don't want to use a Loader initially because they are more memory-intensive than URLStream and they can tend to have more problems with garbage collection. + _fallbackAudit.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener(Event.COMPLETE, _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener("ioError", _auditStreamHandler, false, 0, true); + _fallbackAudit.contentLoaderInfo.addEventListener("securityError", _auditStreamHandler, false, 0, true); + var request:URLRequest = new URLRequest(); + request.data = _request.data; + request.method = _request.method; + _setRequestURL(request, _url, (!_isLocal || _url.substr(0, 4) == "http") ? "gsCacheBusterID=" + (_cacheID++) + "&purpose=audit" : ""); + if (Capabilities.playerType != "Desktop") { //AIR apps will choke on Security.allowDomain() + Security.allowDomain(_url); + } + _fallbackAudit.load(request, _context); + return; + } else { + _closeFallbackAudit(); + } + } + super._auditStreamHandler(event); + } + + /** @private **/ + protected function _refreshLoader(unloadContent:Boolean=true):void { + if (_loader != null) { + //to avoid gc issues and get around a bug in Flash that incorrectly reports progress values on Loaders that were closed before completing, we must force gc and recreate the Loader altogether... + if (_status == LoaderStatus.LOADING) { + try { + _loader.close(); + } catch (error:Error) { + + } + } + _loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, _progressHandler); + _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, _completeHandler); + _loader.contentLoaderInfo.removeEventListener("ioError", _failHandler); + _loader.contentLoaderInfo.removeEventListener("securityError", _securityErrorHandler); + _loader.contentLoaderInfo.removeEventListener("httpStatus", _httpStatusHandler); + _loader.contentLoaderInfo.removeEventListener("httpResponseStatus", _httpStatusHandler); + _loader.contentLoaderInfo.removeEventListener(Event.INIT, _initHandler); + if (_loader.hasOwnProperty("uncaughtErrorEvents")) { //not available when published to FP9, so we reference things this way to avoid compiler errors + Object(_loader).uncaughtErrorEvents.removeEventListener("uncaughtError", _errorHandler); + } + if (unloadContent) { + try { + if (_loader.parent == null && _sprite != null) { + _sprite.addChild(_loader); //adding the _loader to the display list BEFORE calling unloadAndStop() and then removing it will greatly improve its ability to gc correctly if event listeners were added to the stage from within a subloaded swf without specifying "true" for the weak parameter of addEventListener(). The order here is critical. + } + if (_loader.hasOwnProperty("unloadAndStop")) { //Flash Player 10 and later only + (_loader as Object).unloadAndStop(); + } else { + _loader.unload(); + } + + } catch (error:Error) { + + } + if (_loader.parent) { + _loader.parent.removeChild(_loader); + } + if (("autoForceGC" in this.vars) ? this.vars.autoForceGC : defaultAutoForceGC) { + forceGC((this.hasOwnProperty("getClass")) ? 3 : 1); + } + } + } + _initted = false; + _loader = new Loader(); + _loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, _progressHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _completeHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener("ioError", _failHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener("securityError", _securityErrorHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener("httpStatus", _httpStatusHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener("httpResponseStatus", _httpStatusHandler, false, 0, true); + _loader.contentLoaderInfo.addEventListener(Event.INIT, _initHandler, false, 0, true); + if (_loader.hasOwnProperty("uncaughtErrorEvents")) { //not available when published to FP9, so we reference things this way to avoid compiler errors + Object(_loader).uncaughtErrorEvents.addEventListener("uncaughtError", _errorHandler, false, 0, true); + } + } + + /** @private works around bug in Flash Player that prevents SWFs from properly being garbage collected after being unloaded - for certain types of objects like swfs, this needs to be run more than once (spread out over several frames) to force Flash to properly garbage collect everything. **/ + public static function forceGC(cycles:uint=1):void { + if (_gcCycles < cycles) { + _gcCycles = cycles; + if (_gcDispatcher == null) { + _gcDispatcher = new Sprite(); + _gcDispatcher.addEventListener(Event.ENTER_FRAME, _forceGCHandler, false, 0, true); + } + } + } + + /** @private **/ + protected static function _forceGCHandler(event:Event):void { + if (--_gcCycles <= 0) { + _gcDispatcher.removeEventListener(Event.ENTER_FRAME, _forceGCHandler); + _gcDispatcher = null; + } + try { + new LocalConnection().connect("FORCE_GC"); + new LocalConnection().connect("FORCE_GC"); + } catch (error:Error) { + + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=LoaderStatus.READY, suppressEvents:Boolean=false):void { + if (!_stealthMode) { + _refreshLoader(Boolean(scrubLevel != 2)); + } + if (scrubLevel == 1) { //unload + (_sprite as Object).rawContent = null; + } else if (scrubLevel == 2) { //dispose + (_sprite as Object).loader = null; + } else if (scrubLevel == 3) { //unload and dispose + (_sprite as Object).dispose(false, false); //makes sure the ContentDisplay is removed from its parent as well. + } + super._dump(scrubLevel, newStatus, suppressEvents); + } + + /** @private **/ + protected function _determineScriptAccess():void { + if (!_scriptAccessDenied) { + if (!_loader.contentLoaderInfo.childAllowsParent) { + _scriptAccessDenied = true; + dispatchEvent(new LoaderEvent(LoaderEvent.SCRIPT_ACCESS_DENIED, this, "Error #2123: Security sandbox violation: " + this + ". No policy files granted access.")); + } + } + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _securityErrorHandler(event:ErrorEvent):void { + //If a security error is thrown because of a missing crossdomain.xml file for example and the user didn't define a specific LoaderContext, we'll try again without checking the policy file, accepting the restrictions that come along with it because typically people would rather have the content show up on the screen rather than just error out (and they can always check the scriptAccessDenied property if they need to figure out whether it's safe to do BitmapData stuff on it, etc.) + if (_context != null && _context.checkPolicyFile && !(this.vars.context is LoaderContext)) { + _context = new LoaderContext(false); + _scriptAccessDenied = true; + dispatchEvent(new LoaderEvent(LoaderEvent.SCRIPT_ACCESS_DENIED, this, event.text)); + _errorHandler(event); + _load(); + } else { + _failHandler(event); + } + } + + /** @private **/ + protected function _initHandler(event:Event):void { + if (!_initted) { + _initted = true; + if (_content == null) { //_content is set in ImageLoader or SWFLoader (subclasses), but we put this here just in case someone wants to use DisplayObjectLoader on its own as a lighter weight alternative without the bells & whistles of SWFLoader/ImageLoader. + _content = (_scriptAccessDenied) ? _loader : _loader.content; + } + (_sprite as Object).rawContent = (_content as DisplayObject); + dispatchEvent(new LoaderEvent(LoaderEvent.INIT, this)); + } + } + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** A ContentDisplay object (a Sprite) that will contain the remote content as soon as the INIT event has been dispatched. This ContentDisplay can be accessed immediately; you do not need to wait for the content to load. **/ + override public function get content():* { + return _sprite; + } + + /** + * The raw content that was successfully loaded into the content ContentDisplay + * Sprite which varies depending on the type of loader and whether or not script access was denied while + * attempting to load the file: + * + *
    + *
  • ImageLoader with script access granted: flash.display.Bitmap
  • + *
  • ImageLoader with script access denied: flash.display.Loader
  • + *
  • SWFLoader with script access granted: flash.display.DisplayObject (the swf's root)
  • + *
  • SWFLoader with script access denied: flash.display.Loader (the swf's root cannot be accessed because it would generate a security error)
  • + *
+ **/ + public function get rawContent():* { + return _content; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/core/LoaderCore.as b/src/com/greensock/loading/core/LoaderCore.as new file mode 100644 index 0000000..96ae2ce --- /dev/null +++ b/src/com/greensock/loading/core/LoaderCore.as @@ -0,0 +1,570 @@ +/** + * VERSION: 1.935 + * DATE: 2013-03-18 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.core { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.LoaderMax; + import com.greensock.loading.LoaderStatus; + + import flash.display.DisplayObject; + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.ProgressEvent; + import flash.net.LocalConnection; + import flash.system.Capabilities; + import flash.utils.Dictionary; + import flash.utils.getTimer; + + /** Dispatched when the loader starts loading. **/ + [Event(name="open", type="com.greensock.events.LoaderEvent")] + /** Dispatched each time the bytesLoaded value changes while loading (indicating progress). **/ + [Event(name="progress", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader completes. **/ + [Event(name="complete", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader is canceled while loading which can occur either because of a failure or when a sibling loader is prioritized in a LoaderMax queue. **/ + [Event(name="cancel", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader fails. **/ + [Event(name="fail", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader experiences some type of error, like a SECURITY_ERROR or IO_ERROR. **/ + [Event(name="error", type="com.greensock.events.LoaderEvent")] + /** Dispatched when the loader unloads (which happens when either unload() or dispose(true) is called or if a loader is canceled while in the process of loading). **/ + [Event(name="unload", type="com.greensock.events.LoaderEvent")] +/** + * Serves as the base class for GreenSock loading tools like LoaderMax, ImageLoader, XMLLoader, SWFLoader, etc. + * There is no reason to use this class on its own. Please see the documentation for the other classes. + * + *

Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderCore extends EventDispatcher { + /** @private **/ + public static const version:Number = 1.935; + + /** @private **/ + protected static var _loaderCount:uint = 0; + /** @private **/ + protected static var _rootLookup:Dictionary = new Dictionary(false); + /** @private **/ + protected static var _isLocal:Boolean; + /** @private **/ + protected static var _globalRootLoader:LoaderMax; + /** @private **/ + protected static var _listenerTypes:Object = {onOpen:"open", + onInit:"init", + onComplete:"complete", + onProgress:"progress", + onCancel:"cancel", + onFail:"fail", + onError:"error", + onSecurityError:"securityError", + onHTTPStatus:"httpStatus", + onHTTPResponseStatus:"httpResponseStatus", + onIOError:"ioError", + onScriptAccessDenied:"scriptAccessDenied", + onChildOpen:"childOpen", + onChildCancel:"childCancel", + onChildComplete:"childComplete", + onChildProgress:"childProgress", + onChildFail:"childFail", + onRawLoad:"rawLoad", + onUncaughtError:"uncaughtError"}; + /** @private **/ + protected static var _types:Object = {}; + /** @private **/ + protected static var _extensions:Object = {}; + + /** @private **/ + protected var _cachedBytesLoaded:uint; + /** @private **/ + protected var _cachedBytesTotal:uint; + /** @private **/ + protected var _status:int; + /** @private **/ + protected var _prePauseStatus:int; + /** @private **/ + protected var _dispatchProgress:Boolean; + /** @private **/ + protected var _rootLoader:LoaderMax; + /** @private **/ + protected var _cacheIsDirty:Boolean; + /** @private **/ + protected var _auditedSize:Boolean; + /** @private **/ + protected var _dispatchChildProgress:Boolean; + /** @private **/ + protected var _type:String; + /** @private used to store timing information. When the loader begins loading, the startTime is stored here. When it completes or fails, it is set to the total elapsed time between when it started and ended. We reuse this variable like this in order to minimize size. **/ + protected var _time:uint; + /** @private **/ + protected var _content:*; + + /** An object containing optional configuration details, typically passed through a constructor parameter. For example: new SWFLoader("assets/file.swf", {name:"swf1", container:this, autoPlay:true, noCache:true}). See the constructor's documentation for details about what special properties are recognized. **/ + public var vars:Object; + /** A name that you use to identify the loader instance. This name can be fed to the getLoader() or getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public var name:String; + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false. **/ + public var autoDispose:Boolean; + + /** + * Constructor + * + * @param vars An object containing optional parameters like estimatedBytes, name, autoDispose, onComplete, onProgress, onError, etc. For example, {estimatedBytes:2400, name:"myImage1", onComplete:completeHandler}. + */ + public function LoaderCore(vars:Object=null) { + this.vars = (vars != null) ? vars : {}; + if (this.vars.isGSVars) { + this.vars = this.vars.vars; + } + this.name = (this.vars.name != undefined && String(this.vars.name) != "") ? this.vars.name : "loader" + (_loaderCount++); + _cachedBytesLoaded = 0; + _cachedBytesTotal = (uint(this.vars.estimatedBytes) != 0) ? uint(this.vars.estimatedBytes) : LoaderMax.defaultEstimatedBytes; + this.autoDispose = Boolean(this.vars.autoDispose == true); + _status = (this.vars.paused == true) ? LoaderStatus.PAUSED : LoaderStatus.READY; + _auditedSize = Boolean(uint(this.vars.estimatedBytes) != 0 && this.vars.auditSize != true); + + if (_globalRootLoader == null) { + if (this.vars.__isRoot == true) { + return; + } + _globalRootLoader = new LoaderMax({name:"root", __isRoot:true}); + _isLocal = Boolean(Capabilities.playerType == "Desktop" || (new LocalConnection( ).domain == "localhost")); //alt method (Capabilities.playerType != "ActiveX" && Capabilities.playerType != "PlugIn") doesn't work when testing locally in an html wrapper + } + + _rootLoader = (this.vars.requireWithRoot is DisplayObject) ? _rootLookup[this.vars.requireWithRoot] : _globalRootLoader; + + if (_rootLoader == null) { + _rootLookup[this.vars.requireWithRoot] = _rootLoader = new LoaderMax(); + _rootLoader.name = "subloaded_swf_" + ((this.vars.requireWithRoot.loaderInfo != null) ? this.vars.requireWithRoot.loaderInfo.url : String(_loaderCount)); + _rootLoader.skipFailed = false; + } + + for (var p:String in _listenerTypes) { + if (p in this.vars && this.vars[p] is Function) { + this.addEventListener(_listenerTypes[p], this.vars[p], false, 0, true); + } + } + + _rootLoader.append(this); + } + + /** + * Loads the loader's content, optionally flushing any previously loaded content first. For example, + * a LoaderMax may have already loaded 4 out of the 10 loaders in its queue but if you want it to + * flush the data and start again, set the flushContent parameter to true (it is + * false by default). + * + * @param flushContent If true, any previously loaded content in the loader will be flushed so that it loads again from the beginning. For example, a LoaderMax may have already loaded 4 out of the 10 loaders in its queue but if you want it to flush the data and start again, set the flushContent parameter to true (it is false by default). + */ + public function load(flushContent:Boolean=false):void { + var time:uint = getTimer(); + if (this.status == LoaderStatus.PAUSED) { //use this.status instead of _status so that LoaderMax instances have a chance to do their magic in the getter and make sure their status is calibrated properly in case any of its children changed status after the LoaderMax completed (maybe they were manually loaded or failed, etc.). + _status = (_prePauseStatus <= LoaderStatus.LOADING) ? LoaderStatus.READY : _prePauseStatus; + if (_status == LoaderStatus.READY && this is LoaderMax) { + time -= _time; //when a LoaderMax is resumed, we should offset the start time. + } + } + if (flushContent || _status == LoaderStatus.FAILED) { + _dump(1, LoaderStatus.READY); + } + + if (_status == LoaderStatus.READY) { + _status = LoaderStatus.LOADING; + _time = time; + _load(); + if (this.progress < 1) { //in some cases, an OPEN event should be dispatched, like if load() is called on an empty LoaderMax, it will just dispatch a PROGRESS and COMPLETE event right away. It wouldn't make sense to dispatch an OPEN event right after that. + dispatchEvent(new LoaderEvent(LoaderEvent.OPEN, this)); + } + } else if (_status == LoaderStatus.COMPLETED) { + _completeHandler(null); + } + } + + /** @private Only called when load() was called and the _status was LoaderStatus.READY - we use this internally to make it simpler to extend (the conditional logic stays in the main load() method). **/ + protected function _load():void { + //override in subclasses + } + + /** Pauses the loader immediately. This is the same as setting the paused property to true. Some loaders may not stop loading immediately in order to work around some garbage collection issues in the Flash Player, but they will stop as soon as possible after calling pause(). **/ + public function pause():void { + this.paused = true; + } + + /** Unpauses the loader and resumes loading immediately. **/ + public function resume():void { + this.paused = false; + load(false); + } + + /** + * If the loader is currently loading (status is LoaderStatus.LOADING), it will be canceled + * immediately and its status will change to LoaderStatus.READY. This does NOT pause the + * loader - it simply halts the progress and it remains eligible for loading by any of its parent LoaderMax instances. + * A paused loader, however, cannot be loaded by any of its parent LoaderMax instances until you unpause it (by either + * calling resume() or setting its paused property to false). + * @see #unload() + * @see #dispose() + **/ + public function cancel():void { + if (_status == LoaderStatus.LOADING) { + _dump(0, LoaderStatus.READY); + } + } + + /** + * @private + * Cancels, unloads, and/or disposes of the loader depending on the scrubLevel. This consolidates + * the actions into a single function to conserve file size and because many of the same tasks must + * be performed regardless of the scrubLevel, so this eliminates redundant code. + * + * @param scrubLevel 0 = cancel, 1 = unload, 2 = dispose, 3 = flush (like unload and dispose, but in the case of ImageLoaders, SWFLoaders, and VideoLoaders, it also removes the ContentDisplay from the display list) + * @param newStatus The new LoaderStatus to which the loader should be set. + * @param suppressEvents To prevent events from being dispatched (like CANCEL or DISPOSE or PROGRESS), set suppressEvents to true. + **/ + protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + _content = null; + var isLoading:Boolean = Boolean(_status == LoaderStatus.LOADING); + if (_status == LoaderStatus.PAUSED && newStatus != LoaderStatus.PAUSED && newStatus != LoaderStatus.FAILED) { + _prePauseStatus = newStatus; + } else if (_status != LoaderStatus.DISPOSED) { + _status = newStatus; + } + if (isLoading) { + _time = getTimer() - _time; + } + _cachedBytesLoaded = 0; + if (_status < LoaderStatus.FAILED) { + if (this is LoaderMax) { + _calculateProgress(); + } + if (_dispatchProgress && !suppressEvents) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } + if (!suppressEvents) { + if (isLoading) { + dispatchEvent(new LoaderEvent(LoaderEvent.CANCEL, this)); + } + if (scrubLevel != 2) { + dispatchEvent(new LoaderEvent(LoaderEvent.UNLOAD, this)); + } + } + if (newStatus == LoaderStatus.DISPOSED) { + if (!suppressEvents) { + dispatchEvent(new Event("dispose")); + } + for (var p:String in _listenerTypes) { + if (p in this.vars && this.vars[p] is Function) { + this.removeEventListener(_listenerTypes[p], this.vars[p]); + } + } + } + } + + /** + * Removes any content that was loaded and sets bytesLoaded back to zero. When you + * unload() a LoaderMax instance, it will also call unload() on all of its + * children as well. If the loader is in the process of loading, it will automatically be canceled. + * + * @see #dispose() + **/ + public function unload():void { + _dump(1, LoaderStatus.READY); + } + + /** + * Disposes of the loader and releases it internally for garbage collection. If it is in the process of loading, it will also + * be cancelled immediately. By default, dispose() does NOT unload its content, but + * you may set the flushContent parameter to true in order to flush/unload the content as well + * (in the case of ImageLoaders, SWFLoaders, and VideoLoaders, this will also destroy its ContentDisplay Sprite, removing it + * from the display list if necessary). When a loader is disposed, all of the listeners that were added through the + * vars object (like {onComplete:completeHandler, onProgress:progressHandler}) are removed. + * If you manually added listeners, though, you should remove those yourself. + * + * @param flushContent If true, the loader's content will be unloaded as well (flushContent is false by default). In the case of ImageLoaders, SWFLoaders, and VideoLoaders, their ContentDisplay will also be removed from the display list if necessary when flushContent is true. + * @see #unload() + **/ + public function dispose(flushContent:Boolean=false):void { + _dump((flushContent ? 3 : 2), LoaderStatus.DISPOSED); + } + + /** + * Immediately prioritizes the loader inside any LoaderMax instances that contain it, + * forcing it to the top position in their queue and optionally calls load() + * immediately as well. If one of its parent LoaderMax instances is currently loading a + * different loader, that one will be temporarily cancelled. + * + *

By contrast, when load() is called, it doesn't change the loader's position/index + * in any LoaderMax queues. For example, if a LoaderMax is working on loading the first object in + * its queue, you can call load() on the 20th item and it will honor your request without + * changing its index in the queue. prioritize(), however, affects the position + * in the queue and optionally loads it immediately as well.

+ * + *

So even if your LoaderMax hasn't begun loading yet, you could prioritize(false) + * a loader and it will rise to the top of all LoaderMax instances to which it belongs, but not + * start loading yet. If the goal is to load something immediately, you can just use the + * load() method.

+ * + *

You may use the static LoaderMax.prioritize() method instead and simply pass + * the name or url of the loader as the first parameter like:

+ * + * LoaderMax.prioritize("myLoaderName", true);

+ * + * @param loadNow If true (the default), the loader will start loading immediately (otherwise it is simply placed at the top the queue in any LoaderMax instances to which it belongs). + * @see #load() + **/ + public function prioritize(loadNow:Boolean=true):void { + dispatchEvent(new Event("prioritize")); + if (loadNow && _status != LoaderStatus.COMPLETED && _status != LoaderStatus.LOADING) { + load(false); + } + } + + /** @inheritDoc **/ + override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void { + if (type == LoaderEvent.PROGRESS) { + _dispatchProgress = true; + } else if (type == LoaderEvent.CHILD_PROGRESS && this is LoaderMax) { + _dispatchChildProgress = true; + } + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + /** @private **/ + protected function _calculateProgress():void { + //override in subclasses if necessary + } + + /** + * Attempts loading just enough of the content to accurately determine the bytesTotal + * in order to improve the accuracy of the progress property. Once the + * bytesTotal has been determined or the auditSize() attempt fails due + * to an error (typically IO_ERROR or SECURITY_ERROR), the auditedSize property will be + * set to true. Auditing the size opens a URLStream that will be closed + * as soon as a response is received. + **/ + public function auditSize():void { + //override in subclasses + } + + /** Returns information about the loader, like its type, its name, and its url (if it has one). **/ + override public function toString():String { + return _type + " '" + this.name + "'" + ((this is LoaderItem) ? " (" + (this as LoaderItem).url + ")" : ""); + } + +//---- STATIC METHODS ------------------------------------------------------------------------------------ + + /** @private **/ + protected static function _activateClass(type:String, loaderClass:Class, extensions:String):Boolean { + if (type != "") { + _types[type.toLowerCase()] = loaderClass; + } + var a:Array = extensions.split(","); + var i:int = a.length; + while (--i > -1) { + _extensions[a[i]] = loaderClass; + } + return true; + } + + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _progressHandler(event:Event):void { + if (event is ProgressEvent) { + _cachedBytesLoaded = (event as ProgressEvent).bytesLoaded; + _cachedBytesTotal = (event as ProgressEvent).bytesTotal; + if (!_auditedSize) { + _auditedSize = true; + dispatchEvent(new Event("auditedSize")); + } + } + if (_dispatchProgress && _status == LoaderStatus.LOADING && _cachedBytesLoaded != _cachedBytesTotal) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + } + } + + /** @private **/ + protected function _completeHandler(event:Event=null):void { + _cachedBytesLoaded = _cachedBytesTotal; + if (_status != LoaderStatus.COMPLETED) { + dispatchEvent(new LoaderEvent(LoaderEvent.PROGRESS, this)); + _status = LoaderStatus.COMPLETED; + _time = getTimer() - _time; + } + dispatchEvent(new LoaderEvent(LoaderEvent.COMPLETE, this)); + if (this.autoDispose) { + dispose(); + } + } + + /** @private **/ + protected function _errorHandler(event:Event):void { + var target:Object = event.target; //trigger the LoaderEvent's target getter once first in order to ensure that it reports properly - see the notes in LoaderEvent.target for more details. + target = (event is LoaderEvent && this.hasOwnProperty("getChildren")) ? event.target : this; + var text:String = ""; + if (event.hasOwnProperty("error") && Object(event).error is Error) { + text = Object(event).error.message; + } else if (event.hasOwnProperty("text")) { + text = Object(event).text; + } + if (event.type != LoaderEvent.ERROR && event.type != LoaderEvent.FAIL && this.hasEventListener(event.type)) { + dispatchEvent(new LoaderEvent(event.type, target, text, event)); + } + if (event.type != "uncaughtError") { + trace("----\nError on " + this.toString() + ": " + text + "\n----"); + if (this.hasEventListener(LoaderEvent.ERROR)) { + dispatchEvent(new LoaderEvent(LoaderEvent.ERROR, target, this.toString() + " > " + text, event)); + } + } + } + + /** @private **/ + protected function _failHandler(event:Event, dispatchError:Boolean=true):void { + _dump(0, LoaderStatus.FAILED, true); + if (dispatchError) { + _errorHandler(event); + } else { + var target:Object = event.target; //trigger the LoaderEvent's target getter once first in order to ensure that it reports properly - see the notes in LoaderEvent.target for more details. + } + dispatchEvent(new LoaderEvent(LoaderEvent.FAIL, ((event is LoaderEvent && this.hasOwnProperty("getChildren")) ? event.target : this), this.toString() + " > " + (event as Object).text, event)); + dispatchEvent(new LoaderEvent(LoaderEvent.CANCEL, this)); + } + + /** @private **/ + protected function _passThroughEvent(event:Event):void { + var type:String = event.type; + var target:Object = this; + if (this.hasOwnProperty("getChildren")) { + if (event is LoaderEvent) { + target = event.target; + } + if (type == "complete") { + type = "childComplete"; + } else if (type == "open") { + type = "childOpen"; + } else if (type == "cancel") { + type = "childCancel"; + } else if (type == "fail") { + type = "childFail"; + } + } + if (this.hasEventListener(type)) { + dispatchEvent(new LoaderEvent(type, target, (event.hasOwnProperty("text") ? Object(event).text : ""), (event is LoaderEvent && LoaderEvent(event).data != null) ? LoaderEvent(event).data : event)); + } + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** If a loader is paused, its progress will halt and any LoaderMax instances to which it belongs will either skip over it or stop when its position is reached in the queue (depending on whether or not the LoaderMax's skipPaused property is true). **/ + public function get paused():Boolean { + return Boolean(_status == LoaderStatus.PAUSED); + } + public function set paused(value:Boolean):void { + if (value && _status != LoaderStatus.PAUSED) { + _prePauseStatus = _status; + if (_status == LoaderStatus.LOADING) { + _dump(0, LoaderStatus.PAUSED); + } + _status = LoaderStatus.PAUSED; + + } else if (!value && _status == LoaderStatus.PAUSED) { + if (_prePauseStatus == LoaderStatus.LOADING) { + load(false); //will change the _status for us inside load() + } else { + _status = _prePauseStatus || LoaderStatus.READY; + } + } + } + + /** Integer code indicating the loader's status; options are LoaderStatus.READY, LoaderStatus.LOADING, LoaderStatus.COMPLETED, LoaderStatus.PAUSED, and LoaderStatus.DISPOSED. **/ + public function get status():int { + return _status; + } + + /** Bytes loaded **/ + public function get bytesLoaded():uint { + if (_cacheIsDirty) { + _calculateProgress(); + } + return _cachedBytesLoaded; + } + + /** Total bytes that are to be loaded by the loader. Initially, this value is set to the estimatedBytes if one was defined in the vars object via the constructor, or it defaults to LoaderMax.defaultEstimatedBytes. When the loader loads enough of the content to accurately determine the bytesTotal, it will do so automatically. **/ + public function get bytesTotal():uint { + if (_cacheIsDirty) { + _calculateProgress(); + } + return _cachedBytesTotal; + } + + /** A value between 0 and 1 indicating the overall progress of the loader. When nothing has loaded, it will be 0; when it is halfway loaded, progress will be 0.5, and when it is fully loaded it will be 1. **/ + public function get progress():Number { + return (this.bytesTotal != 0) ? _cachedBytesLoaded / _cachedBytesTotal : (_status == LoaderStatus.COMPLETED) ? 1 : 0; + } + + /** @private Every loader is associated with a root-level LoaderMax which will be the _globalQueue unless the loader had a requireWithRoot value passed into the constructor via the vars parameter. This enables us to chain things properly in subloaded swfs if, for example, a subloaded swf has LoaderMax instances of its own and we want the SWFLoader to accurately report its loading status based not only on the subloaded swf, but also the subloaded swf's LoaderMax instances. **/ + public function get rootLoader():LoaderMax { + return _rootLoader; + } + + /** + * The content that was loaded by the loader which varies by the type of loader: + *
    + *
  • ImageLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the ImageLoader's rawContent (a flash.display.Bitmap unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • SWFLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the SWFLoader's rawContent (the swf's root DisplayObject unless script access was denied in which case rawContent will be a flash.display.Loader to avoid security errors). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • VideoLoader - A com.greensock.loading.display.ContentDisplay (a Sprite) which contains the VideoLoader's rawContent (a Video object to which the NetStream was attached). For Flex users, you can set LoaderMax.defaultContentDisplay to FlexContentDisplay in which case ImageLoaders, SWFLoaders, and VideoLoaders will return a com.greensock.loading.display.FlexContentDisplay instance instead.
  • + *
  • XMLLoader - XML
  • + *
  • DataLoader + *
      + *
    • String if the DataLoader's format vars property is "text" (the default).
    • + *
    • flash.utils.ByteArray if the DataLoader's format vars property is "binary".
    • + *
    • flash.net.URLVariables if the DataLoader's format vars property is "variables".
    • + *
  • + *
  • CSSLoader - flash.text.StyleSheet
  • + *
  • MP3Loader - flash.media.Sound
  • + *
  • LoaderMax - an array containing the content objects from each of its child loaders.
  • + *
+ **/ + public function get content():* { + return _content; + } + + /** + * Indicates whether or not the loader's bytesTotal value has been set by any of the following: + *
    + *
  • Defining an estimatedBytes in the vars object passed to the constructor
  • + *
  • Calling auditSize() and getting a response (an error is also considered a response)
  • + *
  • When a LoaderMax instance begins loading, it will automatically force a call to auditSize() for any of its children that don't have an estimatedBytes defined. You can disable this behavior by passing auditSize:false through the constructor's vars object.
  • + *
+ **/ + public function get auditedSize():Boolean { + return _auditedSize; + } + + /** + * The number of seconds that elapsed between when the loader began and when it either completed, failed, + * or was canceled. You may check a loader's loadTime anytime, not just after it completes. For + * example, you could access this value in an onProgress handler and you'd see it steadily increase as the loader + * loads and then when it completes, loadTime will stop increasing. LoaderMax instances ignore + * any pauses when calculating this value, so if a LoaderMax begins loading and after 1 second it gets paused, + * and then 10 seconds later it resumes and takes an additional 14 seconds to complete, its loadTime + * would be 15, not 25. + **/ + public function get loadTime():Number { + if (_status == LoaderStatus.READY) { + return 0; + } else if (_status == LoaderStatus.LOADING) { + return (getTimer() - _time) / 1000; + } else { + return _time / 1000; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/core/LoaderItem.as b/src/com/greensock/loading/core/LoaderItem.as new file mode 100644 index 0000000..86df56a --- /dev/null +++ b/src/com/greensock/loading/core/LoaderItem.as @@ -0,0 +1,243 @@ +/** + * VERSION: 1.941 + * DATE: 2015-01-20 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.core { + import com.greensock.events.LoaderEvent; + import com.greensock.loading.LoaderMax; + import com.greensock.loading.LoaderStatus; + + import flash.events.Event; + import flash.events.ProgressEvent; + import flash.net.URLRequest; + import flash.net.URLStream; + import flash.net.URLVariables; + + /** Dispatched when the loader experiences an IO_ERROR while loading or auditing its size. **/ + [Event(name="ioError", type="com.greensock.events.LoaderEvent")] +/** + * Serves as the base class for all individual loaders (not LoaderMax) like ImageLoader, + * XMLLoader, SWFLoader, MP3Loader, etc. There is no reason to use this class on its own. + * Please see the documentation for the other classes. + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderItem extends LoaderCore { + /** @private **/ + protected static var _cacheID:Number = new Date().getTime(); + /** @private **/ + protected static var _underlineExp:RegExp = /%5f/gi; + + /** @private **/ + protected var _url:String; + /** @private **/ + protected var _request:URLRequest; + /** @private **/ + protected var _scriptAccessDenied:Boolean; + /** @private used in auditSize() just to preload enough of the file to determine bytesTotal. **/ + protected var _auditStream:URLStream; + /** @private For certain types of loaders like SWFLoader and XMLLoader where there may be nested loaders found, it's better to prioritize the estimatedBytes if one is defined. Otherwise, the file size will be used which may be MUCH smaller than all the assets inside of it (like an XML file with a bunch of VideoLoaders).**/ + protected var _preferEstimatedBytesInAudit:Boolean; + /** @private **/ + protected var _httpStatus:int; + /** @private used to prevent problems that could occur if an audit is in process and load() is called on a bad URL - the audit could fail first and swap the URL and then when the real load fails just after that, we couldn't just do if (_url != this.vars.alternateURL) because the audit would have already changed it. **/ + protected var _skipAlternateURL:Boolean; + + /** + * Constructor + * + * @param urlOrRequest The url (String) or URLRequest from which the loader should get its content + * @param vars An object containing optional parameters like estimatedBytes, name, autoDispose, onComplete, onProgress, onError, etc. For example, {estimatedBytes:2400, name:"myImage1", onComplete:completeHandler}. + */ + public function LoaderItem(urlOrRequest:*, vars:Object=null) { + super(vars); + _request = (urlOrRequest is URLRequest) ? urlOrRequest as URLRequest : new URLRequest(urlOrRequest); + _url = _request.url; + _setRequestURL(_request, _url); + } + + /** @private **/ + protected function _prepRequest():void { + _scriptAccessDenied = false; + _httpStatus = 0; + _closeStream(); + if (this.vars.noCache && (!_isLocal || _url.substr(0, 4) == "http")) { + _setRequestURL(_request, _url, "gsCacheBusterID=" + (_cacheID++)); + } + } + + /** @private Flash doesn't properly apply extra GET url parameters when the URL contains them already (like "http://www.greensock.com?id=2") - it ends up missing an "&" delimiter so this method splits any that exist out into a URLVariables object and optionally adds extra parameters like gsCacheBusterID, etc. **/ + protected function _setRequestURL(request:URLRequest, url:String, extraParams:String=""):void { + var a:Array = (this.vars.allowMalformedURL) ? [url] : url.split("?"); + + //in order to avoid a VERY strange bug in certain versions of the Flash Player (like 10.0.12.36), we must loop through each character and rebuild a separate String variable instead of just using a[0], otherwise the "?" delimiter will be omitted when GET parameters are appended to the URL by Flash! Performing any String manipulations on the url will cause the issue as long as there is a "?" in the url. Like url.split("?") or url.substr(0, url.indexOf("?"), etc. Absolutely baffling. Definitely a bug in the Player - it was fixed in 10.1. + var s:String = a[0]; + var parsedURL:String = ""; + for (var i:int = 0; i < s.length; i++) { + parsedURL += s.charAt(i); + } + + request.url = parsedURL; + if (a.length >= 2) { + extraParams += (extraParams == "") ? a[1] : "&" + a[1]; + } + if (extraParams != "") { + var data:URLVariables = new URLVariables( ((request.data is URLVariables) ? request.data.toString() : null) ); + a = extraParams.split("&"); + i = a.length; + var pair:Array; + while (--i > -1) { + pair = a[i].split("="); + data[pair.shift()] = pair.join("="); + } + request.data = data.toString().replace(_underlineExp, "_"); + + if (this.vars.allowMalformedURL) { + request.url += ((request.url.indexOf("?") == -1) ? "?" : "&") + request.data.toString(); + request.data = null; + } + } + if (_isLocal && this.vars.allowMalformedURL != true && _request.data != null && _request.url.substr(0, 4) != "http") { + _request.method = "POST"; //to avoid errors when loading local files with GET URL parameters + } + } + + /** @private scrubLevel: 0 = cancel, 1 = unload, 2 = dispose, 3 = flush **/ + override protected function _dump(scrubLevel:int=0, newStatus:int=0, suppressEvents:Boolean=false):void { + _closeStream(); + super._dump(scrubLevel, newStatus, suppressEvents); + } + + /** @inheritDoc **/ + override public function auditSize():void { + if (_auditStream == null) { + _auditStream = new URLStream(); + _auditStream.addEventListener(ProgressEvent.PROGRESS, _auditStreamHandler, false, 0, true); + _auditStream.addEventListener(Event.COMPLETE, _auditStreamHandler, false, 0, true); + _auditStream.addEventListener("ioError", _auditStreamHandler, false, 0, true); + _auditStream.addEventListener("securityError", _auditStreamHandler, false, 0, true); + var request:URLRequest = new URLRequest(); + request.data = _request.data; + request.method = _request.method; + _setRequestURL(request, _url, (!_isLocal || _url.substr(0, 4) == "http") ? "gsCacheBusterID=" + (_cacheID++) + "&purpose=audit" : ""); + _auditStream.load(request); + } + } + + /** @private **/ + protected function _closeStream():void { + if (_auditStream != null) { + _auditStream.removeEventListener(ProgressEvent.PROGRESS, _auditStreamHandler); + _auditStream.removeEventListener(Event.COMPLETE, _auditStreamHandler); + _auditStream.removeEventListener("ioError", _auditStreamHandler); + _auditStream.removeEventListener("securityError", _auditStreamHandler); + try { + _auditStream.close(); + } catch (error:Error) { + + } + _auditStream = null; + } + } + +//---- EVENT HANDLERS ------------------------------------------------------------------------------------ + + /** @private **/ + protected function _auditStreamHandler(event:Event):void { + if (event is ProgressEvent) { + _cachedBytesTotal = (event as ProgressEvent).bytesTotal; + if (_preferEstimatedBytesInAudit && uint(this.vars.estimatedBytes) > _cachedBytesTotal) { + _cachedBytesTotal = uint(this.vars.estimatedBytes); + } + } else if (event.type == "ioError" || event.type == "securityError") { + if (this.vars.alternateURL != undefined && this.vars.alternateURL != "" && this.vars.alternateURL != _url) { + _errorHandler(event); + if (_status != LoaderStatus.DISPOSED) { //it is conceivable that the user disposed the loader in an onError handler + _url = this.vars.alternateURL; + _setRequestURL(_request, _url); + var request:URLRequest = new URLRequest(); + request.data = _request.data; + request.method = _request.method; + _setRequestURL(request, _url, (!_isLocal || _url.substr(0, 4) == "http") ? "gsCacheBusterID=" + (_cacheID++) + "&purpose=audit" : ""); + _auditStream.load(request); + } + return; + } else { + //note: a CANCEL event won't be dispatched because technically the loader wasn't officially loading - we were only briefly checking the bytesTotal with a URLStream. + super._failHandler(event); + } + } + _auditedSize = true; + _closeStream(); + dispatchEvent(new Event("auditedSize")); + } + + /** @private **/ + override protected function _failHandler(event:Event, dispatchError:Boolean=true):void { + if (this.vars.alternateURL != undefined && this.vars.alternateURL != "" && !_skipAlternateURL) { //don't do (_url != vars.alternateURL) because the audit could have changed it already - that's the whole purpose of _skipAlternateURL. + _errorHandler(event); + _skipAlternateURL = true; + _url = "temp" + (new Date().getTime()); //in case the audit already changed the _url to vars.alternateURL, we temporarily make it something different in order to force the refresh in the url setter which skips running the code if the url is set to the same value as it previously was. Don't use Math.random() because for some reason, Google Display Network disallows it (citing security reasons). + this.url = this.vars.alternateURL; //also calls _load() + } else { + super._failHandler(event, dispatchError); + } + } + + + /** @private **/ + protected function _httpStatusHandler(event:Event):void { + _httpStatus = (event as Object).status; + dispatchEvent(new LoaderEvent(event.type, this, String(_httpStatus), event)); + } + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** The url from which the loader should get its content. **/ + public function get url():String { + return _url; + } + public function set url(value:String):void { + if (_url != value) { + _url = value; + _setRequestURL(_request, _url); + var isLoading:Boolean = Boolean(_status == LoaderStatus.LOADING); + _dump(1, LoaderStatus.READY, true); + _auditedSize = Boolean(uint(this.vars.estimatedBytes) != 0 && this.vars.auditSize != true); + _cachedBytesTotal = (uint(this.vars.estimatedBytes) != 0) ? uint(this.vars.estimatedBytes) : LoaderMax.defaultEstimatedBytes; + _cacheIsDirty = true; + if (isLoading) { + _load(); + } + } + } + + /** The URLRequest associated with the loader. **/ + public function get request():URLRequest { + return _request; + } + + /** The httpStatus code of the loader. You may listen for LoaderEvent.HTTP_STATUS events on certain types of loaders to be notified when it changes, but in some environments the Flash player cannot sense httpStatus codes in which case the value will remain 0. **/ + public function get httpStatus():int { + return _httpStatus; + } + + /** + * If the loaded content is denied script access (because of security sandbox restrictions, + * a missing crossdomain.xml file, etc.), scriptAccessDenied will be set to true. + * In the case of loaded images or swf files, this means that you should not attempt to perform + * BitmapData operations on the content. An image's smoothing property cannot be set + * to true either. Even if script access is denied for particular content, LoaderMax will still + * attempt to load it. + **/ + public function get scriptAccessDenied():Boolean { + return _scriptAccessDenied; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/CSSLoaderVars.as b/src/com/greensock/loading/data/CSSLoaderVars.as new file mode 100644 index 0000000..ab0f391 --- /dev/null +++ b/src/com/greensock/loading/data/CSSLoaderVars.as @@ -0,0 +1,184 @@ +/** + * VERSION: 1.2 + * DATE: 2011-03-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; +/** + * Can be used instead of a generic Object to define the vars parameter of a CSSLoader's constructor. + * + *

There are 2 primary benefits of using a CSSLoaderVars instance to define your CSSLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in CSSLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the CSSLoaderVars class adds slightly more kb to your swf.

+ * + *

USAGE:

+ *

Note that each method returns the CSSLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without CSSLoaderVars:

+ * new CSSLoader("styles.css", {name:"css", estimatedBytes:1500, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With CSSLoaderVars

+ * new CSSLoader("styles.css", new CSSLoaderVars().name("css").estimatedBytes(1500).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that CSSLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this:
    + * new CSSLoader("styles.css", new CSSLoaderVars().name("css").estimatedBytes(1500).vars);
  • + *
  • Using CSSLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class CSSLoaderVars { + /** @private **/ + public static const version:Number = 1.2; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this CSSLoaderVars instance. + */ + public function CSSLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):CSSLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):CSSLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):CSSLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):CSSLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):CSSLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):CSSLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):CSSLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):CSSLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):CSSLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):CSSLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):CSSLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):CSSLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):CSSLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):CSSLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):CSSLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):CSSLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):CSSLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the CSSLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/DataLoaderVars.as b/src/com/greensock/loading/data/DataLoaderVars.as new file mode 100644 index 0000000..4b9b4e6 --- /dev/null +++ b/src/com/greensock/loading/data/DataLoaderVars.as @@ -0,0 +1,192 @@ +/** + * VERSION: 1.2 + * DATE: 2011-03-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; +/** + * Can be used instead of a generic Object to define the vars parameter of a DataLoader's constructor. + * + *

There are 2 primary benefits of using a DataLoaderVars instance to define your DataLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in DataLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the DataLoaderVars class adds slightly more kb to your swf.

+ * + *

USAGE:

+ *

Note that each method returns the DataLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without DataLoaderVars:

+ * new DataLoader("getData.php", {name:"myData", estimatedBytes:1500, format:"text", onComplete:completeHandler, onProgress:progressHandler});

+ * + *

With DataLoaderVars

+ * new DataLoader("getData.php", new DataLoaderVars().name("myData").estimatedBytes(1500).format("text").onComplete(completeHandler).onProgress(progressHandler));

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that DataLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new DataLoader("getData.php", new DataLoaderVars().name("myData").estimatedBytes(1500).format("text").vars);
  • + *
  • Using DataLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class DataLoaderVars { + /** @private **/ + public static const version:Number = 1.2; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this DataLoaderVars instance. + */ + public function DataLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):DataLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):DataLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):DataLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):DataLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):DataLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):DataLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):DataLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):DataLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):DataLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):DataLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):DataLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):DataLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):DataLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):DataLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):DataLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):DataLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):DataLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- DATALOADER PROPERTIES ------------------------------------------------------------- + + /** Controls whether the downloaded data is received as text ("text"), raw binary data ("binary"), or URL-encoded variables ("variables"). **/ + public function format(value:String):DataLoaderVars { + return _set("format", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the DataLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/ImageLoaderVars.as b/src/com/greensock/loading/data/ImageLoaderVars.as new file mode 100644 index 0000000..810c77c --- /dev/null +++ b/src/com/greensock/loading/data/ImageLoaderVars.as @@ -0,0 +1,341 @@ +/** + * VERSION: 1.22 + * DATE: 2011-05-05 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.system.LoaderContext; +/** + * Can be used instead of a generic object to define the vars parameter of an ImageLoader's constructor. + * + *

There are 2 primary benefits of using a ImageLoaderVars instance to define your ImageLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in ImageLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

USAGE

+ *

Note that each method returns the ImageLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without ImageLoaderVars:

+ * new ImageLoader("photo1.jpg", {name:"photo1", estimatedBytes:11500, container:this, width:200, height:100, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With ImageLoaderVars

+ * new ImageLoader("photo1.jpg", new ImageLoaderVars().name("photo1").estimatedBytes(11500).container(this).width(200).height(100).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that ImageLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new ImageLoader("photo1.jpg", new ImageLoaderVars().name("photo1").estimatedBytes(11500).vars);
  • + *
  • Using ImageLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ImageLoaderVars { + /** @private **/ + public static const version:Number = 1.22; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * + * @param vars A generic Object containing properties that you'd like to add to this ImageLoaderVars instance. + */ + public function ImageLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):ImageLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):ImageLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):ImageLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):ImageLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):ImageLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):ImageLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):ImageLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):ImageLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):ImageLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):ImageLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):ImageLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):ImageLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):ImageLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):ImageLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):ImageLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):ImageLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):ImageLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- DISPLAYOBJECTLOADER PROPERTIES ------------------------------------------------------------ + + + /** Sets the ContentDisplay's alpha property. **/ + public function alpha(value:Number):ImageLoaderVars { + return _set("alpha", value); + } + + /** Controls the alpha of the rectangle that is drawn when a width and height are defined. **/ + public function bgAlpha(value:Number):ImageLoaderVars { + return _set("bgAlpha", value); + } + + /** When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgColor if you prefer. **/ + public function bgColor(value:uint):ImageLoaderVars { + return _set("bgColor", value); + } + + /** Sets the ContentDisplay's blendMode property. **/ + public function blendMode(value:String):ImageLoaderVars { + return _set("blendMode", value); + } + + /** If true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center. **/ + public function centerRegistration(value:Boolean):ImageLoaderVars { + return _set("centerRegistration", value); + } + + /** A DisplayObjectContainer into which the ContentDisplay Sprite should be added immediately. **/ + public function container(value:DisplayObjectContainer):ImageLoaderVars { + return _set("container", value); + } + + /** To control whether or not a policy file is checked (which is required if you're loading an image from another domain and you want to use it in BitmapData operations), define a LoaderContext object. By default, the policy file will be checked when running remotely, so make sure the appropriate crossdomain.xml file is in place. See Adobe's LoaderContext documentation for details and precautions. **/ + public function context(value:LoaderContext):ImageLoaderVars { + return _set("context", value); + } + + /** When a width and height are defined, setting crop to true will cause the image to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the image that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area. **/ + public function crop(value:Boolean):ImageLoaderVars { + return _set("crop", value); + } + + /** + * When a width and height is defined, the hAlign determines how the image is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered horizontally in the area
  • + *
  • "left" - The image will be aligned with the left side of the area
  • + *
  • "right" - The image will be aligned with the right side of the area
  • + *
+ **/ + public function hAlign(value:String):ImageLoaderVars { + return _set("hAlign", value); + } + + /** Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY). **/ + public function height(value:Number):ImageLoaderVars { + return _set("height", value); + } + + /** A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onSecurityError(value:Function):ImageLoaderVars { + return _set("onSecurityError", value); + } + + /** Sets the ContentDisplay's rotation property. **/ + public function rotation(value:Number):ImageLoaderVars { + return _set("rotation", value); + } + + /** Sets the ContentDisplay's rotationX property. **/ + public function rotationX(value:Number):ImageLoaderVars { + return _set("rotationX", value); + } + + /** Sets the ContentDisplay's rotationY property. **/ + public function rotationY(value:Number):ImageLoaderVars { + return _set("rotationY", value); + } + + /** Sets the ContentDisplay's rotationZ property. **/ + public function rotationZ(value:Number):ImageLoaderVars { + return _set("rotationZ", value); + } + + /** + * When a width and height are defined, the scaleMode controls how the loaded image will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
    + *
  • "stretch" (the default) - The image will fill the width/height exactly.
  • + *
  • "proportionalInside" - The image will be scaled proportionally to fit inside the area defined by the width/height
  • + *
  • "proportionalOutside" - The image will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
  • + *
  • "widthOnly" - Only the width of the image will be adjusted to fit.
  • + *
  • "heightOnly" - Only the height of the image will be adjusted to fit.
  • + *
  • "none" - No scaling of the image will occur.
  • + *
+ **/ + public function scaleMode(value:String):ImageLoaderVars { + return _set("scaleMode", value); + } + + /** Sets the ContentDisplay's scaleX property. **/ + public function scaleX(value:Number):ImageLoaderVars { + return _set("scaleX", value); + } + + /** Sets the ContentDisplay's scaleY property. **/ + public function scaleY(value:Number):ImageLoaderVars { + return _set("scaleY", value); + } + + /** + * When a width and height is defined, the vAlign determines how the image is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered vertically in the area
  • + *
  • "top" - The image will be aligned with the top of the area
  • + *
  • "bottom" - The image will be aligned with the bottom of the area
  • + *
+ **/ + public function vAlign(value:String):ImageLoaderVars { + return _set("vAlign", value); + } + + /** Sets the ContentDisplay's visible property. **/ + public function visible(value:Boolean):ImageLoaderVars { + return _set("visible", value); + } + + /** Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY). **/ + public function width(value:Number):ImageLoaderVars { + return _set("width", value); + } + + /** Sets the ContentDisplay's x property (for positioning on the stage). **/ + public function x(value:Number):ImageLoaderVars { + return _set("x", value); + } + + /** Sets the ContentDisplay's y property (for positioning on the stage). **/ + public function y(value:Number):ImageLoaderVars { + return _set("y", value); + } + + /** Sets the ContentDisplay's z property (for positioning on the stage). **/ + public function z(value:Number):ImageLoaderVars { + return _set("z", value); + } + + +//---- IMAGELOADER PROPERTIES ------------------------------------------------------------ + + /** When smoothing is true (the default), smoothing will be enabled for the image which typically leads to much better scaling results (otherwise the image can look crunchy/jagged). If your image is loaded from another domain where the appropriate crossdomain.xml file doesn't grant permission, Flash will not allow smoothing to be enabled (it's a security restriction). **/ + public function smoothing(value:Boolean):ImageLoaderVars { + return _set("smoothing", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the ImageLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/LoaderMaxVars.as b/src/com/greensock/loading/data/LoaderMaxVars.as new file mode 100644 index 0000000..660f904 --- /dev/null +++ b/src/com/greensock/loading/data/LoaderMaxVars.as @@ -0,0 +1,225 @@ +/** + * VERSION: 1.2 + * DATE: 2011-04-26 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; +/** + * Can be used instead of a generic Object to define the vars parameter of a LoaderMax's constructor. + * + *

There are 2 primary benefits of using a LoaderMaxVars instance to define your LoaderMax variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in LoaderMax
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the LoaderMaxVars class adds slightly more kb to your swf.

+ * + *

USAGE

+ *

Note that each method returns the LoaderMaxVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without LoaderMaxVars:

+ * new LoaderMax({name:"queue", maxConnections:1, onComplete:completeHandler, onProgress:progressHandler});

+ * + *

With LoaderMaxVars

+ * new LoaderMax(new LoaderMaxVars().name("queue").maxConnections(1).onComplete(completeHandler).onProgress(progressHandler));

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that LoaderMaxVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new LoaderMax(new LoaderMaxVars().name("queue").maxConnections(1).vars);
  • + *
  • Using LoaderMaxVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class LoaderMaxVars { + /** @private **/ + public static const version:Number = 1.1; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this LoaderMaxVars instance. + */ + public function LoaderMaxVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):LoaderMaxVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):LoaderMaxVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):LoaderMaxVars { + return _set("autoDispose", value); + } + + /** If true, the LoaderMax instance will automatically call load() whenever you insert()/append()/prepend() a new loader whose status is LoaderStatus.READY. This basically makes it easy to create a LoaderMax queue and dump stuff into it whenever you want something to load without having to check the LoaderMax's status and call load() manually if it's not already loading. **/ + public function autoLoad(value:Boolean):LoaderMaxVars { + return _set("autoLoad", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):LoaderMaxVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):LoaderMaxVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):LoaderMaxVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):LoaderMaxVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):LoaderMaxVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):LoaderMaxVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):LoaderMaxVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):LoaderMaxVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):LoaderMaxVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):LoaderMaxVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERMAX PROPERTIES ------------------------------------------------------------- + + /** By default, when the LoaderMax begins to load it quickly loops through its children and if it finds any that don't have an estimatedBytes defined, it will briefly open a URLStream in order to attempt to determine its bytesTotal, immediately closing the URLStream once the value has been determined. This causes a brief delay initially, but greatly improves the accuracy of the progress and bytesTotal values. Set auditSize to false to prevent the LoaderMax from auditing its childrens' size (it is true by default). For maximum performance, it is best to define an estimatedBytes value for as many loaders as possible to avoid the delay caused by audits. When the LoaderMax audits an XMLLoader, it cannot recognize loaders that will be created from the XML data nor can it recognize loaders inside subloaded swf files from a SWFLoader (it would take far too long to load sufficient data for that - audits should be as fast as possible). If you do not set an appropriate estimatedSize for XMLLoaders or SWFLoaders that contain LoaderMax loaders, you'll notice that the parent LoaderMax's progress and bytesTotal change when the nested loaders are recognized (this is normal). To control the default auditSize value, use the static LoaderMax.defaultAuditSize property. **/ + public function auditSize(value:Boolean):LoaderMaxVars { + return _set("auditSize", value); + } + + /** Maximum number of simultaneous connections that should be used while loading the LoaderMax queue. A higher number will generally result in faster overall load times for the group. The default is 2. This value is instance-based, not system-wide, so if you have two LoaderMax instances that both have a maxConnections value of 3 and they are both loading, there could be up to 6 connections at a time total. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define. **/ + public function maxConnections(value:uint):LoaderMaxVars { + return _set("maxConnections", value); + } + + /** If skipFailed is true (the default), any failed loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a failed loader and the LoaderMax's status will become LoaderStatus.FAILED. **/ + public function skipFailed(value:Boolean):LoaderMaxVars { + return _set("skipFailed", value); + } + + /** If skipPaused is true (the default), any paused loaders in the queue will be skipped. Otherwise, the LoaderMax will stop when it hits a paused loader and the LoaderMax's status will become LoaderStatus.FAILED. **/ + public function skipPaused(value:Boolean):LoaderMaxVars { + return _set("skipPaused", value); + } + + /** An array of loaders (ImageLoaders, SWFLoaders, XMLLoaders, MP3Loaders, other LoaderMax instances, etc.) that should be immediately inserted into the LoaderMax. **/ + public function loaders(value:Array):LoaderMaxVars { + return _set("loaders", value); + } + + /** A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time one of the loader's children (or any descendant) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildOpen(value:Function):LoaderMaxVars { + return _set("onChildOpen", value); + } + + /** A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time one of the loader's children (or any descendant) dispatches a PROGRESS event. To listen for changes in the LoaderMax's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the LoaderMax, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildProgress(value:Function):LoaderMaxVars { + return _set("onChildProgress", value); + } + + /** A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time one of the loader's children (or any descendant) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildComplete(value:Function):LoaderMaxVars { + return _set("onChildComplete", value); + } + + /** A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on one of the loader's children (or any descendant) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildCancel(value:Function):LoaderMaxVars { + return _set("onChildCancel", value); + } + + /** A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time one of the loader's children (or any descendant) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildFail(value:Function):LoaderMaxVars { + return _set("onChildFail", value); + } + + /** A handler function for LoaderEvent.SCRIPT_ACCESS_DENIED events which are dispatched when one of the LoaderMax's children (or any descendant) is loaded from another domain and no crossdomain.xml is in place to grant full script access for things like smoothing or BitmapData manipulation. Make sure your function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onScriptAccessDenied(value:Function):LoaderMaxVars { + return _set("onScriptAccessDenied", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the LoaderMaxVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/MP3LoaderVars.as b/src/com/greensock/loading/data/MP3LoaderVars.as new file mode 100644 index 0000000..5961bda --- /dev/null +++ b/src/com/greensock/loading/data/MP3LoaderVars.as @@ -0,0 +1,214 @@ +/** + * VERSION: 1.2 + * DATE: 2011-03-23 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; + import flash.media.SoundLoaderContext; +/** + * Can be used instead of a generic Object to define the vars parameter of a MP3Loader's constructor. + * + *

There are 2 primary benefits of using a MP3LoaderVars instance to define your MP3Loader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in MP3Loader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the MP3LoaderVars class adds slightly more kb to your swf.

+ * + *

USAGE

+ *

Note that each method returns the MP3LoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without MP3LoaderVars:

+ * new MP3Loader("audio.mp3", {name:"audio", estimatedBytes:11500, autoPlay:false, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With MP3LoaderVars

+ * new MP3Loader("audio.mp3", new MP3LoaderVars().name("audio").estimatedBytes(11500).autoPlay(false).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that MP3LoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new MP3Loader("audio.mp3", new MP3LoaderVars().name("mp3").estimatedBytes(11500).autoPlay(false).vars);
  • + *
  • Using MP3LoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + **/ + public class MP3LoaderVars { + /** @private **/ + public static const version:Number = 1.2; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this MP3LoaderVars instance. + */ + public function MP3LoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):MP3LoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):MP3LoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):MP3LoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):MP3LoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):MP3LoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):MP3LoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):MP3LoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):MP3LoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):MP3LoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):MP3LoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):MP3LoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):MP3LoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):MP3LoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):MP3LoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):MP3LoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):MP3LoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):MP3LoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- MP3LOADER PROPERTIES -------------------------------------------------------------- + + /** By default the MP3 will begin playing immediately when enough of the file has buffered, but to prevent it from autoPlaying, set autoPlay to false. **/ + public function autoPlay(value:Boolean):MP3LoaderVars { + return _set("autoPlay", value); + } + + /** Number of times that the mp3 should repeat. To repeat indefinitely, use -1. Default is 0. **/ + public function repeat(value:int):MP3LoaderVars { + return _set("repeat", value); + } + + /** A value between 0 and 1 indicating the volume at which the sound should play when the MP3Loader's controls are used to play the sound, like playSound() or when autoPlay is true (default volume is 1). **/ + public function volume(value:Number):MP3LoaderVars { + return _set("volume", value); + } + + /** To control things like the buffer time and whether or not a policy file is checked, define a SoundLoaderContext object. The default context is null. See Adobe's SoundLoaderContext documentation for details. **/ + public function context(value:SoundLoaderContext):MP3LoaderVars { + return _set("context", value); + } + + /** The minimum number of bytesLoaded to wait for before the LoaderEvent.INIT event is dispatched - the higher the number the more accurate the duration estimate will be when the INIT event is dispatched (the default value is 102400 which is 100k). The MP3's duration cannot be determined with 100% accuracy until it has completely loaded, but it is estimated with more and more accuracy as the file loads. **/ + public function initThreshold(value:uint):MP3LoaderVars { + return _set("initThreshold", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the MP3LoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/SWFLoaderVars.as b/src/com/greensock/loading/data/SWFLoaderVars.as new file mode 100644 index 0000000..b9ebdeb --- /dev/null +++ b/src/com/greensock/loading/data/SWFLoaderVars.as @@ -0,0 +1,381 @@ +/** + * VERSION: 1.23 + * DATE: 2011-07-30 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.system.LoaderContext; +/** + * Can be used instead of a generic object to define the vars parameter of an SWFLoader's constructor. + * + *

There are 2 primary benefits of using a SWFLoaderVars instance to define your SWFLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in SWFLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

USAGE

+ *

Note that each method returns the SWFLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without SWFLoaderVars:

+ * new SWFLoader("child.swf", {name:"swf", estimatedBytes:11500, container:this, width:400, height:300, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With SWFLoaderVars

+ * new SWFLoader("photo1.jpg", new SWFLoaderVars().name("swf").estimatedBytes(11500).container(this).width(400).height(300).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that SWFLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new SWFLoader("child.swf", new SWFLoaderVars().name("swf").estimatedBytes(11500).vars);
  • + *
  • Using SWFLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SWFLoaderVars { + /** @private **/ + public static const version:Number = 1.23; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * + * @param vars A generic Object containing properties that you'd like to add to this SWFLoaderVars instance. + */ + public function SWFLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):SWFLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):SWFLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):SWFLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):SWFLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):SWFLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):SWFLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):SWFLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):SWFLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):SWFLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):SWFLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):SWFLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):SWFLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):SWFLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various SWFLoaders for example). **/ + public function alternateURL(value:String):SWFLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):SWFLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):SWFLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):SWFLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- DISPLAYOBJECTLOADER PROPERTIES ------------------------------------------------------------ + + /** Sets the ContentDisplay's alpha property. **/ + public function alpha(value:Number):SWFLoaderVars { + return _set("alpha", value); + } + + /** Controls the alpha of the rectangle that is drawn when a width and height are defined. **/ + public function bgAlpha(value:Number):SWFLoaderVars { + return _set("bgAlpha", value); + } + + /** When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgColor if you prefer. **/ + public function bgColor(value:uint):SWFLoaderVars { + return _set("bgColor", value); + } + + /** Sets the ContentDisplay's blendMode property. **/ + public function blendMode(value:String):SWFLoaderVars { + return _set("blendMode", value); + } + + /** If true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center. **/ + public function centerRegistration(value:Boolean):SWFLoaderVars { + return _set("centerRegistration", value); + } + + /** A DisplayObjectContainer into which the ContentDisplay Sprite should be added immediately. **/ + public function container(value:DisplayObjectContainer):SWFLoaderVars { + return _set("container", value); + } + + /** To control whether or not a policy file is checked (which is required if you're loading an image from another domain and you want to use it in BitmapData operations), define a LoaderContext object. By default, the policy file will be checked when running remotely, so make sure the appropriate crossdomain.xml file is in place. See Adobe's LoaderContext documentation for details and precautions. **/ + public function context(value:LoaderContext):SWFLoaderVars { + return _set("context", value); + } + + /** When a width and height are defined, setting crop to true will cause the image to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the image that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area. **/ + public function crop(value:Boolean):SWFLoaderVars { + return _set("crop", value); + } + + /** + * When a width and height is defined, the hAlign determines how the image is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered horizontally in the area
  • + *
  • "left" - The image will be aligned with the left side of the area
  • + *
  • "right" - The image will be aligned with the right side of the area
  • + *
+ **/ + public function hAlign(value:String):SWFLoaderVars { + return _set("hAlign", value); + } + + /** Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY). **/ + public function height(value:Number):SWFLoaderVars { + return _set("height", value); + } + + /** A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onSecurityError(value:Function):SWFLoaderVars { + return _set("onSecurityError", value); + } + + /** Sets the ContentDisplay's rotation property. **/ + public function rotation(value:Number):SWFLoaderVars { + return _set("rotation", value); + } + + /** Sets the ContentDisplay's rotationX property. **/ + public function rotationX(value:Number):SWFLoaderVars { + return _set("rotationX", value); + } + + /** Sets the ContentDisplay's rotationY property. **/ + public function rotationY(value:Number):SWFLoaderVars { + return _set("rotationY", value); + } + + /** Sets the ContentDisplay's rotationZ property. **/ + public function rotationZ(value:Number):SWFLoaderVars { + return _set("rotationZ", value); + } + + /** + * When a width and height are defined, the scaleMode controls how the loaded image will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
    + *
  • "stretch" (the default) - The image will fill the width/height exactly.
  • + *
  • "proportionalInside" - The image will be scaled proportionally to fit inside the area defined by the width/height
  • + *
  • "proportionalOutside" - The image will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
  • + *
  • "widthOnly" - Only the width of the image will be adjusted to fit.
  • + *
  • "heightOnly" - Only the height of the image will be adjusted to fit.
  • + *
  • "none" - No scaling of the image will occur.
  • + *
+ **/ + public function scaleMode(value:String):SWFLoaderVars { + return _set("scaleMode", value); + } + + /** Sets the ContentDisplay's scaleX property. **/ + public function scaleX(value:Number):SWFLoaderVars { + return _set("scaleX", value); + } + + /** Sets the ContentDisplay's scaleY property. **/ + public function scaleY(value:Number):SWFLoaderVars { + return _set("scaleY", value); + } + + /** + * When a width and height is defined, the vAlign determines how the image is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered vertically in the area
  • + *
  • "top" - The image will be aligned with the top of the area
  • + *
  • "bottom" - The image will be aligned with the bottom of the area
  • + *
+ **/ + public function vAlign(value:String):SWFLoaderVars { + return _set("vAlign", value); + } + + /** Sets the ContentDisplay's visible property. **/ + public function visible(value:Boolean):SWFLoaderVars { + return _set("visible", value); + } + + /** Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY). **/ + public function width(value:Number):SWFLoaderVars { + return _set("width", value); + } + + /** Sets the ContentDisplay's x property (for positioning on the stage). **/ + public function x(value:Number):SWFLoaderVars { + return _set("x", value); + } + + /** Sets the ContentDisplay's y property (for positioning on the stage). **/ + public function y(value:Number):SWFLoaderVars { + return _set("y", value); + } + + /** Sets the ContentDisplay's z property (for positioning on the stage). **/ + public function z(value:Number):SWFLoaderVars { + return _set("z", value); + } + + +//---- SWFLOADER PROPERTIES ------------------------------------------------------------ + + /** If autoPlay is true (the default), the swf will begin playing immediately when the INIT event fires. To prevent this behavior, set autoPlay to false which will also mute the swf until the SWFLoader completes. This only calls stop() on the main timeline but it does not prevent scripted animations. **/ + public function autoPlay(value:Boolean):SWFLoaderVars { + return _set("autoPlay", value); + } + /** By default, a SWFLoader instance will automatically look for LoaderMax loaders in the swf when it initializes. Every loader found with a requireWithRoot parameter set to that swf's root will be integrated into the SWFLoader's overall progress. The SWFLoader's COMPLETE event won't fire until all such loaders are also complete. If you prefer NOT to integrate the subloading loaders into the SWFLoader's overall progress, set integrateProgress to false. **/ + public function integrateProgress(value:Boolean):SWFLoaderVars { + return _set("integrateProgress", value); + } + /** A handler function for LoaderEvent.INIT events which are called when the swf has streamed enough of its content to render the first frame and determine if there are any required LoaderMax-related loaders recognized. It also adds the swf to the ContentDisplay Sprite at this point. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onInit(value:Function):SWFLoaderVars { + return _set("onInit", value); + } + /** A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onChildOpen(value:Function):SWFLoaderVars { + return _set("onChildOpen", value); + } + /** A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) dispatches a PROGRESS event. To listen for changes in the SWFLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the SWFLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onChildProgress(value:Function):SWFLoaderVars { + return _set("onChildProgress", value); + } + /** A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildComplete(value:Function):SWFLoaderVars { + return _set("onChildComplete", value); + } + /** A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildCancel(value:Function):SWFLoaderVars { + return _set("onChildCancel", value); + } + /** A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders (active ones that the SWFLoader found inside the subloading swf that had their requireWithRoot set to its root) fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onChildFail(value:Function):SWFLoaderVars { + return _set("onChildFail", value); + } + /** A handler function for LoaderEvent.UNCAUGHT_ERROR events which are dispatched when the subloaded swf encounters an UncaughtErrorEvent meaning an Error was thrown outside of a try...catch statement. This can be useful when subloading swfs from a 3rd party that may contain errors. However, UNCAUGHT_ERROR events will only be dispatched if the parent swf is published for Flash Player 10.1 or later! See SWFLoader's suppressUncaughtErrors special property if you'd like to have it automatically suppress these errors. The original UncaughtErrorEvent is stored in the LoaderEvent's data property. So, for example, if you'd like to call preventDefault() on that UncaughtErrorEvent, you'd do myLoaderEvent.data.preventDefault(). **/ + public function onUncaughtError(value:Function):SWFLoaderVars { + return _set("onUncaughtError", value); + } + + /** If true, the SWFLoader will suppress the REMOVED_FROM_STAGE and ADDED_TO_STAGE events that are normally dispatched when the subloaded swf is reparented into the ContentDisplay (this always happens in Flash when any DisplayObject that's in the display list gets reparented - SWFLoader just circumvents it by default initially to avoid common problems that could arise if the child swf is coded a certain way). For example, if your subloaded swf has this code: addEventListener(Event.REMOVED_FROM_STAGE, disposeEverything) and you set suppressInitReparentEvents to false, disposeEverything() would get called as soon as the swf inits (assuming the ContentDisplay is in the display list). **/ + public function suppressInitReparentEvents(value:Boolean):SWFLoaderVars { + return _set("suppressInitReparentEvents", value); + } + /** To automatically suppress uncaught errors in the subloaded swf (errors that are thrown outside of a try...catch statement), set suppressUncaughtErrors to true, but please note that this will ONLY work if the parent swf is published to Flash Player 10.1 or later. Suppressing the UncaughtErrorEvent simply means calling its preventDefault() and stopImmediatePropagation() methods as well as preventing it from bubbling up to its parent LoaderMax/SWFLoader anscestors. If you'd rather listen for these events so that you can handle them yourself, listen for the LoaderEvent.UNCAUGHT_ERROR event. The original UncaughtErrorEvent instance will be stored in the LoaderEvent's data property. **/ + public function suppressUncaughtErrors(value:Boolean):SWFLoaderVars { + return _set("suppressUncaughtErrors", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the SWFLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/VideoLoaderVars.as b/src/com/greensock/loading/data/VideoLoaderVars.as new file mode 100644 index 0000000..dba2ff8 --- /dev/null +++ b/src/com/greensock/loading/data/VideoLoaderVars.as @@ -0,0 +1,391 @@ +/** + * VERSION: 1.24 + * DATE: 2011-11-03 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; +/** + * Can be used instead of a generic Object to define the vars parameter of a VideoLoader's constructor. + * + *

There are 2 primary benefits of using a VideoLoaderVars instance to define your VideoLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in VideoLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the VideoLoaderVars class adds slightly more kb to your swf.

+ * + *

USAGE

+ *

Note that each method returns the VideoLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without VideoLoaderVars:

+ * new VideoLoader("video.flv", {name:"video", estimatedBytes:111500, container:this, width:200, height:100, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With VideoLoaderVars

+ * new VideoLoader("video.flv", new VideoLoaderVars().name("video").estimatedBytes(111500).container(this).width(200).height(100).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that VideoLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new VideoLoader("video.flv", new VideoLoaderVars().name("video").estimatedBytes(111500).vars);
  • + *
  • Using VideoLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class VideoLoaderVars { + /** @private **/ + public static const version:Number = 1.23; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this VideoLoaderVars instance. + */ + public function VideoLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):VideoLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + * For example, to set an "index" property to 5, do: + * + * prop("index", 5); + * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):VideoLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):VideoLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):VideoLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):VideoLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):VideoLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):VideoLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):VideoLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):VideoLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.INIT events which will be called when the video's metaData has been received and the video is placed into the ContentDisplay. Make sure your onInit function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onInit(value:Function):VideoLoaderVars { + return _set("onInit", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):VideoLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):VideoLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):VideoLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):VideoLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):VideoLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):VideoLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):VideoLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):VideoLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- DISPLAYOBJECTLOADER PROPERTIES ------------------------------------------------------------ + + /** Sets the ContentDisplay's alpha property. **/ + public function alpha(value:Number):VideoLoaderVars { + return _set("alpha", value); + } + + /** Controls the alpha of the rectangle that is drawn when a width and height are defined. **/ + public function bgAlpha(value:Number):VideoLoaderVars { + return _set("bgAlpha", value); + } + + /** When a width and height are defined, a rectangle will be drawn inside the ContentDisplay Sprite immediately in order to ease the development process. It is transparent by default, but you may define a bgColor if you prefer. **/ + public function bgColor(value:uint):VideoLoaderVars { + return _set("bgColor", value); + } + + /** Sets the ContentDisplay's blendMode property. **/ + public function blendMode(value:String):VideoLoaderVars { + return _set("blendMode", value); + } + + /** If true, the registration point will be placed in the center of the ContentDisplay which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center. **/ + public function centerRegistration(value:Boolean):VideoLoaderVars { + return _set("centerRegistration", value); + } + + /** A DisplayObjectContainer into which the ContentDisplay Sprite should be added immediately. **/ + public function container(value:DisplayObjectContainer):VideoLoaderVars { + return _set("container", value); + } + + /** When a width and height are defined, setting crop to true will cause the image to be cropped within that area (by applying a scrollRect for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" or "none" so that any parts of the image that exceed the dimensions defined by width and height are visually chopped off. Use the hAlign and vAlign special properties to control the vertical and horizontal alignment within the cropped area. **/ + public function crop(value:Boolean):VideoLoaderVars { + return _set("crop", value); + } + + /** + * When a width and height is defined, the hAlign determines how the image is horizontally aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered horizontally in the area
  • + *
  • "left" - The image will be aligned with the left side of the area
  • + *
  • "right" - The image will be aligned with the right side of the area
  • + *
+ **/ + public function hAlign(value:String):VideoLoaderVars { + return _set("hAlign", value); + } + + /** Sets the ContentDisplay's height property (applied before rotation, scaleX, and scaleY). **/ + public function height(value:Number):VideoLoaderVars { + return _set("height", value); + } + + /** A handler function for LoaderEvent.SECURITY_ERROR events which onError handles as well, so you can use that as more of a catch-all whereas onSecurityError is specifically for SECURITY_ERROR events. Make sure your onSecurityError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onSecurityError(value:Function):VideoLoaderVars { + return _set("onSecurityError", value); + } + + /** Sets the ContentDisplay's rotation property. **/ + public function rotation(value:Number):VideoLoaderVars { + return _set("rotation", value); + } + + /** Sets the ContentDisplay's rotationX property. **/ + public function rotationX(value:Number):VideoLoaderVars { + return _set("rotationX", value); + } + + /** Sets the ContentDisplay's rotationY property. **/ + public function rotationY(value:Number):VideoLoaderVars { + return _set("rotationY", value); + } + + /** Sets the ContentDisplay's rotationZ property. **/ + public function rotationZ(value:Number):VideoLoaderVars { + return _set("rotationZ", value); + } + + /** + * When a width and height are defined, the scaleMode controls how the loaded image will be scaled to fit the area. The following values are recognized (you may use the com.greensock.layout.ScaleMode constants if you prefer): + *
    + *
  • "stretch" (the default) - The image will fill the width/height exactly.
  • + *
  • "proportionalInside" - The image will be scaled proportionally to fit inside the area defined by the width/height
  • + *
  • "proportionalOutside" - The image will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
  • + *
  • "widthOnly" - Only the width of the image will be adjusted to fit.
  • + *
  • "heightOnly" - Only the height of the image will be adjusted to fit.
  • + *
  • "none" - No scaling of the image will occur.
  • + *
+ **/ + public function scaleMode(value:String):VideoLoaderVars { + return _set("scaleMode", value); + } + + /** Sets the ContentDisplay's scaleX property. **/ + public function scaleX(value:Number):VideoLoaderVars { + return _set("scaleX", value); + } + + /** Sets the ContentDisplay's scaleY property. **/ + public function scaleY(value:Number):VideoLoaderVars { + return _set("scaleY", value); + } + + /** + * When a width and height is defined, the vAlign determines how the image is vertically aligned within that area. The following values are recognized (you may use the com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The image will be centered vertically in the area
  • + *
  • "top" - The image will be aligned with the top of the area
  • + *
  • "bottom" - The image will be aligned with the bottom of the area
  • + *
+ **/ + public function vAlign(value:String):VideoLoaderVars { + return _set("vAlign", value); + } + + /** Sets the ContentDisplay's visible property. **/ + public function visible(value:Boolean):VideoLoaderVars { + return _set("visible", value); + } + + /** Sets the ContentDisplay's width property (applied before rotation, scaleX, and scaleY). **/ + public function width(value:Number):VideoLoaderVars { + return _set("width", value); + } + + /** Sets the ContentDisplay's x property (for positioning on the stage). **/ + public function x(value:Number):VideoLoaderVars { + return _set("x", value); + } + + /** Sets the ContentDisplay's y property (for positioning on the stage). **/ + public function y(value:Number):VideoLoaderVars { + return _set("y", value); + } + + /** Sets the ContentDisplay's z property (for positioning on the stage). **/ + public function z(value:Number):VideoLoaderVars { + return _set("z", value); + } + + +//---- VIDEOLOADER PROPERTIES ------------------------------------------------------------ + + + /** If the buffer becomes empty during playback and autoAdjustBuffer is true (the default), it will automatically attempt to adjust the NetStream's bufferTime based on the rate at which the video has been loading, estimating what it needs to be in order to play the rest of the video without emptying the buffer again. This can prevent the annoying problem of video playback start/stopping/starting/stopping on a system tht doesn't have enough bandwidth to adequately buffer the video. You may also set the bufferTime in the constructor's vars parameter to set the initial value. **/ + public function autoAdjustBuffer(value:Boolean):VideoLoaderVars { + return _set("autoAdjustBuffer", value); + } + + /** If true, the NetStream will only be attached to the Video object (the rawContent) when it is in the display list (on the stage). This conserves memory but it can cause a very brief rendering delay when the content is initially added to the stage (often imperceptible). Also, if you add it to the stage when the videoTime is after its last encoded keyframe, it will render at that last keyframe. **/ + public function autoDetachNetStream(value:Boolean):VideoLoaderVars { + return _set("autoDetachNetStream", value); + } + + /** By default, the video will begin playing as soon as it has been adequately buffered, but to prevent it from playing initially, set autoPlay to false. **/ + public function autoPlay(value:Boolean):VideoLoaderVars { + return _set("autoPlay", value); + } + + /** When true, the loader will report its progress only in terms of the video's buffer which can be very convenient if, for example, you want to display loading progress for the video's buffer or tuck it into a LoaderMax with other loaders and allow the LoaderMax to dispatch its COMPLETE event when the buffer is full instead of waiting for the whole file to download. When bufferMode is true, the VideoLoader will dispatch its COMPLETE event when the buffer is full as opposed to waiting for the entire video to load. You can toggle the bufferMode anytime. Please read the full bufferMode property ASDoc description below for details about how it affects things like bytesTotal.**/ + public function bufferMode(value:Boolean):VideoLoaderVars { + return _set("bufferMode", value); + } + + /** The amount of time (in seconds) that should be buffered before the video can begin playing (set autoPlay to false to pause the video initially).**/ + public function bufferTime(value:Number):VideoLoaderVars { + return _set("bufferTime", value); + } + + /** If true, the VideoLoader will check for a crossdomain.xml file on the remote host (only useful when loading videos from other domains - see Adobe's docs for details about NetStream's checkPolicyFile property). **/ + public function checkPolicyFile(value:Boolean):VideoLoaderVars { + return _set("checkPolicyFile", value); + } + + /** Indicates the type of filter applied to decoded video as part of post-processing. The default value is 0, which lets the video compressor apply a deblocking filter as needed. See Adobe's flash.media.Video class docs for details. **/ + public function deblocking(value:int):VideoLoaderVars { + return _set("deblocking", value); + } + + /** Estimated duration of the video in seconds. VideoLoader will only use this value until it receives the necessary metaData from the video in order to accurately determine the video's duration. You do not need to specify an estimatedDuration, but doing so can help make the playProgress and some other values more accurate (until the metaData has loaded). It can also make the progress/bytesLoaded/bytesTotal more accurate when a estimatedDuration is defined, particularly in bufferMode.**/ + public function estimatedDuration(value:Number):VideoLoaderVars { + return _set("estimatedDuration", value); + } + + /** Number of times that the video should repeat. To repeat indefinitely, use -1. Default is 0. **/ + public function repeat(value:int):VideoLoaderVars { + return _set("repeat", value); + } + + /** When smoothing is true (the default), smoothing will be enabled for the video which typically leads to better scaling results. **/ + public function smoothing(value:Boolean):VideoLoaderVars { + return _set("smoothing", value); + } + + /** A value between 0 and 1 indicating the volume at which the video should play (default is 1).**/ + public function volume(value:Number):VideoLoaderVars { + return _set("volume", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the VideoLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/data/XMLLoaderVars.as b/src/com/greensock/loading/data/XMLLoaderVars.as new file mode 100644 index 0000000..48ae2da --- /dev/null +++ b/src/com/greensock/loading/data/XMLLoaderVars.as @@ -0,0 +1,247 @@ +/** + * VERSION: 1.22 + * DATE: 2011-04-20 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.data { + import flash.display.DisplayObject; +/** + * Can be used instead of a generic Object to define the vars parameter of a XMLLoader's constructor. + * + *

There are 2 primary benefits of using a XMLLoaderVars instance to define your XMLLoader variables:

+ *
    + *
  1. In most code editors, code hinting will be activated which helps remind you which special properties are available in XMLLoader
  2. + *
  3. It enables strict data typing for improved debugging (ensuring, for example, that you don't define a Boolean value for onComplete where a Function is expected).
  4. + *
+ * + *

The down side, of course, is that the code is more verbose and the XMLLoaderVars class adds slightly more kb to your swf.

+ * + *

USAGE

+ *

Note that each method returns the XMLLoaderVars instance, so you can reduce the lines of code by method chaining (see example below).

+ * + *

Without XMLLoaderVars:

+ * new XMLLoader("data.xml", {name:"css", estimatedBytes:1500, onComplete:completeHandler, onProgress:progressHandler})

+ * + *

With XMLLoaderVars

+ * new XMLLoader("data.xml", new XMLLoaderVars().name("data").estimatedBytes(1500).onComplete(completeHandler).onProgress(progressHandler))

+ * + *

NOTES:

+ *
    + *
  • To get the generic vars object that XMLLoaderVars builds internally, simply access its "vars" property. + * In fact, if you want maximum backwards compatibility, you can tack ".vars" onto the end of your chain like this: + * new XMLLoader("data.xml", new XMLLoaderVars().name("data").estimatedBytes(1500).onComplete(completeHandler).vars)
  • + *
  • Using XMLLoaderVars is completely optional. If you prefer the shorter synatax with the generic Object, feel + * free to use it. The purpose of this class is simply to enable code hinting and to allow for strict data typing.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class XMLLoaderVars { + /** @private **/ + public static const version:Number = 1.22; + + /** @private **/ + protected var _vars:Object; + + /** + * Constructor + * @param vars A generic Object containing properties that you'd like to add to this XMLLoaderVars instance. + */ + public function XMLLoaderVars(vars:Object=null) { + _vars = {}; + if (vars != null) { + for (var p:String in vars) { + _vars[p] = vars[p]; + } + } + } + + /** @private **/ + protected function _set(property:String, value:*):XMLLoaderVars { + if (value == null) { + delete _vars[property]; //in case it was previously set + } else { + _vars[property] = value; + } + return this; + } + + /** + * Adds a dynamic property to the vars object containing any value you want. This can be useful + * in situations where you need to associate certain data with a particular loader. Just make sure + * that the property name is a valid variable name (starts with a letter or underscore, no special characters, etc.) + * and that it doesn't use a reserved property name like "name" or "onComplete", etc. + * + *

For example, to set an "index" property to 5, do:

+ * + *

prop("index", 5);

+ * + * @param property Property name + * @param value Value + */ + public function prop(property:String, value:*):XMLLoaderVars { + return _set(property, value); + } + + +//---- LOADERCORE PROPERTIES ----------------------------------------------------------------- + + /** When autoDispose is true, the loader will be disposed immediately after it completes (it calls the dispose() method internally after dispatching its COMPLETE event). This will remove any listeners that were defined in the vars object (like onComplete, onProgress, onError, onInit). Once a loader is disposed, it can no longer be found with LoaderMax.getLoader() or LoaderMax.getContent() - it is essentially destroyed but its content is not unloaded (you must call unload() or dispose(true) to unload its content). The default autoDispose value is false.**/ + public function autoDispose(value:Boolean):XMLLoaderVars { + return _set("autoDispose", value); + } + + /** A name that is used to identify the loader instance. This name can be fed to the LoaderMax.getLoader() or LoaderMax.getContent() methods or traced at any time. Each loader's name should be unique. If you don't define one, a unique name will be created automatically, like "loader21". **/ + public function name(value:String):XMLLoaderVars { + return _set("name", value); + } + + /** A handler function for LoaderEvent.CANCEL events which are dispatched when loading is aborted due to either a failure or because another loader was prioritized or cancel() was manually called. Make sure your onCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onCancel(value:Function):XMLLoaderVars { + return _set("onCancel", value); + } + + /** A handler function for LoaderEvent.COMPLETE events which are dispatched when the loader has finished loading successfully. Make sure your onComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onComplete(value:Function):XMLLoaderVars { + return _set("onComplete", value); + } + + /** A handler function for LoaderEvent.ERROR events which are dispatched whenever the loader experiences an error (typically an IO_ERROR or SECURITY_ERROR). An error doesn't necessarily mean the loader failed, however - to listen for when a loader fails, use the onFail special property. Make sure your onError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onError(value:Function):XMLLoaderVars { + return _set("onError", value); + } + + /** A handler function for LoaderEvent.FAIL events which are dispatched whenever the loader fails and its status changes to LoaderStatus.FAILED. Make sure your onFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onFail(value:Function):XMLLoaderVars { + return _set("onFail", value); + } + + /** A handler function for LoaderEvent.HTTP_STATUS events. Make sure your onHTTPStatus function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can determine the httpStatus code using the LoaderEvent's target.httpStatus (LoaderItems keep track of their httpStatus when possible, although certain environments prevent Flash from getting httpStatus information).**/ + public function onHTTPStatus(value:Function):XMLLoaderVars { + return _set("onHTTPStatus", value); + } + + /** A handler function for LoaderEvent.IO_ERROR events which will also call the onError handler, so you can use that as more of a catch-all whereas onIOError is specifically for LoaderEvent.IO_ERROR events. Make sure your onIOError function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onIOError(value:Function):XMLLoaderVars { + return _set("onIOError", value); + } + + /** A handler function for LoaderEvent.OPEN events which are dispatched when the loader begins loading. Make sure your onOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent).**/ + public function onOpen(value:Function):XMLLoaderVars { + return _set("onOpen", value); + } + + /** A handler function for LoaderEvent.PROGRESS events which are dispatched whenever the bytesLoaded changes. Make sure your onProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). You can use the LoaderEvent's target.progress to get the loader's progress value or use its target.bytesLoaded and target.bytesTotal.**/ + public function onProgress(value:Function):XMLLoaderVars { + return _set("onProgress", value); + } + + /** LoaderMax supports subloading, where an object can be factored into a parent's loading progress. If you want LoaderMax to require this loader as part of its parent SWFLoader's progress, you must set the requireWithRoot property to your swf's root. For example, vars.requireWithRoot = this.root;. **/ + public function requireWithRoot(value:DisplayObject):XMLLoaderVars { + return _set("requireWithRoot", value); + } + + +//---- LOADERITEM PROPERTIES ------------------------------------------------------------- + + /** If you define an alternateURL, the loader will initially try to load from its original url and if it fails, it will automatically (and permanently) change the loader's url to the alternateURL and try again. Think of it as a fallback or backup url. It is perfectly acceptable to use the same alternateURL for multiple loaders (maybe a default image for various ImageLoaders for example). **/ + public function alternateURL(value:String):XMLLoaderVars { + return _set("alternateURL", value); + } + + /** Initially, the loader's bytesTotal is set to the estimatedBytes value (or LoaderMax.defaultEstimatedBytes if one isn't defined). Then, when the loader begins loading and it can accurately determine the bytesTotal, it will do so. Setting estimatedBytes is optional, but the more accurate the value, the more accurate your loaders' overall progress will be initially. If the loader is inserted into a LoaderMax instance (for queue management), its auditSize feature can attempt to automatically determine the bytesTotal at runtime (there is a slight performance penalty for this, however - see LoaderMax's documentation for details). **/ + public function estimatedBytes(value:uint):XMLLoaderVars { + return _set("estimatedBytes", value); + } + + /** If true, a "gsCacheBusterID" parameter will be appended to the url with a random set of numbers to prevent caching (don't worry, this info is ignored when you LoaderMax.getLoader() or LoaderMax.getContent() by url or when you're running locally). **/ + public function noCache(value:Boolean):XMLLoaderVars { + return _set("noCache", value); + } + + /** Normally, the URL will be parsed and any variables in the query string (like "?name=test&state=il&gender=m") will be placed into a URLVariables object which is added to the URLRequest. This avoids a few bugs in Flash, but if you need to keep the entire URL intact (no parsing into URLVariables), set allowMalformedURL:true. For example, if your URL has duplicate variables in the query string like http://www.greensock.com/?c=S&c=SE&c=SW, it is technically considered a malformed URL and a URLVariables object can't properly contain all the duplicates, so in this case you'd want to set allowMalformedURL to true. **/ + public function allowMalformedURL(value:Boolean):XMLLoaderVars { + return _set("allowMalformedURL", value); + } + + +//---- XMLLOADER PROPERTIES -------------------------------------------------------------- + + /** By default, the XMLLoader will automatically look for LoaderMax-related nodes like <LoaderMax>, <ImageLoader>, <SWFLoader>, <XMLLoader>, <MP3Loader>, <DataLoader>, and <XMLLoader> inside the XML when it inits. If it finds any that have a load="true" attribute, it will begin loading them and integrate their progress into the XMLLoader's overall progress. Its COMPLETE event won't fire until all of these loaders have completed as well. If you prefer NOT to integrate the dynamically-created loader instances into the XMLLoader's overall progress, set integrateProgress to false. **/ + public function integrateProgress(value:Boolean):XMLLoaderVars { + return _set("integrateProgress", value); + } + + /** Maximum number of simultaneous connections that should be used while loading child loaders that were parsed from the XML and had their "load" attribute set to "true" (like <ImageLoader url="1.jpg" load="true" />). A higher number will generally result in faster overall load times for the group. The default is 2. Sometimes there are limits imposed by the Flash Player itself or the browser or the user's system, but LoaderMax will do its best to honor the maxConnections you define. **/ + public function maxConnections(value:uint):XMLLoaderVars { + return _set("maxConnections", value); + } + + /** A handler function for LoaderEvent.CHILD_OPEN events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML begins loading. Make sure your onChildOpen function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildOpen(value:Function):XMLLoaderVars { + return _set("onChildOpen", value); + } + + /** A handler function for LoaderEvent.CHILD_PROGRESS events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML dispatches a PROGRESS event. To listen for changes in the XMLLoader's overall progress, use the onProgress special property instead. You can use the LoaderEvent's target.progress to get the child loader's progress value or use its target.bytesLoaded and target.bytesTotal. The LoaderEvent's currentTarget refers to the XMLLoader, so you can check its overall progress with the LoaderEvent's currentTarget.progress. Make sure your onChildProgress function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildProgress(value:Function):XMLLoaderVars { + return _set("onChildProgress", value); + } + + /** A handler function for LoaderEvent.CHILD_COMPLETE events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML finishes loading successfully. Make sure your onChildComplete function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildComplete(value:Function):XMLLoaderVars { + return _set("onChildComplete", value); + } + + /** A handler function for LoaderEvent.CHILD_CANCEL events which are dispatched each time loading is aborted on any nested LoaderMax-related loaders that were defined in the XML due to either an error or because another loader was prioritized in the queue or because cancel() was manually called on the child loader. Make sure your onChildCancel function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildCancel(value:Function):XMLLoaderVars { + return _set("onChildCancel", value); + } + + /** A handler function for LoaderEvent.CHILD_FAIL events which are dispatched each time any nested LoaderMax-related loaders that were defined in the XML fails (and its status chances to LoaderStatus.FAILED). Make sure your onChildFail function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent). **/ + public function onChildFail(value:Function):XMLLoaderVars { + return _set("onChildFail", value); + } + + /** A handler function for LoaderEvent.INIT events which are dispatched when the XML finishes loading and its contents are parsed (creating any dynamic XML-driven loader instances necessary). If any dynamic loaders are created and have a load="true" attribute, they will begin loading at this point and the XMLLoader's COMPLETE will not be dispatched until the loaders have completed as well. **/ + public function onInit(value:Function):XMLLoaderVars { + return _set("onInit", value); + } + + /** A handler function for XMLLoader.RAW_LOAD events which are dispatched when the loader finishes loading the XML but has NOT parsed the XML yet. This can be useful in rare situations when you want to alter the XML before it is parsed by XMLLoader (for identifying LoaderMax-related nodes like <ImageLoader>, etc.). Make sure your onRawLoad function accepts a single parameter of type LoaderEvent (com.greensock.events.LoaderEvent) **/ + public function onRawLoad(value:Function):XMLLoaderVars { + return _set("onRawLoad", value); + } + + /** A String that should be prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.) as soon as the XML has been parsed. For example, if your XML has the following node: <ImageLoader url="1.jpg" /> and prependURLs is set to "../images/", then the ImageLoader's url will end up being "../images/1.jpg". prependURLs affects ALL parsed loaders in the XML. However, if you have an <XMLLoader> node inside your XML that also loads another XML doc and you'd like to recursively prepend all of the URLs in this loader's XML as well as the subloading one and all of its children, use recursivePrependURLs instead of prependURLs. **/ + public function prependURLs(value:String):XMLLoaderVars { + return _set("prependURLs", value); + } + + /** A String that should be recursively prepended to all parsed LoaderMax-related loader URLs (from nodes like <ImageLoader>, <XMLLoader>, etc.). The functionality is identical to prependURLs except that it is recursive, affecting all parsed loaders in subloaded XMLLoaders (other XML files that this one loads too). For example, if your XML has the following node: <XMLLoader url="doc2.xml" /> and recursivePrependURLs is set to "../xml/", then the nested XMLLoader's URL will end up being "../xml/doc2.xml". Since it is recursive, parsed loaders inside doc2.xml and any other XML files that it loads will all have their URLs prepended. So if you load doc1.xml which loads doc2.xml which loads doc3.xml (due to <XMLLoader> nodes discovered in each XML file), recursivePrependURLs will affect all of the parsed LoaderMax-related URLs in all 3 documents. If you'd prefer to only have the URLs affected that are in the XML file that this XMLLoader is loading, use prependURLs instead of recursivePrependURLs. **/ + public function recursivePrependURLs(value:String):XMLLoaderVars { + return _set("recursivePrependURLs", value); + } + + /** By default, XMLLoader will parse any LoaderMax-related loaders in the XML and load any that have their "load" attribute set to "true" and then if any fail to load, they will simply be skipped. But if you prefer to have the XMLLoader fail immediately if one of the parsed loaders fails to load, set skipFailed to false (it is true by default). **/ + public function skipFailed(value:Boolean):XMLLoaderVars { + return _set("skipFailed", value); + } + + +//---- GETTERS / SETTERS ----------------------------------------------------------------- + + /** The generic Object populated by all of the method calls in the XMLLoaderVars instance. This is the raw data that gets passed to the loader. **/ + public function get vars():Object { + return _vars; + } + + /** @private **/ + public function get isGSVars():Boolean { + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/display/ContentDisplay.as b/src/com/greensock/loading/display/ContentDisplay.as new file mode 100644 index 0000000..985d22f --- /dev/null +++ b/src/com/greensock/loading/display/ContentDisplay.as @@ -0,0 +1,451 @@ +/** + * VERSION: 1.896 + * DATE: 2012-01-06 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.display { + import com.greensock.loading.core.LoaderItem; + + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.display.Loader; + import flash.display.LoaderInfo; + import flash.display.Sprite; + import flash.geom.Matrix; + import flash.geom.Rectangle; + import flash.media.Video; + +/** + * A container for visual content that is loaded by any of the following: ImageLoaders, SWFLoaders, + * or VideoLoaders. It is essentially a Sprite that has a loader property for easily referencing + * the original loader, as well as several other useful properties for controling the placement of + * rawContent and the way it is scaled to fit (if at all). You can add a ContentDisplay + * to the display list or populate an array with as many as you want and then if you ever need to unload() + * the content or reload it or figure out its url, etc., you can reference your ContentDisplay's loader + * property like myContent.loader.url or (myContent.loader as SWFLoader).getClass("com.greensock.TweenLite"); + * + * + *

Flex users can utilize the FlexContentDisplay class instead which extends UIComponent (a Flex requirement). + * All you need to do is set the LoaderMax.contentDisplayClass property to FlexContentDisplay once like:

+ * + import com.greensock.loading.~~; + import com.greensock.loading.display.~~; + +LoaderMax.contentDisplayClass = FlexContentDisplay; + + * + *

After that, all ImageLoaders, SWFLoaders, and VideoLoaders will return FlexContentDisplay objects + * as their content instead of regular ContentDisplay objects.

+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ContentDisplay extends Sprite { + /** @private **/ + protected static var _transformProps:Object = {x:1, y:1, z:1, rotationX:1, rotationY:1, rotationZ:1, scaleX:1, scaleY:1, rotation:1, alpha:1, visible:true, blendMode:"normal", centerRegistration:false, crop:false, scaleMode:"stretch", hAlign:"center", vAlign:"center"}; + /** @private **/ + protected var _loader:LoaderItem; + /** @private **/ + protected var _rawContent:DisplayObject; + /** @private **/ + protected var _centerRegistration:Boolean; + /** @private **/ + protected var _crop:Boolean; + /** @private **/ + protected var _scaleMode:String = "stretch"; + /** @private **/ + protected var _hAlign:String = "center"; + /** @private **/ + protected var _vAlign:String = "center"; + /** @private **/ + protected var _bgColor:uint; + /** @private **/ + protected var _bgAlpha:Number = 0; + /** @private **/ + protected var _fitWidth:Number; + /** @private **/ + protected var _fitHeight:Number; + /** @private only used when crop is true - works around bugs in Flash with the way it reports getBounds() on objects with a scrollRect. **/ + protected var _cropContainer:Sprite; + /** @private Primarily for Video objects which don't act like anything else - we must store the original width/height ratio in this variable so that we can properly apply scaleModes **/ + protected var _nativeRect:Rectangle; + + /** @private A place to reference an object that should be protected from gc - this is used in VideoLoader in order to protect the NetStream object when the loader is disposed. **/ + public var gcProtect:*; + /** Arbitrary data that you can associate with the ContentDisplay instance. For example, you could set data to be an object containing various other properties or set it to an index number related to an array in your application. It is completely optional and arbitrary. **/ + public var data:*; + + /** + * Constructor + * + * @param loader The Loader object that will populate the ContentDisplay's rawContent. + */ + public function ContentDisplay(loader:LoaderItem) { + super(); + this.loader = loader; + } + + /** + * Removes the ContentDisplay from the display list (if necessary), dumps the rawContent, + * and calls unload() and dispose() on the loader (unless you define otherwise with + * the optional parameters). This essentially destroys the ContentDisplay and makes it eligible for garbage + * collection internally, although if you added any listeners manually, you should remove them as well. + * + * @param unloadLoader If true, unload() will be called on the loader. It is true by default. + * @param disposeLoader If true, dispose() will be called on the loader. It is true by default. + */ + public function dispose(unloadLoader:Boolean=true, disposeLoader:Boolean=true):void { + this.rawContent = null; + if (this.parent != null) { + this.parent.removeChild(this); + } + this.gcProtect = null; + if (_loader != null) { + if (unloadLoader) { + _loader.unload(); + } + if (disposeLoader) { + _loader.dispose(false); + _loader = null; + } + } + } + + /** @private **/ + protected function _update():void { + var left:Number = (_centerRegistration && _fitWidth > 0) ? _fitWidth / -2 : 0; + var top:Number = (_centerRegistration && _fitHeight > 0) ? _fitHeight / -2 : 0; + graphics.clear(); + if (_fitWidth > 0 && _fitHeight > 0) { + graphics.beginFill(_bgColor, _bgAlpha); + graphics.drawRect(left, top, _fitWidth, _fitHeight); + graphics.endFill(); + } + if (_rawContent == null) { + return; + } + + var mc:DisplayObject = _rawContent; + var m:Matrix = mc.transform.matrix; + var nativeBounds:Object, contentWidth:Number, contentHeight:Number; + if (mc is Video) {//Video objects don't accurately report getBounds() - they act like their native dimension is always 160x320. + nativeBounds = _nativeRect; + contentWidth = mc.width; + contentHeight = mc.height; + } else { + if (mc is Loader) { + nativeBounds = Loader(mc).contentLoaderInfo; + } else if (_loader != null && _loader.hasOwnProperty("getClass")) { + nativeBounds = mc.loaderInfo; //for SWFLoaders, use loaderInfo.width/height so that everything is based on the stage size, not the bounding box of the DisplayObjects that happen to be on the stage (which could be much larger or smaller than the swf's stage) + } else { + nativeBounds = mc.getBounds(mc); + } + if (nativeBounds is LoaderInfo && _loader != null && _loader.progress < 1) { + try { + contentWidth = nativeBounds.width; //if not enough of the file has loaded, this can throw a runtime error saying that the "width" isn't known yet. + } catch (error:Error) { + nativeBounds = mc.getBounds(mc); + } + } + contentWidth = nativeBounds.width * Math.abs(m.a) + nativeBounds.height * Math.abs(m.b); + contentHeight = nativeBounds.width * Math.abs(m.c) + nativeBounds.height * Math.abs(m.d); + } + + if (_fitWidth > 0 && _fitHeight > 0) { + var w:Number = _fitWidth; + var h:Number = _fitHeight; + + var wGap:Number = w - contentWidth; + var hGap:Number = h - contentHeight; + + if (_scaleMode != "none") { + var displayRatio:Number = w / h; + var contentRatio:Number = nativeBounds.width / nativeBounds.height; + if ((contentRatio < displayRatio && _scaleMode == "proportionalInside") || (contentRatio > displayRatio && _scaleMode == "proportionalOutside")) { + w = h * contentRatio; + } + if ((contentRatio > displayRatio && _scaleMode == "proportionalInside") || (contentRatio < displayRatio && _scaleMode == "proportionalOutside")) { + h = w / contentRatio; + } + + if (_scaleMode != "heightOnly") { + mc.width *= w / contentWidth; + wGap = _fitWidth - w; + } + if (_scaleMode != "widthOnly") { + mc.height *= h / contentHeight; + hGap = _fitHeight - h; + } + } + + if (_hAlign == "left") { + wGap = 0; + } else if (_hAlign != "right") { + wGap /= 2; + } + if (_vAlign == "top") { + hGap = 0; + } else if (_vAlign != "bottom") { + hGap /= 2; + } + + if (_crop) { + //due to bugs in the way Flash reports getBounds() on objects with a scrollRect, we need to just wrap the rawContent in a container and apply the scrollRect to the container. + if (_cropContainer == null || mc.parent != _cropContainer) { + _cropContainer = new Sprite(); + this.addChildAt(_cropContainer, this.getChildIndex(mc)); + _cropContainer.addChild(mc); + } + _cropContainer.x = left; + _cropContainer.y = top; + _cropContainer.scrollRect = new Rectangle(0, 0, _fitWidth, _fitHeight); + mc.x = wGap; + mc.y = hGap; + } else { + if (_cropContainer != null) { + this.addChildAt(mc, this.getChildIndex(_cropContainer)); + _cropContainer = null; + } + mc.x = left + wGap; + mc.y = top + hGap; + } + + } else { + mc.x = (_centerRegistration) ? contentWidth / -2 : 0; + mc.y = (_centerRegistration) ? contentHeight / -2 : 0; + } + } + + + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** + * The width to which the rawContent should be fit according to the ContentDisplay's scaleMode + * (this width is figured before rotation, scaleX, and scaleY). When a "width" property is defined in the loader's vars + * property/parameter, it is automatically applied to this fitWidth property. For example, the following code will + * set the loader's ContentDisplay fitWidth to 100:

+ * + * var loader:ImageLoader = new ImageLoader("photo.jpg", {width:100, height:80, container:this});

+ * + * @see #fitHeight + * @see #scaleMode + **/ + public function get fitWidth():Number { + return _fitWidth; + } + public function set fitWidth(value:Number):void { + _fitWidth = value; + _update(); + } + + /** + * The height to which the rawContent should be fit according to the ContentDisplay's scaleMode + * (this height is figured before rotation, scaleX, and scaleY). When a "height" property is defined in the loader's vars + * property/parameter, it is automatically applied to this fitHeight property. For example, the following code will + * set the loader's ContentDisplay fitHeight to 80:

+ * + * var loader:ImageLoader = new ImageLoader("photo.jpg", {width:100, height:80, container:this});

+ * + * @see #fitWidth + * @see #scaleMode + **/ + public function get fitHeight():Number { + return _fitHeight; + } + public function set fitHeight(value:Number):void { + _fitHeight = value; + _update(); + } + + /** + * When the ContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the scaleMode controls how + * the rawContent will be scaled to fit the area. The following values are recognized (you may use the + * com.greensock.layout.ScaleMode constants if you prefer): + *
    + *
  • "stretch" (the default) - The rawContent will fill the width/height exactly.
  • + *
  • "proportionalInside" - The rawContent will be scaled proportionally to fit inside the area defined by the width/height
  • + *
  • "proportionalOutside" - The rawContent will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
  • + *
  • "widthOnly" - Only the width of the rawContent will be adjusted to fit.
  • + *
  • "heightOnly" - Only the height of the rawContent will be adjusted to fit.
  • + *
  • "none" - No scaling of the rawContent will occur.
  • + *
+ **/ + public function get scaleMode():String { + return _scaleMode; + } + public function set scaleMode(value:String):void { + if (_rawContent != null) { + _rawContent.scaleX = _rawContent.scaleY = 1; + } + _scaleMode = value; + _update(); + } + + /** + * If true, the ContentDisplay's registration point will be placed in the center of the rawContent + * which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center. + * @see #scaleMode + **/ + public function get centerRegistration():Boolean { + return _centerRegistration; + } + public function set centerRegistration(value:Boolean):void { + _centerRegistration = value; + _update(); + } + + /** + * When the ContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), setting crop to + * true will cause the rawContent to be cropped within that area (by applying a scrollRect + * for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" + * or "none" so that any parts of the rawContent that exceed the dimensions defined by + * fitWidth and fitHeight are visually chopped off. Use the hAlign and + * vAlign properties to control the vertical and horizontal alignment within the cropped area. + * + * @see #scaleMode + **/ + public function get crop():Boolean { + return _crop; + } + public function set crop(value:Boolean):void { + _crop = value; + _update(); + } + + /** + * When the ContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the hAlign determines how + * the rawContent is horizontally aligned within that area. The following values are recognized (you may use the + * com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The rawContent will be centered horizontally in the ContentDisplay
  • + *
  • "left" - The rawContent will be aligned with the left side of the ContentDisplay
  • + *
  • "right" - The rawContent will be aligned with the right side of the ContentDisplay
  • + *
+ * @see #scaleMode + * @see #vAlign + **/ + public function get hAlign():String { + return _hAlign; + } + public function set hAlign(value:String):void { + _hAlign = value; + _update(); + } + + /** + * When the ContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the vAlign determines how + * the rawContent is vertically aligned within that area. The following values are recognized (you may use the + * com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The rawContent will be centered vertically in the ContentDisplay
  • + *
  • "top" - The rawContent will be aligned with the top of the ContentDisplay
  • + *
  • "bottom" - The rawContent will be aligned with the bottom of the ContentDisplay
  • + *
+ * @see #scaleMode + * @see #hAlign + **/ + public function get vAlign():String { + return _vAlign; + } + public function set vAlign(value:String):void { + _vAlign = value; + _update(); + } + + /** + * When the ContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), a rectangle will be drawn inside the + * ContentDisplay object immediately in order to ease the development process (for example, you can add ROLL_OVER/ROLL_OUT + * event listeners immediately). It is transparent by default, but you may define a bgAlpha if you prefer. + * @see #bgAlpha + * @see #fitWidth + * @see #fitHeight + **/ + public function get bgColor():uint { + return _bgColor; + } + public function set bgColor(value:uint):void { + _bgColor = value; + _update(); + } + + /** + * Controls the alpha of the rectangle that is drawn when the ContentDisplay's fitWidth and fitHeight + * properties are defined (or width and height in the loader's vars property/parameter). + * @see #bgColor + * @see #fitWidth + * @see #fitHeight + **/ + public function get bgAlpha():Number { + return _bgAlpha; + } + public function set bgAlpha(value:Number):void { + _bgAlpha = value; + _update(); + } + + /** The raw content which can be a Bitmap, a MovieClip, a Loader, or a Video depending on the type of loader associated with the ContentDisplay. **/ + public function get rawContent():* { + return _rawContent; + } + public function set rawContent(value:*):void { + if (_rawContent != null && _rawContent != value) { + if (_rawContent.parent == this) { + removeChild(_rawContent); + } else if (_cropContainer != null && _rawContent.parent == _cropContainer) { + _cropContainer.removeChild(_rawContent); + removeChild(_cropContainer); + _cropContainer = null; + } + } + _rawContent = value as DisplayObject; + if (_rawContent == null) { + return; + } else if (_rawContent.parent == null || (_rawContent.parent != this && _rawContent.parent != _cropContainer)) { + addChildAt(_rawContent as DisplayObject, 0); + } + _nativeRect = new Rectangle(0, 0, _rawContent.width, _rawContent.height); + _update(); + } + + /** The loader whose rawContent populates this ContentDisplay. If you get the loader's content, it will return this ContentDisplay object. **/ + public function get loader():LoaderItem { + return _loader; + } + public function set loader(value:LoaderItem):void { + _loader = value; + if (_loader == null) { + return; + } else if (!_loader.hasOwnProperty("setContentDisplay")) { + throw new Error("Incompatible loader used for a ContentDisplay"); + } + this.name = _loader.name; + var type:String; + for (var p:String in _transformProps) { + if (p in _loader.vars) { + type = typeof(_transformProps[p]); + this[p] = (type == "number") ? Number(_loader.vars[p]) : (type == "string") ? String(_loader.vars[p]) : Boolean(_loader.vars[p]); + } + } + _bgColor = uint(_loader.vars.bgColor); + _bgAlpha = ("bgAlpha" in _loader.vars) ? Number(_loader.vars.bgAlpha) : ("bgColor" in _loader.vars) ? 1 : 0; + _fitWidth = ("fitWidth" in _loader.vars) ? Number(_loader.vars.fitWidth) : Number(_loader.vars.width); + _fitHeight = ("fitHeight" in _loader.vars) ? Number(_loader.vars.fitHeight) : Number(_loader.vars.height); + _update(); + if (_loader.vars.container is DisplayObjectContainer) { + (_loader.vars.container as DisplayObjectContainer).addChild(this); + } + if (_loader.content != this) { + (_loader as Object).setContentDisplay(this); + } + this.rawContent = (_loader as Object).rawContent; + } + } +} \ No newline at end of file diff --git a/src/com/greensock/loading/display/FlexContentDisplay.as b/src/com/greensock/loading/display/FlexContentDisplay.as new file mode 100644 index 0000000..a829d12 --- /dev/null +++ b/src/com/greensock/loading/display/FlexContentDisplay.as @@ -0,0 +1,478 @@ +/** + * VERSION: 1.896 + * DATE: 2012-01-06 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com/loadermax/ + **/ +package com.greensock.loading.display { + import com.greensock.loading.core.LoaderItem; + + import flash.display.DisplayObject; + import flash.display.DisplayObjectContainer; + import flash.display.Loader; + import flash.display.LoaderInfo; + import flash.display.Sprite; + import flash.geom.Matrix; + import flash.geom.Rectangle; + import flash.media.Video; + + import mx.core.UIComponent; +/** + * A container for visual content that is loaded by any of the following: ImageLoaders, SWFLoaders, + * or VideoLoaders which is to be used in Flex. It is essentially a UIComponent that has a loader + * property for easily referencing the original loader, as well as several other useful properties for + * controling the placement of rawContent and the way it is scaled to fit (if at all). That way, + * you can add a FlexContentDisplay to the display list or populate an array with as many as you want and then if + * you ever need to unload() the content or reload it or figure out its url, etc., you can reference your + * FlexContentDisplay's loader property like myContent.loader.url or + * (myContent.loader as SWFLoader).getClass("com.greensock.TweenLite");. + * + * + *

IMPORTANT: In order for the LoaderMax loaders to use FlexContentDisplay instead of + * the regular ContentDisplay class, you must set the LoaderMax.contentDisplayClass property + * to FlexContentDisplay once like:

+ * + import com.greensock.loading.~~; + import com.greensock.loading.display.~~; + + LoaderMax.contentDisplayClass = FlexContentDisplay; + + * + *

After that, all ImageLoaders, SWFLoaders, and VideoLoaders will return FlexContentDisplay objects + * as their content instead of regular ContentDisplay objects.

+ * + *

Copyright 2009-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class FlexContentDisplay extends UIComponent { + /** @private **/ + protected static var _transformProps:Object = {x:1, y:1, z:1, rotationX:1, rotationY:1, rotationZ:1, scaleX:1, scaleY:1, rotation:1, alpha:1, visible:true, blendMode:"normal", centerRegistration:false, crop:false, scaleMode:"stretch", hAlign:"center", vAlign:"center"}; + /** @private **/ + protected var _loader:LoaderItem; + /** @private **/ + protected var _rawContent:DisplayObject; + /** @private **/ + protected var _centerRegistration:Boolean; + /** @private **/ + protected var _crop:Boolean; + /** @private **/ + protected var _scaleMode:String = "stretch"; + /** @private **/ + protected var _hAlign:String = "center"; + /** @private **/ + protected var _vAlign:String = "center"; + /** @private **/ + protected var _bgColor:uint; + /** @private **/ + protected var _bgAlpha:Number = 0; + /** @private **/ + protected var _fitWidth:Number; + /** @private **/ + protected var _fitHeight:Number; + /** @private only used when crop is true - works around bugs in Flash with the way it reports getBounds() on objects with a scrollRect. **/ + protected var _cropContainer:Sprite; + /** @private Primarily for Video objects which don't act like anything else - we must store the original width/height ratio in this variable so that we can properly apply scaleModes **/ + protected var _nativeRect:Rectangle; + + /** @private A place to reference an object that should be protected from gc - this is used in VideoLoader in order to protect the NetStream object when the loader is disposed. **/ + public var gcProtect:*; + /** Arbitrary data that you can associate with the FlexContentDisplay instance. For example, you could set data to be an object containing various other properties or set it to an index number related to an array in your application. It is completely optional and arbitrary. **/ + public var data:*; + + /** + * Constructor + * + * @param loader The Loader object that will populate the FlexContentDisplay's rawContent. + */ + public function FlexContentDisplay(loader:LoaderItem) { + super(); + this.loader = loader; + } + /** + * Removes the FlexContentDisplay from the display list (if necessary), dumps the rawContent, + * and calls unload() and dispose() on the loader (unless you define otherwise with + * the optional parameters). This essentially destroys the FlexContentDisplay and makes it eligible for garbage + * collection internally, although if you added any listeners manually, you should remove them as well. + * + * @param unloadLoader If true, unload() will be called on the loader. It is true by default. + * @param disposeLoader If true, dispose() will be called on the loader. It is true by default. + */ + public function dispose(unloadLoader:Boolean=true, disposeLoader:Boolean=true):void { + if (this.parent != null) { + if (this.parent.hasOwnProperty("removeElement")) { + (this.parent as Object).removeElement(this); + } else { + this.parent.removeChild(this); + } + } + this.rawContent = null; + this.gcProtect = null; + _cropContainer = null; + if (_loader != null) { + if (unloadLoader) { + _loader.unload(); + } + if (disposeLoader) { + _loader.dispose(false); + _loader = null; + } + } + } + + /** @private **/ + protected function _update():void { + var left:Number = (_centerRegistration && _fitWidth > 0) ? _fitWidth / -2 : 0; + var top:Number = (_centerRegistration && _fitHeight > 0) ? _fitHeight / -2 : 0; + graphics.clear(); + if (_fitWidth > 0 && _fitHeight > 0) { + graphics.beginFill(_bgColor, _bgAlpha); + graphics.drawRect(left, top, _fitWidth, _fitHeight); + graphics.endFill(); + } + if (_rawContent == null) { + measure(); + return; + } + var mc:DisplayObject = _rawContent; + var m:Matrix = mc.transform.matrix; + var nativeBounds:Object, contentWidth:Number, contentHeight:Number; + if (mc is Video) {//Video objects don't accurately report getBounds() - they act like their native dimension is always 160x320. + nativeBounds = _nativeRect; + contentWidth = mc.width; + contentHeight = mc.height; + } else { + if (mc is Loader) { + nativeBounds = Loader(mc).contentLoaderInfo; + } else if (_loader != null && _loader.hasOwnProperty("getClass")) { + nativeBounds = mc.loaderInfo; //for SWFLoaders, use loaderInfo.width/height so that everything is based on the stage size, not the bounding box of the DisplayObjects that happen to be on the stage (which could be much larger or smaller than the swf's stage) + } else { + nativeBounds = mc.getBounds(mc); + } + if (nativeBounds is LoaderInfo && _loader != null && _loader.progress < 1) { + try { + contentWidth = nativeBounds.width; //if not enough of the file has loaded, this can throw a runtime error saying that the "width" isn't known yet. + } catch (error:Error) { + nativeBounds = mc.getBounds(mc); + } + } + contentWidth = nativeBounds.width * Math.abs(m.a) + nativeBounds.height * Math.abs(m.b); + contentHeight = nativeBounds.width * Math.abs(m.c) + nativeBounds.height * Math.abs(m.d); + } + + if (_fitWidth > 0 && _fitHeight > 0) { + var w:Number = _fitWidth; + var h:Number = _fitHeight; + + var wGap:Number = w - contentWidth; + var hGap:Number = h - contentHeight; + + if (_scaleMode != "none") { + var displayRatio:Number = w / h; + var contentRatio:Number = nativeBounds.width / nativeBounds.height; + if ((contentRatio < displayRatio && _scaleMode == "proportionalInside") || (contentRatio > displayRatio && _scaleMode == "proportionalOutside")) { + w = h * contentRatio; + } + if ((contentRatio > displayRatio && _scaleMode == "proportionalInside") || (contentRatio < displayRatio && _scaleMode == "proportionalOutside")) { + h = w / contentRatio; + } + + if (_scaleMode != "heightOnly") { + mc.width *= w / contentWidth; + wGap = _fitWidth - w; + } + if (_scaleMode != "widthOnly") { + mc.height *= h / contentHeight; + hGap = _fitHeight - h; + } + } + + if (_hAlign == "left") { + wGap = 0; + } else if (_hAlign != "right") { + wGap /= 2; + } + if (_vAlign == "top") { + hGap = 0; + } else if (_vAlign != "bottom") { + hGap /= 2; + } + + if (_crop) { + //due to bugs in the way Flash reports getBounds() on objects with a scrollRect, we need to just wrap the rawContent in a container and apply the scrollRect to the container. + if (_cropContainer == null || mc.parent != _cropContainer) { + _cropContainer = new Sprite(); + this.addChildAt(_cropContainer, this.getChildIndex(mc)); + _cropContainer.addChild(mc); + } + _cropContainer.x = left; + _cropContainer.y = top; + _cropContainer.scrollRect = new Rectangle(0, 0, _fitWidth, _fitHeight); + mc.x = wGap; + mc.y = hGap; + } else { + if (_cropContainer != null) { + this.addChildAt(mc, this.getChildIndex(_cropContainer)); + _cropContainer = null; + } + mc.x = left + wGap; + mc.y = top + hGap; + } + + } else { + mc.x = (_centerRegistration) ? contentWidth / -2 : 0; + mc.y = (_centerRegistration) ? contentHeight / -2 : 0; + } + measure(); + } + + /** @private **/ + override protected function measure():void { + var bounds:Rectangle; + if (this.parent) { + bounds = this.getBounds(this.parent); + this.width = bounds.width; + this.height = bounds.height; + } + bounds = this.getBounds(this); + this.explicitWidth = bounds.width; + this.explicitHeight = bounds.height; + super.measure(); + } + +//---- GETTERS / SETTERS ------------------------------------------------------------------------- + + /** + * The width to which the rawContent should be fit according to the FlexContentDisplay's scaleMode + * (this width is figured before rotation, scaleX, and scaleY). When a "width" property is defined in the loader's vars + * property/parameter, it is automatically applied to this fitWidth property. For example, the following code will + * set the loader's FlexContentDisplay fitWidth to 100:

+ * + * var loader:ImageLoader = new ImageLoader("photo.jpg", {width:100, height:80, container:this});

+ * + * @see #fitHeight + * @see #scaleMode + **/ + public function get fitWidth():Number { + return _fitWidth; + } + public function set fitWidth(value:Number):void { + _fitWidth = value; + _update(); + } + + /** + * The height to which the rawContent should be fit according to the FlexContentDisplay's scaleMode + * (this height is figured before rotation, scaleX, and scaleY). When a "height" property is defined in the loader's vars + * property/parameter, it is automatically applied to this fitHeight property. For example, the following code will + * set the loader's FlexContentDisplay fitHeight to 80:

+ * + * var loader:ImageLoader = new ImageLoader("photo.jpg", {width:100, height:80, container:this});

+ * + * @see #fitWidth + * @see #scaleMode + **/ + public function get fitHeight():Number { + return _fitHeight; + } + public function set fitHeight(value:Number):void { + _fitHeight = value; + _update(); + } + + /** + * When the FlexContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the scaleMode controls how + * the rawContent will be scaled to fit the area. The following values are recognized (you may use the + * com.greensock.layout.ScaleMode constants if you prefer): + *
    + *
  • "stretch" (the default) - The rawContent will fill the width/height exactly.
  • + *
  • "proportionalInside" - The rawContent will be scaled proportionally to fit inside the area defined by the width/height
  • + *
  • "proportionalOutside" - The rawContent will be scaled proportionally to completely fill the area, allowing portions of it to exceed the bounds defined by the width/height.
  • + *
  • "widthOnly" - Only the width of the rawContent will be adjusted to fit.
  • + *
  • "heightOnly" - Only the height of the rawContent will be adjusted to fit.
  • + *
  • "none" - No scaling of the rawContent will occur.
  • + *
+ **/ + public function get scaleMode():String { + return _scaleMode; + } + public function set scaleMode(value:String):void { + if (_rawContent != null) { + _rawContent.scaleX = _rawContent.scaleY = 1; + } + _scaleMode = value; + _update(); + } + + /** + * If true, the FlexContentDisplay's registration point will be placed in the center of the rawContent + * which can be useful if, for example, you want to animate its scale and have it grow/shrink from its center. + * @see #scaleMode + **/ + public function get centerRegistration():Boolean { + return _centerRegistration; + } + public function set centerRegistration(value:Boolean):void { + _centerRegistration = value; + _update(); + } + + /** + * When the FlexContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), setting crop to + * true will cause the rawContent to be cropped within that area (by applying a scrollRect + * for maximum performance). This is typically useful when the scaleMode is "proportionalOutside" + * or "none" so that any parts of the rawContent that exceed the dimensions defined by + * fitWidth and fitHeight are visually chopped off. Use the hAlign and + * vAlign properties to control the vertical and horizontal alignment within the cropped area. + * + * @see #scaleMode + **/ + public function get crop():Boolean { + return _crop; + } + public function set crop(value:Boolean):void { + _crop = value; + _update(); + } + + /** + * When the FlexContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the hAlign determines how + * the rawContent is horizontally aligned within that area. The following values are recognized (you may use the + * com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The rawContent will be centered horizontally in the FlexContentDisplay
  • + *
  • "left" - The rawContent will be aligned with the left side of the FlexContentDisplay
  • + *
  • "right" - The rawContent will be aligned with the right side of the FlexContentDisplay
  • + *
+ * @see #scaleMode + * @see #vAlign + **/ + public function get hAlign():String { + return _hAlign; + } + public function set hAlign(value:String):void { + _hAlign = value; + _update(); + } + + /** + * When the FlexContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), the vAlign determines how + * the rawContent is vertically aligned within that area. The following values are recognized (you may use the + * com.greensock.layout.AlignMode constants if you prefer): + *
    + *
  • "center" (the default) - The rawContent will be centered vertically in the FlexContentDisplay
  • + *
  • "top" - The rawContent will be aligned with the top of the FlexContentDisplay
  • + *
  • "bottom" - The rawContent will be aligned with the bottom of the FlexContentDisplay
  • + *
+ * @see #scaleMode + * @see #hAlign + **/ + public function get vAlign():String { + return _vAlign; + } + public function set vAlign(value:String):void { + _vAlign = value; + _update(); + } + + /** + * When the FlexContentDisplay's fitWidth and fitHeight properties are defined (or width + * and height in the loader's vars property/parameter), a rectangle will be drawn inside the + * FlexContentDisplay object immediately in order to ease the development process (for example, you can add ROLL_OVER/ROLL_OUT + * event listeners immediately). It is transparent by default, but you may define a bgAlpha if you prefer. + * @see #bgAlpha + * @see #fitWidth + * @see #fitHeight + **/ + public function get bgColor():uint { + return _bgColor; + } + public function set bgColor(value:uint):void { + _bgColor = value; + _update(); + } + + /** + * Controls the alpha of the rectangle that is drawn when the FlexContentDisplay's fitWidth and fitHeight + * properties are defined (or width and height in the loader's vars property/parameter). + * @see #bgColor + * @see #fitWidth + * @see #fitHeight + **/ + public function get bgAlpha():Number { + return _bgAlpha; + } + public function set bgAlpha(value:Number):void { + _bgAlpha = value; + _update(); + } + + + /** The raw content which can be a Bitmap, a MovieClip, a Loader, or a Video depending on the type of loader associated with the FlexContentDisplay. **/ + public function get rawContent():* { + return _rawContent; + } + + public function set rawContent(value:*):void { + if (_rawContent != null && _rawContent != value) { + if (_rawContent.parent == this) { + removeChild(_rawContent); + } else if (_cropContainer != null && _rawContent.parent == _cropContainer) { + _cropContainer.removeChild(_rawContent); + removeChild(_cropContainer); + _cropContainer = null; + } + } + _rawContent = value as DisplayObject; + if (_rawContent == null) { + return; + } else if (_rawContent.parent == null || (_rawContent.parent != this && _rawContent.parent != _cropContainer)) { + addChildAt(_rawContent as DisplayObject, 0); + } + _nativeRect = new Rectangle(0, 0, _rawContent.width, _rawContent.height); + _update(); + } + + /** The loader whose rawContent populates this FlexContentDisplay. If you get the loader's content, it will return this FlexContentDisplay object. **/ + public function get loader():LoaderItem { + return _loader; + } + + public function set loader(value:LoaderItem):void { + _loader = value; + if (value == null) { + return; + } else if (!_loader.hasOwnProperty("setContentDisplay")) { + throw new Error("Incompatible loader used for a FlexContentDisplay"); + } + this.name = _loader.name; + var type:String; + for (var p:String in _transformProps) { + if (p in _loader.vars) { + type = typeof(_transformProps[p]); + this[p] = (type == "number") ? Number(_loader.vars[p]) : (type == "string") ? String(_loader.vars[p]) : Boolean(_loader.vars[p]); + } + } + _bgColor = uint(_loader.vars.bgColor); + _bgAlpha = ("bgAlpha" in _loader.vars) ? Number(_loader.vars.bgAlpha) : ("bgColor" in _loader.vars) ? 1 : 0; + _fitWidth = ("fitWidth" in _loader.vars) ? Number(_loader.vars.fitWidth) : Number(_loader.vars.width); + _fitHeight = ("fitHeight" in _loader.vars) ? Number(_loader.vars.fitHeight) : Number(_loader.vars.height); + _update(); + if (_loader.vars.container is DisplayObjectContainer) { + if (_loader.vars.container.hasOwnProperty("addElement")) { + (_loader.vars.container as Object).addElement(this); + } else { + (_loader.vars.container as DisplayObjectContainer).addChild(this); + } + } + if (_loader.content != this) { + (_loader as Object).setContentDisplay(this); + } + this.rawContent = (_loader as Object).rawContent; + } + } +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/CirclePath2D.as b/src/com/greensock/motionPaths/CirclePath2D.as new file mode 100644 index 0000000..b42ab5b --- /dev/null +++ b/src/com/greensock/motionPaths/CirclePath2D.as @@ -0,0 +1,244 @@ +/** + * VERSION: 0.4.1 (beta) + * DATE: 2013-03-11 + * AS3 + * UPDATES AND DOCS AT: http://www.GreenSock.com + **/ +package com.greensock.motionPaths { + import flash.display.Graphics; + import flash.events.Event; + import flash.geom.Matrix; + +/** + * [AS3 only] A CirclePath2D defines a circular path on which a PathFollower can be placed, making it simple to tween objects + * along a circle or oval (make an oval by altering the width/height/scaleX/scaleY properties). A PathFollower's + * position along the path is described using its progress property, a value between 0 and 1 where + * 0 is at the beginning of the path, 0.5 is in the middle, and 1 is at the very end of the path. So to tween a + * PathFollower along the path, you can simply tween its progress property. To tween ALL of the + * followers on the path at once, you can tween the CirclePath2D's progress property. PathFollowers + * automatically wrap so that if the progress value exceeds 1 or drops below 0, it shows up on + * the other end of the path. + * + *

Since CirclePath2D extends the Shape class, you can add an instance to the display list to see a line representation + * of the path drawn which can be helpful especially during the production phase. Use lineStyle() + * to adjust the color, thickness, and other attributes of the line that is drawn (or set the CirclePath2D's + * visible property to false or don't add it to the display list if you don't want to see the line + * at all). You can also adjust all of its properties like radius, scaleX, scaleY, rotation, width, height, x, + * and y. That means you can tween those values as well to achieve very dynamic, complex effects with ease.

+ * + * +import com.greensock.~~; +import com.greensock.plugins.~~; +import com.greensock.motionPaths.~~; +TweenPlugin.activate([CirclePath2DPlugin]); //only needed once in your swf, and only if you plan to use the CirclePath2D tweening feature for convenience + +//create a circle motion path at coordinates x:150, y:150 with a radius of 100 +var circle:CirclePath2D = new CirclePath2D(150, 150, 100); + +//tween mc along the path from the bottom (90 degrees) to 315 degrees in the counter-clockwise direction and make an extra revolution +TweenLite.to(mc, 3, {circlePath2D:{path:circle, startAngle:90, endAngle:315, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:1}}); + +//tween the circle's rotation, scaleX, scaleY, x, and y properties: +TweenLite.to(circle, 3, {rotation:180, scaleX:0.5, scaleY:2, x:250, y:200}); + +//show the path visually by adding it to the display list (optional) +this.addChild(circle); + + +//--- Instead of using the plugin, you could manually manage followers and tween their "progress" property... + +//make the MovieClip "mc2" follow the circle and start at a position of 90 degrees (this returns a PathFollower instance) +var follower:PathFollower = circle.addFollower(mc2, circle.angleToProgress(90)); + +//tween the follower clockwise along the path to 315 degrees +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 315, Direction.CLOCKWISE)}); + +//tween the follower counter-clockwise to 200 degrees and add an extra revolution +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 200, Direction.COUNTER_CLOCKWISE, 1)}); + + * + *

NOTES

+ *
    + *
  • All followers's positions are automatically updated when you alter the MotionPath that they're following.
  • + *
  • To tween all followers along the path at once, simply tween the MotionPath's progress + * property which will provide better performance than tweening each follower independently.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class CirclePath2D extends MotionPath { + /** @private **/ + protected var _radius:Number; + + /** + * Constructor + * + * @param x The x coordinate of the origin (center) of the circle + * @param y The y coordinate of the origin (center) of the circle + * @param radius The radius of the circle + */ + public function CirclePath2D(x:Number, y:Number, radius:Number) { + super(); + _radius = radius; + super.x = x; + super.y = y; + } + + /** @inheritDoc**/ + override public function update(event:Event=null):void { + var angle:Number, px:Number, py:Number; + var m:Matrix = this.transform.matrix; + var a:Number = m.a, b:Number = m.b, c:Number = m.c, d:Number = m.d, tx:Number = m.tx, ty:Number = m.ty; + var f:PathFollower = _rootFollower; + while (f) { + angle = f.cachedProgress * Math.PI * 2; + px = Math.cos(angle) * _radius; + py = Math.sin(angle) * _radius; + f.target.x = px * a + py * c + tx; + f.target.y = px * b + py * d + ty; + + if (f.autoRotate) { + angle += Math.PI / 2; + px = Math.cos(angle) * _radius; + py = Math.sin(angle) * _radius; + f.target.rotation = Math.atan2(px * m.b + py * m.d, px * m.a + py * m.c) * _RAD2DEG + f.rotationOffset; + } + + f = f.cachedNext; + } + if (_redrawLine) { + var g:Graphics = this.graphics; + g.clear(); + g.lineStyle(_thickness, _color, _lineAlpha, _pixelHinting, _scaleMode, _caps, _joints, _miterLimit); + g.drawCircle(0, 0, _radius); + _redrawLine = false; + } + } + + /** @inheritDoc **/ + override public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void { + var angle:Number = progress * Math.PI * 2; + var m:Matrix = this.transform.matrix; + var px:Number = Math.cos(angle) * _radius; + var py:Number = Math.sin(angle) * _radius; + target.x = px * m.a + py * m.c + m.tx; + target.y = px * m.b + py * m.d + m.ty; + + if (autoRotate) { + angle += Math.PI / 2; + px = Math.cos(angle) * _radius; + py = Math.sin(angle) * _radius; + target.rotation = Math.atan2(px * m.b + py * m.d, px * m.a + py * m.c) * _RAD2DEG + rotationOffset; + } + } + + + /** + * Translates an angle (in degrees or radians) to the associated progress value + * on the CirclePath2D. For example, to position mc on the CirclePath2D at 90 degrees + * (bottom), you'd do:

+ * + * var follower:PathFollower = myCircle.addFollower(mc, myCircle.angleToProgress(90));
+ * + *
+ * + * @param angle The angle whose progress value you want to determine + * @param useRadians If you prefer to define the angle in radians instead of degrees, set this to true (it is false by default) + * @return The progress value associated with the angle + */ + public function angleToProgress(angle:Number, useRadians:Boolean=false):Number { + var revolution:Number = useRadians ? Math.PI * 2 : 360; + if (angle < 0) { + angle += (int(-angle / revolution) + 1) * revolution; + } else if (angle > revolution) { + angle -= int(angle / revolution) * revolution; + } + return angle / revolution; + } + + /** + * Translates a progress value (typically between 0 and 1 where 0 is the beginning of the path, + * 0.5 is in the middle, and 1 is at the end) to the associated angle on the CirclePath2D. + * For example, to find out what angle a particular PathFollower is at, you'd do:

+ * + * var angle:Number = myCircle.progressToAngle(myFollower.progress, false);
+ * + *
+ * + * @param progress The progress value to translate into an angle + * @param useRadians If you prefer that the angle be described in radians instead of degrees, set this to true (it is false by default) + * @return The angle (in degrees or radians depending on the useRadians value) associated with the progress value. + */ + public function progressToAngle(progress:Number, useRadians:Boolean=false):Number { + var revolution:Number = useRadians ? Math.PI * 2 : 360; + return progress * revolution; + } + + /** + * Simplifies tweening by determining a relative change in the progress value of a follower based on the + * endAngle, direction, and extraRevolutions that you define. For example, to tween myFollower + * from wherever it is currently to the position at 315 degrees, moving in the COUNTER_CLOCKWISE direction + * and going 2 extra revolutions, you could do:

+ * + * TweenLite.to(myFollower, 2, {progress:myCircle.followerTween(myFollower, 315, Direction.COUNTER_CLOCKWISE, 2)}); + * + * + * @param follower The PathFollower (or its associated target) that will be tweened (determines the start angle) + * @param endAngle The destination (end) angle + * @param direction The direction in which to travel - options are Direction.CLOCKWISE ("clockwise"), Direction.COUNTER_CLOCKWISE ("counterClockwise"), or Direction.SHORTEST ("shortest"). + * @param extraRevolutions If instead of going directly to the endAngle, you want the target to travel one or more extra revolutions around the path before going to the endAngle, define that number of revolutions here. + * @param useRadians If you prefer to define the angle in radians instead of degrees, set this to true (it is false by default) + * @return A String representing the amount of change in the progress value (feel free to cast it as a Number if you want, but it returns a String because TweenLite/Max/Nano recognize Strings as relative values. + */ + public function followerTween(follower:*, endAngle:Number, direction:String="clockwise", extraRevolutions:uint=0, useRadians:Boolean=false):String { + var revolution:Number = useRadians ? Math.PI * 2 : 360; + return String(anglesToProgressChange(getFollower(follower).progress * revolution, endAngle, direction, extraRevolutions, useRadians)); + } + + /** + * Returns the amount of progress change between two angles on the CirclePath2D, allowing special + * parameters like direction and extraRevolutions. + * + * @param startAngle The starting angle + * @param endAngle The ending angle + * @param direction The direction in which to travel - options are Direction.CLOCKWISE ("clockwise"), Direction.COUNTER_CLOCKWISE ("counterClockwise"), or Direction.SHORTEST ("shortest"). + * @param extraRevolutions If instead of going directly to the endAngle, you want the target to travel one or more extra revolutions around the path before going to the endAngle, define that number of revolutions here. + * @param useRadians If you prefer to define the angle in radians instead of degrees, set this to true (it is false by default) + * @return A Number representing the amount of change in the progress value. + */ + public function anglesToProgressChange(startAngle:Number, endAngle:Number, direction:String="clockwise", extraRevolutions:uint=0, useRadians:Boolean=false):Number { + var revolution:Number = useRadians ? Math.PI * 2 : 360; + var dif:Number = endAngle - startAngle; + if (dif < 0 && direction == "clockwise") { + dif += (int(-dif / revolution) + 1) * revolution; + } else if (dif > 0 && direction == "counterClockwise") { + dif -= (int(dif / revolution) + 1) * revolution; + } else if (direction == "shortest") { + dif = dif % revolution; + if (dif != dif % (revolution * 0.5)) { + dif = (dif < 0) ? dif + revolution : dif - revolution; + } + } + if (dif < 0 || (dif == 0 && direction == "counterClockwise")) { + dif -= extraRevolutions * revolution; + } else { + dif += extraRevolutions * revolution; + } + return dif / revolution; + } + + /** radius of the circle (does not factor in any transformations like scaleX/scaleY) **/ + public function get radius():Number { + return _radius; + } + public function set radius(value:Number):void { + _radius = value; + _redrawLine = true; + update(); + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/Direction.as b/src/com/greensock/motionPaths/Direction.as new file mode 100644 index 0000000..1c403c4 --- /dev/null +++ b/src/com/greensock/motionPaths/Direction.as @@ -0,0 +1,21 @@ +/** + * VERSION: 0.1 (beta) + * DATE: 1/19/2010 + * ACTIONSCRIPT VERSION: 3.0 + * UPDATES AND DOCUMENTATION AT: http://www.GreenSock.com + **/ +package com.greensock.motionPaths { + +/** + * [AS3 only] Constants for defining the direction in which to travel on a MotionPath (like CLOCKWISE, COUNTER_CLOCKWISE, SHORTEST, etc.). + * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class Direction { + public static const CLOCKWISE:String="clockwise"; + public static const COUNTER_CLOCKWISE:String="counterClockwise"; + public static const SHORTEST:String="shortest"; + } +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/LinePath2D.as b/src/com/greensock/motionPaths/LinePath2D.as new file mode 100644 index 0000000..a99ffc0 --- /dev/null +++ b/src/com/greensock/motionPaths/LinePath2D.as @@ -0,0 +1,554 @@ +/** + * VERSION: 0.5 + * DATE: 2012-02-16 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.motionPaths { + import flash.display.Graphics; + import flash.events.Event; + import flash.geom.Matrix; + import flash.geom.Point; + +/** + * [AS3 only] A LinePath2D defines a path (using as many Points as you want) on which a PathFollower can be + * placed and animated. A PathFollower's position along the path is described using the PathFollower's + * progress property, a value between 0 and 1 where 0 is at the beginning of the path, + * 0.5 is in the middle, and 1 is at the very end. To tween a PathFollower along the path, simply tween its + * progress property. To tween ALL of the followers on the path at once, you can tween the + * LinePath2D's progress property which performs better than tweening every PathFollower's + * progress property individually. PathFollowers automatically wrap so that if the + * progress value exceeds 1 it continues at the beginning of the path, meaning that tweening + * its progress from 0 to 2 would have the same effect as tweening it from 0 to 1 twice + * (it would appear to loop). + * + *

Since LinePath2D extends the Shape class, you can add an instance to the display list to see a line representation + * of the path drawn which can be particularly helpful during the production phase. Use lineStyle() + * to adjust the color, thickness, and other attributes of the line that is drawn (or set the LinePath2D's + * visible property to false or don't add it to the display list if you don't want to see the line + * at all). You can also adjust all of its properties like scaleX, scaleY, rotation, width, height, x, + * and y. That means you can tween those values as well to achieve very dynamic, complex effects + * with ease.

+ * + * +import com.greensock.~~; +import com.greensock.easing.~~; +import com.greensock.motionPaths.~~; +import flash.geom.Point; + +//create a LinePath2D with 5 Points +var path:LinePath2D = new LinePath2D([new Point(0, 0), + new Point(100, 100), + new Point(350, 150), + new Point(50, 200), + new Point(550, 400)]); + +//add it to the display list so we can see it (you can skip this if you prefer) +addChild(path); + +//create an array containing 30 blue squares +var boxes:Array = []; +for (var i:int = 0; i < 30; i++) { + boxes.push(createSquare(10, 0x0000FF)); +} + +//distribute the blue squares evenly across the entire path and set them to autoRotate +path.distribute(boxes, 0, 1, true); + +//put a red square exactly halfway through the 2nd segment +path.addFollower(createSquare(10, 0xFF0000), path.getSegmentProgress(2, 0.5)); + +//tween all of the squares through the path once (wrapping when they reach the end) +TweenMax.to(path, 20, {progress:1}); + +//while the squares are animating through the path, tween the path's position and rotation too! +TweenMax.to(path, 3, {rotation:180, x:550, y:400, ease:Back.easeOut, delay:3}); + +//method for creating squares +function createSquare(size:Number, color:uint=0xFF0000):Shape { + var s:Shape = new Shape(); + s.graphics.beginFill(color, 1); + s.graphics.drawRect(-size / 2, -size / 2, size, size); + s.graphics.endFill(); + this.addChild(s); + return s; +} + + * + *

NOTES

+ *
    + *
  • All followers' positions are automatically updated when you alter the MotionPath that they're following.
  • + *
  • To tween all followers along the path at once, simply tween the MotionPath's progress + * property which will provide better performance than tweening each follower independently.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class LinePath2D extends MotionPath { + /** @private **/ + protected var _first:PathPoint; + /** @private **/ + protected var _points:Array; + /** @private **/ + protected var _totalLength:Number; + /** @private **/ + protected var _hasAutoRotate:Boolean; + /** @private **/ + protected var _prevMatrix:Matrix; + + /** If true, the LinePath2D will analyze every Point whenever it renders to see if any Point's x or y value has changed, thus making it possible to tween them dynamically. Setting autoUpdatePoints to true increases the CPU load due to the extra processing, so only set it to true if you plan to change one or more of the Points' position. **/ + public var autoUpdatePoints:Boolean; + + /** + * Constructor + * + * @param points An array of Points that define the line + * @param x The x coordinate of the origin of the line + * @param y The y coordinate of the origin of the line + * @param autoUpdatePoints If true, the LinePath2D will analyze every Point whenever it renders to see if any Point's x or y value has changed, thus making it possible to tween them dynamically. Setting autoUpdatePoints to true increases the CPU load due to the extra processing, so only set it to true if you plan to change one or more of the Points' position. + */ + public function LinePath2D(points:Array=null, x:Number=0, y:Number=0, autoUpdatePoints:Boolean=false) { + super(); + _points = []; + _totalLength = 0; + this.autoUpdatePoints = autoUpdatePoints; + if (points != null) { + insertMultiplePoints(points, 0); + } + super.x = x; + super.y = y; + } + + /** + * Adds a Point to the end of the current LinePath2D (essentially redefining its end point). + * + * @param point A Point describing the local coordinates through which the line should be drawn. + **/ + public function appendPoint(point:Point):void { + _insertPoint(point, _points.length, false); + } + + /** + * Inserts a Point at a particular index value in the points array, similar to splice() in an array. + * For example, if a LinePath2D instance has 3 Points already and you want to insert a new Point right after the + * first one, you would do: + * +var path:LinePath2D = new LinePath2D([new Point(0, 0), + new Point(100, 50), + new Point(200, 300)]); +path.insertPoint(new Point(50, 50), 1); + + * + * @param point A Point describing the local coordinates through which the line should be drawn. + * @param index The index value in the points array at which the Point should be inserted. + **/ + public function insertPoint(point:Point, index:uint=0):void { + _insertPoint(point, index, false); + } + + /** @private **/ + protected function _insertPoint(point:Point, index:uint, skipOrganize:Boolean):void { + _points.splice(index, 0, new PathPoint(point)); + if (!skipOrganize) { + _organize(); + } + } + + + /** + * Appends multiple Points to the end of the points array. Identical to + * the appendPoint() method, but accepts an array of Points instead of just one. + * + * @param points An array of Points to append. + */ + public function appendMultiplePoints(points:Array):void { + insertMultiplePoints(points, _points.length); + } + + /** + * Inserts multiple Points into the points array at a particular index/position. + * Identical to the insertPoint() method, but accepts an array of points instead of just one. + * + * @param points An array of Points to insert. + * @param index The index value in the points array at which the Points should be inserted. + */ + public function insertMultiplePoints(points:Array, index:uint=0):void { + var l:int = points.length; + for (var i:int = 0; i < l; i++) { + _insertPoint(points[i], index + i, true); + } + _organize(); + } + + /** + * Removes a particular Point instance from the points array. + * + * @param point The Point object to remove from the points array. + */ + public function removePoint(point:Point):void { + var i:int = _points.length; + while (--i > -1) { + if (_points[i].point == point) { + _points.splice(i, 1); + } + } + _organize(); + } + + /** + * Removes the Point that resides at a particular index/position in the points array. + * Just like in arrays, the index is zero-based. For example, to remove the second Point in the array, + * do removePointByIndex(1); + * + * @param index The index value of the Point that should be removed from the points array. + */ + public function removePointByIndex(index:uint):void { + _points.splice(index, 1); + _organize(); + } + + /** @private **/ + protected function _organize():void { + _totalLength = 0; + _hasAutoRotate = false; + var last:int = _points.length - 1; + if (last == -1) { + _first = null; + } else if (last == 0) { + _first = _points[0]; + _first.progress = _first.xChange = _first.yChange = _first.length = 0; + return; + } + var pp:PathPoint; + for (var i:int = 0; i <= last; i++) { + if (_points[i] != null) { + pp = _points[i]; + pp.x = pp.point.x; + pp.y = pp.point.y; + if (i == last) { + pp.length = 0; + pp.next = null; + } else { + pp.next = _points[i + 1]; + pp.xChange = pp.next.x - pp.x; + pp.yChange = pp.next.y - pp.y; + pp.length = Math.sqrt(pp.xChange * pp.xChange + pp.yChange * pp.yChange); + _totalLength += pp.length; + } + } + } + _first = pp = _points[0]; + var curTotal:Number = 0; + while (pp) { + pp.progress = curTotal / _totalLength; + curTotal += pp.length; + pp = pp.next; + } + _updateAngles(); + } + + /** @private **/ + protected function _updateAngles():void { + var m:Matrix = this.transform.matrix; + var pp:PathPoint = _first; + while (pp) { + pp.angle = Math.atan2(pp.xChange * m.b + pp.yChange * m.d, pp.xChange * m.a + pp.yChange * m.c) * _RAD2DEG; + pp = pp.next; + } + _prevMatrix = m; + } + + /** @inheritDoc **/ + override public function update(event:Event=null):void { + if (_first == null || _points.length <= 1) { + return; + } + var updatedAngles:Boolean = false; + var px:Number, py:Number, pp:PathPoint, followerProgress:Number, pathProg:Number; + var m:Matrix = this.transform.matrix; + var a:Number = m.a, b:Number = m.b, c:Number = m.c, d:Number = m.d, tx:Number = m.tx, ty:Number = m.ty; + var f:PathFollower = _rootFollower; + + if (autoUpdatePoints) { + pp = _first; + while (pp) { + if (pp.point.x != pp.x || pp.point.y != pp.y) { + _organize(); + _redrawLine = true; + update(); + return; + } + pp = pp.next; + } + } + + while (f) { + + followerProgress = f.cachedProgress; + pp = _first; + while (pp != null && pp.next.progress < followerProgress) { + pp = pp.next; + } + + if (pp != null) { + pathProg = (followerProgress - pp.progress) / (pp.length / _totalLength); + px = pp.x + pathProg * pp.xChange; + py = pp.y + pathProg * pp.yChange; + f.target.x = px * a + py * c + tx; + f.target.y = px * b + py * d + ty; + + if (f.autoRotate) { + if (!updatedAngles && (_prevMatrix.a != a || _prevMatrix.b != b || _prevMatrix.c != c || _prevMatrix.d != d)) { + _updateAngles(); //only need to update the angles once during the render cycle + updatedAngles = true; + } + f.target.rotation = pp.angle + f.rotationOffset; + } + } + + f = f.cachedNext; + } + if (_redrawLine) { + var g:Graphics = this.graphics; + g.clear(); + g.lineStyle(_thickness, _color, _lineAlpha, _pixelHinting, _scaleMode, _caps, _joints, _miterLimit); + pp = _first; + g.moveTo(pp.x, pp.y); + while (pp) { + g.lineTo(pp.x, pp.y); + pp = pp.next; + } + _redrawLine = false; + } + } + + /** @inheritDoc **/ + override public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void { + if (progress > 1) { + progress -= int(progress); + } else if (progress < 0) { + progress -= int(progress) - 1; + } + if (_first == null) { + return; + } + + var pp:PathPoint = _first; + while (pp.next != null && pp.next.progress < progress) { + pp = pp.next; + } + + if (pp != null) { + var pathProg:Number = (progress - pp.progress) / (pp.length / _totalLength); + var px:Number = pp.x + pathProg * pp.xChange; + var py:Number = pp.y + pathProg * pp.yChange; + + var m:Matrix = this.transform.matrix; + target.x = px * m.a + py * m.c + m.tx; + target.y = px * m.b + py * m.d + m.ty; + + if (autoRotate) { + if (_prevMatrix.a != m.a || _prevMatrix.b != m.b || _prevMatrix.c != m.c || _prevMatrix.d != m.d) { + _updateAngles(); + } + target.rotation = pp.angle + rotationOffset; + } + } + + } + + /** + * Translates the progress along a particular segment of the LinePath2D to an overall progress + * value, making it easy to position an object like "halfway along the 2nd segment of the line". For example: + *

+ * + * path.addFollower(mc, path.getSegmentProgress(2, 0.5)); + * + *

+ * + * @param segment The segment number of the line. For example, a line defined by 3 Points would have two segments. + * @param progress The progress along the segment. For example, the midpoint of the second segment would be getSegmentProgress(2, 0.5);. + * @return The progress value (between 0 and 1) describing the overall progress on the entire LinePath2D. + */ + public function getSegmentProgress(segment:uint, progress:Number):Number { + if (_first == null) { + return 0; + } else if (_points.length <= segment) { + segment = _points.length; + } + var pp:PathPoint = _points[segment - 1]; + return pp.progress + ((progress * pp.length) / _totalLength); + } + + /** + * Finds the segment associated with a particular a progress value along the entire LinePath2D. + * For example, to find which segment is halfway along the LinePath2D: + * + *

+ * path.getSegment(0.5); + *

+ * + *

To find the segment associated with the LinePath2D's current progress, simply omit the + * progress parameter:

+ * + *

+ * var curSegment = path.getSegment(); + *

+ * + * @param progress The progress along the entire LinePath2D (a value between 0 and 1). For example, the midpoint would be getSegment(0.5);. + * @return An integer describing the segment number where the first is 1, second is 2, etc. + */ + public function getSegment(progress:Number=NaN):uint { + if (!(progress || progress == 0)) { + progress = _progress; + } + if (_points.length < 2) { + return 0; + } + var l:int = _points.length; + for (var i:int = 1; i < l; i++) { + if (progress < (_points[i] as PathPoint).progress) { + return i; + } + } + return _points.length - 1; + } + + /** + * Allows you to snap an object like a Sprite, Point, MovieClip, etc. to the LinePath2D by determining + * the closest position along the line to the current position of the object. It will automatically + * create a PathFollower instance for the target object and reposition it on the LinePath2D. + * + * @param target The target object that should be repositioned onto the LinePath2D. + * @param autoRotate When autoRotate is true, the follower will automatically be rotated so that it is oriented to the angle of the path that it is following. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + * @return A PathFollower instance that was created for the target. + */ + public function snap(target:Object, autoRotate:Boolean=false, rotationOffset:Number=0):PathFollower { + return this.addFollower(target, getClosestProgress(target), autoRotate, rotationOffset); + } + + /** + * Finds the closest overall progress value on the LinePath2D based on the + * target object's current position (x and y properties). For example, + * to position the mc object on the LinePath2D at the spot that's closest to the Point x:100, y:50, + * you could do:

+ * + * path.addFollower(mc, path.getClosestProgress(new Point(100, 50))); + * + *

+ * + * @param target The target object whose position (x/y property values) are analyzed for proximity to the LinePath2D. + * @return The overall progress value describing the position on the LinePath2D that is closest to the target's current position. + */ + public function getClosestProgress(target:Object):Number { + if (_first == null || _points.length == 1) { + return 0; + } + + var closestPath:PathPoint; + var closest:Number = 9999999999; + var length:Number = 0; + var halfPI:Number = Math.PI / 2; + + var xTarg:Number = target.x; + var yTarg:Number = target.y; + var pp:PathPoint = _first; + var dxTarg:Number, dyTarg:Number, dxNext:Number, dyNext:Number, dTarg:Number, angle:Number, next:PathPoint, curDist:Number; + while (pp) { + dxTarg = xTarg - pp.x; + dyTarg = yTarg - pp.y; + next = (pp.next != null) ? pp.next : pp; + dxNext = next.x - pp.x; + dyNext = next.y - pp.y; + dTarg = Math.sqrt(dxTarg * dxTarg + dyTarg * dyTarg); + + angle = Math.atan2(dyTarg, dxTarg) - Math.atan2(dyNext, dxNext); + if (angle < 0) { + angle = -angle; + } + + if (angle > halfPI) { //obtuse + if (dTarg < closest) { + closest = dTarg; + closestPath = pp; + length = 0; + } + } else { + curDist = Math.cos(angle) * dTarg; + if (curDist < 0) { + curDist = -curDist; + } + if (curDist > pp.length) { + dxNext = xTarg - next.x; + dyNext = yTarg - next.y; + curDist = Math.sqrt(dxNext * dxNext + dyNext * dyNext); + if (curDist < closest) { + closest = curDist; + closestPath = pp; + length = pp.length; + } + } else { + curDist = Math.sin(angle) * dTarg; + if (curDist < closest) { + closest = curDist; + closestPath = pp; + length = Math.cos(angle) * dTarg; + } + } + } + pp = pp.next; + } + + return closestPath.progress + (length / _totalLength); + } + + +//---- GETTERS / SETTERS ---------------------------------------------------------------------- + + /** Total length of the LinePath2D as though it were stretched out in a straight, flat line. **/ + public function get totalLength():Number { + return _totalLength; + } + + /** The array of Points through which the LinePath2D is drawn. IMPORTANT: Changes to the array are NOT automatically applied or reflected in the LinePath2D - just like the filters property of a DisplayObject, you must set the points property of a LinePath2D directly to ensure that any changes are applied internally. **/ + public function get points():Array { + var a:Array = []; + var l:int = _points.length; + for (var i:int = 0; i < l; i++) { + a[i] = _points[i].point; + } + return a; + } + public function set points(value:Array):void { + _points = []; + insertMultiplePoints(value, 0); + _redrawLine = true; + update(null); + } + + } +} +import flash.geom.Point; + +internal class PathPoint { + public var x:Number; + public var y:Number; + public var progress:Number; + public var xChange:Number; + public var yChange:Number; + public var point:Point; + public var length:Number; + public var angle:Number; + + public var next:PathPoint; + + public function PathPoint(point:Point) { + this.x = point.x; + this.y = point.y; + this.point = point; + } + +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/MotionPath.as b/src/com/greensock/motionPaths/MotionPath.as new file mode 100644 index 0000000..86d8b18 --- /dev/null +++ b/src/com/greensock/motionPaths/MotionPath.as @@ -0,0 +1,522 @@ +/** + * VERSION: 0.6 + * DATE: 2011-08-19 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.motionPaths { + import flash.display.Shape; + import flash.events.Event; +/** + * [AS3 only] A MotionPath defines a path along which a PathFollower can travel, making it relatively simple to do + * things like tween an object in a circular path. A PathFollower's position along the path is described using + * its progress property, a value between 0 and 1 where 0 is at the beginning of the path, 0.5 is in + * the middle, and 1 is at the very end of the path. So to tween a PathFollower along the path, you can simply + * tween its progress property. To tween ALL of the followers on the path at once, you can + * tween the MotionPath's progress property. PathFollowers automatically wrap so that if + * the progress value exceeds 1 or drops below 0, it shows up on the other end of the path + * + *

Since MotionPath extends the Shape class, you can add an instance to the display list to see a line representation + * of the path drawn which can be helpful especially during the production phase. Use lineStyle() + * to adjust the color, thickness, and other attributes of the line that is drawn (or set the MotionPath's + * visible property to false or don't add it to the display list if you don't want to see the line + * at all). You can also adjust all of its properties like scaleX, scaleY, rotation, width, height, x, + * and y just like any DisplayObject. That means you can tween those values as well to achieve very + * dynamic, complex effects with ease.

+ * + * +import com.greensock.~~; +import com.greensock.plugins.~~; +import com.greensock.motionPaths.~~; +TweenPlugin.activate([CirclePath2DPlugin]); //only needed once in your swf, and only if you plan to use the circlePath2D tweening feature for convenience + +//create a circle motion path at coordinates x:150, y:150 with a radius of 100 +var circle:CirclePath2D = new CirclePath2D(150, 150, 100); + +//tween mc along the path from the bottom (90 degrees) to 315 degrees in the counter-clockwise direction and make an extra revolution +TweenLite.to(mc, 3, {circlePath2D:{path:circle, startAngle:90, endAngle:315, autoRotate:true, direction:Direction.COUNTER_CLOCKWISE, extraRevolutions:1}}); + +//tween the circle's rotation, scaleX, scaleY, x, and y properties: +TweenLite.to(circle, 3, {rotation:180, scaleX:0.5, scaleY:2, x:250, y:200}); + +//show the path visually by adding it to the display list (optional) +this.addChild(circle); + + +//--- Instead of using the plugin, you could manually manage followers and tween their "progress" property... + +//make the MovieClip "mc2" follow the circle and start at a position of 90 degrees (this returns a PathFollower instance) +var follower:PathFollower = circle.addFollower(mc2, circle.angleToProgress(90)); + +//tween the follower clockwise along the path to 315 degrees +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 315, Direction.CLOCKWISE)}); + +//tween the follower counter-clockwise to 200 degrees and add an extra revolution +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 200, Direction.COUNTER_CLOCKWISE, 1)}); + + * + *

NOTES

+ *
    + *
  • All followers are automatically updated when you alter the MotionPath that they're following.
  • + *
  • To tween all followers along the path at once, simply tween the MotionPath's progress + * property which will provide better performance than tweening each follower independently.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class MotionPath extends Shape { + /** @private **/ + protected static const _RAD2DEG:Number = 180 / Math.PI; + /** @private **/ + protected static const _DEG2RAD:Number = Math.PI / 180; + + /** @private **/ + protected var _redrawLine:Boolean; + + /** @private **/ + protected var _thickness:Number; + /** @private **/ + protected var _color:uint; + /** @private **/ + protected var _lineAlpha:Number; + /** @private **/ + protected var _pixelHinting:Boolean; + /** @private **/ + protected var _scaleMode:String; + /** @private **/ + protected var _caps:String; + /** @private **/ + protected var _joints:String; + /** @private **/ + protected var _miterLimit:Number; + + /** @private **/ + protected var _rootFollower:PathFollower; + /** @private **/ + protected var _progress:Number; + /** @private not re-interpolated between 0 and 1. **/ + protected var _rawProgress:Number; + + /** @private **/ + public function MotionPath() { + _progress = _rawProgress = 0; + lineStyle(1, 0x666666, 1, false, "none", null, null, 3, true); + this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true); + } + + /** @private **/ + protected function onAddedToStage(event:Event):void { + update(); + } + + /** + * Adds a follower to the path, optionally setting it to a particular progress position. If + * the target isn't a PathFollower instance already, one will be created for it. The target + * can be any object that has x and y properties. + * + * @param target Any object that has x and y properties that you'd like to follow the path. Existing PathFollower instances are allowed. + * @param progress The progress position at which the target should be placed initially (0 by default) + * @param autoRotate When autoRotate is true, the target will automatically be rotated so that it is oriented to the angle of the path. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + * @return A PathFollower instance associated with the target (you can tween this PathFollower's progress property to move it along the path). + */ + public function addFollower(target:*, progress:Number=0, autoRotate:Boolean=false, rotationOffset:Number=0):PathFollower { + var f:PathFollower = getFollower(target); + if (f == null) { + f = new PathFollower(target); + } + f.autoRotate = autoRotate; + f.rotationOffset = rotationOffset; + if (f.path != this) { + if (_rootFollower) { + _rootFollower.cachedPrev = f; + } + f.cachedNext = _rootFollower; + _rootFollower = f; + f.path = this; + f.progress = progress; + } + return f; + } + + /** + * Removes the target as a follower. The target can be a PathFollower instance or the target associated + * with one of the PathFollower instances. + * + * @param target the target or PathFollower instance to remove. + */ + public function removeFollower(target:*):void { + var f:PathFollower = getFollower(target); + if (f == null) { + return; + } + if (f.cachedNext) { + f.cachedNext.cachedPrev = f.cachedPrev; + } + if (f.cachedPrev) { + f.cachedPrev.cachedNext = f.cachedNext; + } else if (_rootFollower == f) { + _rootFollower = f.cachedNext; + } + f.cachedNext = f.cachedPrev = null; + f.path = null; + } + + /** Removes all followers. **/ + public function removeAllFollowers():void { + var f:PathFollower = _rootFollower; + var next:PathFollower; + while (f) { + next = f.cachedNext; + f.cachedNext = f.cachedPrev = null; + f.path = null; + f = next; + } + _rootFollower = null; + } + + /** + * Distributes objects evenly along the MotionPath. You can optionally define minimum and maximum + * progress values between which the objects will be distributed. For example, if you want them + * distributed from the very beginning of the path to the middle, you would do:

+ * + * path.distribute([mc1, mc2, mc3], 0, 0.5);

+ * + *

As it loops through the targets array, if a target is found for which a PathFollower + * doesn't exist, one will automatically be created and added to the path. The targets + * array can be populated with PathFollowers or DisplayObjects or Points or pretty much any object.

+ * + * @param targets An array of targets (PathFollowers, DisplayObjects, Points, or pretty much any object) that should be distributed evenly along the MotionPath. As it loops through the targets array, if a target is found for which a PathFollower doesn't exist, one will automatically be created and added to the path. + * @param min The minimum progress value at which the targets will begin being distributed. This value will always be between 0 and 1. For example, if the targets should be distributed from the midpoint of the path through the end, the min parameter would be 0.5 and the max parameter would be 1. + * @param max The maximum progress value where the targets will end distribution. This value will always be between 0 and 1. For example, if the targets should be distributed from the midpoint of the path through the end, the min parameter would be 0.5 and the max parameter would be 1. + * @param autoRotate When autoRotate is true, the target will automatically be rotated so that it is oriented to the angle of the path. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. For example, to always add 90 degrees to the autoRotation, rotationOffset would be 90. + */ + public function distribute(targets:Array=null, min:Number=0, max:Number=1, autoRotate:Boolean=false, rotationOffset:Number=0):void { + if (targets == null) { + targets = this.followers; + } + min = _normalize(min); + max = _normalize(max); + var f:PathFollower; + var i:int = targets.length; + var space:Number = (i > 1) ? (max - min) / (i - 1) : 1; + while (--i > -1) { + f = getFollower(targets[i]); + if (f == null) { + f = this.addFollower(targets[i], 0, autoRotate, rotationOffset); + } + f.cachedProgress = f.cachedRawProgress = min + (space * i); + this.renderObjectAt(f.target, f.cachedProgress, autoRotate, rotationOffset); + } + } + + /** @private **/ + protected function _normalize(num:Number):Number { + if (num > 1) { + num -= int(num); + } else if (num < 0) { + num -= int(num) - 1; + } + return num; + } + + /** + * Returns the PathFollower instance associated with a particular target or null if none exists. + * + * @param target The target whose PathFollower instance you want returned. + * @return PathFollower instance + */ + public function getFollower(target:Object):PathFollower { + if (target is PathFollower) { + return target as PathFollower; + } + var f:PathFollower = _rootFollower; + while (f) { + if (f.target == target) { + return f; + } + f = f.cachedNext; + } + return null; + } + + /** + * Forces the MotionPath to re-render itself and all of its followers. + * + * @param event An optional Event that is accepted just to make it easier for use as an event handler (to have it update automatically on every frame, for example, you could add an ENTER_FRAME listener and point it to this method). **/ + public function update(event:Event=null):void { + + } + + /** + * Positions any object with x and y properties on the path at a specific progress position. + * For example, to position mc in the middle of the path, you would do:

+ * + * myPath.renderObjectAt(mc, 0.5);

+ * + *

Some paths have methods to translate other meaningful information into a progress value, like + * for a CirclePath2D you can get the progress associated with the 90-degree position with the + * angleToPosition() method like this:

+ * + * myCircle.renderObjectAt(mc, myCircle.angleToProgress(90)); + * + *

+ * + * @param target The target object to position + * @param progress The progress value (typically between 0 and 1 where 0 is the beginning of the path, 0.5 is in the middle, and 1 is at the end) + * @param autoRotate When autoRotate is true, the target will automatically be rotated so that it is oriented to the angle of the path. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + */ + public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void { + + } + + /** + * Sets the line style for the path which you will only see if you add the path to the display list + * with something like addChild() and make sure the visible property is true. For example, to make + * a CirclePath2D visible with a red line red that's 3 pixels thick, you could do: + * + * +var myCircle:CirclePath2D = new CirclePath2D(150, 150, 100); +myCircle.lineStyle(3, 0xFF0000); +addChild(myCircle); + + * + * @param thickness line thickness + * @param color line color + * @param alpha line alpha + * @param pixelHinting pixel hinting + * @param scaleMode scale mode + * @param caps caps + * @param joints joints + * @param miterLimit miter limit + * @param skipRedraw if true, the redraw will be skipped. + */ + public function lineStyle(thickness:Number=1, color:uint=0x666666, alpha:Number=1, pixelHinting:Boolean=false, scaleMode:String="none", caps:String=null, joints:String=null, miterLimit:Number=3, skipRedraw:Boolean=false):void { + _thickness = thickness; + _color = color; + _lineAlpha = alpha; + _pixelHinting = pixelHinting; + _scaleMode = scaleMode; + _caps = caps; + _joints = joints; + _miterLimit = miterLimit; + _redrawLine = true; + if (!skipRedraw) { + update(); + } + } + + /** @inheritDoc **/ + override public function get rotation():Number { + return super.rotation; + } + override public function set rotation(value:Number):void { + super.rotation = value; + update(); + } + + /** @inheritDoc **/ + override public function get scaleX():Number { + return super.scaleX; + } + override public function set scaleX(value:Number):void { + super.scaleX = value; + update(); + } + + /** @inheritDoc **/ + override public function get scaleY():Number { + return super.scaleY; + } + override public function set scaleY(value:Number):void { + super.scaleY = value; + update(); + } + + /** @inheritDoc **/ + override public function get x():Number { + return super.x; + } + override public function set x(value:Number):void { + super.x = value; + update(); + } + + /** @inheritDoc **/ + override public function get y():Number { + return super.y; + } + override public function set y(value:Number):void { + super.y = value; + update(); + } + + /** @inheritDoc **/ + override public function get width():Number { + return super.width; + } + override public function set width(value:Number):void { + super.width = value; + update(); + } + + /** @inheritDoc **/ + override public function get height():Number { + return super.height; + } + override public function set height(value:Number):void { + super.height = value; + update(); + } + + /** @inheritDoc **/ + override public function get visible():Boolean { + return super.visible; + } + override public function set visible(value:Boolean):void { + super.visible = value; + _redrawLine = true; + update(); + } + + /** + * Identical to progress except that the value is not re-interpolated between 0 and 1. + * For example, if you set the motion path's rawProgress to 2.1, progress + * would be 0.1 (the corresponding value between 0 and 1), essentially wrapping it. If rawProgress + * is set to -3.4, progress would be 0.6. Setting progress affects rawProgress + * and vice versa. For example: + * + * +myPath.progress = 2.1; +trace(myPath.progress); //traces "0.1" +trace(myPath.rawProgress); //traces "2.1" + + * + *

Either property can be used to move all followers along the path. Unlike a PathFollower's + * progress or rawProgress, this value is not absolute for motion paths - it simply + * facilitates relative movement of followers together along the path in a way that performs better than + * tweening each follower independently (plus it's easier). If your goal is to tween all followers around + * a CirclePath2D twice completely, for example, you could just add 2 to the progress or + * rawProgress value or use a relative value in the tween, like:

+ * + * TweenLite.to(myCircle, 5, {rawProgress:"2"}); //or myCircle.rawProgress + 2 + * + *

+ * @see #progress + **/ + public function get rawProgress():Number { + return _rawProgress; + } + public function set rawProgress(value:Number):void { + this.progress = value; + } + + /** + * A value between 0 and 1 that can be used to move all followers along the path. progress + * is identical to rawProgress except that the rawProgress is not re-interpolated + * between 0 and 1. For example, if you set the motion path's rawProgress to 2.1, progress + * would be 0.1 (the corresponding value between 0 and 1), essentially wrapping it. If rawProgress + * is set to -3.4, progress would be 0.6. You may set progress to any value but it will + * be re-interpolated to its corresponding value between 0 and 1 very much like a DisplayObject's "rotation" + * property in Flash where setting it to 270 works fine but when you trace() the rotation value it will report + * as -90 instead because rotation is always interpolated to be between 180 and -180. Setting progress + * affects rawProgress too. For example: + * + * +myPath.progress = 2.1; +trace(myPath.progress); //traces "0.1" +trace(myPath.rawProgress); //traces "2.1" + + * + *

Either property can be used to move all followers along the path. Unlike a PathFollower's + * progress or rawProgress, this value is not absolute for motion paths - it simply + * facilitates movement of followers together along the path in a way that performs better than + * tweening each follower independently (plus it's easier). If your goal is to tween all followers around + * a CirclePath2D twice completely, you could just add 2 to the progress or + * rawProgress value or use a relative value in the tween, like:

+ * + * TweenLite.to(myCircle, 5, {progress:"2"}); //or myCircle.progress + 2

+ * + *

Also note that if you set progress to any value outside of the 0-1 range, + * rawProgress will be set to that exact value. If progress is + * set to a value within the typical 0-1 range, it will only affect the decimal value of + * rawProgress. For example, if rawProgress is 3.4 and then you + * set progress to 0.1, rawProgress will end up at 3.1 (notice + * the "3" integer was kept). But if progress was instead set to 5.1, since + * it exceeds the 0-1 range, rawProgress would become 5.1. This behavior was + * adopted in order to deal most effectively with wrapping situations. For example, if + * rawProgress was tweened to 3.4 and then later you wanted to fine-tune + * where things were positioned by tweening progress to 0.8, it still may be + * important to be able to determine how many loops/wraps occurred, so rawProgress + * should be 3.8, not reset to 0.8. Feel free to use rawProgress exclusively if you + * prefer to avoid any of the re-interpolation that occurs with progress.

+ * + * @see #rawProgress + **/ + public function get progress():Number { + return _progress; + } + public function set progress(value:Number):void { + if (value > 1) { + _rawProgress = value; + value -= int(value); + if (value == 0) { + value = 1; + } + } else if (value < 0) { + _rawProgress = value; + value -= int(value) - 1; + } else { + _rawProgress = int(_rawProgress) + value; + } + var dif:Number = value - _progress; + var f:PathFollower = _rootFollower; + while (f) { + f.cachedProgress += dif; + f.cachedRawProgress += dif; + + if (f.cachedProgress > 1) { + f.cachedProgress -= int(f.cachedProgress); + if (f.cachedProgress == 0) { + f.cachedProgress = 1; + } + } else if (f.cachedProgress < 0) { + f.cachedProgress -= int(f.cachedProgress) - 1; + } + + f = f.cachedNext; + } + _progress = value; + update(); + } + + /** Returns an array of all PathFollower instances associated with this path **/ + public function get followers():Array { + var a:Array = []; + var cnt:uint = 0; + var f:PathFollower = _rootFollower; + while (f) { + a[cnt++] = f; + f = f.cachedNext; + } + return a; + } + + /** Returns an array of all target instances associated with the PathFollowers of this path **/ + public function get targets():Array { + var a:Array = []; + var cnt:uint = 0; + var f:PathFollower = _rootFollower; + while (f) { + a[cnt++] = f.target; + f = f.cachedNext; + } + return a; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/PathFollower.as b/src/com/greensock/motionPaths/PathFollower.as new file mode 100644 index 0000000..d8a3204 --- /dev/null +++ b/src/com/greensock/motionPaths/PathFollower.as @@ -0,0 +1,157 @@ +/** + * VERSION: 0.51 + * DATE: 2011-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.motionPaths { +/** + * [AS3 only] A PathFollower is used to associate a particular target object (like a MovieClip, Point, Sprite, etc.) + * with a MotionPath and it offers a tweenable progress property that manages positioning + * the target on the path accordingly. The progress property is a value between + * 0 and 1 where 0 is at the beginning of the path, 0.5 is in the middle, and 1 is at the end. + * When the follower's autoRotate property is true, the target will be + * rotated in relation to the path that it is following. + * + * +import com.greensock.~~; +import com.greensock.motionPaths.~~; + +//create a circle motion path at coordinates x:150, y:150 with a radius of 100 +var circle:CirclePath2D = new CirclePath2D(150, 150, 100); + +//make the MovieClip "mc" follow the circle and start at a position of 90 degrees (this returns a PathFollower instance) +var follower:PathFollower = circle.addFollower(mc, circle.angleToProgress(90), true); + +//tween the follower clockwise along the path to 315 degrees +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 315, Direction.CLOCKWISE)}); + +//tween the follower counter-clockwise to 200 degrees and add an extra revolution +TweenLite.to(follower, 2, {progress:circle.followerTween(follower, 200, Direction.COUNTER_CLOCKWISE, 1)}); + + * + *

NOTES

+ *
    + *
  • All followers are automatically updated when you alter the MotionPath that they're following.
  • + *
  • To tween all followers along the path at once, simply tween the MotionPath's progress + * property which will provide better performance than tweening each follower independently.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class PathFollower { + /** The target object associated with the PathFollower (like a Sprite, MovieClip, Point, etc.). The object must have x and y properties. **/ + public var target:Object; + + /** @private **/ + public var cachedProgress:Number; + /** @private not re-interpolated between 0 and 1. We store this value and cachedProgress instead of calculating one of them on the fly in order to maximize rendering performance. **/ + public var cachedRawProgress:Number; + /** @private **/ + public var cachedNext:PathFollower; + /** @private **/ + public var cachedPrev:PathFollower; + + /** The MotionPath instance that this PathFollower should follow **/ + public var path:MotionPath; + /** When autoRotate is true, the follower will automatically be rotated so that it is oriented to the angle of the path that it is following. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. **/ + public var autoRotate:Boolean; + /** When autoRotate is true, this value will always be added to the resulting rotation of the target. **/ + public var rotationOffset:Number; + + /** + * Constructor + * + * @param target The target object associated with the PathFollower (like a Sprite, MovieClip, Point, etc.). The object must have x and y properties. + * @param autoRotate When autoRotate is true, the follower will automatically be rotated so that it is oriented to the angle of the path that it is following. To offset this value (like to always add 90 degrees for example), use the rotationOffset property. + * @param rotationOffset When autoRotate is true, this value will always be added to the resulting rotation of the target. + */ + public function PathFollower(target:Object, autoRotate:Boolean=false, rotationOffset:Number=0) { + this.target = target; + this.autoRotate = autoRotate; + this.rotationOffset = rotationOffset; + this.cachedProgress = this.cachedRawProgress = 0; + } + + /** + * Identical to progress except that the value doesn't get re-interpolated between 0 and 1. + * rawProgress (and progress) indicates the follower's position along the motion path. + * For example, to place the object on the path at the halfway point, you could set its rawProgress + * to 0.5. You can tween to values that are greater than 1 or less than 0. For example, setting rawProgress + * to 1.2 also sets progress to 0.2 and setting rawProgress to -0.2 is the + * same as setting progress to 0.8. If your goal is to tween the PathFollower around a CirclePath2D twice + * completely, you could just add 2 to the rawProgress value or use a relative value in the tween, like:

+ * + * TweenLite.to(myFollower, 5, {rawProgress:"2"}); //or myFollower.rawProgress + 2 + * + *

+ * + *

Since rawProgress doesn't re-interpolate values to always fitting between 0 and 1, it + * can be useful if you need to find out how many times the PathFollower has wrapped.

+ * + * @see #progress + **/ + public function get rawProgress():Number { + return this.cachedRawProgress; + } + public function set rawProgress(value:Number):void { + this.progress = value; + } + + /** + * A value between 0 and 1 that indicates the follower's position along the motion path. For example, + * to place the object on the path at the halfway point, you would set its progress to 0.5. + * You can tween to values that are greater than 1 or less than 0 but the values are simply wrapped. + * So, for example, setting progress to 1.2 is the same as setting it to 0.2 and -0.2 is the + * same as 0.8. If your goal is to tween the PathFollower around a CirclePath2D twice completely, you could just + * add 2 to the progress value or use a relative value in the tween, like:

+ * + * TweenLite.to(myFollower, 5, {progress:"2"}); //or myFollower.progress + 2

+ * + *

progress is identical to rawProgress except that rawProgress + * does not get re-interpolated between 0 and 1. For example, if rawProgress + * is set to -3.4, progress would be 0.6. rawProgress can be useful if + * you need to find out how many times the PathFollower has wrapped.

+ * + *

Also note that if you set progress to any value outside of the 0-1 range, + * rawProgress will be set to that exact value. If progress is + * set to a value within the typical 0-1 range, it will only affect the decimal value of + * rawProgress. For example, if rawProgress is 3.4 and then you + * set progress to 0.1, rawProgress will end up at 3.1 (notice + * the "3" integer was kept). But if progress was instead set to 5.1, since + * it exceeds the 0-1 range, rawProgress would become 5.1. This behavior was + * adopted in order to deal most effectively with wrapping situations. For example, if + * rawProgress was tweened to 3.4 and then later you wanted to fine-tune + * where things were positioned by tweening progress to 0.8, it still may be + * important to be able to determine how many loops/wraps occurred, so rawProgress + * should be 3.8, not reset to 0.8. Feel free to use rawProgress exclusively if you + * prefer to avoid any of the re-interpolation that occurs with progress.

+ * + * @see #rawProgress + **/ + public function get progress():Number { + return this.cachedProgress; + } + public function set progress(value:Number):void { + if (value > 1) { + this.cachedRawProgress = value; + this.cachedProgress = value - int(value); + if (this.cachedProgress == 0) { + this.cachedProgress = 1; + } + } else if (value < 0) { + this.cachedRawProgress = value; + this.cachedProgress = value - (int(value) - 1); + } else { + this.cachedRawProgress = int(this.cachedRawProgress) + value; + this.cachedProgress = value; + } + if (this.path) { + this.path.renderObjectAt(this.target, this.cachedProgress, this.autoRotate, this.rotationOffset); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/motionPaths/RectanglePath2D.as b/src/com/greensock/motionPaths/RectanglePath2D.as new file mode 100644 index 0000000..46f3c64 --- /dev/null +++ b/src/com/greensock/motionPaths/RectanglePath2D.as @@ -0,0 +1,218 @@ +/** + * VERSION: 0.4 (beta) + * DATE: 2011-09-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.motionPaths { + import flash.display.Graphics; + import flash.geom.Matrix; + import flash.events.Event; +/** + * [AS3 only] A RectanglePath2D defines a rectangular path on which a PathFollower can be placed, making it simple to tween objects + * along a rectangle's perimeter. A PathFollower's position along the path is described using its progress property, + * a value between 0 and 1 where 0 is at the beginning of the path (top left corner), and as the value increases, it + * moves clockwise along the path so that 0.5 would be at the lower right corner, and 1 is all the way back at the + * upper left corner of the path. So to tween a PathFollower along the path, you can simply tween its + * progress property. To tween ALL of the followers on the path at once, you can tween the + * RectanglePath2D's progress property. PathFollowers automatically wrap so that if the progress + * value exceeds 1 it continues at the beginning of the path. + * + *

Since RectanglePath2D extends the Shape class, you can add an instance to the display list to see a line representation + * of the path drawn which can be helpful especially during the production phase. Use lineStyle() + * to adjust the color, thickness, and other attributes of the line that is drawn (or set the RectanglePath2D's + * visible property to false or don't add it to the display list if you don't want to see the line + * at all). You can also adjust all of its properties like scaleX, scaleY, rotation, width, height, x, + * and y. That means you can tween those values as well to achieve very dynamic, complex effects + * with ease.

+ * + * +import com.greensock.~~; +import com.greensock.motionPaths.~~; + +//create a rectangular motion path at coordinates x:25, y:25 with a width of 150 and a height of 100 +var rect:RectanglePath2D = new RectanglePath2D(25, 25, 150, 100, false); + +//position the MovieClip "mc" at the beginning of the path (upper left corner), and reference the resulting PathFollower instance with a "follower" variable. +var follower:PathFollower = rect.addFollower(mc, 0); + +//tween the follower clockwise along the path all the way to the end, one full revolution +TweenLite.to(follower, 2, {progress:1}); + +//tween the follower counter-clockwise by using a negative progress value +TweenLite.to(follower, 2, {progress:-1}); + + * + *

NOTES

+ *
    + *
  • All followers' positions are automatically updated when you alter the MotionPath that they're following.
  • + *
  • To tween all followers along the path at once, simply tween the MotionPath's progress + * property which will provide better performance than tweening each follower independently.
  • + *
+ * + *

Copyright 2010-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class RectanglePath2D extends MotionPath { + /** @private **/ + protected var _rawWidth:Number; + /** @private **/ + protected var _rawHeight:Number; + /** @private **/ + protected var _centerOrigin:Boolean; + + /** + * Constructor + * + * @param x The x coordinate of the origin of the rectangle (typically its top left corner unless centerOrigin is true) + * @param y The y coordinate of the origin of the rectangle (typically its top left corner unless centerOrigin is true) + * @param rawWidth The width of the rectangle in its unrotated and unscaled state + * @param rawHeight The height of the rectangle in its unrotated and unscaled state + * @param centerOrigin To position the origin (registration point around which transformations occur) at the center of the rectangle instead of its upper left corner, set centerOrigin to true (it is false by default). + */ + public function RectanglePath2D(x:Number, y:Number, rawWidth:Number, rawHeight:Number, centerOrigin:Boolean=false) { + super(); + _rawWidth = rawWidth; + _rawHeight = rawHeight; + _centerOrigin = centerOrigin; + super.x = x; + super.y = y; + } + + /** @inheritDoc **/ + override public function update(event:Event=null):void { + var xOffset:Number = _centerOrigin ? _rawWidth / -2 : 0; + var yOffset:Number = _centerOrigin ? _rawHeight / -2 : 0; + + var length:Number, px:Number, py:Number, xFactor:Number, yFactor:Number; + var m:Matrix = this.transform.matrix; + var a:Number = m.a, b:Number = m.b, c:Number = m.c, d:Number = m.d, tx:Number = m.tx, ty:Number = m.ty; + var f:PathFollower = _rootFollower; + while (f) { + px = xOffset; + py = yOffset; + if (f.cachedProgress < 0.5) { + length = f.cachedProgress * (_rawWidth + _rawHeight) * 2; + if (length > _rawWidth) { //top + px += _rawWidth; + py += length - _rawWidth; + xFactor = 0; + yFactor = _rawHeight; + } else { //right + px += length; + xFactor = _rawWidth; + yFactor = 0; + } + } else { + length = (f.cachedProgress - 0.5) / 0.5 * (_rawWidth + _rawHeight); + if (length <= _rawWidth) { //bottom + px += _rawWidth - length; + py += _rawHeight; + xFactor = -_rawWidth; + yFactor = 0; + } else { //left + py += _rawHeight - (length - _rawWidth); + xFactor = 0; + yFactor = -_rawHeight; + } + } + + f.target.x = px * a + py * c + tx; + f.target.y = px * b + py * d + ty; + + if (f.autoRotate) { + f.target.rotation = Math.atan2(xFactor * b + yFactor * d, xFactor * a + yFactor * c) * _RAD2DEG + f.rotationOffset; + } + + f = f.cachedNext; + } + if (_redrawLine) { + var g:Graphics = this.graphics; + g.clear(); + g.lineStyle(_thickness, _color, _lineAlpha, _pixelHinting, _scaleMode, _caps, _joints, _miterLimit); + g.drawRect(xOffset, yOffset, _rawWidth, _rawHeight); + _redrawLine = false; + } + } + + /** @inheritDoc **/ + override public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void { + if (progress > 1) { + progress -= int(progress); + } else if (progress < 0) { + progress -= int(progress) - 1; + } + + var px:Number = _centerOrigin ? _rawWidth / -2 : 0; + var py:Number = _centerOrigin ? _rawHeight / -2 : 0; + var length:Number, xFactor:Number, yFactor:Number; + if (progress < 0.5) { + length = progress * (_rawWidth + _rawHeight) * 2; + if (length > _rawWidth) { + px += _rawWidth; + py += length - _rawWidth; + xFactor = 0; + yFactor = _rawHeight; + } else { + px += length; + xFactor = _rawWidth; + yFactor = 0; + } + } else { + length = (progress - 0.5) / 0.5 * (_rawWidth + _rawHeight); + if (length <= _rawWidth) { + px += _rawWidth - length; + py += _rawHeight; + xFactor = -_rawWidth; + yFactor = 0; + } else { + py += _rawHeight - (length - _rawWidth); + xFactor = 0; + yFactor = -_rawHeight; + } + } + var m:Matrix = this.transform.matrix; + target.x = px * m.a + py * m.c + m.tx; + target.y = px * m.b + py * m.d + m.ty; + + if (autoRotate) { + target.rotation = Math.atan2(xFactor * m.b + yFactor * m.d, xFactor * m.a + yFactor * m.c) * _RAD2DEG + rotationOffset; + } + } + + +//---- GETTERS / SETTERS ---------------------------------------------------------------------- + + /** width of the rectangle in its unrotated, unscaled state (does not factor in any transformations like scaleX/scaleY/rotation) **/ + public function get rawWidth():Number { + return _rawWidth; + } + public function set rawWidth(value:Number):void { + _rawWidth = value; + _redrawLine = true; + update(); + } + + /** height of the rectangle in its unrotated, unscaled state (does not factor in any transformations like scaleX/scaleY/rotation) **/ + public function get rawHeight():Number { + return _rawHeight; + } + public function set rawHeight(value:Number):void { + _rawHeight = value; + _redrawLine = true; + update(); + } + + /** If true, the origin (registration point) of the RectanglePath2D will be in its center rather than its upper left corner. **/ + public function get centerOrigin():Boolean { + return _centerOrigin; + } + public function set centerOrigin(value:Boolean):void { + _centerOrigin; + _redrawLine = true; + update(); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/AutoAlphaPlugin.as b/src/com/greensock/plugins/AutoAlphaPlugin.as new file mode 100644 index 0000000..a935e2f --- /dev/null +++ b/src/com/greensock/plugins/AutoAlphaPlugin.as @@ -0,0 +1,66 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only, although autoAlpha is recognized inside the CSSPlugin for JavaScript] Tweening "autoAlpha" is + * exactly the same as tweening an object's "alpha" except that it ensures that the object's "visible" property + * is true until autoAlpha reaches zero at which point it will toggle the "visible" property to false. That not + * only improves rendering performance in the Flash Player, but also hides DisplayObjects so that they don't + * interact with the mouse. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.AutoAlphaPlugin; +TweenPlugin.activate([AutoAlphaPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 2, {autoAlpha:0}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class AutoAlphaPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _ignoreVisible:Boolean; + + /** @private **/ + public function AutoAlphaPlugin() { + super("autoAlpha,alpha,visible"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target; + _addTween(target, "alpha", target.alpha, value, "alpha"); + return true; + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + _ignoreVisible = ("visible" in lookup); + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + if (!_ignoreVisible) { + _target.visible = (_target.alpha != 0); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/BevelFilterPlugin.as b/src/com/greensock/plugins/BevelFilterPlugin.as new file mode 100644 index 0000000..072293c --- /dev/null +++ b/src/com/greensock/plugins/BevelFilterPlugin.as @@ -0,0 +1,62 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.filters.BevelFilter; +/** + * [AS3/AS2 only] Tweens a BevelFilter. The following properties are available (you only need to define the ones you want to tween): + * + *
    + *
  • distance : Number [0]
  • + *
  • angle : Number [0]
  • + *
  • highlightColor : uint [0xFFFFFF]
  • + *
  • highlightAlpha : Number [0.5]
  • + *
  • shadowColor : uint [0x000000]
  • + *
  • shadowAlpha :Number [0.5]
  • + *
  • blurX : Number [2]
  • + *
  • blurY : Number [2]
  • + *
  • strength : Number [0]
  • + *
  • quality : uint [2]
  • + *
  • index : uint
  • + *
  • addFilter : Boolean [false]
  • + *
  • remove : Boolean [false]
  • + *
+ *
+ * + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.BevelFilterPlugin; +TweenPlugin.activate([BevelFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {bevelFilter:{blurX:10, blurY:10, distance:6, angle:45, strength:1}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class BevelFilterPlugin extends FilterPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static var _propNames:Array = ["distance","angle","highlightColor","highlightAlpha","shadowColor","shadowAlpha","blurX","blurY","strength","quality"]; + + /** @private **/ + public function BevelFilterPlugin() { + super("bevelFilter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + return _initFilter(target, value, tween, BevelFilter, new BevelFilter(0, 0, 0xFFFFFF, 0.5, 0x000000, 0.5, 2, 2, 0, value.quality || 2), _propNames); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/BezierPlugin.as b/src/com/greensock/plugins/BezierPlugin.as new file mode 100644 index 0000000..31a1075 --- /dev/null +++ b/src/com/greensock/plugins/BezierPlugin.as @@ -0,0 +1,870 @@ +/** + * VERSION: 12.14 + * DATE: 2014-03-12 + * AS3 (AS2 and JavaScript versions also available) + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + + import flash.geom.Point; + +/** + * Animate virtually any property (or properties) along a Bezier (curved) path which you define + * as an array of points/values that can be interpreted 4 different ways (described as the Bezier's "type", like type:"quadratic"): + * + *
    + *
  • "thru" (the default) - the plugin figures out how to draw the Bezier naturally through + * the supplied values using a proprietary GreenSock algorithm. The values you provide in the array are essentially + * treated as anchors on the Bezier and the plugin calculates the control points. The target's current/starting + * values are used as the initial anchor. You can define a curviness special property that + * allows you to adjust the tension on the Bezier where 0 has no curviness (straight lines), 1 is normal + * curviness, 2 is twice the normal curviness, etc. Since "thru" is the default Bezier type, you don't + * need to define a type at all if this is the one you want.
  • + * + *
  • "soft" - the values that you provide in the array act almost like magnets that attract the + * curve towards them, but the Bezier doesn't typically travel through them. They are treated + * as control points on a Quadratic Bezier and the plugin creates the necessary intermediate anchors. + * The target's current/starting values are used as the initial anchor.
  • + * + *
  • "quadratic" - allows you to define standard Quadratic Bezier data (Quadratic Beziers have + * 1 control point between each anchor). The array should start with the first anchor, then control point, + * then anchor, control point, etc. for as many iterations as you want, but obviously make sure that it + * starts and ends with anchors.
  • + * + *
  • "cubic" - allows you to define standard Cubic Bezier data (Cubic Beziers have + * 2 control points between each anchor). The array should start with the first anchor, then 2 control points, + * then anchor, 2 control points, anchor, etc. for as many iterations as you want, but obviously make sure that it + * starts and ends with anchors.
  • + * + *
  • "thruBasic" - the same as "thru" except that it uses a less complex + * algorithm for the initial plotting of the Bezier through the supplied values. The "thruBasic" + * algorithm is a slightly enhanced version of a somewhat common method that does a decent job + * but it is more prone to having kinks or harsh angles when there is a very large segment right + * next to a very short one or when two anchors are very close and the one inbetween them is very distant. + * The proprietary GreenSock "thru" algorithm almost always delivers more natural curves + * than "thruBasic". In terms of calculation expense, "thruBasic" is only about 15-20% faster + * on the initial setup (when the tween begins), but then every update during the tween the speed is + * identical, so overall improvement is negligible (probably less than 1%). The primary reason the + * "thruBasic" option is available is to offer a different style for drawing the Bezier + * through the supplied values. If decreasing load on the CPU is your goal, you'd get better + * results by decreasing the timeResolution, particularly to 0.
  • + *
+ * + *

While it is most common to use x and y (and sometimes z) properties for + * Bezier tweens, you can use any properties (even ones that are function-based getters/setters).

+ * + *

Inside the bezier object, you must define at least a values property, and there are + * several other optional special properties that the BezierPlugin will recognize. Here is a list of them all:

+ * + *
    + *
  • values : Array [REQUIRED] - the array of your Bezier values as generic objects + * (or Point instances). Each object in the array should have matching property names + * (like "x" and "y"). For example, the array might look like: + * [{x:100, y:250}, {x:300, y:0}, {x:500, y:400}]
  • + * + *
  • type : String (default:"thru") - Either "thru", "thruBasic", "soft", "quadratic", + * or "cubic" as described above, indicating how the values should be interpreted.
  • + * + *
  • timeResolution : int (default:6) - due to the nature of Beziers, plotting the progression + * of an object on the path over time can make it appear to speed up or slow down based on the placement + * of the control points and the length of each successive segment on the path, so BezierPlugin implements + * a technique that reduces or eliminates that variance, but it involves breaking the segments down into + * a certain number of pieces which is what timeResolution controls. The greater the number, + * the more accurate the time remapping but there is a processing price to pay for greater precision. + * The default value of 6 is typically fine, but if you notice slight pace changes on the path you can increase + * the timeResolution value. Or, if you want to prioritize speed you could reduce the number. + * If you use a timeResolution value of 0, no length measurements will take place internally which + * delivers maximum processing speed, but you may notice changes in speed during the animation.
  • + * + *
  • curviness : Number (default:1) (only applies to type:"thru") - allows you to adjust the + * tension on the Bezier where 0 has no curviness (straight lines), 1 is normal curviness, 2 is twice + * the normal curviness, etc.
  • + * + *
  • autoRotate : Boolean or Array (default:false) - to automatically rotate the target according + * to its position on the Bezier path, you can use the autoRotate feature (previously called + * orientToBezier). If your Bezier is affecting the "x" and "y" properties of your target + * and you don't need to offset the rotation by a certain amount more than normal, then you can simply + * set autoRotate:true. Or if you want to offset the rotation by a certain amount (in degrees), + * you can define a number like autoRotate:90 (adding 90 degrees in this example). Or for more + * advanced controls, you can define autoRotate as an array. In order to adjust a rotation + * property accurately, the plugin needs 5 pieces of information: + *
      + *
    1. Position property 1 (typically "x")
    2. + *
    3. Position property 2 (typically "y")
    4. + *
    5. Rotational property (typically "rotation")
    6. + *
    7. Number of degrees (or radians) to add to the new rotation (optional - makes it easy to orient your target properly)
    8. + *
    9. Boolean value indicating whether or not the rotational property should be defined in radians rather than degrees (default is false which results in degrees)
    10. + *
    + * The autoRotate property should be an Array containing these values, like + * ["x","y","rotation",90,false]. And if you need to affect multiple rotational + * properties (like in 3D tweens where the Bezier is going through x,y,z points which could affect rotationX, rotationY, and rotationZ), + * you can use an array of arrays, like + * [["x","y","rotationZ",0,false], ["z","x","rotationY",0,false], ["z","y","rotationX",0,false]].
  • + * + *
  • correlate : String (default:"x,y,z") (only applies to type:"thru") - + * a comma-delimited list of property names whose relative distances should be correlated when calculating + * the Bezier that travels through the points. Since x, y, and z are all spacial, it is almost always good + * to correlate them, but properties like scaleX, scaleY, etc. don't typically need to be correlated. + * It is rarely necessary to alter the default correlate value.
  • + *
+ * + * + * SYNTAX + * +//animate obj through the points in the array (notice we're passing the array directly to the bezier rather than creating an object with "values" because we're accepting the defaults) +TweenMax.to(obj, 5, {bezier:[{x:100, y:250}, {x:300, y:0}, {x:500, y:400}], ease:Power1.easeInOut}); + +//if we want to customize things, like the curviness and setting autoRotate:true, we need to define the bezier as an object instead, and pass our array as the "values" property +TweenMax.to(obj, 5, {bezier:{curviness:1.25, values:[{x:100, y:250}, {x:300, y:0}, {x:500, y:400}], autoRotate:true}, ease:Power1.easeInOut}); + +//let's define the type as "soft" instead of using the default "thru" +TweenMax.to(obj, 5, {bezier:{type:"soft", values:[{x:100, y:250}, {x:300, y:0}, {x:500, y:400}], autoRotate:true}, ease:Power1.easeInOut}); + +//now we'll do a cubic Bezier and make our target auto rotate but add 45 degrees to the rotation +TweenMax.to(obj, 5, {bezier:{type:"cubic", values:[{x:100, y:250}, {x:150, y:100}, {x:300, y:500}, {x:500, y:400}], autoRotate:["x","y","rotation",45,false]}, ease:Power1.easeInOut}); + + * + *

You can tap into BezierPlugin's Bezier drawing algorithm by passing its bezierThrough() method your + * array of points/objects and it will spit back and object with all the necessary data, either in Cubic Bezier + * form or in Quadratic Bezier form so that you could, for example, draw the path using Flash's curveTo() functionality. + * It also has some useful static cubicToQuadratic() and quadraticToCubic() conversion methods.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class BezierPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private precalculated for speed **/ + protected static const _RAD2DEG:Number = 180 / Math.PI; + /** @private temporary storage for bezierThrough calculations. ratio 1 (p1) **/ + protected static var _r1:Array = []; + /** @private temporary storage for bezierThrough calculations. ratio 2 (p1 + p2) **/ + protected static var _r2:Array = []; + /** @private temporary storage for bezierThrough calculations. **/ + protected static var _r3:Array = []; + /** @private used to store a boolean value indicating whether or not a particular property should be correlated. Basically a lookup table to speed things up. This allows us to avoid garbage collection headaches because bezierThrough() might be called a LOT in an app, thus creating a temporary local variable each time in the method would be more problematic. **/ + protected static var _corProps:Object = {}; + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _autoRotate:Array; + /** @private If the values should be rounded to the nearest integer, _round will be set to true. **/ + protected var _round:Object; + /** @private array containing the numeric length of each segment, like [3, 5, 19, 2] **/ + protected var _lengths:Array; + /** @private array containing arrays of length values for each segment, like [[2,4,12,56], [3,6,23,45,3]] (all arrays will contain the same number of elements, determined by "precision") **/ + protected var _segments:Array; + /** @private approximate total length of all Bezier segments combined **/ + protected var _length:Number; + /** @private a lookup table to figure out if a property is a function or not **/ + protected var _func:Object; + /** @private array of properties that are being tweened, like ["x","y"] **/ + protected var _props:Array; + /** @private the lower (minimum) threshold that still applies to the current segment. Like if the entire group of Beziers is 100 long, the first one might be from 0 to 50, the next 50 to 70, and the last 70 to 100, so _l1 would be 0, 50, or 70 depending on which segment was rendered last (this is like a caching mechanism that enhances performance and avoids lookups in many situations) **/ + protected var _l1:Number; //length 1 (lower) + /** @private the upper (maximum) threshold that still applies to the current segment. Like if the entire group of Beziers is 100 long, the first one might be from 0 to 50, the next 50 to 70, and the last 70 to 100, so _l2 would be 50, 70, or 100 depending on which segment was rendered last (this is like a caching mechanism that enhances performance and avoids lookups in many situations) **/ + protected var _l2:Number; //length 2 (upper) + /** @private the index number of the current segment (from the _lengths array) **/ + protected var _li:Number; //length index + /** @private the current array of segment lengths from the _segments array. **/ + protected var _curSeg:Array; //segment array + /** @private the lower (minimum) threshold that still applies to the current segment length from inside the _curSeg array. Like if the current segment is 100 long, the first measurement might be from 0 to 50, the next 50 to 70, and the last 70 to 100, so _s1 would be 0, 50, or 70 depending on which segment piece was rendered last (this is like a caching mechanism that enhances performance and avoids lookups in many situations) **/ + protected var _s1:Number; //segment 1 (lower) + /** @private the upper (maximum) threshold that still applies to the current segment length from inside the _curSeg array. Like if the current segment is 100 long, the first measurement might be from 0 to 50, the next 50 to 70, and the last 70 to 100, so _s2 would be 50, 70, or 100 depending on which segment piece was rendered last (this is like a caching mechanism that enhances performance and avoids lookups in many situations) **/ + protected var _s2:Number; //segment 2 (upper) + /** @private the index number of the current segment length from _curSeg **/ + protected var _si:Number; //segment index; + /** @private **/ + protected var _beziers:Object; + /** @private total number of segments **/ + protected var _segCount:int; + /** @private 1 / precision (precalculated for speed) **/ + protected var _prec:Number; //precision + /** @private **/ + protected var _timeRes:int; + /** @private we need to store the initial rotation for autoRotate tweens so that if/when the tween is rewound completely, the original value gets re-applied. **/ + protected var _initialRotations:Array; + /** @private we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (which indicates it's a from() tween) in which case it's 1. **/ + protected var _startRatio:int; + + + /** @private **/ + public function BezierPlugin() { + super("bezier"); + this._overwriteProps.pop(); + this._func = {}; + this._round = {}; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + this._target = target; + var vars:Object = (value is Array) ? {values:value} : value; + this._props = []; + this._timeRes = (vars.timeResolution == null) ? 6 : int(vars.timeResolution); + var values:Array = vars.values || [], + first:Object = {}, + second:Object = values[0], + autoRotate:Object = vars.autoRotate || tween.vars.orientToBezier, + p:String, isFunc:Boolean, i:int, j:int, ar:Array, prepend:Object; + + this._autoRotate = autoRotate ? (autoRotate is Array) ? autoRotate as Array : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate))]] : null; + + if (second is Point) { + this._props = ["x","y"]; + } else { + for (p in second) { + this._props.push(p); + } + } + + i = this._props.length; + while (--i > -1) { + p = this._props[i]; + this._overwriteProps.push(p); + isFunc = this._func[p] = (target[p] is Function); + first[p] = (!isFunc) ? target[p] : target[ ((p.indexOf("set") || !("get" + p.substr(3) in target)) ? p : "get" + p.substr(3)) ](); + if (!prepend) if (first[p] !== values[0][p]) { + prepend = first; + } + } + this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate || "x,y,z", prepend) : _parseBezierData(values, vars.type, first); + this._segCount = this._beziers[p].length; + + if (this._timeRes) { + var ld:Object = _parseLengthData(this._beziers, this._timeRes); + this._length = ld.length; + this._lengths = ld.lengths; + this._segments = ld.segments; + this._l1 = this._li = this._s1 = this._si = 0; + this._l2 = this._lengths[0]; + this._curSeg = this._segments[0]; + this._s2 = this._curSeg[0]; + this._prec = 1 / this._curSeg.length; + } + + if ((ar = this._autoRotate)) { + this._initialRotations = []; + if (!(ar[0] is Array)) { + this._autoRotate = ar = [ar]; + } + i = ar.length; + while (--i > -1) { + for (j = 0; j < 3; j++) { + p = ar[i][j]; + this._func[p] = (target[p] is Function) ? target[ ((p.indexOf("set") || !("get" + p.substr(3) in target)) ? p : "get" + p.substr(3)) ] : false; + } + p = ar[i][2]; + this._initialRotations[i] = this._func[p] ? this._func[p]() : this._target[p]; + } + } + _startRatio = tween.vars.runBackwards ? 1 : 0; + return true; + } + + /** + * Takes an array that contains objects (could be Points, could be generic objects with + * any number of properties but they should all match in terms of the names of properties like + * [{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}]) and plots Bezier + * segments THROUGH those values and returns an array containing a generic object for each Bezier segment. By default + * Cubic Beziers (which use 2 control points per segment) are used but you can optionally request Quadratic Beziers (1 control + * point per segment) instead using the quadratic parameter. + * + *

For Cubic Beziers (the default), each segment object will have a, b, c, and d properties:

+ * + *
    + *
  • a - the starting anchor value of the Cubic Bezier segment. For example, + * bezierThrough([{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}]); + * would return an object with "x", "y", and "scaleX" properties, each containing an array of objects, one per Bezier segment and you could + * access the first Bezier's initial anchor values like: + * myReturnedObject.x[0].a, myReturnedObject.y[0].a, and myReturnedObject.scaleX[0].a
  • + *
  • b - the first control point value of the Cubic Bezier segment. For example, + * bezierThrough([{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}]); + * would return an object with "x", "y", and "scaleX" properties, each containing an array of objects, one per Bezier segment and you could + * access the first Bezier's first control point values like: + * myReturnedObject.x[0].b, myReturnedObject.y[0].b, and myReturnedObject.scaleX[0].b
  • + *
  • c - the second control point value of the Cubic Bezier segment. For example, + * bezierThrough([{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}]); + * would return an object with "x", "y", and "scaleX" properties, each containing an array of objects, one per Bezier segment and you could + * access the first Bezier's second control point values like: + * myReturnedObject.x[0].c, myReturnedObject.y[0].c, and myReturnedObject.scaleX[0].c
  • + *
  • d - the final anchor value of the Cubic Bezier segment. For example, + * bezierThrough([{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}]); + * would return an object with "x", "y", and "scaleX" properties, each containing an array of objects, one per Bezier segment and you could + * access the first Bezier's final anchor values like: + * myReturnedObject.x[0].d, myReturnedObject.y[0].d, and myReturnedObject.scaleX[0].d
  • + *
+ * + * + *

If you set the quadratic parameter to true, all of the Bezier segments will contain a, b, + * and c properties (NOT d) where b is the only control point. This can be + * very useful because some drawing APIs only understand Quadratic Beziers. There are 4 times as many Quadratic Beziers returned as + * Cubic Beziers, though, due to the fact that the internal algorithm uses Cubic Beziers to plot the points (they're much more flexible) + * and then splits each into 4 Quadratic ones.

+ * + * + //input: + var beziers:Object = BezierPlugin.bezierThrough([{x:0, y:0}, {x:250, y:400}, {x:500, y:0}]); + + //output: + { + x:[{a:0, b:0, c:125, d:250}, {a:250, b:375, c:500, d:500}], + y:[{a:0, b:0, c:400, d:400}, {a:400, b:400, c:0, d:0}] + } + * + * + * + //get quadratic beziers so that we can use Flash's drawing API... + var beziers:Object = BezierPlugin.bezierThrough([{x:0, y:0}, {x:250, y:400}, {x:500, y:0}], 1, true); + + var bx:Array = beziers.x; //the "x" Beziers + var by:Array = beziers.y; //the "y" Beziers + + //draw the curve in Flash using AS3: + var g:Graphics = this.graphics; + g.moveTo(bx[0].a, by[0].a); + for (var i:int = 0; i < bx.length; i++) { + g.curveTo(bx[i].b, by[i].b, bx[i].c, by[i].c); + } + + * + * + * @param values An array containing generic objects with matching properties (or Point instances) through which the Beziers should be plotted, like [{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}] + * @param curviness A number (default: 1) that controls the strength of the curves that are plotted through the values. A curviness of 0 would be result in straight lines, 1 is normal curviness, and 2 would be extreme curves. Use any value. + * @param quadratic if true, quadratic Bezier information will be returned instead of cubic Bezier data, thus each object in the returned array will only contain a, b, and c properties where b is the control point. + * @param basic if true, a faster algorithm will be used for calculating the control points which can be less aesthetically pleasing in situations where there are large differences in the spaces or angles between the values provided (the curves are more likely to get kinks or harsh angles) + * @param correlate [optional] a comma-delimited list of property names whose relative distances should be correlated with each other when calculating the curvature of the Bezier through the values (the default is "x,y,z" because those are almost always properties that should be correlated). + * @param prepend [optional] an object to treat as though it is the first element in the values array (typically only used internally for adding a tween's starting values) + * @return An object with properties matching those from the objects in the values array, with an array assigned to each property populated with an object for each Bezier. The Bezier objects will contain a, b, c (and d if quadratic is not true) properties for the anchors and control points. + */ + public static function bezierThrough(values:Array, curviness:Number=1, quadratic:Boolean=false, basic:Boolean=false, correlate:String="x,y,z", prepend:Object=null):Object { + var obj:Object = {}, + first:Object = prepend || values[0], + props:Array, i:int, p:String, j:int, a:Array, l:int, r:Number, seamless:Boolean, last:Object; + correlate = ","+correlate+","; + if (first is Point) { + props = ["x","y"]; + } else { + props = []; + for (p in first) { + props.push(p); + } + } + //check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later) + if (values.length > 1) { + last = values[values.length - 1]; + seamless = true; + i = props.length; + while (--i > -1) { + p = props[i]; + if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors. For example, if you set an object's position to 4.945, Flash will make it 4.9 + seamless = false; + break; + } + } + if (seamless) { + values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens + if (prepend) { + values.unshift(prepend); + } + values.push(values[1]); + prepend = values[values.length - 3]; + } + } + _r1.length = _r2.length = _r3.length = 0; + i = props.length; + while (--i > -1) { + p = props[i]; + _corProps[p] = (correlate.indexOf(","+p+",") !== -1); + obj[p] = _parseAnchors(values, p, _corProps[p], prepend); + } + i = _r1.length; + while (--i > -1) { + _r1[i] = Math.sqrt(_r1[i]); + _r2[i] = Math.sqrt(_r2[i]); + } + if (!basic) { + i = props.length; + while (--i > -1) { + if (_corProps[p]) { + a = obj[props[i]]; + l = a.length - 1; + for (j = 0; j < l; j++) { + r = a[j+1].da / _r2[j] + a[j].da / _r1[j]; + _r3[j] = (_r3[j] || 0) + r * r; + } + } + } + i = _r3.length; + while (--i > -1) { + _r3[i] = Math.sqrt(_r3[i]); + } + } + i = props.length; + j = quadratic ? 4 : 1; + while (--i > -1) { + p = props[i]; + a = obj[p]; + _calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties + if (seamless) { + a.splice(0, j); + a.splice(a.length - j, j); + } + } + return obj; + } + + /** @private parses the bezier data passed into the tween and organizes it into the appropriate format with an array for each property. **/ + public static function _parseBezierData(values:Array, type:String, prepend:Object=null):Object { + type = type || "soft"; + var obj:Object = {}, + inc:int = (type === "cubic") ? 3 : 2, + soft:Boolean = (type === "soft"), + a:Number, b:Number, c:Number, d:Number, cur:Array, props:Array, i:int, j:int, l:int, p:String, cnt:int, tmp:Object; + if (soft && prepend) { + values = [prepend].concat(values); + } + if (values == null || values.length < inc + 1) { throw new Error("invalid Bezier data"); } + if (values[1] is Point) { + props = ["x","y"]; + } else { + props = []; + for (p in values[0]) { + props.push(p); + } + } + + i = props.length; + while (--i > -1) { + p = props[i]; + obj[p] = cur = []; + cnt = 0; + l = values.length; + for (j = 0; j < l; j++) { + a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp); + if (soft) if (j > 1) if (j < l - 1) { + cur[cnt++] = (a + cur[cnt-2]) / 2; + } + cur[cnt++] = a; + } + l = cnt - inc + 1; + cnt = 0; + for (j = 0; j < l; j += inc) { + a = cur[j]; + b = cur[j+1]; + c = cur[j+2]; + d = (inc === 2) ? 0 : cur[j+3]; + cur[cnt++] = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); + } + cur.length = cnt; + } + return obj; + } + + /** + * @private + * Takes a "values" array that contains objects (could be Points, could be generic objects with + * any number of properties but they should all match in terms of the names of properties like + * [{x:100, y:200, rotation:20},{x:30, y:10, rotation:290}]) and populates an array + * with a generic object for each cubic Bezier segment, adding only the a and d + * properties (which are the beginning and ending anchors). We don't populate the control points yet because + * we must first loop through all of the properties for each segment so that we can determine the relative + * distances between each point which will determine the ratios we use to correctly weight the control + * points on each side. The goal is to use an algorithm that keeps the handle tighter/shorter the closer + * it is to the next control point. Imagine an anchor where on one side there's a very short segment and on + * the other side a very long one - we must determine all the relative changes for the properties (like + * x and y rather than only x or only y) and then leverage that to get the totals and assign the correct + * ratio. + * + * @param values An array containing generic objects with matching properties (or Point instances) through which the Beziers should be plotted, like [{x:0, y:0, scaleX:0.5}, {x:100, y:-200, scaleX:1.2}, {x:300, y:20, scaleX:0.8}] + * @param p Property name that the method should focus on (like "x" or "y" or "scaleX" or whatever) + * @param correlate if true, this property's relative distances will be recorded internally so that they can be correlated with others when calculating the curvature of the Bezier through the values (typically x, y, and z properties should be correlated). + * @param prepend An object to treat as though it is the first element in the values array (typically only used internally for adding a tween's starting values) + * @return An array of partially populated Bezier data (only "a" and "d" properties) + */ + protected static function _parseAnchors(values:Array, p:String, correlate:Boolean, prepend:Object):Array { + var a:Array = [], + l:int, i:int, p1:Number, p2:Number, p3:Number, tmp:Object; + if (prepend) { + values = [prepend].concat(values); + i = values.length; + while (--i > -1) { + if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") { + values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons + } + } + } + + l = values.length - 2; + if (l < 0) { + a[0] = new Segment(values[0][p], 0, 0, values[(l < -1) ? 0 : 1][p]); + return a; + } + + for (i = 0; i < l; i++) { + p1 = values[i][p]; + p2 = values[i+1][p]; + a[i] = new Segment(p1, 0, 0, p2); + if (correlate) { + p3 = values[i+2][p]; + _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1); + _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2); + } + } + a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]); + return a; + } + + /** + * @private + * [Note: must run _parseAnchors() on all properties first to generate the a array with the start/end anchors and assign the r1 and r2 ratio values] + * Iterates through an array of cubic Bezier-related data generated by _parseAnchors() and assigns the control + * point values (b and c) for them according to a particular "curviness" amount. + * + * @param a An array that has already been populated by _parseAnchors() with start/end anchors and r1/r2 ratio values for all properties. Each object in the array should have a, b, c, d, r1, and r2 properties. + * @param curviness A number (typically between 0 and 1, and by default 0.5) that controls the strength of the curves that are plotted. + * @param quad If true, Quadratic Beziers will be used instead of Cubic Beziers. + * @param basic if true, a faster algorithm will be used for calculating the control points which can be less aesthetically pleasing in situations where there are large differences in the spaces or angles between the values provided (the curves are more likely to get kinks or harsh angles) + * @param correlate if true, this property's relative distances will be correlated with others when calculating the curvature of the Bezier through the values (typically x, y, and z properties should be correlated). + */ + protected static function _calculateControlPoints(a:Array, curviness:Number=1, quad:Boolean=false, basic:Boolean=false, correlate:Boolean=false):void { + var l:int = a.length - 1, + ii:int = 0, + cp1:Number = a[0].a, + i:int, p1:Number, p2:Number, p3:Number, seg:Segment, m1:Number, m2:Number, mm:Number, cp2:Number, qb:Array, r1:Number, r2:Number, tl:Number; + for (i = 0; i < l; i++) { + seg = a[ii]; + p1 = seg.a; + p2 = seg.d; + p3 = a[ii+1].d; + + if (correlate) { + r1 = _r1[i]; + r2 = _r2[i]; + tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5); + m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0)); + m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0)); + mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0)); + } else { + m1 = p2 - (p2 - p1) * curviness * 0.5; + m2 = p2 + (p3 - p2) * curviness * 0.5; + mm = p2 - (m1 + m2) / 2; + } + m1 += mm; + m2 += mm; + + seg.c = cp2 = m1; + if (i != 0) { + seg.b = cp1; + } else { + seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly. + } + + seg.da = p2 - p1; + seg.ca = cp2 - p1; + seg.ba = cp1 - p1; + + if (quad) { + qb = cubicToQuadratic(p1, cp1, cp2, p2); + a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); + ii += 4; + } else { + ii++; + } + + cp1 = m2; + } + seg = a[ii]; + seg.b = cp1; + seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly. + seg.da = seg.d - seg.a; + seg.ca = seg.c - seg.a; + seg.ba = cp1 - seg.a; + if (quad) { + qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d); + a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); + } + } + + /** + * Using the fixed midpoint approach, we return an array of 4 quadratic Beziers that + * closely approximates the cubic Bezier data provided. Each quadratic Bezier object contains + * a, b, and c properties where a is the starting anchor value, + * b is the control point, and c is the ending anchor value. + * + * @param a starting anchor of the cubic Bezier + * @param b first control point of the cubic Bezier + * @param c second control point of the cubic Bezier + * @param d final anchor of the cubic Bezier + * @return an array of 4 objects, one for each quadratic Bezier with a, b, and c properties + */ + public static function cubicToQuadratic(a:Number, b:Number, c:Number, d:Number):Array { + var q1:Object = {a:a}, + q2:Object = {}, + q3:Object = {}, + q4:Object = {c:d}, + mab:Number = (a + b) / 2, + mbc:Number = (b + c) / 2, + mcd:Number = (c + d) / 2, + mabc:Number = (mab + mbc) / 2, + mbcd:Number = (mbc + mcd) / 2, + m8:Number = (mbcd - mabc) / 8; + q1.b = mab + (a - mab) / 4; + q2.b = mabc + m8; + q1.c = q2.a = (q1.b + q2.b) / 2; + q2.c = q3.a = (mabc + mbcd) / 2; + q3.b = mbcd - m8; + q4.b = mcd + (d - mcd) / 4; + q3.c = q4.a = (q3.b + q4.b) / 2; + return [q1, q2, q3, q4]; + } + + /** + * Returns the Cubic equivalent of a Quadratic Bezier. This method returns an object with a, b, c, and d properties + * representing the starting anchor value (a), first control point (b), second control point (c), and ending anchor value (d) + * of a Cubic Bezier matching the Quadratic Bezier data passed in. + * + * @param a The starting anchor value + * @param b The control point value + * @param c The ending anchor value + * @return An object with a, b, c, and d properties representing the starting anchor value (a), first control point (b), second control point (c), and ending anchor value (d) of a Cubic Bezier matching the Quadratic Bezier data passed in. + */ + public static function quadraticToCubic(a:Number, b:Number, c:Number):Object { + return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); + } + + /** + * @private + * Analyzes the object in the form of {x:[...bezier segments...], y:[...bezier segments...]} (with any + * properties, not limited to only "x" and "y") and approximates the lengths of the segments, returning an object + * with "length" (total length of all segments), "lengths" (an array of the individual lengths of each segment), + * and "segments" (an array containing an array for each segment - each of those arrays has the same number of elements + * as the precision parameter, each indicating how long the segment is up to that point) properties. + * For example: + * + *

{length:348.214, lengths:[20, 328.14], segments:[[2,4,8,10,14.14], [1,3,4.5,6,8.9]]}

+ * + *

The purpose of the arrays is to allow more accurate calculation of where progress points should land + * such that [almost] perfectly linear easing is possible on the Bezier(s).

+ * + * @param obj The object containing the arrays of Bezier data like {x:[{a:1, b:2, c:3, d:4}], y:[{a:3, b:2, c:4, d:3}]} + * @param precision Number of segments to use when determining the length of each Bezier segment (default 6) + * @return An object with "length", "lengths", and "segements" properties where "length" is the total length of all Bezier segments + */ + protected static function _parseLengthData(obj:Object, precision:uint=6):Object { + var a:Array = [], + lengths:Array = [], + d:Number = 0, + total:Number = 0, + threshold:int = precision - 1, + segments:Array = [], + curLS:Array = [], //current length segments array + p:String, i:int, l:int, index:Number; + for (p in obj) { + _addCubicLengths(obj[p], a, precision); + } + l = a.length; + for (i = 0; i < l; i++) { + d += Math.sqrt(a[i]); + index = i % precision; + curLS[index] = d; + if (index == threshold) { + total += d; + index = (i / precision) >> 0; + segments[index] = curLS; + lengths[index] = total; + d = 0; + curLS = []; + } + } + return {length:total, lengths:lengths, segments:segments}; + } + + /** @private Used by _parseLengthData() **/ + private static function _addCubicLengths(a:Array, steps:Array, precision:uint=6):void { + var inc:Number = 1 / precision, + j:int = a.length, + d:Number, d1:Number, s:Number, da:Number, ca:Number, ba:Number, p:Number, i:int, inv:Number, bez:Segment, index:int; + while (--j > -1) { + bez = a[j]; + s = bez.a; + da = bez.d - s; + ca = bez.c - s; + ba = bez.b - s; + d = d1 = 0; + for (i = 1; i <= precision; i++) { + p = inc * i; + inv = 1 - p; + d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p); + index = j * precision + i - 1; + steps[index] = (steps[index] || 0) + d * d; + } + } + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + var a:Array = this._props, + p:String, i:int; + for (p in _beziers) { + if (p in lookup) { + delete _beziers[p]; + delete _func[p]; + i = a.length; + while (--i > -1) { + if (a[i] === p) { + a.splice(i, 1); + } + } + } + } + return super._kill(lookup); + } + + /** @private **/ + override public function _roundProps(lookup:Object, value:Boolean=true):void { + var op:Array = this._overwriteProps, + i:int = op.length; + while (--i > -1) { + if ((op[i] in lookup) || ("bezier" in lookup) || ("bezierThrough" in lookup)) { + this._round[op[i]] = value; + } + } + } + + + /** @private **/ + override public function setRatio(v:Number):void { + var segments:int = this._segCount, + func:Object = this._func, + target:Object = this._target, + notStart:Boolean = (v !== this._startRatio), + curIndex:int, inv:Number, i:int, p:String, b:Segment, t:Number, val:Number, l:int, lengths:Array, curSeg:Array; + if (this._timeRes == 0) { + curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0; + t = (v - (curIndex * (1 / segments))) * segments; + } else { + lengths = this._lengths; + curSeg = this._curSeg; + v *= this._length; + i = this._li; + //find the appropriate segment (if the currently cached one isn't correct) + if (v > this._l2 && i < segments - 1) { + l = segments - 1; + while (i < l && (this._l2 = lengths[++i]) <= v) { } + this._l1 = lengths[i-1]; + this._li = i; + this._curSeg = curSeg = this._segments[i]; + this._s2 = curSeg[(this._s1 = this._si = 0)]; + } else if (v < this._l1 && i > 0) { + while (i > 0 && (this._l1 = lengths[--i]) >= v) { } + if (i === 0 && v < this._l1) { + this._l1 = 0; + } else { + i++; + } + this._l2 = lengths[i]; + this._li = i; + this._curSeg = curSeg = this._segments[i]; + this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0; + this._s2 = curSeg[this._si]; + } + curIndex = i; + //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one) + v -= this._l1; + i = this._si; + if (v > this._s2 && i < curSeg.length - 1) { + l = curSeg.length - 1; + while (i < l && (this._s2 = curSeg[++i]) <= v) { } + this._s1 = curSeg[i-1]; + this._si = i; + } else if (v < this._s1 && i > 0) { + while (i > 0 && (this._s1 = curSeg[--i]) >= v) { } + if (i === 0 && v < this._s1) { + this._s1 = 0; + } else { + i++; + } + this._s2 = curSeg[i]; + this._si = i; + } + t = (i + (v - this._s1) / (this._s2 - this._s1)) * this._prec; + } + inv = 1 - t; + + i = this._props.length; + while (--i > -1) { + p = this._props[i]; + b = this._beziers[p][curIndex]; + val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a; + if (this._round[p]) { + val = (val + ((val > 0) ? 0.5 : -0.5)) >> 0; + } + if (func[p]) { + target[p](val); + } else { + target[p] = val; + } + } + + if (this._autoRotate != null) { + var ar:Array = this._autoRotate, + b2:Segment, x1:Number, y1:Number, x2:Number, y2:Number, add:Number, conv:Number; + i = ar.length; + while (--i > -1) { + p = ar[i][2]; + add = ar[i][3] || 0; + conv = (ar[i][4] == true) ? 1 : _RAD2DEG; + b = this._beziers[ar[i][0]][curIndex]; + b2 = this._beziers[ar[i][1]][curIndex]; + + x1 = b.a + (b.b - b.a) * t; + x2 = b.b + (b.c - b.b) * t; + x1 += (x2 - x1) * t; + x2 += ((b.c + (b.d - b.c) * t) - x2) * t; + + y1 = b2.a + (b2.b - b2.a) * t; + y2 = b2.b + (b2.c - b2.b) * t; + y1 += (y2 - y1) * t; + y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t; + + val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i]; + + if (func[p]) { + target[p](val); + } else { + target[p] = val; + } + } + } + + } + + } +} + +internal class Segment { + public var a:Number; + public var b:Number; + public var c:Number; + public var d:Number; + public var da:Number; + public var ca:Number; + public var ba:Number; + + public function Segment(a:Number, b:Number, c:Number, d:Number) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.da = d - a; + this.ca = c - a; + this.ba = b - a; + } + +} \ No newline at end of file diff --git a/src/com/greensock/plugins/BezierThroughPlugin.as b/src/com/greensock/plugins/BezierThroughPlugin.as new file mode 100644 index 0000000..6e4e251 --- /dev/null +++ b/src/com/greensock/plugins/BezierThroughPlugin.as @@ -0,0 +1,37 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * @private + * [AS3/AS2 only] LEGACY + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class BezierThroughPlugin extends BezierPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function BezierThroughPlugin() { + super(); + _propName = "bezierThrough"; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (value is Array) { + value = {values:value}; + } + value.type = "thru"; + return super._onInitTween(target, value, tween); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/BlurFilterPlugin.as b/src/com/greensock/plugins/BlurFilterPlugin.as new file mode 100644 index 0000000..f24ced4 --- /dev/null +++ b/src/com/greensock/plugins/BlurFilterPlugin.as @@ -0,0 +1,56 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.filters.BlurFilter; +/** + * [AS3/AS2 only] Tweens a BlurFilter. The following properties are available (you only need to define the ones you want to tween): + * + *
    + *
  • blurX : Number [0]
  • + *
  • blurY : Number [0]
  • + *
  • quality : uint [2]
  • + *
  • index : uint
  • + *
  • addFilter : Boolean [false]
  • + *
  • remove : Boolean [false]
  • + *
+ *
+ * + *

Set remove to true if you want the filter to be removed when the tween completes.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.BlurFilterPlugin; +TweenPlugin.activate([BlurFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {blurFilter:{blurX:10, blurY:10}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class BlurFilterPlugin extends FilterPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static var _propNames:Array = ["blurX","blurY","quality"]; + + /** @private **/ + public function BlurFilterPlugin() { + super("blurFilter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + return _initFilter(target, value, tween, BlurFilter, new BlurFilter(0, 0, value.quality || 2), _propNames); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/CacheAsBitmapPlugin.as b/src/com/greensock/plugins/CacheAsBitmapPlugin.as new file mode 100644 index 0000000..1aa5ca6 --- /dev/null +++ b/src/com/greensock/plugins/CacheAsBitmapPlugin.as @@ -0,0 +1,73 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.display.DisplayObject; +/** + * [AS3/AS2 only] Forces the cacheAsBitmap property of a DisplayObject to be a certain value (true or false) + * during the tween and then sets it back to whatever it was before the tween was rendered for the first time. This can improve + * performance in certain situations, like when the DisplayObject NOT tweening its rotation, scaleX, scaleY, or similar + * things with its transform.matrix. See Adobe's docs for details about when it is appropriate to set cacheAsBitmap + * to true. Also beware that whenever a DisplayObject's cacheAsBitmap is true, it will ONLY be + * rendered on whole pixel values which can lead to animation that looks "choppy" at slow speeds. + * + *

For example, if you want to set cacheAsBitmap to true while the tween is running, do:

+ * + * TweenLite.to(mc, 1, {x:100, cacheAsBitmap:true});

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.CacheAsBitmapPlugin; +TweenPlugin.activate([CacheAsBitmapPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {x:100, cacheAsBitmap:true}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class CacheAsBitmapPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:DisplayObject; + /** @private **/ + protected var _tween:TweenLite; + /** @private **/ + protected var _cacheAsBitmap:Boolean; + /** @private **/ + protected var _initVal:Boolean; + + /** @private **/ + public function CacheAsBitmapPlugin() { + super("cacheAsBitmap"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target as DisplayObject; + _tween = tween; + _initVal = _target.cacheAsBitmap; + _cacheAsBitmap = Boolean(value); + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + if ((v == 1 && _tween._duration == _tween._time && _tween.data != "isFromStart") || (v == 0 && _tween._time == 0)) { //a changeFactor of 1 doesn't necessarily mean the tween is done - if the ease is Elastic.easeOut or Back.easeOut for example, they could hit 1 mid-tween. + _target.cacheAsBitmap = _initVal; + } else if (_target.cacheAsBitmap != _cacheAsBitmap) { + _target.cacheAsBitmap = _cacheAsBitmap; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/CirclePath2DPlugin.as b/src/com/greensock/plugins/CirclePath2DPlugin.as new file mode 100644 index 0000000..5a9b7e9 --- /dev/null +++ b/src/com/greensock/plugins/CirclePath2DPlugin.as @@ -0,0 +1,132 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import com.greensock.motionPaths.CirclePath2D; + import com.greensock.motionPaths.PathFollower; + import flash.geom.Matrix; +/** + * [AS3 only] Tweens an object along a CirclePath2D motion path in any direction (clockwise, counter-clockwise, or shortest). + * The plugin recognizes the following properties: + *
    + *
  • path : CirclePath2D - The CirclePath2D instance to follow (com.greensock.motionPaths.CirclePath2D)
  • + *
  • startAngle : Number - The position at which the target should begin its rotation (described + * in degrees unless useRadians is true in which case it is described in radians). + * For example, to begin at the top of the circle, use 270 or -90 as the startAngle.
  • + *
  • endAngle : Number - The position at which the target should end its rotation (described in + * degrees unless useRadians is true in which case it is described in radians). + * For example, to end at the bottom of the circle, use 90 as the endAngle
  • + *
  • autoRotate : Boolean - When autoRotate is true, the target will automatically + * be rotated so that it is oriented to the angle of the path. To offset this value (like to always add + * 90 degrees for example), use the rotationOffset property.
  • + *
  • rotationOffset : Number - When autoRotate is true, this value will always + * be added to the resulting rotation of the target.
  • + *
  • direction : String - The direction in which the target should travel around the path. Options are + * Direction.CLOCKWISE ("clockwise"), Direction.COUNTER_CLOCKWISE + * ("counterClockwise"), or Direction.SHORTEST ("shortest").
  • + *
  • extraRevolutions : uint - If instead of going directly to the endAngle, you want the target to + * travel one or more extra revolutions around the path before going to the endAngle, + * define that number of revolutions here.
  • + *
  • useRadians : Boolean - If you prefer to define values in radians instead of degrees, set useRadians to true.
  • + *
+ * + * + * + *

USAGE:

+ * +import com.greensock.~~; +import com.greensock.plugins.~~; +import com.greensock.motionPaths.~~; +TweenPlugin.activate([CirclePath2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +var circle:CirclePath2D = new CirclePath2D(150, 150, 100); +TweenLite.to(mc, 2, {circlePath2D:{path:circle, startAngle:90, endAngle:270, direction:Direction.CLOCKWISE, extraRevolutions:2}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class CirclePath2DPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static const _2PI:Number = Math.PI * 2; + /** @private **/ + private static const _RAD2DEG:Number = 180 / Math.PI; + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _autoRemove:Boolean; + /** @private **/ + protected var _start:Number; + /** @private **/ + protected var _change:Number; + /** @private **/ + protected var _circle:CirclePath2D; + /** @private **/ + protected var _autoRotate:Boolean; + /** @private **/ + protected var _rotationOffset:Number; + + /** @private **/ + public function CirclePath2DPlugin() { + super("circlePath2D,x,y"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!("path" in value) || !(value.path is CirclePath2D)) { + trace("CirclePath2DPlugin error: invalid 'path' property. Please define a CirclePath2D instance."); + return false; + } + _target = target; + _circle = value.path as CirclePath2D; + _autoRotate = Boolean(value.autoRotate == true); + _rotationOffset = value.rotationOffset || 0; + + var f:PathFollower = _circle.getFollower(target); + if (f != null && !("startAngle" in value)) { + _start = f.progress; + } else { + _start = _circle.angleToProgress(value.startAngle || 0, value.useRadians); + _circle.renderObjectAt(_target, _start); + } + _change = Number(_circle.anglesToProgressChange(_circle.progressToAngle(_start), value.endAngle || 0, value.direction || "clockwise", value.extraRevolutions || 0, Boolean(value.useRadians))); + return true; + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + if (("x" in lookup) || ("y" in lookup)) { + _overwriteProps = []; + } + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + var angle:Number = (_start + (_change * v)) * _2PI, + radius:Number = _circle.radius, + m:Matrix = _circle.transform.matrix, + px:Number = Math.cos(angle) * radius, + py:Number = Math.sin(angle) * radius; + + _target.x = px * m.a + py * m.c + m.tx; + _target.y = px * m.b + py * m.d + m.ty; + + if (_autoRotate) { + angle += Math.PI / 2; + px = Math.cos(angle) * _circle.radius; + py = Math.sin(angle) * _circle.radius; + _target.rotation = Math.atan2(px * m.b + py * m.d, px * m.a + py * m.c) * _RAD2DEG + _rotationOffset; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/ColorMatrixFilterPlugin.as b/src/com/greensock/plugins/ColorMatrixFilterPlugin.as new file mode 100644 index 0000000..78d97bd --- /dev/null +++ b/src/com/greensock/plugins/ColorMatrixFilterPlugin.as @@ -0,0 +1,234 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + + import flash.filters.ColorMatrixFilter; +/** + * [AS3/AS2 only] ColorMatrixFilter tweening offers an easy way to tween a DisplayObject's saturation, hue, contrast, + * brightness, and colorization. The following properties are available (you only need to define the ones you want to tween): + *
    + *
  • colorize : uint (colorizing a DisplayObject makes it look as though you're seeing it through a colored piece of glass whereas tinting it makes every pixel exactly that color. You can control the amount of colorization using the "amount" value where 1 is full strength, 0.5 is half-strength, and 0 has no colorization effect.)
  • + *
  • amount : Number [1] (only used in conjunction with "colorize")
  • + *
  • contrast : Number (1 is normal contrast, 0 has no contrast, and 2 is double the normal contrast, etc.)
  • + *
  • saturation : Number (1 is normal saturation, 0 makes the DisplayObject look black and white, and 2 would be double the normal saturation)
  • + *
  • hue : Number (changes the hue of every pixel. Think of it as degrees, so 180 would be rotating the hue to be exactly opposite as normal, 360 would be the same as 0, etc.)
  • + *
  • brightness : Number (1 is normal brightness, 0 is much darker than normal, and 2 is twice the normal brightness, etc.)
  • + *
  • threshold : Number (number from 0 to 255 that controls the threshold of where the pixels turn white or black)
  • + *
  • matrix : Array (If you already have a matrix from a ColorMatrixFilter that you want to tween to, pass it in with the "matrix" property. This makes it possible to match effects created in the Flash IDE.)
  • + *
  • index : Number (only necessary if you already have a filter applied and you want to target it with the tween.)
  • + *
  • addFilter : Boolean [false]
  • + *
  • remove : Boolean [false] (Set remove to true if you want the filter to be removed when the tween completes.)
  • + *
+ *

HINT: If you'd like to match the ColorMatrixFilter values you created in the Flash IDE on a particular object, you can get its matrix like this:

+ * +import flash.display.DisplayObject; +import flash.filters.ColorMatrixFilter; + +function getColorMatrix(mc:DisplayObject):Array { + var f:Array = mc.filters, i:uint; + for (i = 0; i < f.length; i++) { + if (f[i] is ColorMatrixFilter) { + return f[i].matrix; + } + } + return null; +} + +var myOriginalMatrix:Array = getColorMatrix(my_mc); //store it so you can tween back to it anytime like TweenMax.to(my_mc, 1, {colorMatrixFilter:{matrix:myOriginalMatrix}}); + + * + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ColorMatrixFilterPlugin; +TweenPlugin.activate([ColorMatrixFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {colorMatrixFilter:{colorize:0xFF0000}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ColorMatrixFilterPlugin extends FilterPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static var _propNames:Array = []; + + /** @private **/ + protected static var _idMatrix:Array = [1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]; + /** @private **/ + protected static var _lumR:Number = 0.212671; //Red constant - used for a few color matrix filter functions + /** @private **/ + protected static var _lumG:Number = 0.715160; //Green constant - used for a few color matrix filter functions + /** @private **/ + protected static var _lumB:Number = 0.072169; //Blue constant - used for a few color matrix filter functions + + /** @private **/ + protected var _matrix:Array; + /** @private **/ + protected var _matrixTween:EndArrayPlugin; + + /** @private **/ + public function ColorMatrixFilterPlugin() { + super("colorMatrixFilter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + var cmf:Object = value; + _initFilter(target, {remove:value.remove, index:value.index, addFilter:value.addFilter}, tween, ColorMatrixFilter, new ColorMatrixFilter(_idMatrix.slice()), _propNames); + if (_filter == null) { + trace("FILTER NULL! "); + return true; + } + + _matrix = ColorMatrixFilter(_filter).matrix; + var endMatrix:Array = []; + + if (cmf.matrix != null && (cmf.matrix is Array)) { + endMatrix = cmf.matrix; + } else { + if (cmf.relative == true) { + endMatrix = _matrix.slice(); + } else { + endMatrix = _idMatrix.slice(); + } + endMatrix = setBrightness(endMatrix, cmf.brightness); + endMatrix = setContrast(endMatrix, cmf.contrast); + endMatrix = setHue(endMatrix, cmf.hue); + endMatrix = setSaturation(endMatrix, cmf.saturation); + endMatrix = setThreshold(endMatrix, cmf.threshold); + if (!isNaN(cmf.colorize)) { + endMatrix = colorize(endMatrix, cmf.colorize, cmf.amount); + } + } + _matrixTween = new EndArrayPlugin(); + _matrixTween._init(_matrix, endMatrix); + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + _matrixTween.setRatio(v); + ColorMatrixFilter(_filter).matrix = _matrix; + super.setRatio(v); + } + + +//---- MATRIX OPERATIONS -------------------------------------------------------------------------------- + + /** @private **/ + public static function colorize(m:Array, color:Number, amount:Number = 1):Array { + if (isNaN(color)) { + return m; + } else if (isNaN(amount)) { + amount = 1; + } + var r:Number = ((color >> 16) & 0xff) / 255, + g:Number = ((color >> 8) & 0xff) / 255, + b:Number = (color & 0xff) / 255, + inv:Number = 1 - amount, + temp:Array = [inv + amount * r * _lumR, amount * r * _lumG, amount * r * _lumB, 0, 0, + amount * g * _lumR, inv + amount * g * _lumG, amount * g * _lumB, 0, 0, + amount * b * _lumR, amount * b * _lumG, inv + amount * b * _lumB, 0, 0, + 0, 0, 0, 1, 0]; + return applyMatrix(temp, m); + } + + /** @private **/ + public static function setThreshold(m:Array, n:Number):Array { + if (isNaN(n)) { + return m; + } + var temp:Array = [_lumR * 256, _lumG * 256, _lumB * 256, 0, -256 * n, + _lumR * 256, _lumG * 256, _lumB * 256, 0, -256 * n, + _lumR * 256, _lumG * 256, _lumB * 256, 0, -256 * n, + 0, 0, 0, 1, 0]; + return applyMatrix(temp, m); + } + + /** @private **/ + public static function setHue(m:Array, n:Number):Array { + if (isNaN(n)) { + return m; + } + n *= Math.PI / 180; + var c:Number = Math.cos(n), + s:Number = Math.sin(n), + temp:Array = [(_lumR + (c * (1 - _lumR))) + (s * (-_lumR)), (_lumG + (c * (-_lumG))) + (s * (-_lumG)), (_lumB + (c * (-_lumB))) + (s * (1 - _lumB)), 0, 0, (_lumR + (c * (-_lumR))) + (s * 0.143), (_lumG + (c * (1 - _lumG))) + (s * 0.14), (_lumB + (c * (-_lumB))) + (s * -0.283), 0, 0, (_lumR + (c * (-_lumR))) + (s * (-(1 - _lumR))), (_lumG + (c * (-_lumG))) + (s * _lumG), (_lumB + (c * (1 - _lumB))) + (s * _lumB), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]; + return applyMatrix(temp, m); + } + + /** @private **/ + public static function setBrightness(m:Array, n:Number):Array { + if (isNaN(n)) { + return m; + } + n = (n * 100) - 100; + return applyMatrix([1,0,0,0,n, + 0,1,0,0,n, + 0,0,1,0,n, + 0,0,0,1,0, + 0,0,0,0,1], m); + } + + /** @private **/ + public static function setSaturation(m:Array, n:Number):Array { + if (isNaN(n)) { + return m; + } + var inv:Number = 1 - n, + r:Number = inv * _lumR, + g:Number = inv * _lumG, + b:Number = inv * _lumB, + temp:Array = [r + n, g , b , 0, 0, + r , g + n, b , 0, 0, + r , g , b + n, 0, 0, + 0 , 0 , 0 , 1, 0]; + return applyMatrix(temp, m); + } + + /** @private **/ + public static function setContrast(m:Array, n:Number):Array { + if (isNaN(n)) { + return m; + } + n += 0.01; + var temp:Array = [n,0,0,0,128 * (1 - n), + 0,n,0,0,128 * (1 - n), + 0,0,n,0,128 * (1 - n), + 0,0,0,1,0]; + return applyMatrix(temp, m); + } + + /** @private **/ + public static function applyMatrix(m:Array, m2:Array):Array { + if (!(m is Array) || !(m2 is Array)) { + return m2; + } + var temp:Array = [], i:int = 0, z:int = 0, y:int, x:int; + for (y = 0; y < 4; y += 1) { + for (x = 0; x < 5; x += 1) { + z = (x == 4) ? m[i + 4] : 0; + temp[i + x] = m[i] * m2[x] + + m[i+1] * m2[x + 5] + + m[i+2] * m2[x + 10] + + m[i+3] * m2[x + 15] + + z; + } + i += 5; + } + return temp; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/ColorTransformPlugin.as b/src/com/greensock/plugins/ColorTransformPlugin.as new file mode 100644 index 0000000..8cffdc5 --- /dev/null +++ b/src/com/greensock/plugins/ColorTransformPlugin.as @@ -0,0 +1,104 @@ +/** + * VERSION: 12.1.3 + * DATE: 2013-12-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.display.DisplayObject; + import flash.geom.ColorTransform; +/** + * [AS3/AS2 only] Ever wanted to tween ColorTransform properties of a DisplayObject to do advanced effects like overexposing, altering + * the brightness or setting the percent/amount of tint? Or maybe tween individual ColorTransform + * properties like redMultiplier, redOffset, blueMultiplier, blueOffset, etc. ColorTransformPlugin gives you an easy way to + * do just that. + * + *

PROPERTIES:

+ *
    + *
  • tint (or color) : uint - Color of the tint. Use a hex value, like 0xFF0000 for red.
  • + *
  • tintAmount : Number - Number between 0 and 1. Works with the "tint" property and indicats how much of an effect the tint should have. 0 makes the tint invisible, 0.5 is halfway tinted, and 1 is completely tinted.
  • + *
  • brightness : Number - Number between 0 and 2 where 1 is normal brightness, 0 is completely dark/black, and 2 is completely bright/white
  • + *
  • exposure : Number - Number between 0 and 2 where 1 is normal exposure, 0, is completely underexposed, and 2 is completely overexposed. Overexposing an object is different then changing the brightness - it seems to almost bleach the image and looks more dynamic and interesting (subjectively speaking).
  • + *
  • redOffset : Number
  • + *
  • greenOffset : Number
  • + *
  • blueOffset : Number
  • + *
  • alphaOffset : Number
  • + *
  • redMultiplier : Number
  • + *
  • greenMultiplier : Number
  • + *
  • blueMultiplier : Number
  • + *
  • alphaMultiplier : Number
  • + *
+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ColorTransformPlugin; +TweenPlugin.activate([ColorTransformPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {colorTransform:{tint:0xFF0000, tintAmount:0.5}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ColorTransformPlugin extends TintPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function ColorTransformPlugin() { + super(); + _propName = "colorTransform"; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + var start:ColorTransform, end:ColorTransform = new ColorTransform(); + if (target is DisplayObject) { + _transform = DisplayObject(target).transform; + start = _transform.colorTransform; + } else if (target is ColorTransform) { + start = target as ColorTransform; + } else { + return false; + } + if (value is ColorTransform) { + end.concat(value); + } else { + end.concat(start); + } + for (var p:String in value) { + if (p == "tint" || p == "color") { + if (value[p] != null) { + end.color = int(value[p]); + } + } else if (p == "tintAmount" || p == "exposure" || p == "brightness") { + //handle this later... + } else { + end[p] = value[p]; + } + } + if (!(value is ColorTransform)) { + if (!isNaN(value.tintAmount)) { + var ratio:Number = value.tintAmount / (1 - ((end.redMultiplier + end.greenMultiplier + end.blueMultiplier) / 3)); + end.redOffset *= ratio; + end.greenOffset *= ratio; + end.blueOffset *= ratio; + end.redMultiplier = end.greenMultiplier = end.blueMultiplier = 1 - value.tintAmount; + } else if (!isNaN(value.exposure)) { + end.redOffset = end.greenOffset = end.blueOffset = 255 * (value.exposure - 1); + end.redMultiplier = end.greenMultiplier = end.blueMultiplier = 1; + } else if (!isNaN(value.brightness)) { + end.redOffset = end.greenOffset = end.blueOffset = Math.max(0, (value.brightness - 1) * 255); + end.redMultiplier = end.greenMultiplier = end.blueMultiplier = 1 - Math.abs(value.brightness - 1); + } + } + _init(start, end); + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/DirectionalRotationPlugin.as b/src/com/greensock/plugins/DirectionalRotationPlugin.as new file mode 100644 index 0000000..af41311 --- /dev/null +++ b/src/com/greensock/plugins/DirectionalRotationPlugin.as @@ -0,0 +1,123 @@ +/** + * VERSION: 12.0.5 + * DATE: 2013-03-26 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import com.greensock.core.PropTween; +/** + * Tweens any rotation-related property to another value in a particular direction which can be either clockwise + * ("_cw" suffix), counter-clockwise ("_ccw" suffix), or in the shortest direction + * ("_short" suffix) in which case the plugin chooses the direction based on the shortest path. For example: + * + * +//obj.rotation starts at 45 +var obj:Object = {rotation:45}; + +//tweens to the 270 position in a clockwise direction +TweenLite.to(obj, 1, {directionalRotation:"270_cw"}); + +//tweens to the 270 position in a counter-clockwise direction +TweenLite.to(obj, 1, {directionalRotation:"270_ccw"}); + +//tweens to the 270 position in the shortest direction (which, in this case, is counter-clockwise) +TweenLite.to(obj, 1, {directionalRotation:"270_short"}); + + * + *

Notice that the value is in quotes, thus a string with a particular suffix indicating the direction + * ("_cw", "_ccw", or "_short"). You can also use the "+=" or "-=" prefix to + * indicate relative values.

+ * + *

By default, directionalRotation assumes you're attempting to tween the "rotation" property + * of the target, but you can define any rotational property name (including MULTIPLE properties) by passing an + * object instead, like this:

+ * + * +//animate obj.rotationX and obj.rotationY: +TweenLite.to(obj, 1, {directionalRotation:{rotationX:"-140_cw", rotationY:"70_short"}, ease:Power2.easeIn}); + + * + *

If you want to define the values in radians instead of degrees, you can use the special useRadians:true flag, like this:

+ * + * +TweenLite.to(obj, 1, {directionalRotation:{rotation:"1.5_ccw", useRadians:true}, ease:Power2.easeInOut}); + + * + *

And if the value that you want to pass in is a numeric variable, you can easily append the appropriate suffix like this:

+ * + * +var myValue:Number = -270; +TweenLite.to(obj, 1, {directionalRotation: (myValue + "_short") }); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class DirectionalRotationPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + protected var finals:Object; + + /** @private **/ + public function DirectionalRotationPlugin() { + super("directionalRotation"); + _overwriteProps.pop(); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (typeof(value) !== "object") { + value = {rotation:value}; + } + finals = {}; + var cap:Number = (value.useRadians === true) ? Math.PI * 2 : 360, + p:String, v:Object, start:Number, end:Number, dif:Number, split:Array, type:String; + for (p in value) { + if (p !== "useRadians") { + split = (value[p] + "").split("_"); + v = split[0]; + type = split[1]; + start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() ); + end = finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0; + dif = end - start; + if (type === "short") { + dif = dif % cap; + if (dif !== dif % (cap / 2)) { + dif = (dif < 0) ? dif + cap : dif - cap; + } + } else if (type === "cw" && dif < 0) { + dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; + } else if (type === "ccw" && dif > 0) { + dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; + } + _addTween(target, p, start, start + dif, p); + _overwriteProps.push(p); + } + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + var pt:PropTween; + if (v !== 1) { + super.setRatio(v); + } else { + pt = _firstPT; + while (pt) { + if (pt.f) { + pt.t[pt.p](finals[pt.p]); + } else { + pt.t[pt.p] = finals[pt.p]; + } + pt = pt._next; + } + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/DropShadowFilterPlugin.as b/src/com/greensock/plugins/DropShadowFilterPlugin.as new file mode 100644 index 0000000..8d00666 --- /dev/null +++ b/src/com/greensock/plugins/DropShadowFilterPlugin.as @@ -0,0 +1,63 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.filters.DropShadowFilter; +/** + * [AS3/AS2 only] Tweens a DropShadowFilter. The following properties are available (you only need to define the ones you want to tween): + * + *
    + *
  • distance : Number [0]
  • + *
  • angle : Number [45]
  • + *
  • color : uint [0x000000]
  • + *
  • alpha :Number [0]
  • + *
  • blurX : Number [0]
  • + *
  • blurY : Number [0]
  • + *
  • strength : Number [1]
  • + *
  • quality : uint [2]
  • + *
  • inner : Boolean [false]
  • + *
  • knockout : Boolean [false]
  • + *
  • hideObject : Boolean [false]
  • + *
  • index : uint
  • + *
  • addFilter : Boolean [false]
  • + *
  • remove : Boolean [false]
  • + *
+ *
+ *

Set remove to true if you want the filter to be removed when the tween completes.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.DropShadowFilterPlugin; +TweenPlugin.activate([DropShadowFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {dropShadowFilter:{blurX:5, blurY:5, distance:5, alpha:0.6}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class DropShadowFilterPlugin extends FilterPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static var _propNames:Array = ["distance","angle","color","alpha","blurX","blurY","strength","quality","inner","knockout","hideObject"]; + + /** @private **/ + public function DropShadowFilterPlugin() { + super("dropShadowFilter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + return _initFilter(target, value, tween, DropShadowFilter, new DropShadowFilter(0, 45, 0x000000, 0, 0, 0, 1, value.quality || 2, value.inner, value.knockout, value.hideObject), _propNames); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/EndArrayPlugin.as b/src/com/greensock/plugins/EndArrayPlugin.as new file mode 100644 index 0000000..77fdc4f --- /dev/null +++ b/src/com/greensock/plugins/EndArrayPlugin.as @@ -0,0 +1,98 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] Tweens numbers in an Array. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.EndArrayPlugin; +TweenPlugin.activate([EndArrayPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +var myArray:Array = [1,2,3,4]; +TweenLite.to(myArray, 1.5, {endArray:[10,20,30,40]}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class EndArrayPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _a:Array; + /** @private If the values should be rounded to the nearest integer, _round will be set to true. **/ + protected var _round:Boolean; + /** @private **/ + protected var _info:Array = []; + + /** @private **/ + public function EndArrayPlugin() { + super("endArray"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(target is Array) || !(value is Array)) { + return false; + } + _init(target as Array, value); + return true; + } + + /** @private **/ + public function _init(start:Array, end:Array):void { + _a = start; + var i:int = end.length, cnt:int = 0; + while (--i > -1) { + if (start[i] != end[i] && start[i] != null) { + _info[cnt++] = new ArrayTweenInfo(i, _a[i], end[i] - _a[i]); + } + } + } + + override public function _roundProps(lookup:Object, value:Boolean=true):void { + if ("endArray" in lookup) { + _round = value; + } + } + + /** @private **/ + override public function setRatio(v:Number):void { + var i:int = _info.length, ti:ArrayTweenInfo, val:Number; + if (_round) { + while (--i > -1) { + ti = _info[i]; + _a[ti.i] = ((val = ti.c * v + ti.s) > 0) ? (val + 0.5) >> 0 : (val - 0.5) >> 0; + } + } else { + while (--i > -1) { + ti = _info[i]; + _a[ti.i] = ti.c * v + ti.s; + } + } + } + + } +} + +internal class ArrayTweenInfo { + public var i:uint; + public var s:Number; + public var c:Number; + + public function ArrayTweenInfo(index:uint, start:Number, change:Number) { + this.i = index; + this.s = start; + this.c = change; + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/EndVectorPlugin.as b/src/com/greensock/plugins/EndVectorPlugin.as new file mode 100644 index 0000000..dbf6a9e --- /dev/null +++ b/src/com/greensock/plugins/EndVectorPlugin.as @@ -0,0 +1,111 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + + import __AS3__.vec.Vector; //due to a bug in Flex Builder, this must be included in order to correctly compile a swc +/** + * [AS3 only] Tweens numbers in a Vector.<Number>. Remember, Vectors require that you publish to Flash Player 10 or later. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.EndVectorPlugin; +TweenPlugin.activate([EndVectorPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +var v:Vector.<Number> = new Vector.<Number>(); + v[0] = 0; + v[1] = 1; + v[2] = 2; +var end:Vector.<Number> = new Vector.<Number>(); + end[0] = 100; + end[1] = 250; + end[2] = 500; +TweenLite.to(v, 3, {endVector:end, onUpdate:report}); +function report():void { + trace(v); +} + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class EndVectorPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _v:Vector.; + /** @private **/ + protected var _info:Vector. = new Vector.(); + /** @private If the values should be rounded to the nearest integer, _round will be set to true. **/ + public var _round:Boolean; + + /** @private **/ + public function EndVectorPlugin() { + super("endVector"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(target is Vector.) || !(value is Vector.)) { + return false; + } + _init(target as Vector., value as Vector.); + return true; + } + + /** @private **/ + public function _init(start:Vector., end:Vector.):void { + _v = start; + var i:int = end.length, cnt:uint = 0; + while (--i > -1) { + if (_v[i] != end[i]) { + _info[cnt++] = new VectorInfo(i, _v[i], end[i] - _v[i]); + } + } + } + + override public function _roundProps(lookup:Object, value:Boolean=true):void { + if ("endVector" in lookup) { + _round = value; + } + } + + + /** @private **/ + override public function setRatio(v:Number):void { + var i:int = _info.length, vi:VectorInfo, val:Number; + if (_round) { + while (--i > -1) { + vi = _info[i]; + _v[vi.i] = ((val = vi.c * v + vi.s) > 0) ? (val + 0.5) >> 0 : (val - 0.5) >> 0; + } + } else { + while (--i > -1) { + vi = _info[i]; + _v[vi.i] = vi.c * v + vi.s; + } + } + } + + } +} + +internal class VectorInfo { + public var i:uint; + public var s:Number; + public var c:Number; + + public function VectorInfo(index:uint, start:Number, change:Number) { + this.i = index; + this.s = start; + this.c = change; + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/FilterPlugin.as b/src/com/greensock/plugins/FilterPlugin.as new file mode 100644 index 0000000..569979a --- /dev/null +++ b/src/com/greensock/plugins/FilterPlugin.as @@ -0,0 +1,112 @@ +/** + * VERSION: 12.0.1 + * DATE: 2013-05-21 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.filters.BitmapFilter; + import flash.filters.BlurFilter; +/** + * @private + * Base class for all filter plugins (like blurFilter, colorMatrixFilter, glowFilter, etc.). Handles common routines. + * There is no reason to use this class directly.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class FilterPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _type:Class; + /** @private **/ + protected var _filter:BitmapFilter; + /** @private **/ + protected var _index:int; + /** @private **/ + protected var _remove:Boolean; + /** @private **/ + private var _tween:TweenLite; + + /** @private **/ + public function FilterPlugin(props:String="", priority:Number=0) { + super(props, priority); + } + + /** @private **/ + protected function _initFilter(target:*, props:Object, tween:TweenLite, type:Class, defaultFilter:BitmapFilter, propNames:Array):Boolean { + _target = target; + _tween = tween; + _type = type; + var filters:Array = _target.filters, p:String, i:int, colorTween:HexColorsPlugin; + var extras:Object = (props is BitmapFilter) ? {} : props; + if (extras.index != null) { + _index = extras.index; + } else { + _index = filters.length; + if (extras.addFilter != true) { + while (--_index > -1 && !(filters[_index] is _type)) { }; + } + } + if (_index < 0 || !(filters[_index] is _type)) { + if (_index < 0) { + _index = filters.length; + } + if (_index > filters.length) { //in case the requested index is too high, pad the lower elements with BlurFilters that have a blur of 0. + i = filters.length - 1; + while (++i < _index) { + filters[i] = new BlurFilter(0, 0, 1); + } + } + filters[_index] = defaultFilter; + _target.filters = filters; + } + _filter = filters[_index]; + _remove = (extras.remove == true); + i = propNames.length; + while (--i > -1) { + p = propNames[i]; + if (p in props && _filter[p] != props[p]) { + if (p == "color" || p == "highlightColor" || p == "shadowColor") { + colorTween = new HexColorsPlugin(); + colorTween._initColor(_filter, p, props[p]); + _addTween(colorTween, "setRatio", 0, 1, _propName); + } else if (p == "quality" || p == "inner" || p == "knockout" || p == "hideObject") { + _filter[p] = props[p]; + } else { + _addTween(_filter, p, _filter[p], props[p], _propName); + } + } + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + var filters:Array = _target.filters; + if (!(filters[_index] is _type)) { //a filter may have been added or removed since the tween began, changing the index. + _index = filters.length; //default (in case it was removed) + while (--_index > -1 && !(filters[_index] is _type)) { }; + if (_index == -1) { + _index = filters.length; + } + } + if (v == 1 && _remove && _tween._time == _tween._duration && _tween.data != "isFromStart") { + if (_index < filters.length) { + filters.splice(_index, 1); + } + } else { + filters[_index] = _filter; + } + _target.filters = filters; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/FrameBackwardPlugin.as b/src/com/greensock/plugins/FrameBackwardPlugin.as new file mode 100644 index 0000000..c9d53a9 --- /dev/null +++ b/src/com/greensock/plugins/FrameBackwardPlugin.as @@ -0,0 +1,42 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { +/** + * [AS3/AS2 only] Tweens a MovieClip backward to a particular frame number, wrapping it if/when it reaches the beginning + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 15, a normal frame tween would go forward from 10 to 15, but a frameBackward + * would go from 10 to 1 (the beginning) and wrap to the end and continue tweening from 20 to 15. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.~~; +TweenPlugin.activate([FrameBackwardPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {frameBackward:15}); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline (as "stream") will not be played. + * Doing so would be impossible because the tween might speed up or slow down the MovieClip to any degree.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class FrameBackwardPlugin extends FrameForwardPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function FrameBackwardPlugin() { + super(); + _propName = "frameBackward"; + _backward = true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/FrameForwardPlugin.as b/src/com/greensock/plugins/FrameForwardPlugin.as new file mode 100644 index 0000000..481656e --- /dev/null +++ b/src/com/greensock/plugins/FrameForwardPlugin.as @@ -0,0 +1,82 @@ +/** + * VERSION: 12.0.2 + * DATE: 2013-04-09 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + + import flash.display.MovieClip; +/** + * [AS3/AS2 only] Tweens a MovieClip forward to a particular frame number, wrapping it if/when it reaches the end + * of the timeline. For example, if your MovieClip has 20 frames total and it is currently at frame 10 + * and you want tween to frame 5, a normal frame tween would go backwards from 10 to 5, but a frameForward + * would go from 10 to 20 (the end) and wrap to the beginning and continue tweening from 1 to 5. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.~~; +TweenPlugin.activate([FrameForwardPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {frameForward:5}); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline (as "stream") will not be played. + * Doing so would be impossible because the tween might speed up or slow down the MovieClip to any degree.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class FrameForwardPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _start:int; + /** @private **/ + protected var _change:int; + /** @private **/ + protected var _max:uint; + /** @private **/ + protected var _target:Object; + /** @private Allows FrameBackwardPlugin to extend this class and only use an extremely small amount of kb (because the functionality is combined here) **/ + protected var _backward:Boolean; + + /** @private **/ + public function FrameForwardPlugin() { + super("frameForward,frame,frameLabel,frameBackward"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target; + _start = _target.currentFrame; + _max = _target.totalFrames; + _change = (typeof(value) === "number") ? Number(value) - _start : (typeof(value) === "string" && value.charAt(1) === "=") ? int(value.charAt(0) + "1") * Number(value.substr(2)) : Number(value) || 0; + if (!_backward && _change < 0) { + _change = ((_change + (_max * 99999)) % _max) + ((_change / _max) | 0) * _max; + } else if (_backward && _change > 0) { + _change = ((_change - (_max * 99999)) % _max) - ((_change / _max) | 0) * _max; + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + var frame:Number = (_change * v + _start) % _max; + if (frame < 0.5 && frame >= -0.5) { + frame = _max; + } else if (frame < 0) { + frame += _max; + } + frame = (frame + 0.5) | 0; + if (frame != _target.currentFrame) { + _target.gotoAndStop( frame ); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/FrameLabelPlugin.as b/src/com/greensock/plugins/FrameLabelPlugin.as new file mode 100644 index 0000000..28aeed5 --- /dev/null +++ b/src/com/greensock/plugins/FrameLabelPlugin.as @@ -0,0 +1,63 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.display.MovieClip; +/** + * [AS3/AS2 only] Tweens a MovieClip to a particular frame label.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.FrameLabelPlugin; +TweenPlugin.activate([FrameLabelPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {frameLabel:"myLabel"}); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline (as "stream") will not be played. + * Doing so would be impossible because the tween might speed up or slow down the MovieClip to any degree.

+ * + * Copyright 2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership. + * + * @author Jack Doyle, jack@greensock.com + */ + public class FrameLabelPlugin extends FramePlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function FrameLabelPlugin() { + super(); + _propName = "frameLabel"; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!tween.target is MovieClip) { + return false; + } + _target = target as MovieClip; + this.frame = _target.currentFrame; + var labels:Array = _target.currentLabels, label:String = value, endFrame:int = _target.currentFrame; + var i:int = labels.length; + while (--i > -1) { + if (labels[i].name == label) { + endFrame = labels[i].frame; + break; + } + } + if (this.frame != endFrame) { + _addTween(this, "frame", this.frame, endFrame, "frame", true); + } + return true; + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/FramePlugin.as b/src/com/greensock/plugins/FramePlugin.as new file mode 100644 index 0000000..870d705 --- /dev/null +++ b/src/com/greensock/plugins/FramePlugin.as @@ -0,0 +1,64 @@ +/** + * VERSION: 12.01 + * DATE: 2012-06-25 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.display.MovieClip; +/** + * [AS3/AS2 only] Tweens a MovieClip to a particular frame number.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.FramePlugin; +TweenPlugin.activate([FramePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {frame:125}); + + * + *

Note: When tweening the frames of a MovieClip, any audio that is embedded on the MovieClip's timeline (as "stream") will not be played. + * Doing so would be impossible because the tween might speed up or slow down the MovieClip to any degree.

+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class FramePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public var frame:int; + /** @private **/ + protected var _target:MovieClip; + + /** @private **/ + public function FramePlugin() { + super("frame,frameLabel,frameForward,frameBackward"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(target is MovieClip) || isNaN(value)) { + return false; + } + _target = target as MovieClip; + this.frame = _target.currentFrame; + _addTween(this, "frame", this.frame, value, "frame", true); + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + if (this.frame != _target.currentFrame) { + _target.gotoAndStop(this.frame); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/GlowFilterPlugin.as b/src/com/greensock/plugins/GlowFilterPlugin.as new file mode 100644 index 0000000..8746657 --- /dev/null +++ b/src/com/greensock/plugins/GlowFilterPlugin.as @@ -0,0 +1,61 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-12 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.filters.GlowFilter; +/** + * [AS3/AS2 only] Tweens a GlowFilter. The following properties are available (you only need to define the ones you want to tween): + * + *
    + *
  • color : uint [0x000000]
  • + *
  • alpha :Number [0]
  • + *
  • blurX : Number [0]
  • + *
  • blurY : Number [0]
  • + *
  • strength : Number [1]
  • + *
  • quality : uint [2]
  • + *
  • inner : Boolean [false]
  • + *
  • knockout : Boolean [false]
  • + *
  • index : uint
  • + *
  • addFilter : Boolean [false]
  • + *
  • remove : Boolean [false]
  • + *
+ *
+ * + *

Set remove to true if you want the filter to be removed when the tween completes.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.GlowFilterPlugin; +TweenPlugin.activate([GlowFilterPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {glowFilter:{color:0x00FF00, blurX:10, blurY:10, strength:1, alpha:1}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class GlowFilterPlugin extends FilterPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static var _propNames:Array = ["color","alpha","blurX","blurY","strength","quality","inner","knockout"]; + + /** @private **/ + public function GlowFilterPlugin() { + super("glowFilter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + return _initFilter(target, value, tween, GlowFilter, new GlowFilter(0xFFFFFF, 0, 0, 0, value.strength || 1, value.quality || 2, value.inner, value.knockout), _propNames); + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/HexColorsPlugin.as b/src/com/greensock/plugins/HexColorsPlugin.as new file mode 100644 index 0000000..4b58151 --- /dev/null +++ b/src/com/greensock/plugins/HexColorsPlugin.as @@ -0,0 +1,134 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] Although hex colors are technically numbers, if you try to tween them conventionally, + * you'll notice that they don't tween smoothly. To tween them properly, the red, green, and + * blue components must be extracted and tweened independently. The HexColorsPlugin makes it easy. + * To tween a property of your object that's a hex color to another hex color, just pass a hexColors + * Object with properties named the same as your object's hex color properties. For example, + * if myObject has a "myHexColor" property that you'd like to tween to red (0xFF0000) over the + * course of 2 seconds, you'd do:

+ * + * TweenMax.to(myObject, 2, {hexColors:{myHexColor:0xFF0000}});

+ * + *

You can pass in any number of properties.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.HexColorsPlugin; +TweenPlugin.activate([HexColorsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myObject, 2, {hexColors:{myProperty:0xFF0000}}); + + * + *

Or if you just want to tween a color and apply it somewhere on every frame, you could do:

+ * +var myColor:Object = {hex:0xFF0000}; +TweenLite.to(myColor, 2, {hexColors:{hex:0x0000FF}, onUpdate:applyColor}); +function applyColor() { + mc.graphics.clear(); + mc.graphics.beginFill(myColor.hex, 1); + mc.graphics.drawRect(0, 0, 100, 100); + mc.graphics.endFill(); +} + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class HexColorsPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _colors:Array; + + /** @private **/ + public function HexColorsPlugin() { + super("hexColors"); + _overwriteProps = []; + _colors = []; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + for (var p:String in value) { + _initColor(target, p, uint(value[p])); + } + return true; + } + + /** @private **/ + public function _initColor(target:Object, p:String, end:uint):void { + var isFunc:Boolean = (typeof(target[p]) == "function"), + start:uint = (!isFunc) ? target[p] : target[ ((p.indexOf("set") || !("get" + p.substr(3) in target)) ? p : "get" + p.substr(3)) ](); + if (start != end) { + var r:uint = start >> 16, + g:uint = (start >> 8) & 0xff, + b:uint = start & 0xff; + _colors[_colors.length] = new ColorProp(target, p, isFunc, r, (end >> 16) - r, g, ((end >> 8) & 0xff) - g, b, (end & 0xff) - b); + _overwriteProps[_overwriteProps.length] = p; + } + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + var i:int = _colors.length; + while (i--) { + if (lookup[_colors[i].p] != null) { + _colors.splice(i, 1); + } + } + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + var i:int = _colors.length, clr:ColorProp, val:Number; + while (--i > -1) { + clr = _colors[i]; + val = (clr.rs + (v * clr.rc)) << 16 | (clr.gs + (v * clr.gc)) << 8 | (clr.bs + (v * clr.bc)); + if (clr.f) { + clr.t[clr.p](val); + } else { + clr.t[clr.p] = val; + } + } + } + + + } +} + +internal class ColorProp { + public var t:Object; + public var p:String; + public var f:Boolean; + public var rs:int; + public var rc:int; + public var gs:int; + public var gc:int; + public var bs:int; + public var bc:int; + + public function ColorProp(t:Object, p:String, f:Boolean, rs:int, rc:int, gs:int, gc:int, bs:int, bc:int) { + this.t = t; + this.p = p; + this.f = f; + this.rs = rs; + this.rc = rc; + this.gs = gs; + this.gc = gc; + this.bs = bs; + this.bc = bc; + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/OnChangeRatioPlugin.as b/src/com/greensock/plugins/OnChangeRatioPlugin.as new file mode 100644 index 0000000..4060d84 --- /dev/null +++ b/src/com/greensock/plugins/OnChangeRatioPlugin.as @@ -0,0 +1,74 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] This plugin allows you to define a callback method that should be called whenever the tween's "ratio" property + * changes which effectively means that the tweening values changed. This is typically only useful in conjunction with + * SteppedEase. Also note that the callback should accept one parameter which will refer to the tween itself. + * This is different than most other callback types, like onComplete and onUpdate which don't pass parameters by default + * unless you use their onCompleteParams and onUpdateParams counterparts. The reason onChangeRatio works this unique way + * is to minimize file size (adding an onChangeRatioParams would require either another plugin or adding kb to the + * main TweenLite class or changing the syntax altogether to onChangeRatio:{func:myFunction, params:[1,2]} which is + * even more inconsistent) and because it is such a niche plugin (typically only used with SteppedEase which is quite + * niche itself). It can be very useful to reuse a single callback method but it must be able to figure out which tween + * changed its ratio and access its target which is why onChangeRatio passes the tween as the parameter. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.OnChangeRatioPlugin; +TweenPlugin.activate([OnChangeRatioPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 3, {x:500, onChangeRatio:changeHandler, ease:SteppedEase.create(5)}); +function changeHandler(tween:TweenLite):void { + trace("ratio: " + tween.ratio); +} + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class OnChangeRatioPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _func:Function; + /** @private **/ + protected var _tween:TweenLite; + /** @private **/ + private var _ratio:Number; + + /** @private **/ + public function OnChangeRatioPlugin() { + super("onChangeRatio"); + _ratio = 0; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(value is Function)) { + return false; + } + _func = value as Function; + _tween = tween; + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + if (_ratio != v) { + _func(_tween); + _ratio = v; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/OnCompleteRenderPlugin.as b/src/com/greensock/plugins/OnCompleteRenderPlugin.as new file mode 100644 index 0000000..8020178 --- /dev/null +++ b/src/com/greensock/plugins/OnCompleteRenderPlugin.as @@ -0,0 +1,75 @@ +/** + * VERSION: 12.0 + * DATE: 2012-08-27 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + + import flash.display.DisplayObject; + import flash.events.Event; + +/** + * [AS3 only] Calls a function as soon as the tween completes AND the final frame actually renders to the screen. + * It accomplishes this by waiting until the next ENTER_FRAME event gets dispatched before calling the function. + * (a regular onComplete gets called as soon as the tween sets its final values but before things are rendered graphically to the screen). + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.OnCompleteRenderPlugin; +TweenPlugin.activate([OnCompleteRenderPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {x:100, onCompleteRender:myFunc}); //tweens horizontal and vertical scale simultaneously + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class OnCompleteRenderPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; + /** @private **/ + private var _target:DisplayObject; + /** @private **/ + private var _func:Function; + /** @private **/ + private var _tween:TweenLite; + + /** @private **/ + public function OnCompleteRenderPlugin() { + super("onCompleteRender,onCompleteRenderParams"); + TweenLite._plugins.onCompleteRenderParams = OnCompleteRenderPlugin; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (value is Array) { + return true; //assume it's onCompleteRenderParams + } else if (!(target is DisplayObject)) { + trace("Error: onCompleteRender was used on a tween whose target is not a DisplayObject"); + return false; + } + _target = target as DisplayObject; + _func = value; + _tween = tween; + return true; + } + + private function _enterFrameHandler(event:Event):void { + _target.removeEventListener("enterFrame", _enterFrameHandler); + _func.apply(null, _tween.vars.onCompleteRenderParams); + } + + /** @private **/ + override public function setRatio(v:Number):void { + if (v == 1 || v == 0) if (_func != null) if (_tween._time == _tween._duration && _tween.data != "isFromStart") { //if _func is null, this plugin was used to init the onCompleteRenderParams, so just ignore it (we'll reference it in the onCompleteRender instance instead). + _target.addEventListener("enterFrame", _enterFrameHandler, false, 100, true); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/Positions2DPlugin.as b/src/com/greensock/plugins/Positions2DPlugin.as new file mode 100644 index 0000000..a3e27b7 --- /dev/null +++ b/src/com/greensock/plugins/Positions2DPlugin.as @@ -0,0 +1,67 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] This plugin allows you to define an array of Points at which the target should be positioned during the course of + * the tween (in order). So if 4 Points are in the array, the target will be rendered at the first Point's x/y at the + * beginning of the tween, then at around 25% through the tween, it will jump to the 2nd Point's position, etc. until + * it arrives at the last Point's position. The array can be populated with any object that has x and y properties + * (they don't need to be Points - they could be generic Objects). + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.Positions2DPlugin; +TweenPlugin.activate([Positions2DPlugin]); //activation is permanent in the SWF, so this line only needs to be run once + +TweenLite.to(mc, 3, {positions2D:[{x:250, y:50}, {x:500, y:0}]}); + + * + *

Copyright 2008-2013, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class Positions2DPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _positions:Array; + + /** @private **/ + public function Positions2DPlugin() { + super("positions2D,x,y"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(value is Array)) { + return false; + } + _target = target; + _positions = value as Array; + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + if (v < 0) { + v = 0; + } else if (v >= 1) { + v = 0.999999999; + } + var position:Object = _positions[ int(_positions.length * v) ]; + _target.x = position.x; + _target.y = position.y; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/QuaternionsPlugin.as b/src/com/greensock/plugins/QuaternionsPlugin.as new file mode 100644 index 0000000..3351f6d --- /dev/null +++ b/src/com/greensock/plugins/QuaternionsPlugin.as @@ -0,0 +1,151 @@ +/** + * VERSION: 12.0.1 + * DATE: 2013-12-26 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] Performs SLERP interpolation between 2 Quaternions. Each Quaternion should have x, y, z, and w properties. + * Simply pass in an Object containing properties that correspond to your object's quaternion properties. + * For example, if your myCamera3D has an "orientation" property that's a Quaternion and you want to + * tween its values to x:1, y:0.5, z:0.25, w:0.5, you could do:

+ * + * TweenLite.to(myCamera3D, 2, {quaternions:{orientation:new Quaternion(0, 1, 0, 0)}});

+ * + *

You can define as many quaternion properties as you want.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.QuaternionsPlugin; +TweenPlugin.activate([QuaternionsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myCamera3D, 2, {quaternions:{orientation:new Quaternion(0, 1, 0, 0)}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class QuaternionsPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected static const _RAD2DEG:Number = 180 / Math.PI; //precalculate for speed + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _quaternions:Array = []; + + /** @private **/ + public function QuaternionsPlugin() { + super("quaternions"); + _overwriteProps.pop(); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (value == null) { + return false; + } + _target = target; + for (var p:String in value) { + _initQuaternion(value[p], p); + } + return true; + } + + /** @private **/ + public function _initQuaternion(end:Object, p:String):void { + var angle:Number, q1:Object, q2:Object, x1:Number, x2:Number, y1:Number, y2:Number, z1:Number, z2:Number, w1:Number, w2:Number, theta:Number; + var isFunc:Boolean = (_target[p] is Function); + q1 = (!isFunc) ? _target[p] : _target[ ((p.indexOf("set") || !("get" + p.substr(3) in _target)) ? p : "get" + p.substr(3)) ](); + q2 = end; + x1 = q1.x; x2 = q2.x; + y1 = q1.y; y2 = q2.y; + z1 = q1.z; z2 = q2.z; + w1 = q1.w; w2 = q2.w; + angle = x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2; + if (angle < 0) { + x1 *= -1; + y1 *= -1; + z1 *= -1; + w1 *= -1; + angle *= -1; + } + if ((angle + 1) < 0.000001) { + y2 = -y1; + x2 = x1; + w2 = -w1; + z2 = z1; + } + theta = Math.acos(angle); + _quaternions[_quaternions.length] = [q1, p, x1, x2, y1, y2, z1, z2, w1, w2, angle, theta, 1 / Math.sin(theta), isFunc]; + _overwriteProps[_overwriteProps.length] = p; + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + var i:int = _quaternions.length; + while (--i > -1) { + if (lookup[_quaternions[i][1]] != null) { + _quaternions.splice(i, 1); + } + } + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + var i:int = _quaternions.length, q:Array, scale:Number, invScale:Number; + while (--i > -1) { + q = _quaternions[i]; + if ((q[10] + 1) > 0.000001) { + if ((1 - q[10]) >= 0.000001) { + scale = Math.sin(q[11] * (1 - v)) * q[12]; + invScale = Math.sin(q[11] * v) * q[12]; + } else { + scale = 1 - v; + invScale = v; + } + } else { + scale = Math.sin(Math.PI * (0.5 - v)); + invScale = Math.sin(Math.PI * v); + } + q[0].x = scale * q[2] + invScale * q[3]; + q[0].y = scale * q[4] + invScale * q[5]; + q[0].z = scale * q[6] + invScale * q[7]; + q[0].w = scale * q[8] + invScale * q[9]; + if (q[13]) { + _target[q[1]](q[0]); + } else { + _target[q[1]] = q[0]; + } + } + /* + Array access is faster (though less readable). Here is the key: + 0 - target + 1 = p + 2 = x1 + 3 = x2 + 4 = y1 + 5 = y2 + 6 = z1 + 7 = z2 + 8 = w1 + 9 = w2 + 10 = angle + 11 = theta + 12 = invTheta + 13 = isFunction + */ + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/RemoveTintPlugin.as b/src/com/greensock/plugins/RemoveTintPlugin.as new file mode 100644 index 0000000..f469d53 --- /dev/null +++ b/src/com/greensock/plugins/RemoveTintPlugin.as @@ -0,0 +1,37 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.plugins.TintPlugin; +/** + * [AS3/AS2 only] [deprecated in favor of tint:null] Removes the tint of a DisplayObject over time. + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.RemoveTintPlugin; +TweenPlugin.activate([RemoveTintPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {removeTint:true}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class RemoveTintPlugin extends TintPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function RemoveTintPlugin() { + super(); + _propName = "removeTint"; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/RoundPropsPlugin.as b/src/com/greensock/plugins/RoundPropsPlugin.as new file mode 100644 index 0000000..8c308d1 --- /dev/null +++ b/src/com/greensock/plugins/RoundPropsPlugin.as @@ -0,0 +1,95 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import com.greensock.core.PropTween; +/** + * If you'd like the inbetween values in a tween to always get rounded to the nearest integer, use the roundProps + * special property. Just pass in a comma-delimited String containing the property names that you'd like rounded. For example, + * if you're tweening the x, y, and alpha properties of mc and you want to round the x and y values (not alpha) + * every time the tween is rendered, you'd do:

+ * + * TweenMax.to(mc, 2, {x:300, y:200, alpha:0.5, roundProps:"x,y"});

+ * + *

USAGE:

+ * +import com.greensock.TweenMax; +import com.greensock.plugins.RoundPropsPlugin; +TweenPlugin.activate([RoundPropsPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenMax.to(mc, 2, {x:300, y:200, alpha:0.5, roundProps:"x,y"}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class RoundPropsPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _tween:TweenLite; + + /** @private **/ + public function RoundPropsPlugin() { + super("roundProps", -1); + _overwriteProps.length = 0; + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _tween = tween; + return true; + } + + /** @private **/ + public function _onInitAllProps():Boolean { + var rp:Array = (_tween.vars.roundProps is Array) ? _tween.vars.roundProps : _tween.vars.roundProps.split(","), + i:int = rp.length, + lookup:Object = {}, + rpt:PropTween = _tween._propLookup.roundProps, + prop:String, pt:PropTween, next:PropTween; + while (--i > -1) { + lookup[rp[i]] = 1; + } + i = rp.length; + while (--i > -1) { + prop = rp[i]; + pt = _tween._firstPT; + while (pt) { + next = pt._next; //record here, because it may get removed + if (pt.pg) { + pt.t._roundProps(lookup, true); + } else if (pt.n == prop) { + _add(pt.t, prop, pt.s, pt.c); + //remove from linked list + if (next) { + next._prev = pt._prev; + } + if (pt._prev) { + pt._prev._next = next; + } else if (_tween._firstPT == pt) { + _tween._firstPT = next; + } + pt._next = pt._prev = null; + _tween._propLookup[prop] = rpt; + } + pt = next; + } + } + return false; + } + + /** @private **/ + public function _add(target:Object, p:String, s:Number, c:Number):void { + _addTween(target, p, s, s + c, p, true); + _overwriteProps[_overwriteProps.length] = p; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/ScalePlugin.as b/src/com/greensock/plugins/ScalePlugin.as new file mode 100644 index 0000000..9d1c3d9 --- /dev/null +++ b/src/com/greensock/plugins/ScalePlugin.as @@ -0,0 +1,46 @@ +/** + * VERSION: 12.0 + * DATE: 2012-03-29 + * AS3 + * UPDATES AND DOCS AT: http://www.TweenMax.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] ScalePlugin combines scaleX and scaleY into one "scale" property.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ScalePlugin; +TweenPlugin.activate([ScalePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {scale:2}); //tweens horizontal and vertical scale simultaneously + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ScalePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; + + /** @private **/ + public function ScalePlugin() { + super("scale,scaleX,scaleY,width,height"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!target.hasOwnProperty("scaleX")) { + return false; + } + _addTween(target, "scaleX", target.scaleX, value, "scaleX"); + _addTween(target, "scaleY", target.scaleY, value, "scaleY"); + return true; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/ScrollRectPlugin.as b/src/com/greensock/plugins/ScrollRectPlugin.as new file mode 100644 index 0000000..4b8aa2c --- /dev/null +++ b/src/com/greensock/plugins/ScrollRectPlugin.as @@ -0,0 +1,80 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import flash.display.DisplayObject; + import flash.geom.Rectangle; + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] Tweens the scrollRect property of a DisplayObject. You can define any (or all) of the following + * properties: + * + *
    + *
  • x : Number
  • + *
  • y : Number
  • + *
  • width : Number
  • + *
  • height : Number
  • + *
  • top : Number
  • + *
  • bottom : Number
  • + *
  • left : Number
  • + *
  • right : Number
  • + *
+ *

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ScrollRectPlugin; +TweenPlugin.activate([ScrollRectPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {scrollRect:{x:50, y:300, width:100, height:100}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ScrollRectPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:DisplayObject; + /** @private **/ + protected var _rect:Rectangle; + + /** @private **/ + public function ScrollRectPlugin() { + super("scrollRect"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(target is DisplayObject)) { + return false; + } + _target = target as DisplayObject; + if (_target.scrollRect != null) { + _rect = _target.scrollRect; + } else { + var r:Rectangle = _target.getBounds(_target); + _rect = new Rectangle(0, 0, r.width + r.x, r.height + r.y); + } + for (var p:String in value) { + _addTween(_rect, p, _rect[p], value[p], "scrollRect"); + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + _target.scrollRect = _rect; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/SetActualSizePlugin.as b/src/com/greensock/plugins/SetActualSizePlugin.as new file mode 100644 index 0000000..c750a01 --- /dev/null +++ b/src/com/greensock/plugins/SetActualSizePlugin.as @@ -0,0 +1,90 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * A3S + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.*; +/** + * [AS3 only] Some components require resizing with setActualSize() instead of standard tweens of width/height in + * order to scale properly. The SetActualSizePlugin accommodates this easily. You can define the width, + * height, or both.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.SetActualSizePlugin; +TweenPlugin.activate([SetActualSizePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myComponent, 1, {setActualSize:{width:200, height:30}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SetActualSizePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public var width:Number; + /** @private **/ + public var height:Number; + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _setWidth:Boolean; + /** @private **/ + protected var _setHeight:Boolean; + /** @private **/ + protected var _hasSetSize:Boolean; + + /** @private **/ + public function SetActualSizePlugin() { + super("setActualSize,setSize,width,height,scaleX,scaleY"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target; + _hasSetSize = Boolean("setActualSize" in _target); + if ("width" in value && _target.width != value.width) { + _addTween((_hasSetSize) ? this : _target, "width", _target.width, value.width, "width", true); + _setWidth = _hasSetSize; + } + if ("height" in value && _target.height != value.height) { + _addTween((_hasSetSize) ? this : _target, "height", _target.height, value.height, "height", true); + _setHeight = _hasSetSize; + } + if (_firstPT == null) { //protects against occassions when the tween's start and end values are the same. + _hasSetSize = false; + } + return true; + } + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + if ("width" in lookup || "scaleX" in lookup) { + _setWidth = false; + } + if ("height" in lookup || "scaleY" in lookup) { + _setHeight = false; + } + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + if (_hasSetSize) { + _target.setActualSize((_setWidth) ? this.width : _target.width, (_setHeight) ? this.height : _target.height); + } + } + + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/SetSizePlugin.as b/src/com/greensock/plugins/SetSizePlugin.as new file mode 100644 index 0000000..7ce13fd --- /dev/null +++ b/src/com/greensock/plugins/SetSizePlugin.as @@ -0,0 +1,90 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.*; +/** + * [AS3/AS2 only] Some components require resizing with setSize() instead of standard tweens of width/height in + * order to scale properly. The SetSizePlugin accommodates this easily. You can define the width, + * height, or both.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.SetSizePlugin; +TweenPlugin.activate([SetSizePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(myComponent, 1, {setSize:{width:200, height:30}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SetSizePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public var width:Number; + /** @private **/ + public var height:Number; + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _setWidth:Boolean; + /** @private **/ + protected var _setHeight:Boolean; + /** @private **/ + protected var _hasSetSize:Boolean; + + /** @private **/ + public function SetSizePlugin() { + super("setSize,setActualSize,width,height,scaleX,scaleY"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target; + _hasSetSize = Boolean("setSize" in _target); + if ("width" in value && _target.width != value.width) { + _addTween((_hasSetSize) ? this : _target, "width", _target.width, value.width, "width", true); + _setWidth = _hasSetSize; + } + if ("height" in value && _target.height != value.height) { + _addTween((_hasSetSize) ? this : _target, "height", _target.height, value.height, "height", true); + _setHeight = _hasSetSize; + } + if (_firstPT == null) { + _hasSetSize = false; //protects from situations where the start and end values are the same, thus we're not really tweening anything. + } + return true; + } + + + /** @private **/ + override public function _kill(lookup:Object):Boolean { + if ("setSize" in lookup || "width" in lookup || "scaleX" in lookup) { + _setWidth = false; + } + if ("setSize" in lookup || "height" in lookup || "scaleY" in lookup) { + _setHeight = false; + } + return super._kill(lookup); + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + if (_hasSetSize) { + _target.setSize((_setWidth) ? this.width : _target.width, (_setHeight) ? this.height : _target.height); + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/ShortRotationPlugin.as b/src/com/greensock/plugins/ShortRotationPlugin.as new file mode 100644 index 0000000..716e8f5 --- /dev/null +++ b/src/com/greensock/plugins/ShortRotationPlugin.as @@ -0,0 +1,78 @@ +/** + * VERSION: 12.0 + * DATE: 2012-02-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] To tween any rotation property of the target object in the shortest direction, use "shortRotation" + * For example, if myObject.rotation is currently 170 degrees and you want to tween it to -170 degrees, + * a normal rotation tween would travel a total of 340 degrees in the counter-clockwise direction, + * but if you use shortRotation, it would travel 20 degrees in the clockwise direction instead. You + * can define any number of rotation properties in the shortRotation object which makes 3D tweening + * easier, like:

+ * + * TweenMax.to(mc, 2, {shortRotation:{rotationX:-170, rotationY:35, rotationZ:200}});

+ * + *

Normally shortRotation is defined in degrees, but if you prefer to have it work with radians instead, + * simply set the useRadians special property to true like:

+ * + * TweenMax.to(myCustomObject, 2, {shortRotation:{customRotationProperty:Math.PI, useRadians:true}});

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.ShortRotationPlugin; +TweenPlugin.activate([ShortRotationPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {shortRotation:{rotation:-170}}); + +//or for a 3D tween with multiple rotation values... +TweenLite.to(mc, 1, {shortRotation:{rotationX:-170, rotationY:35, rotationZ:10}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class ShortRotationPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + public function ShortRotationPlugin() { + super("shortRotation"); + _overwriteProps.pop(); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (typeof(value) == "number") { + return false; + } + var useRadians:Boolean = Boolean(value.useRadians == true), start:Number; + for (var p:String in value) { + if (p != "useRadians") { + start = (target[p] is Function) ? target[ ((p.indexOf("set") || !("get" + p.substr(3) in target)) ? p : "get" + p.substr(3)) ]() : target[p]; + _initRotation(target, p, start, (typeof(value[p]) == "number") ? Number(value[p]) : start + Number(value[p].split("=").join("")), useRadians); + } + } + return true; + } + + /** @private **/ + public function _initRotation(target:Object, p:String, start:Number, end:Number, useRadians:Boolean=false):void { + var cap:Number = useRadians ? Math.PI * 2 : 360, + dif:Number = (end - start) % cap; + if (dif != dif % (cap / 2)) { + dif = (dif < 0) ? dif + cap : dif - cap; + } + _addTween(target, p, start, start + dif, p); + _overwriteProps[_overwriteProps.length] = p; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/SoundTransformPlugin.as b/src/com/greensock/plugins/SoundTransformPlugin.as new file mode 100644 index 0000000..258be2f --- /dev/null +++ b/src/com/greensock/plugins/SoundTransformPlugin.as @@ -0,0 +1,61 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.media.SoundTransform; +/** + * [AS3 only] Tweens properties of an object's soundTransform property (like the volume, pan, leftToRight, etc. of a MovieClip/SoundChannel/NetStream).

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.SoundTransformPlugin; +TweenPlugin.activate([SoundTransformPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {soundTransform:{volume:0.2, pan:0.5}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class SoundTransformPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _st:SoundTransform; + + /** @private **/ + public function SoundTransformPlugin() { + super("soundTransform,volume"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!target.hasOwnProperty("soundTransform")) { + return false; + } + _target = target; + _st = _target.soundTransform; + for (var p:String in value) { + _addTween(_st, p, _st[p], value[p], p); + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + _target.soundTransform = _st; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/StageQualityPlugin.as b/src/com/greensock/plugins/StageQualityPlugin.as new file mode 100644 index 0000000..6eccdee --- /dev/null +++ b/src/com/greensock/plugins/StageQualityPlugin.as @@ -0,0 +1,71 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-14 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.display.Stage; + import flash.display.StageQuality; +/** + * [AS3 only] Sets the stage's quality to a particular value during a tween and another value after + * the tween which can be useful for improving rendering performance in the Flash Player while things are animating.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.StageQualityPlugin; +import flash.display.StageQuality; +TweenPlugin.activate([StageQualityPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {x:100, stageQuality:{stage:this.stage, during:StageQuality.LOW, after:StageQuality.HIGH}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class StageQualityPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _stage:Stage; + /** @private **/ + protected var _during:String; + /** @private **/ + protected var _after:String; + /** @private **/ + protected var _tween:TweenLite; + + /** @private **/ + public function StageQualityPlugin() { + super("stageQuality"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(value.stage is Stage)) { + trace("You must define a 'stage' property for the stageQuality object in your tween."); + return false; + } + _stage = value.stage as Stage; + _tween = tween; + _during = ("during" in value) ? value.during : StageQuality.MEDIUM; + _after = ("after" in value) ? value.after : _stage.quality; + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + if ((v == 1 && _tween._duration == _tween._time && _tween.data != "isFromStart") || (v == 0 && _tween._time == 0)) { //a changeFactor of 1 doesn't necessarily mean the tween is done - if the ease is Elastic.easeOut or Back.easeOut for example, they could hit 1 mid-tween. The reason we check to see if cachedTime is 0 is for from() tweens + _stage.quality = _after; + } else if (_stage.quality != _during) { + _stage.quality = _during; + } + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/TintPlugin.as b/src/com/greensock/plugins/TintPlugin.as new file mode 100644 index 0000000..6199abd --- /dev/null +++ b/src/com/greensock/plugins/TintPlugin.as @@ -0,0 +1,89 @@ +/** + * VERSION: 12.01 + * DATE: 2012-07-28 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.*; + import com.greensock.core.*; + + import flash.display.*; + import flash.geom.ColorTransform; + import flash.geom.Transform; +/** + * [AS3/AS2 only] To change a DisplayObject's tint/color, set this to the hex value of the tint you'd like + * to end up at (or begin at if you're using TweenMax.from()). An example hex value would be 0xFF0000. + * + *

To remove a tint completely, set the tint to null

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.TintPlugin; +TweenPlugin.activate([TintPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {tint:0xFF0000}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TintPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + protected static var _props:Array = ["redMultiplier","greenMultiplier","blueMultiplier","alphaMultiplier","redOffset","greenOffset","blueOffset","alphaOffset"]; + + /** @private **/ + protected var _transform:Transform; + + /** @private **/ + public function TintPlugin() { + super("tint,colorTransform,removeTint"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (!(target is DisplayObject)) { + return false; + } + var end:ColorTransform = new ColorTransform(); + if (value != null && tween.vars.removeTint != true) { + end.color = uint(value); + } + _transform = DisplayObject(target).transform; + var ct:ColorTransform = _transform.colorTransform; + end.alphaMultiplier = ct.alphaMultiplier; + end.alphaOffset = ct.alphaOffset; + _init(ct, end); + return true; + } + + /** @private **/ + public function _init(start:ColorTransform, end:ColorTransform):void { + var i:int = _props.length, + p:String; + while (--i > -1) { + p = _props[i]; + if (start[p] != end[p]) { + _addTween(start, p, start[p], end[p], "tint"); + } + } + } + + /** @private **/ + override public function setRatio(v:Number):void { + var ct:ColorTransform = _transform.colorTransform, //don't just use _ct because if alpha changes are made separately, they won't get applied properly. + pt:PropTween = _firstPT; + while (pt) { + ct[pt.p] = pt.c * v + pt.s; + pt = pt._next; + } + _transform.colorTransform = ct; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/TransformMatrixPlugin.as b/src/com/greensock/plugins/TransformMatrixPlugin.as new file mode 100644 index 0000000..8b7b2ef --- /dev/null +++ b/src/com/greensock/plugins/TransformMatrixPlugin.as @@ -0,0 +1,240 @@ +/** + * VERSION: 12.0.1 + * DATE: 2013-02-09 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import flash.geom.Matrix; + import flash.geom.Transform; +/** + * [AS3/AS2 only] TransformMatrixPlugin allows you to tween a DisplayObject's transform.matrix values directly + * (a, b, c, d, tx, and ty) or use common properties like x, y, scaleX, scaleY, + * skewX, skewY, rotation and even shortRotation. + * To skew without adjusting scale visually, use skewX2 and skewY2 instead of skewX and skewY. + * + *

transformMatrix tween will affect all of the DisplayObject's transform properties, so do not use + * it in conjunction with regular x/y/scaleX/scaleY/rotation tweens concurrently.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.~~; +TweenPlugin.activate([TransformMatrixPlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {transformMatrix:{x:50, y:300, scaleX:2, scaleY:2}}); + +//-OR- + +TweenLite.to(mc, 1, {transformMatrix:{tx:50, ty:300, a:2, d:2}}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TransformMatrixPlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + /** @private **/ + private static const _DEG2RAD:Number = Math.PI / 180; + + /** @private **/ + protected var _transform:Transform; + /** @private **/ + protected var _matrix:Matrix; + /** @private **/ + protected var _txStart:Number; + /** @private **/ + protected var _txChange:Number; + /** @private **/ + protected var _tyStart:Number; + /** @private **/ + protected var _tyChange:Number; + /** @private **/ + protected var _aStart:Number; + /** @private **/ + protected var _aChange:Number; + /** @private **/ + protected var _bStart:Number; + /** @private **/ + protected var _bChange:Number; + /** @private **/ + protected var _cStart:Number; + /** @private **/ + protected var _cChange:Number; + /** @private **/ + protected var _dStart:Number; + /** @private **/ + protected var _dChange:Number; + /** @private **/ + protected var _angleChange:Number = 0; + + /** @private **/ + public function TransformMatrixPlugin() { + super("transformMatrix,x,y,scaleX,scaleY,rotation,width,height,transformAroundPoint,transformAroundCenter"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _transform = target.transform as Transform; + _matrix = _transform.matrix; + var matrix:Matrix = _matrix.clone(); + _txStart = matrix.tx; + _tyStart = matrix.ty; + _aStart = matrix.a; + _bStart = matrix.b; + _cStart = matrix.c; + _dStart = matrix.d; + + if ("x" in value) { + _txChange = (typeof(value.x) == "number") ? value.x - _txStart : Number(value.x.split("=").join("")); + } else if ("tx" in value) { + _txChange = value.tx - _txStart; + } else { + _txChange = 0; + } + if ("y" in value) { + _tyChange = (typeof(value.y) == "number") ? value.y - _tyStart : Number(value.y.split("=").join("")); + } else if ("ty" in value) { + _tyChange = value.ty - _tyStart; + } else { + _tyChange = 0; + } + _aChange = ("a" in value) ? value.a - _aStart : 0; + _bChange = ("b" in value) ? value.b - _bStart : 0; + _cChange = ("c" in value) ? value.c - _cStart : 0; + _dChange = ("d" in value) ? value.d - _dStart : 0; + + if (("rotation" in value) || ("shortRotation" in value) || ("scale" in value && !(value is Matrix)) || ("scaleX" in value) || ("scaleY" in value) || ("skewX" in value) || ("skewY" in value) || ("skewX2" in value) || ("skewY2" in value)) { + var ratioX:Number, ratioY:Number; + var scaleX:Number = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b); //Bugs in the Flex framework prevent DisplayObject.scaleX from working consistently, so we must determine it using the matrix. + if (scaleX == 0) { + matrix.a = scaleX = 0.0001; + } else if (matrix.a < 0 && matrix.d > 0) { + scaleX = -scaleX; + } + var scaleY:Number = Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d); //Bugs in the Flex framework prevent DisplayObject.scaleY from working consistently, so we must determine it using the matrix. + if (scaleY == 0) { + matrix.d = scaleY = 0.0001; + } else if (matrix.d < 0 && matrix.a > 0) { + scaleY = -scaleY; + } + var angle:Number = Math.atan2(matrix.b, matrix.a); //Bugs in the Flex framework prevent DisplayObject.rotation from working consistently, so we must determine it using the matrix + if (matrix.a < 0 && matrix.d >= 0) { + angle += (angle <= 0) ? Math.PI : -Math.PI; + } + var skewX:Number = Math.atan2(-_matrix.c, _matrix.d) - angle; + var finalAngle:Number = angle; + if ("shortRotation" in value) { + var dif:Number = ((value.shortRotation * _DEG2RAD) - angle) % (Math.PI * 2); + if (dif > Math.PI) { + dif -= Math.PI * 2; + } else if (dif < -Math.PI) { + dif += Math.PI * 2; + } + finalAngle += dif; + } else if ("rotation" in value) { + finalAngle = (typeof(value.rotation) == "number") ? value.rotation * _DEG2RAD : Number(value.rotation.split("=").join("")) * _DEG2RAD + angle; + } + + var finalSkewX:Number = ("skewX" in value) ? (typeof(value.skewX) == "number") ? Number(value.skewX) * _DEG2RAD : Number(value.skewX.split("=").join("")) * _DEG2RAD + skewX : 0; + + if ("skewY" in value) { //skewY is just a combination of rotation and skewX + var skewY:Number = (typeof(value.skewY) == "number") ? value.skewY * _DEG2RAD : Number(value.skewY.split("=").join("")) * _DEG2RAD - skewX; + finalAngle += skewY + skewX; + finalSkewX -= skewY; + } + + if (finalAngle != angle) { + if (("rotation" in value) || ("shortRotation" in value)) { + _angleChange = finalAngle - angle; + finalAngle = angle; //to correctly affect the skewX calculations below + } else { + matrix.rotate(finalAngle - angle); + } + } + + if ("scale" in value) { + ratioX = Number(value.scale) / scaleX; + ratioY = Number(value.scale) / scaleY; + if (typeof(value.scale) != "number") { //relative value + ratioX += 1; + ratioY += 1; + } + } else { + if ("scaleX" in value) { + ratioX = Number(value.scaleX) / scaleX; + if (typeof(value.scaleX) != "number") { //relative value + ratioX += 1; + } + } + if ("scaleY" in value) { + ratioY = Number(value.scaleY) / scaleY; + if (typeof(value.scaleY) != "number") { //relative value + ratioY += 1; + } + } + } + + if (finalSkewX != skewX) { + matrix.c = -scaleY * Math.sin(finalSkewX + finalAngle); + matrix.d = scaleY * Math.cos(finalSkewX + finalAngle); + } + + if ("skewX2" in value) { + if (typeof(value.skewX2) == "number") { + matrix.c = Math.tan(0 - (value.skewX2 * _DEG2RAD)); + } else { + matrix.c += Math.tan(0 - (Number(value.skewX2) * _DEG2RAD)); + } + } + if ("skewY2" in value) { + if (typeof(value.skewY2) == "number") { + matrix.b = Math.tan(value.skewY2 * _DEG2RAD); + } else { + matrix.b += Math.tan(Number(value.skewY2) * _DEG2RAD); + } + } + + if (ratioX || ratioX == 0) { //faster than isNaN() + matrix.a *= ratioX; + matrix.b *= ratioX; + } + if (ratioY || ratioY == 0) { + matrix.c *= ratioY; + matrix.d *= ratioY; + } + _aChange = matrix.a - _aStart; + _bChange = matrix.b - _bStart; + _cChange = matrix.c - _cStart; + _dChange = matrix.d - _dStart; + } + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + _matrix.a = _aStart + (v * _aChange); + _matrix.b = _bStart + (v * _bChange); + _matrix.c = _cStart + (v * _cChange); + _matrix.d = _dStart + (v * _dChange); + if (_angleChange) { + //about 3-4 times faster than _matrix.rotate(_angleChange * n); + var cos:Number = Math.cos(_angleChange * v), + sin:Number = Math.sin(_angleChange * v), + a:Number = _matrix.a, + c:Number = _matrix.c; + _matrix.a = a * cos - _matrix.b * sin; + _matrix.b = a * sin + _matrix.b * cos; + _matrix.c = c * cos - _matrix.d * sin; + _matrix.d = c * sin + _matrix.d * cos; + } + _matrix.tx = _txStart + (v * _txChange); + _matrix.ty = _tyStart + (v * _tyChange); + _transform.matrix = _matrix; + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/TweenPlugin.as b/src/com/greensock/plugins/TweenPlugin.as new file mode 100644 index 0000000..66d4e51 --- /dev/null +++ b/src/com/greensock/plugins/TweenPlugin.as @@ -0,0 +1,336 @@ +/** + * VERSION: 12.1.5 + * DATE: 2013-07-21 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; + import com.greensock.core.PropTween; +/** + * TweenPlugin is the base class for all TweenLite and TweenMax plugins, but generally isn't used directly. + * + *

USAGE:

+ * + *

To create your own plugin, extend TweenPlugin and override whichever methods you need. Typically, + * you only need to override _onInitTween() (which is called when the tween renders for + * the first time) and setRatio() (which is called on every update and passes a progress + * parameter which is typically a value between 0 and 1, but changes according to the ease). I'd recommend + * looking at a simple plugin like ScalePlugin and using it as a template of sorts. There are a few + * key concepts to keep in mind:

+ * + *
    + *
  1. Pass the TweenPlugin constructor a comma-delimited list of property names that the plugin should + * overwrite, the first of which should be the property name that the plugin intercepts. For example, + * the ScalePlugin handles any tweens of "scale" and it also overwrites other concurrent + * tweens that are handling the "scale", "scaleX", and/or "scaleY" properties of the target. Therefore, + * in ScalePlugin's constructor, we'd call super("scale,scaleX,scaleY"). The first name + * in the list must be unique - two plugins cannot handle the same primary property.
  2. + * + *
  3. When a tween that uses your plugin initializes its tween values (normally when it renders the + * first time), a new instance of your plugin will be created and the _onInitTween() method + * is called. That's where you'll want to record any initial values and prepare for the tween. + * _onInitTween() should return a Boolean value that essentially indicates whether + * or not the plugin initted successfully. If you return false, TweenLite/Max will just use a + * normal tween for the value, ignoring the plugin for that particular tween. For example, + * maybe your tween only works with MovieClips, so if the target isn't a MovieClip you could + * return false
  4. + * + *
  5. The setRatio() method will be called on every frame during the course of the tween + * and it will be passed a single parameter that's a multiplier (typically between 0 and 1, according + * to the ease) describing the total amount of change from the beginning of the tween (0). It will be + * zero at the beginning of the tween and 1 at the end, but inbetween it could be any value based on the + * ease applied (for example, an ElasticOut ease would cause the value to shoot past 1 and + * back again before the end of the tween). So if the tween uses the Linear.ease, when it's + * halfway finished, the setRatio() will receive a parameter of 0.5.
  6. + * + *
  7. The _overwriteProps is an array that should contain the properties that your + * plugin should overwrite in "auto" mode. For example, the autoAlpha + * plugin controls the "visible" and "alpha" properties of an object, + * so if another tween is created that controls the alpha of the target object, + * your plugin's _kill() method will be called which should handle killing the + * "alpha" part of the tween. It is your responsibility to populate (and depopulate) + * the _overwriteProps Array. Failure to do so properly can cause odd overwriting + * behavior.
  8. + * + *
  9. There's a _roundProps() method that gets called by the RoundPropsPlugin if the + * user requests that certain properties get rounded to the nearest integer. If you use + * _addTween() method to add property tweens, rounding will happen automatically + * (if necessary), but if you don't use _addTween() and prefer to manually calculate + * tween values in your setRatio() method, just remember to override the _roundProps() + * method if that makes sense in your plugin (some plugins wouldn't need to accommodate rounding, like color + * plugins).
  10. + * + *
  11. If you need to run a function when the tween gets disabled, add an _onDisable() method + * (named exactly that) to your plugin. It will automatically be called when the tween gets disabled (typically + * when it finishes and is removed from its parent timeline). Same for _onEnable() if you + * need to run code when a tween is enabled. These methods should return a Boolean value indicating + * whether or not they changed any properties on the target becaues if so (true), it helps + * notify any initting tweens of the same target to re-init. It is very rare that an _onDisable() + * or _onEnable() method is necessary, but it can be useful for things like MotionBlurPlugin + * which must do some very advanced things, hiding the target, changing its alpha to almost 0, etc. only + * while the tween occurs. If another alpha tween of that same target overwrites an existing motionBlur + * of the same target, the alpha would be at the wrong value normally, but the if the _onDisable() + * returns true, it would force the new tween to re-init AFTER the alpha was fixed inside + * the _onDisable(). Again, this is VERY rare.
  12. + * + *
  13. Please use the same naming convention as the rest of the plugins, like MySpecialPropertyNamePlugin.
  14. + * + *
  15. If you are handling more than one property in your plugin (like RoundPropsPlugin or ShortRotationPlugin), + * make sure you override the _kill() method which will be passed a vars parameter + * with properties that need to be killed (typically for overwriting).
  16. + * + *
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class TweenPlugin { + /** @private **/ + public static const version:String = "12.1.5"; + + /** @private If the API/Framework for plugins changes in the future, this number helps determine compatibility **/ + public static const API:Number = 2; + + /** @private Name of the special property that the plugin should intercept/handle **/ + public var _propName:String; + + /** + * @private + * Array containing the names of the properties that should be overwritten in "auto" mode. + * Typically the only value in this Array is the _propName, but there are cases when it may + * be different. For example, a bezier tween's propName is "bezier" but it can manage many different properties + * like x, y, etc. depending on what's passed in to the tween. + */ + public var _overwriteProps:Array; + + /** @private Priority level in the render queue **/ + public var _priority:int = 0; + + /** @private First property tween in the linked list (if any) **/ + protected var _firstPT:PropTween; + + /** + * @private + * Constructor + * + * @param props A comma-delimited list of properties that will populate the _overwriteProps array, the first of which will be the _propName (the special property that the plugin handles). For example, the ScalePlugin would be "scale,scaleX,scaleY". + * @param priority The priority in the rendering queue (lower priorty renders after higher priority). For example, a motionBlur might need to wait until all other properties like x and y have tweened before it does its magic of figuring out how far things have moved, etc. so motionBlur's priority could be low (like -10). Standard property tweens are always 0. To render before other things, use a high priority. + */ + public function TweenPlugin(props:String="", priority:int=0) { + _overwriteProps = props.split(","); + _propName = _overwriteProps[0]; + _priority = priority || 0; + } + + /** + * @private + * Gets called when any tween of the special property begins. Record any initial values + * that will be used in the setRatio() method. + * + * @param target target object that should be affected. This is the same as the tween's target unless the tween's target is an array in which case a different plugin instance is created for each object in the array, so this target would be the object in the array. + * @param value The value that is passed in through the special property in the tween. For example, if this is the ScalePlugin and the tween is TweenLite.to(mc, 1, {scale:2.5}), the value would be 2.5. + * @param tween The TweenLite or TweenMax instance using this plugin. + * @return If the initialization failed, it returns false. Otherwise true. It may fail if, for example, the plugin requires that the target be a DisplayObject or has some other unmet criteria in which case the plugin is skipped and a normal property tween is used inside TweenLite/Max + */ + public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + return false; + } + + /** + * @private + * Offers a simple way to add tweening values to the plugin. You don't need to use this, + * but it is convenient because the tweens get updated in the setRatio() method which also + * handles rounding. _kill() nicely integrates with most tweens added via _addTween() + * as well, but if you prefer to handle this manually in your plugin, you're welcome to. + * + * @param target Target object whose property you'd like to tween. (i.e. myClip) + * @param propName The property name that should be tweened. (i.e. "x") + * @param start Starting value + * @param end End value (can be either numeric or a string value. If it's a string, it will be interpreted as relative to the starting value) + * @param overwriteProp Name of the property that should be associated with the tween for overwriting purposes. Normally, it's the same as propName, but not always. For example, you may tween the "setRatio" property of a VisiblePlugin, but the property that it's actually controling in the end is "visible", so if a new overlapping tween of the target object is created that affects its "visible" property, this allows the plugin to kill the appropriate tween(s) when _kill() is called. + * @param round If true, the property should be rounded to the closest integer whenever updated + * @return If a PropTween is created (which means a tween was required between the provided start and end values), that PropTween is returned. Otherwise, null is returned. + */ + protected function _addTween(target:Object, propName:String, start:Number, end:*, overwriteProp:String=null, round:Boolean=false):PropTween { + var c:Number = (end == null) ? 0 : (typeof(end) === "number" || end.charAt(1) !== "=") ? Number(end) - start : int(end.charAt(0) + "1") * Number(end.substr(2)); + if (c !== 0) { + _firstPT = new PropTween(target, propName, start, c, overwriteProp || propName, false, _firstPT); + _firstPT.r = round; + return _firstPT; + } + return null; + } + + /** + * @private + * In most cases, your custom updating code should go here. The setRatio() value describes the + * amount of change based on how far along the tween is and the ease applied. It will be zero at the beginning + * of the tween and 1 at the end, but inbetween it could be any value based on the ease applied (for example, + * an ElasticOut tween would cause the value to shoot past 1 and back again before the end of the tween) + * This value gets updated on every frame during the course of the tween. + * + * @param v Multiplier describing the overall amount of change that should be applied since the start. It will be zero at the beginning of the tween and 1 at the end, but inbetween it could be any value based on the ease applied (for example, an ElasticOut tween would cause the value to shoot past 1 and back again before the end of the tween) + */ + public function setRatio(v:Number):void { + var pt:PropTween = _firstPT, val:Number; + while (pt) { + val = pt.c * v + pt.s; + if (pt.r) { + val = (val + ((val > 0) ? 0.5 : -0.5)) | 0; //about 4x faster than Math.round() + } + if (pt.f) { + pt.t[pt.p](val); + } else { + pt.t[pt.p] = val; + } + pt = pt._next; + } + } + + /** + * @private + * Used internally by RoundPropsPlugin which passes _round() lookup object with properties + * that should be rounded to the nearest integer during the tween. For example: + * + *

+ * TweenMax.to(mc, 2, {x:100, y:100, myPlugin:0.5, roundProps:"x,y"}); + *

+ * + *

The above tween will result in RoundPropsPlugin passing the a {x:1,y:1}" object to the + * _roundProps() method of the plugin that's managing the myPlugin special property + * (should be named MyPluginPlugin by naming convention). Some plugins manage more than one property, like + * BezierPlugin, ShortRotationPlugin, etc. so it's possible that only certain properties should be rounded + * inside the plugin. If you're building a plugin that should accommodate rounding and you're not using + * the standard _addTween() to handle the property tweens, you should override this method and + * run your own logic.

+ * + * @param props A lookup object with property names that should be rounded. + */ + public function _roundProps(lookup:Object, value:Boolean=true):void { + var pt:PropTween = _firstPT; + while (pt) { + if ((_propName in lookup) || (pt.n != null && pt.n.split(_propName + "_").join("") in lookup)) { //some properties that are very plugin-specific add a prefix named after the _propName plus an underscore, so we need to ignore that extra stuff here. + pt.r = value; + } + pt = pt._next; + } + } + + /** + * @private + * Gets called on plugins that have multiple overwritable properties in "auto" mode. + * Basically, it instructs the plugin to overwrite certain properties. For example, + * if a bezier tween is affecting x, y, and width, and then a new tween is created while the + * bezier tween is in progress, and the new tween affects the "x" property, we need a way + * to kill just the "x" part of the bezier tween. + * + * @param lookup An object containing properties that should be overwritten. We don't pass in an Array because looking up properties on the object is usually faster because it gives us random access. So to overwrite the "x" and "y" properties, a {x:true, y:true} object would be passed in. + */ + public function _kill(lookup:Object):Boolean { + if (_propName in lookup) { + _overwriteProps = []; + } else { + var i:int = _overwriteProps.length; + while (--i > -1) { + if (_overwriteProps[i] in lookup) { + _overwriteProps.splice(i, 1); + } + } + } + var pt:PropTween = _firstPT; + while (pt) { + if (pt.n in lookup) { + if (pt._next) { + pt._next._prev = pt._prev; + } + if (pt._prev) { + pt._prev._next = pt._next; + pt._prev = null; + } else if (_firstPT == pt) { + _firstPT = pt._next; + } + } + pt = pt._next; + } + return false; + } + + /** + * @private + * This method is called inside TweenLite after significant events occur, like when a tween + * has finished initializing, and (if necessary) when its "enabled" state changes. + * For example, the MotionBlurPlugin must run after normal x/y/alpha PropTweens are rendered, + * so the "_onInitAllProps" event reorders the PropTweens linked list in order of priority. + * Some plugins need to do things when a tween completes or when it gets disabled. Again, this + * method is only for internal use inside TweenLite. It is separated into + * this static method in order to minimize file size inside TweenLite. + * + * @param type The type of event "_onInitAllProps", "_onEnable", or "_onDisable" + * @param tween The TweenLite/Max instance to which the event pertains + * @return A Boolean value indicating whether or not properties of the tween's target may have changed as a result of the event + */ + private static function _onTweenEvent(type:String, tween:TweenLite):Boolean { + var pt:PropTween = tween._firstPT, changed:Boolean; + if (type == "_onInitAllProps") { + //sorts the PropTween linked list in order of priority because some plugins need to render earlier/later than others, like MotionBlurPlugin applies its effects after all x/y/alpha tweens have rendered on each frame. + var pt2:PropTween, first:PropTween, last:PropTween, next:PropTween; + while (pt) { + next = pt._next; + pt2 = first; + while (pt2 && pt2.pr > pt.pr) { + pt2 = pt2._next; + } + if ((pt._prev = pt2 ? pt2._prev : last)) { + pt._prev._next = pt; + } else { + first = pt; + } + if ((pt._next = pt2)) { + pt2._prev = pt; + } else { + last = pt; + } + pt = next; + } + pt = tween._firstPT = first; + } + while (pt) { + if (pt.pg) if (type in pt.t) if (pt.t[type]()) { + changed = true; + } + pt = pt._next; + } + return changed; + } + + /** + * Activates one or more plugins so that TweenLite and TweenMax recognize the associated special properties. + * You only need to activate each plugin once in order for it to be used in your project/app. For example, + * the following code activates the ScalePlugin and RoundPropsPlugin: + * + *

+ * TweenPlugin.activate([ScalePlugin, RoundPropsPlugin]); + *

+ * + *

Thereafter, tweens will recognize the "scale" and "roundProps" special properties associated with + * these plugins. Like TweenLite.to(mc, 1, {scale:5, x:300, roundProps:"x"});

+ * + *

Each plugin must extend TweenPlugin.

+ * + * @param plugins An Array of plugins to be activated. For example, TweenPlugin.activate([FrameLabelPlugin, ShortRotationPlugin, TintPlugin]); + */ + public static function activate(plugins:Array):Boolean { + TweenLite._onPluginEvent = TweenPlugin._onTweenEvent; + var i:int = plugins.length; + while (--i > -1) { + if (plugins[i].API == TweenPlugin.API) { + TweenLite._plugins[(new (plugins[i] as Class)())._propName] = plugins[i]; + } + } + return true + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/VisiblePlugin.as b/src/com/greensock/plugins/VisiblePlugin.as new file mode 100644 index 0000000..79c50a7 --- /dev/null +++ b/src/com/greensock/plugins/VisiblePlugin.as @@ -0,0 +1,67 @@ +/** + * VERSION: 12.1 + * DATE: 2012-06-19 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import com.greensock.TweenLite; +/** + * [AS3/AS2 only] Toggles the visibility at the end of a tween. For example, if you want to set visible to false + * at the end of the tween, do:

+ * + * TweenLite.to(mc, 1, {x:100, visible:false});

+ * + *

The visible property is forced to true during the course of the tween.

+ * + *

USAGE:

+ * +import com.greensock.TweenLite;
+import com.greensock.plugins.TweenPlugin;
+import com.greensock.plugins.VisiblePlugin;
+TweenPlugin.activate([VisiblePlugin]); //activation is permanent in the SWF, so this line only needs to be run once.

+ +TweenLite.to(mc, 1, {x:100, visible:false});

+
+ * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class VisiblePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _tween:TweenLite; + /** @private **/ + protected var _visible:Boolean; + /** @private **/ + protected var _initVal:Boolean; + /** @private **/ + protected var _progress:int; + + /** @private **/ + public function VisiblePlugin() { + super("visible"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + _target = target; + _tween = tween; + _progress = (_tween.vars.runBackwards) ? 0 : 1; + _initVal = _target.visible; + _visible = Boolean(value); + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + _target.visible = (v == 1 && (_tween._time / _tween._duration == _progress || _tween._duration == 0)) ? _visible : _initVal; //a ratio of 1 doesn't necessarily mean the tween is done - if the ease is Elastic.easeOut or Back.easeOut, for example, it could it 1 mid-tween. Also remember that zero-duration tweens will return NaN for _time / _duration. + } + + } +} \ No newline at end of file diff --git a/src/com/greensock/plugins/VolumePlugin.as b/src/com/greensock/plugins/VolumePlugin.as new file mode 100644 index 0000000..55373a0 --- /dev/null +++ b/src/com/greensock/plugins/VolumePlugin.as @@ -0,0 +1,60 @@ +/** + * VERSION: 12.0 + * DATE: 2012-01-15 + * AS3 + * UPDATES AND DOCS AT: http://www.greensock.com + **/ +package com.greensock.plugins { + import flash.media.SoundTransform; + import com.greensock.*; + import com.greensock.plugins.*; +/** + * [AS3/AS2 only] Tweens the volume of an object with a soundTransform property (MovieClip/SoundChannel/NetStream, etc.). + * + *

USAGE:

+ * +import com.greensock.TweenLite; +import com.greensock.plugins.TweenPlugin; +import com.greensock.plugins.VolumePlugin; +TweenPlugin.activate([VolumePlugin]); //activation is permanent in the SWF, so this line only needs to be run once. + +TweenLite.to(mc, 1, {volume:0}); + + * + *

Copyright 2008-2014, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for Club GreenSock members, the software agreement that was issued with the membership.

+ * + * @author Jack Doyle, jack@greensock.com + */ + public class VolumePlugin extends TweenPlugin { + /** @private **/ + public static const API:Number = 2; //If the API/Framework for plugins changes in the future, this number helps determine compatibility + + /** @private **/ + protected var _target:Object; + /** @private **/ + protected var _st:SoundTransform; + + /** @private **/ + public function VolumePlugin() { + super("volume"); + } + + /** @private **/ + override public function _onInitTween(target:Object, value:*, tween:TweenLite):Boolean { + if (isNaN(value) || target.hasOwnProperty("volume") || !target.hasOwnProperty("soundTransform")) { + return false; + } + _target = target; + _st = _target.soundTransform; + _addTween(_st, "volume", _st.volume, value, "volume"); + return true; + } + + /** @private **/ + override public function setRatio(v:Number):void { + super.setRatio(v); + _target.soundTransform = _st; + } + + } +} \ No newline at end of file diff --git a/src/interpreter/Interpreter.as b/src/interpreter/Interpreter.as index a5e1ac9..7f1c61e 100644 --- a/src/interpreter/Interpreter.as +++ b/src/interpreter/Interpreter.as @@ -71,6 +71,7 @@ import primitives.*; import scratch.*; import sound.*; +import flash.text.*; public class Interpreter { @@ -470,6 +471,7 @@ public class Interpreter { primTable["whenKeyPressed"] = primNoop; primTable["whenClicked"] = primNoop; primTable["whenSceneStarts"] = primNoop; + //primTable["run:"] = primRun; specialTable["wait:elapsed:from:"] = primWait; specialTable["doForever"] = function(b:*):* { startCmdList(this.subStack1, true); }; specialTable["doRepeat"] = primRepeat; @@ -546,15 +548,18 @@ public class Interpreter { primTable[Specs.SET_VAR] = primVarSet; primTable[Specs.CHANGE_VAR] = primVarChange; specialTable[Specs.GET_PARAM] = primGetParam; + specialTable[Specs.GET_STACK] = primGetStack; specialTable[Specs.GET_LOOP] = primGetLoop; specialTable["doDefineVars"] = primDefineVars; primTable["getDefinedVars"] = primGetDefinedVars; primTable["setDefinedVars"] = primSetDefinedVars; -// primTable["varSet:colorTo:"] = primVarSetColor; + primTable["move:toX:y:"] =primVarSetXY; + primTable["varSet:colorTo:"] = primVarSetColor; specialTable["getCloud"] = primGetCloud; specialTable["cloudSet"] = primSetCloud; specialTable["cloudChange"] = primChangeCloud; - + primTable["cookieGetVariable"]=primCookieGet; + primTable["cookieSetVariable"]=primCookieSet; // cloud lists specialTable["cloudAdd"] = primCloudAdd; specialTable["cloudDelete"] = primCloudDelete; @@ -594,6 +599,10 @@ public class Interpreter { } public function primNoop(b:Array):void { } + public function primRun(b:Array):void { + if (!(b[0] is String)) return; + + } private function primForLoop(b:Array):void { var list:Array = []; @@ -666,8 +675,8 @@ public class Interpreter { var type:String = b[0]; if (type == 'all') { app.runtime.stopAll(); yield = true } if (type == 'all and press green flag') { - app.runtime.stopAll(); - app.runtime.startGreenFlags(); + app.runtime.stopAll(); + app.runtime.startGreenFlags(); } if (type == 'this script') primReturn([]); if (type == 'other scripts in sprite') stopThreadsFor(activeThread.target, true); @@ -751,8 +760,10 @@ public class Interpreter { // Lookup the procedure and cache for future use var obj:ScratchObj = activeThread.target; var insideLoop:* = null; + var insideStacks:Array=[]; var spec:String = block.spec; if (block.type.indexOf("c") >= 0) insideLoop = block.subStack1; + if (block.substacks.length>0 ) insideStacks = block.substacks; var proc:Block = obj.procCache[spec]; if (!proc) { proc = obj.lookupProcedure(spec); @@ -777,6 +788,16 @@ public class Interpreter { } } activeThread.args = b; + activeThread.stackArgs = insideStacks; + var stackCounter:TextField=new TextField(); + stackCounter.autoSize = TextFieldAutoSize.LEFT; + stackCounter.selectable = false; + stackCounter.background = false; + stackCounter.defaultTextFormat = CSS.normalTextFormat; + stackCounter.textColor = CSS.white; + stackCounter.text=block.substacks.length+""; + //block.addChild(stackCounter); + activeThread.loopBlock = insideLoop; activeThread.pushStateForBlock(report0Block); startCmdList(proc); @@ -821,6 +842,25 @@ public class Interpreter { } activeThread.values.push(v.value); } + private function primCookieGet(b:Array):String { + + //activeThread.popState(); + var v:SharedObject = SharedObject.getLocal(b[0]); + if (v == null) { + return ''; + } + if (v.data.val == null) { + return ''; + } + return v.data.val; + } + private function primCookieSet(b:Array):void { + + //activeThread.popState(); + var v:SharedObject = SharedObject.getLocal(b[0]); + v.data.val=b[1] + } + protected function primVarSet(b:Array):Variable { var v:Variable = activeThread.target.varCache[b[0]]; @@ -832,21 +872,33 @@ public class Interpreter { v.value = b[1]; return v; } - -/* protected function primVarSetColor(b:Block):Variable + protected function primVarSetXY(b:Array):Variable { + var v:Variable = activeThread.target.varCache[b[0]]; + if (!v) { + v = activeThread.target.varCache[b.spec] = activeThread.target.lookupOrCreateVar(b[0]); + if (!v) return null; + } + + v.watcher.x = b[1]; + v.watcher.y = b[2]; + return v; + } + + protected function primVarSetColor(b:Array):Variable { - var v:Variable = activeThread.target.varCache[arg(b, 0)]; + var v:Variable = activeThread.target.varCache[b[0]]; if(!v) { - v = activeThread.target.varCache[b.spec] = activeThread.target.lookupOrCreateVar(arg(b, 0)); + v = activeThread.target.varCache[b.spec] = activeThread.target.lookupOrCreateVar(b[0]); if(!v) { return null; } } - v.color = arg(b, 1); + //v.color = arg(b, 1); + v.setBKColor(b[1]); return v; - }*/ + } protected function primVarChange(b:Array):Variable { var name:String = b[0]; @@ -877,6 +929,26 @@ public class Interpreter { } activeThread.values.push(activeThread.args[block.parameterIndex]); } + private function primGetStack(b:Array):void { + var block:Block = activeThread.block; + + activeThread.popState(); + if (block.stackIndex<0) { + /*var proc:Block = block.topBlock(); + if (proc.parameterNames) block.parameterIndex = proc.parameterNames.indexOf(block.spec); + if (block.parameterIndex < 0) { + activeThread.values.push(0); + return; + }*/ + } + if (block.nextBlock) activeThread.pushStateForBlock(block.nextBlock); + if (activeThread.stackArgs!=null){ + if (activeThread.stackArgs[block.stackIndex]!=null){ + activeThread.pushStateForBlock(activeThread.stackArgs[block.stackIndex]); + } + } + activeThread.firstTime = true; + } private function primDefineVars(b:Array):void { var block:Block = activeThread.block; @@ -1039,7 +1111,7 @@ public class Interpreter { } doYield(); } - + private function primCloudGetItem(b:Array):void { if (activeThread.firstTime) { var request:URLRequest = new URLRequest((cloudServerUrl + "listgetitem/" + escape(encodeURIComponent((b[0]).toString())) + "/" + escape(encodeURIComponent((b[1]).toString())))); @@ -1056,7 +1128,7 @@ public class Interpreter { } doYield(); } - + private function primCloudLength(b:Array):void { if (activeThread.firstTime) { var request:URLRequest = new URLRequest((cloudServerUrl + "listlength/" + escape(encodeURIComponent((b[0]).toString())))); @@ -1073,7 +1145,7 @@ public class Interpreter { } doYield(); } - + private function primCloudContains(b:Array):void { if (activeThread.firstTime) { var request:URLRequest = new URLRequest((cloudServerUrl + "listcontains/" + escape(encodeURIComponent((b[0]).toString())) + "/" + escape(encodeURIComponent((b[1]).toString())))); @@ -1090,4 +1162,4 @@ public class Interpreter { } doYield(); } -}} \ No newline at end of file +}} diff --git a/src/interpreter/Thread.as b/src/interpreter/Thread.as index 6d9c285..8d587c0 100644 --- a/src/interpreter/Thread.as +++ b/src/interpreter/Thread.as @@ -41,6 +41,7 @@ public class Thread { public var tmp:int; // used by repeat and wait public var values:Array; // the evaluated inputs public var args:Array; // arguments to a user-defined procedure + public var stackArgs:Array; // arguments to a user-defined procedure public var loopBlock:*; // used by c-block type procedures public var tempVars:Dictionary = new Dictionary(); @@ -72,6 +73,7 @@ public class Thread { old.tmp = tmp; old.values = values; old.args = args; + old.stackArgs = stackArgs; old.loopBlock = loopBlock; // initForBlock block = b; @@ -90,6 +92,7 @@ public class Thread { tmp = old.tmp; values = old.values; args = old.args; + stackArgs = old.stackArgs; loopBlock = old.loopBlock; if (block && block is Block && !interp.isNormalSpeed()) block.fullBlockHighlight(); return true; @@ -147,6 +150,7 @@ public class Thread { firstTime = true; tmp = 0; loopBlock = null; + stackArgs=[]; } private function growStack():void { @@ -171,5 +175,6 @@ class StackFrame { internal var tmp:int; internal var values:Array; internal var args:Array; + internal var stackArgs:Array; internal var loopBlock:*; -} \ No newline at end of file +} diff --git a/src/interpreter/Variable.as b/src/interpreter/Variable.as index 983757d..f9670be 100644 --- a/src/interpreter/Variable.as +++ b/src/interpreter/Variable.as @@ -31,16 +31,26 @@ public class Variable { public var value:*; public var watcher:*; public var isPersistent:Boolean; + public var color:Number; public function Variable(vName:String, initialValue:*) { name = vName; value = initialValue; + color=0xEE7D16; + } public function writeJSON(json:util.JSON):void { json.writeKeyValue('name', name); json.writeKeyValue('value', value); + json.writeKeyValue('x', this.watcher.x); + json.writeKeyValue('y', this.watcher.y); + json.writeKeyValue('color', this.color); json.writeKeyValue('isPersistent', isPersistent); } + public function setBKColor(color:Number):void{ + this.color=color; + this.watcher.updateForVarColor(); + } }} diff --git a/src/scratch/PaletteBuilder.as b/src/scratch/PaletteBuilder.as index 08549f1..1ebac49 100644 --- a/src/scratch/PaletteBuilder.as +++ b/src/scratch/PaletteBuilder.as @@ -127,7 +127,7 @@ public class PaletteBuilder { private function showMyBlocksPalette(shiftKey:Boolean):void { // show creation button, hat, and call blocks var catColor:int = Specs.blockColor(Specs.procedureColor); - addItem(new Button(Translator.map('Make a Block'), makeNewBlock, false, '/help/studio/tips/blocks/make-a-block/')); + addItem(new Button(Translator.map('Make a Block'), makeNewBlock, false, '/help/studio/tips/blocks/make-a-block/').setRaised(false).withTextColor(catColor)); addBlocksForCategory(10, Specs.procedureColor); var definitions:Array = app.viewedObj().procedureDefinitions(); if (definitions.length > 0) { @@ -159,14 +159,14 @@ public class PaletteBuilder { } protected function addAddExtensionButton():void { - addItem(new Button(Translator.map('Add an Extension'), showAnExtension, false, '/help/studio/tips/blocks/add-an-extension/')); + addItem(new Button(Translator.map('Add an Extension'), showAnExtension, false, '/help/studio/tips/blocks/add-an-extension/').setRaised(false)); } private function showDataCategory():void { var catColor:int = Specs.variableColor; // variable buttons, reporters, and set/change blocks - addItem(new Button(Translator.map('Make a Variable'), makeVariable)); + addItem(new Button(Translator.map('Make a Variable'), makeVariable).setRaised(false).withTextColor(catColor)); var varNames:Array = app.runtime.allVarNames().sort(); if (varNames.length > 0) { for each (var n:String in varNames) { @@ -181,7 +181,7 @@ public class PaletteBuilder { // lists catColor = Specs.listColor; - addItem(new Button(Translator.map('Make a List'), makeList)); + addItem(new Button(Translator.map('Make a List'), makeList).setRaised(false).withTextColor(catColor)); var listNames:Array = app.runtime.allListNames().sort(); if (listNames.length > 0) { @@ -276,7 +276,7 @@ public class PaletteBuilder { d.showOnStage(app.stage, true); specEditor.setInitialFocus(); } - + private function makeNewReporter():void { /* function addReporterHat(dialog:DialogBox):void { var spec:String = specEditor.spec().replace(/^\s+|\s+$/g, ''); diff --git a/src/scratch/ScratchRuntime.as b/src/scratch/ScratchRuntime.as index ed64b8c..1ebb21a 100644 --- a/src/scratch/ScratchRuntime.as +++ b/src/scratch/ScratchRuntime.as @@ -83,10 +83,10 @@ public class ScratchRuntime { protected var saveAfterInstall:Boolean; [Embed(source='../assets/pop.wav', mimeType='application/octet-stream')] protected static var Pop:Class; - [Embed(source='../assets/TeraA.svg', mimeType='application/octet-stream')] protected static var TeraA:Class; - [Embed(source='../assets/TeraB.svg', mimeType='application/octet-stream')] protected static var TeraB:Class; - [Embed(source='../assets/TeraC.svg', mimeType='application/octet-stream')] protected static var TeraC:Class; - [Embed(source='../assets/TeraD.svg', mimeType='application/octet-stream')] protected static var TeraD:Class; + [Embed(source='../assets/BlobA.svg', mimeType='application/octet-stream')] protected static var TeraA:Class; + [Embed(source='../assets/BlobB.svg', mimeType='application/octet-stream')] protected static var TeraB:Class; + [Embed(source='../assets/BlobC.svg', mimeType='application/octet-stream')] protected static var TeraC:Class; + [Embed(source='../assets/BlobD.svg', mimeType='application/octet-stream')] protected static var TeraD:Class; public function ScratchRuntime(app:Scratch, interp:Interpreter) { this.app = app; @@ -179,7 +179,7 @@ public class ScratchRuntime { processEdgeTriggeredHats(); interp.stepThreads(); app.stagePane.commitPenStrokes(); - + if (ready==ReadyLabel.COUNTDOWN || ready==ReadyLabel.READY) { app.stagePane.countdown(count); } @@ -194,7 +194,7 @@ public class ScratchRuntime { private var videoPosition:int; private var videoSeconds:Number; private var videoAlreadyDone:int; - + private var projectSound:Boolean; private var micSound:Boolean; private var showCursor:Boolean; @@ -203,14 +203,14 @@ public class ScratchRuntime { private var videoWidth:int; private var videoHeight:int; public var ready:int=ReadyLabel.NOT_READY; - + private var micBytes:ByteArray; private var micPosition:int = 0; private var mic:Microphone; private var micReady:Boolean; - + private var timeout:int; - + private function saveFrame():void { saveSound(); var t:Number = getTimer()*.001-videoSeconds; @@ -289,7 +289,7 @@ public class ScratchRuntime { videoFrames.push(f); } } - + private function saveSound():void { var floats:Array = []; if (micSound && micBytes.length>0) { @@ -330,17 +330,17 @@ public class ScratchRuntime { videoSounds.push(combinedStream); combinedStream = null; } - - private function micSampleDataHandler(event:SampleDataEvent):void - { - while(event.data.bytesAvailable) + + private function micSampleDataHandler(event:SampleDataEvent):void + { + while(event.data.bytesAvailable) { - var sample:Number = event.data.readFloat(); - micBytes.writeFloat(sample); + var sample:Number = event.data.readFloat(); + micBytes.writeFloat(sample); micBytes.writeFloat(sample); - } - } - + } + } + public function startVideo(editor:RecordingSpecEditor):void { projectSound = editor.soundFlag(); micSound = editor.microphoneFlag(); @@ -352,10 +352,10 @@ public class ScratchRuntime { } micReady = true; if (micSound) { - mic = Microphone.getMicrophone(); + mic = Microphone.getMicrophone(); mic.setSilenceLevel(0); - mic.gain = editor.getMicVolume(); - mic.rate = 44; + mic.gain = editor.getMicVolume(); + mic.rate = 44; micReady=false; } if (fullEditor) { @@ -387,7 +387,7 @@ public class ScratchRuntime { baFlvEncoder.start(); waitAndStart(); } - + public function exportToVideo():void { var specEditor:RecordingSpecEditor = new RecordingSpecEditor(); function startCountdown():void { @@ -395,7 +395,7 @@ public class ScratchRuntime { } DialogBox.close("Record Project Video",null,specEditor,"Start",app.stage,startCountdown); } - + public function stopVideo():void { if (recording) videoTimer.dispatchEvent(new TimerEvent(TimerEvent.TIMER)); else if (ready==ReadyLabel.COUNTDOWN || ReadyLabel.READY) { @@ -404,7 +404,7 @@ public class ScratchRuntime { app.stagePane.countdown(0); } } - + public function finishVideoExport(event:TimerEvent):void { stopRecording(); stopAll(); @@ -413,7 +413,7 @@ public class ScratchRuntime { clearTimeout(timeout); timeout = setTimeout(saveRecording,1); } - + public function waitAndStart():void { if (!micReady && !mic.hasEventListener(StatusEvent.STATUS)) { micBytes = new ByteArray(); @@ -445,7 +445,7 @@ public class ScratchRuntime { videoTimer.addEventListener(TimerEvent.TIMER, finishVideoExport); videoTimer.start(); } - + public function stopRecording():void { recording = false; videoTimer.stop(); @@ -479,7 +479,7 @@ public class ScratchRuntime { videoSounds[videoPosition]=null; videoPosition++; } - if (app.lp) app.lp.setProgress(Math.min((videoPosition-videoAlreadyDone) / (videoFrames.length-videoAlreadyDone), 1)); + if (app.lp) app.lp.setProgress(Math.min((videoPosition-videoAlreadyDone) / (videoFrames.length-videoAlreadyDone), 1)); clearTimeout(timeout); timeout = setTimeout(saveRecording, 1); return; @@ -512,7 +512,7 @@ public class ScratchRuntime { } DialogBox.close("Video Finished!","To save, click the button below.",null,"Save and Download",app.stage,saveFile,releaseVideo,null,true); } - + private function roundToTens(x:Number):Number { return int((x)*10)/10.; } @@ -787,7 +787,7 @@ public class ScratchRuntime { var newStage:ScratchStage = new ScratchStage(); var sprite:ScratchSprite = new ScratchSprite('Sprite1'); var costume1:ScratchCostume = new ScratchCostume(Translator.map('costume1'), new TeraA()); - var costume2:ScratchCostume = new ScratchCostume(Translator.map('costume2'), new TeraB(), 63, 83); // Fixes odd issue with TeraB.svg... + var costume2:ScratchCostume = new ScratchCostume(Translator.map('costume2'), new TeraB()); // Fixes odd issue with TeraB.svg... var costume3:ScratchCostume = new ScratchCostume(Translator.map('costume3'), new TeraC()); var costume4:ScratchCostume = new ScratchCostume(Translator.map('costume4'), new TeraD()); sprite.costumes = [costume1, costume2, costume3, costume4]; diff --git a/src/ui/BlockPalette.as b/src/ui/BlockPalette.as index fe7b5ab..f4a6b02 100644 --- a/src/ui/BlockPalette.as +++ b/src/ui/BlockPalette.as @@ -38,7 +38,8 @@ public class BlockPalette extends ScrollFrameContents { public function BlockPalette():void { super(); - this.color = 0xB1E0FF; + this.color = 0xFFFFFF;//CSS.white;//0xF5F5F5;//0xB1E0FF; + updateSize(); } override public function clear(scrollToOrigin:Boolean = true):void { diff --git a/src/ui/PaletteSelectorItem.as b/src/ui/PaletteSelectorItem.as index ad988c9..62034f6 100644 --- a/src/ui/PaletteSelectorItem.as +++ b/src/ui/PaletteSelectorItem.as @@ -27,19 +27,60 @@ package ui { import flash.display.*; import flash.events.MouseEvent; import flash.text.*; + import com.greensock.TweenLite; public class PaletteSelectorItem extends Sprite { public var categoryID:int; public var label:TextField; + public var label2:TextField; public var isSelected:Boolean; - + public var colorBar:Shape; + public var colorBarMSK1:Shape; + public var colorBarMSK2:Shape; private var color:uint; + public var colorBarW:int=7; public function PaletteSelectorItem(id: int, s:String, c:uint) { categoryID = id; addLabel(s); color = c; + colorBar=new Shape(); + colorBarMSK1=new Shape(); + colorBarMSK2=new Shape(); + var g:Graphics = colorBar.graphics; + + + + g.clear(); + g.beginFill(color);//0x2196F3); + g.drawRect(0, 0, colorBarW,label.height+2); + addChild(colorBar); + colorBar.x=8; + + g = colorBarMSK1.graphics; + + + + g.clear(); + g.beginFill(color);//0x2196F3); + g.drawRect(0, 0, colorBarW,label.height+2); + g = colorBarMSK2.graphics; + addChild(colorBarMSK1); + colorBarMSK1.x=8; + + + + g.clear(); + g.beginFill(color);//0x2196F3); + g.drawRect(0, 0, colorBarW,label.height+2); + //addChild(colorBar); + addChild(colorBarMSK2); + colorBarMSK2.x=8; + addChild(label); + addChild(label2); + label2.mask=colorBarMSK1; + label.mask=colorBarMSK2; setSelected(false); addEventListener(MouseEvent.MOUSE_OVER, mouseOver); addEventListener(MouseEvent.MOUSE_OUT, mouseOut); @@ -52,34 +93,77 @@ public class PaletteSelectorItem extends Sprite { label.selectable = false; label.text = s; addChild(label); + label2 = new TextField(); + label2.autoSize = TextFieldAutoSize.LEFT; + label2.selectable = false; + label2.text = s; + + addChild(label2); } + public function renderColorBar(){ + var tabInset:int = 8; + var w:int = 100; + var g:Graphics = colorBar.graphics; + + + g.clear(); + g.beginFill(color);//0x2196F3); +g.drawRect(0, 0, colorBarW,Math.max(label.height+2,20)); + +g = colorBarMSK1.graphics; + + + +g.clear(); +g.beginFill(color);//0x2196F3); +g.drawRect(0, 0, colorBarW,Math.max(label.height+2,20)); +//colorBarMSK1.x=8; + +g = colorBarMSK2.graphics; + + +g.clear(); +g.beginFill(color);//0x2196F3); +g.drawRect(colorBarW, 0, w - tabInset - 1,Math.max(label.height+2,20)); +//addChild(colorBar); +//colorBarMSK2.x=8; + } public function setSelected(flag:Boolean):void { var w:int = 100; var h:int = label.height + 2; var tabInset:int = 8; var tabW:int = 7; isSelected = flag; - var fmt:TextFormat = new TextFormat(CSS.font, 12, (isSelected ? CSS.white : CSS.offColor), isSelected); + var fmt:TextFormat = new TextFormat(CSS.font, 12, ((isSelected && false) ? CSS.white : CSS.textColor+0x313131),true);// isSelected); label.setTextFormat(fmt); label.x = 17; label.y = 1; + + var fmt2:TextFormat = new TextFormat(CSS.font, 12, ( CSS.white ), true);//isSelected); + label2.setTextFormat(fmt2); + label2.x = 17; + label2.y = 1; + var g:Graphics = this.graphics; g.clear(); g.beginFill(0xFF00, 0); // invisible, but mouse sensitive g.drawRect(0, 0, w, h); g.endFill(); - g.beginFill(color); - g.drawRect(tabInset, 1, isSelected ? w - tabInset - 1 : tabW, h - 2); - g.endFill(); + renderColorBar(); + + TweenLite.to(this,0.5, {colorBarW:flag? w - tabInset - 1:7, onUpdate:renderColorBar}); + //g.beginFill(color); + //g.drawRect(tabInset, 1, isSelected ? w - tabInset - 1 : tabW, h - 2); + //g.endFill(); } private function mouseOver(event:MouseEvent):void { - label.textColor = isSelected ? CSS.white : CSS.buttonLabelOverColor; + //label.textColor = isSelected ? CSS.white : CSS.buttonLabelOverColor; } private function mouseOut(event:MouseEvent):void { - label.textColor = isSelected ? CSS.white : CSS.offColor; + //label.textColor = isSelected ? CSS.white : CSS.offColor; } private function mouseUp(event:MouseEvent):void { diff --git a/src/ui/ProcedureSpecEditor.as b/src/ui/ProcedureSpecEditor.as index bc60b80..e8b77ec 100644 --- a/src/ui/ProcedureSpecEditor.as +++ b/src/ui/ProcedureSpecEditor.as @@ -130,6 +130,7 @@ public class ProcedureSpecEditor extends Sprite { if (argSpec == 's') arg = makeStringArg(); if (argSpec == 'c') arg = makeColorArg(); if (argSpec == 'm') arg = makeMenuArg(); + if (argSpec == 'k') arg = makeStackArg(); if (arg) { arg.setArgValue(inputNames[i++]); addElement(arg); @@ -172,6 +173,7 @@ public class ProcedureSpecEditor extends Sprite { if (arg.type == 's') v = ''; if (arg.type == 'm') v = ''; if (arg.type == 'c') v = 0xA0A0A0; + if (arg.type == 'k') v = null; result.push(v); } } @@ -196,13 +198,13 @@ public class ProcedureSpecEditor extends Sprite { makeLabel("Doesn't report", 14), makeLabel('Reports number or text', 14), makeLabel('Reports true or false', 14), - makeLabel("Runs blocks inside it", 14), + //makeLabel("Runs blocks inside it", 14), ]; shapeButtons = [ new IconButton(function():void { setShape(BlockShape.CmdShape) }, null), new IconButton(function():void { setShape(BlockShape.NumberShape) }, null), - new IconButton(function():void { setShape(BlockShape.BooleanShape) }, null), - new IconButton(function():void { setShape(BlockShape.LoopShape) }, null) + new IconButton(function():void { setShape(BlockShape.BooleanShape) }, null)//, + //new IconButton(function():void { setShape(BlockShape.LoopShape) }, null) ]; buttonLabels = [ makeLabel('Add number input:', 14), @@ -210,6 +212,7 @@ public class ProcedureSpecEditor extends Sprite { makeLabel('Add boolean input:', 14), makeLabel('Add color input:', 14), makeLabel('Add menu input:', 14), + makeLabel('Add stack input:', 14), makeLabel('Add label text:', 14) ]; buttons = [ @@ -218,6 +221,21 @@ public class ProcedureSpecEditor extends Sprite { new Button('', function():void { appendObj(makeBooleanArg()) }), new Button('', function():void { appendObj(makeColorArg()) }), new Button('', function():void { appendObj(makeMenuArg()) }), + new Button('', function():void { appendObj(makeStackArg()); + if(shape!=BlockShape.CmdShape){ + for(var i:int=0;i -1) setFocus(row[oldIndex]); + fixLayout(); + } + if (row.length == 0) { + appendObj(makeTextField('')); + TextField(row[0]).width = 27; + } + } + }}), new Button('text', function():void { appendObj(makeTextField('')) }) ]; @@ -241,7 +259,7 @@ public class ProcedureSpecEditor extends Sprite { icon = new BlockShape(BlockShape.LoopShape, Specs.procedureColor); icon.setWidthAndTopHeight(50, 18, true); - shapeIcons.push(icon); + //shapeIcons.push(icon); icon = new BlockShape(BlockShape.NumberShape, lightGray); icon.setWidthAndTopHeight(25, 14, true); @@ -262,6 +280,9 @@ public class ProcedureSpecEditor extends Sprite { icon = new BlockShape(BlockShape.RectShape, lightGray); icon.setWidthAndTopHeight(25, 14, true); buttons[4].setIcon(icon); + icon = new BlockShape(BlockShape.CmdShape, lightGray); + icon.setWidthAndTopHeight(34, 18, true); + buttons[5].setIcon(icon); } private function addwarpCheckbox():void { @@ -332,10 +353,25 @@ public class ProcedureSpecEditor extends Sprite { for each (var ib:IconButton in shapeButtons) { ib.setOn(ib == ob); } + var k:int = getChildIndex(blockShape); removeChildAt(k); blockShape = new BlockShape(shape, Specs.procedureColor); addChildAt(blockShape, k); + if(shape!=BlockShape.CmdShape){ + for(var i:int=0;i -1) setFocus(row[oldIndex]); + fixLayout(); + } + if (row.length == 0) { + appendObj(makeTextField('')); + TextField(row[0]).width = 27; + } + } + } fixLayout(); } @@ -363,6 +399,11 @@ public class ProcedureSpecEditor extends Sprite { result.setArgValue(unusedArgName('color')); return result; } + private function makeStackArg():BlockArg { + var result:BlockArg = new BlockArg('k', 0xFFFFFF, true); + result.setArgValue(unusedArgName('stack')); + return result; +} private function makeMenuArg():BlockArg { var result:BlockArg = new BlockArg('m', 0xFFFFFF, true); @@ -424,19 +465,82 @@ public class ProcedureSpecEditor extends Sprite { removeDeletedElementsFromRow(); blockShape.x = 10; blockShape.y = 10; + var leftX:int = blockShape.x + (shape == BlockShape.BooleanShape ? 14 : shape == BlockShape.LoopShape ? 10 : 6); var nextX:int = blockShape.x + (shape == BlockShape.BooleanShape ? 14 : shape == BlockShape.LoopShape ? 10 : 6); var nextY:int = blockShape.y + 5; - var maxH:int = 0; + var maxH:int = 20; + var maxW:int=0; + var preWasStack:Boolean=false; for each (var o:DisplayObject in row) maxH = Math.max(maxH, o.height); for each (o in row) { + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (!preWasStack)) { + nextX=leftX+0; + nextY+=30; + } + o.x = nextX; + + o.y = nextY + int((20 - o.height) / 2) + ((o is TextField) ? 1 : 1); + nextX += o.width + 4; + maxW=Math.max(maxW, nextX); + maxH=Math.max(nextY, maxH); + if ((o is BlockArg) && (BlockArg(o).type == 's')) nextX -= 2; + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + nextX=leftX+0; + nextY+=30; + preWasStack=true; + }else{ + preWasStack=false; + } + } + if (preWasStack) { + + maxH=maxH+6; + } + preWasStack=false; + leftX = blockShape.x + (shape == BlockShape.BooleanShape ? maxH/2+5 : shape == BlockShape.LoopShape ? 10 : shape == BlockShape.CmdShape ?6:maxH/2); + nextX= leftX+0; + nextY= blockShape.y + 5; + for each (o in row) { + if ((o is BlockArg) && (BlockArg(o).type == 'k') && (!preWasStack)) { + nextX=leftX+0; + nextY+=30; + } o.x = nextX; - o.y = nextY + int((maxH - o.height) / 2) + ((o is TextField) ? 1 : 1); + + o.y = nextY + int((20 - o.height) / 2) + ((o is TextField) ? 1 : 1); nextX += o.width + 4; + maxW=Math.max(maxW, nextX); + maxH=Math.max(nextY, maxH); if ((o is BlockArg) && (BlockArg(o).type == 's')) nextX -= 2; + if ((o is BlockArg) && (BlockArg(o).type == 'k')) { + nextX=leftX+0; + nextY+=30; + preWasStack=true; + }else{ + preWasStack=false; + } } - var blockW:int = Math.max(40, nextX + (shape == BlockShape.BooleanShape ? 12 : 4) - blockShape.x); - blockShape.setWidthAndTopHeight(blockW, maxH + 11, true); + var blockW:int = Math.max(40, maxW + leftX - blockShape.x-12); + blockShape.setWidthAndTopHeight(blockW, maxH +11, true); + if(shape == BlockShape.CmdShape){ + var kI:int = 0; + if(blockShape.parent!=null) kI=getChildIndex(blockShape); + + if(blockShape.parent!=null) blockShape.parent.removeChild(blockShape); + blockShape = new BlockShape(BlockShape.CmdShape, Specs.procedureColor); + //base.owner=this; + blockShape.setCrazyShape(row,BlockShape.CmdShape, Specs.procedureColor); + blockShape.args=row; + addChildAt(blockShape, kI); + blockShape.redraw(); + for each (o in row) { + if ((o is BlockArg) && (BlockArg(o).type == 'k') ) { + o.x=15 + if(o.parent==null) this.addChild(o); + } + } + } moreButton.x = 0; moreButton.y = blockShape.y + blockShape.height + (shape == BlockShape.CmdShape ? 12 : 15); @@ -559,4 +663,4 @@ public class ProcedureSpecEditor extends Sprite { deleteButton.y = -6; } -}} \ No newline at end of file +}} diff --git a/src/ui/SpriteThumbnail.as b/src/ui/SpriteThumbnail.as index 3f4686b..8d404e5 100644 --- a/src/ui/SpriteThumbnail.as +++ b/src/ui/SpriteThumbnail.as @@ -29,6 +29,7 @@ package ui { import ui.media.MediaInfo; import ui.parts.LibraryPart; import uiwidgets.*; + import flash.filters.DropShadowFilter; public class SpriteThumbnail extends Sprite { @@ -80,6 +81,13 @@ public class SpriteThumbnail extends Sprite { addDetailsButton(); updateThumbnail(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 1; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; + this.filters=[shadow]; } public static function strings():Array { @@ -88,9 +96,19 @@ public class SpriteThumbnail extends Sprite { private function addDetailsButton():void { detailsButton = new IconButton(showSpriteDetails, 'spriteInfo'); detailsButton.x = detailsButton.y = -2; + detailsButton.x=frameW-20; + detailsButton.y=frameH-30; detailsButton.isMomentary = true; detailsButton.visible = false; addChild(detailsButton); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 1; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +//shadow.inset=flag; + detailsButton.filters=[shadow]; } private function addFrame():void { @@ -100,7 +118,7 @@ public class SpriteThumbnail extends Sprite { var g:Graphics = frame.graphics; g.lineStyle(NaN); g.beginFill(0xFFFFFF); - g.drawRoundRect(0, 0, frameW, frameH, 12, 12); + g.drawRoundRect(0, 0, frameW, frameH, 0, 0); g.endFill(); addChild(frame); } @@ -111,7 +129,7 @@ public class SpriteThumbnail extends Sprite { var h:int = targetObj.isStage ? stageFrameH : frameH; g.lineStyle(3, CSS.overColor, 1, true); g.beginFill(CSS.itemSelectedColor); - g.drawRoundRect(0, 0, frameW, h, 12, 12); + g.drawRoundRect(0, 0, frameW, h, 0, 0); g.endFill(); selectedFrame.visible = false; addChild(selectedFrame); @@ -123,7 +141,7 @@ public class SpriteThumbnail extends Sprite { var g:Graphics = highlightFrame.graphics; var h:int = targetObj.isStage ? stageFrameH : frameH; g.lineStyle(2, highlightColor, 1, true); - g.drawRoundRect(1, 1, frameW - 1, h - 1, 12, 12); + g.drawRoundRect(1, 1, frameW - 1, h - 1, 0, 0); highlightFrame.visible = false; addChild(highlightFrame); } @@ -134,14 +152,24 @@ public class SpriteThumbnail extends Sprite { } public function select(flag:Boolean):void { + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 1; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +shadow.inner=flag; + this.filters=[shadow]; if (selectedFrame.visible == flag) return; selectedFrame.visible = flag; detailsButton.visible = flag && !targetObj.isStage; + } public function showHighlight(flag:Boolean):void { // Display a highlight if flag is true (e.g. to show broadcast senders/receivers). highlightFrame.visible = flag; + } public function showInfo(flag:Boolean):void { diff --git a/src/ui/parts/ImagesPart.as b/src/ui/parts/ImagesPart.as index a15d59b..162f355 100644 --- a/src/ui/parts/ImagesPart.as +++ b/src/ui/parts/ImagesPart.as @@ -41,6 +41,7 @@ import translation.Translator; import ui.media.*; import uiwidgets.*; +import flash.filters.DropShadowFilter; public class ImagesPart extends UIPart { @@ -88,6 +89,13 @@ public class ImagesPart extends UIPart { addFlipButtons(); addCenterButton(); updateTranslation(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +//this.filters=[shadow]; } protected function addEditor(isSVG:Boolean):void { diff --git a/src/ui/parts/LibraryPart.as b/src/ui/parts/LibraryPart.as index ba18bfc..b6cdb9f 100644 --- a/src/ui/parts/LibraryPart.as +++ b/src/ui/parts/LibraryPart.as @@ -31,6 +31,7 @@ import translation.Translator; import ui.media.*; import ui.SpriteThumbnail; import uiwidgets.*; +import flash.filters.DropShadowFilter; public class LibraryPart extends UIPart { @@ -92,6 +93,13 @@ public class LibraryPart extends UIPart { spriteDetails.visible = false; updateTranslation(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; + //this.filters=[shadow]; } public static function strings():Array { @@ -126,20 +134,26 @@ public class LibraryPart extends UIPart { } public function setWidthHeight(w:int, h:int):void { + //h=h-4; this.w = w; this.h = h; var g:Graphics = shape.graphics; g.clear(); - drawTopBar(g, CSS.titleBarColors, getTopBarPath(w,CSS.titleBarH), w, CSS.titleBarH); + g.clear(); g.lineStyle(1, CSS.borderColor, 1, true); - g.drawRect(0, CSS.titleBarH, w, h - CSS.titleBarH); + g.beginFill(CSS.grey100);//0xE6E8E8); + +g.drawRect(0,0,w-1,CSS.titleBarH-1); + //drawTopBar(g, CSS.titleBarColors, getTopBarPath(w,CSS.titleBarH), w, CSS.titleBarH); + g.lineStyle(1, CSS.borderColor, 1, true); + g.drawRect(0, CSS.titleBarH-1, w-1, h - CSS.titleBarH); g.lineStyle(1, CSS.borderColor); if (!app.isMicroworld) { g.moveTo(stageAreaWidth, 0); g.lineTo(stageAreaWidth, h); g.lineStyle(); g.beginFill(CSS.tabColor); - g.drawRect(1, CSS.titleBarH + 1, stageAreaWidth - 1, h - CSS.titleBarH - 1); + g.drawRect(1, CSS.titleBarH + 1, stageAreaWidth - 1, h - CSS.titleBarH-1 ); g.endFill(); } fixLayout(); @@ -291,7 +305,7 @@ public class LibraryPart extends UIPart { private function addSpritesArea():void { spritesPane = new ScrollFrameContents(); - spritesPane.color = bgColor; + spritesPane.color = CSS.white;//bgColor; spritesPane.hExtra = spritesPane.vExtra = 0; spritesFrame = new ScrollFrame(); spritesFrame.setContents(spritesPane); @@ -444,7 +458,7 @@ public class LibraryPart extends UIPart { app.selectSprite(app.stagePane); app.setSaveNeeded(true); } - + // More info /* private function showMoreInfo(param1:IconButton):* { var _loc4_:SpriteThumbnail = null; diff --git a/src/ui/parts/LinesPart.as b/src/ui/parts/LinesPart.as new file mode 100644 index 0000000..b0c1c6e --- /dev/null +++ b/src/ui/parts/LinesPart.as @@ -0,0 +1,61 @@ +package ui.parts { +import flash.display.*; +import flash.text.*; +import flash.utils.*; +import scratch.*; +import translation.Translator; +import ui.media.*; +import ui.SpriteThumbnail; +import uiwidgets.*; +import flash.filters.DropShadowFilter; + +public class LinesPart extends UIPart { + + //private var lastUpdate:uint; // time of last thumbnail update + + private var shape:Shape; + + + public function LinesPart(app:Scratch) { + this.app = app; + shape = new Shape(); + addChild(shape); + setWidthHeight(100,100); + } + + + public function setWidthHeight(w:int, h:int):void { + //h=h-4; + this.w = w; + this.h = h; + var g:Graphics = shape.graphics; + g.clear(); + g.clear(); + g.lineStyle(1, CSS.seamColor);//0x9E9E9E); + + g.moveTo(app.stagePart.x*0+app.stagePart.width, 0); + g.lineTo(app.stagePart.x*0+app.stagePart.width, h); + g.moveTo(0, app.stagePart.height-1); + g.lineTo(app.stagePart.x*0+app.stagePart.width, app.stagePart.height-1); + g.moveTo(0, app.stagePart.height-2); + g.lineTo(app.stagePart.x*0+app.stagePart.width, app.stagePart.height-2); + g.moveTo(0, app.stagePart.height); + g.lineTo(app.stagePart.x*0+app.stagePart.width, app.stagePart.height); + g.lineStyle(); + /*g.beginFill(0x9E9E9E); + g.drawRect(app.stagePart.x+app.stagePart.width+1, 0, w-app.stagePart.x-app.stagePart.width, 30 ); + g.endFill();*/ + //if (app.viewedObj()) refresh(); // refresh, but not during initialization + } + + + + + + // ----------------------------- + // Video Button + //------------------------------ + + + +}} diff --git a/src/ui/parts/ScriptsPart.as b/src/ui/parts/ScriptsPart.as index cf707f1..3b50ab1 100644 --- a/src/ui/parts/ScriptsPart.as +++ b/src/ui/parts/ScriptsPart.as @@ -30,7 +30,7 @@ import flash.utils.getTimer; import scratch.*; import ui.*; - +import flash.filters.DropShadowFilter; import uiwidgets.*; public class ScriptsPart extends UIPart { @@ -62,7 +62,7 @@ public class ScriptsPart extends UIPart { addChild(selector = new PaletteSelector(app)); var palette:BlockPalette = new BlockPalette(); - palette.color = CSS.tabColor; + palette.color = CSS.white;//CSS.tabColor; paletteFrame = new ScrollFrame(); paletteFrame.allowHorizontalScrollbar = false; paletteFrame.setContents(palette); @@ -79,7 +79,21 @@ public class ScriptsPart extends UIPart { scriptsFrame = new ScrollFrame(); scriptsFrame.setContents(scriptsPane); addChild(scriptsFrame); - + /*var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +shadow.inner=true;*/ + //scriptsFrame.filters=[shadow]; + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +//this.filters=[shadow]; return scriptsPane; } @@ -134,7 +148,7 @@ public class ScriptsPart extends UIPart { for (var i:int = 0; i < app.palette.numChildren; i++) { var indicator:IndicatorLight = app.palette.getChildAt(i) as IndicatorLight; if (indicator) app.extensionManager.updateIndicator(indicator, indicator.target); - } + } lastUpdateTime = getTimer(); } @@ -183,7 +197,7 @@ public class ScriptsPart extends UIPart { var g:Graphics = shape.graphics; g.clear(); g.lineStyle(1, CSS.borderColor, 1, true); - g.beginFill(CSS.tabColor); + g.beginFill(CSS.borderColor); g.drawRect(0, 0, w, h); g.endFill(); @@ -191,10 +205,10 @@ public class ScriptsPart extends UIPart { var darkerBorder:int = CSS.borderColor - 0x141414; var lighterBorder:int = 0xF2F2F2; if (!app.isMicroworld) { - g.lineStyle(1, darkerBorder, 1, true); + /*g.lineStyle(1, darkerBorder, 1, true); hLine(g, paletteFrame.x + 8, lineY, paletteW - 20); g.lineStyle(1, lighterBorder, 1, true); - hLine(g, paletteFrame.x + 8, lineY + 1, paletteW - 20); + hLine(g, paletteFrame.x + 8, lineY + 1, paletteW - 20);*/ } g.lineStyle(1, darkerBorder, 1, true); diff --git a/src/ui/parts/SoundsPart.as b/src/ui/parts/SoundsPart.as index 761e7a7..02109c0 100644 --- a/src/ui/parts/SoundsPart.as +++ b/src/ui/parts/SoundsPart.as @@ -35,7 +35,7 @@ package ui.parts { import translation.Translator; import ui.media.*; import uiwidgets.*; - +import flash.filters.DropShadowFilter; public class SoundsPart extends UIPart { public var editor:SoundEditor; @@ -67,6 +67,13 @@ public class SoundsPart extends UIPart { addUndoButtons(); app.stage.addEventListener(KeyboardEvent.KEY_DOWN, editor.keyDown); updateTranslation(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; +//this.filters=[shadow]; } public static function strings():Array { @@ -135,12 +142,12 @@ public class SoundsPart extends UIPart { g.clear(); g.lineStyle(0.5, CSS.borderColor, 1, true); - g.beginFill(CSS.tabColor); + g.beginFill(CSS.borderColor); g.drawRect(0, 0, w, h); g.endFill(); g.lineStyle(0.5, CSS.borderColor, 1, true); - g.beginFill(CSS.panelColor); + g.beginFill(CSS.borderColor); g.drawRect(columnWidth + 1, 5, w - columnWidth - 6, h - 10); g.endFill(); diff --git a/src/ui/parts/StagePart.as b/src/ui/parts/StagePart.as index 3846e71..b1c6c4d 100644 --- a/src/ui/parts/StagePart.as +++ b/src/ui/parts/StagePart.as @@ -34,6 +34,7 @@ package ui.parts { import scratch.*; import translation.Translator; import uiwidgets.*; + import flash.filters.DropShadowFilter; public class StagePart extends UIPart { @@ -55,7 +56,7 @@ public class StagePart extends UIPart { private var stopButton:IconButton; private var fullscreenButton:IconButton; private var stageSizeButton:Sprite; - + //video recording tools private var stopRecordingButton:IconButton; private var recordingIndicator:Shape; @@ -86,8 +87,15 @@ public class StagePart extends UIPart { addStageSizeButton(); fixLayout(); addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheel); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 70; + //this.filters=[shadow]; } - + public static function strings():Array { return [ 'by', 'shared', 'unshared', 'Turbo Mode', @@ -154,7 +162,7 @@ public class StagePart extends UIPart { videoProgressBar.visible = (app.runtime.ready==ReadyLabel.COUNTDOWN || app.runtime.recording) && app.editMode; recordingTime.visible = (app.runtime.ready==ReadyLabel.COUNTDOWN || app.runtime.recording) && app.editMode; recordingIndicator.visible = app.runtime.recording && app.editMode; - + if (app.editMode) { fullscreenButton.setOn(false); drawStageSizeButton(); @@ -172,9 +180,17 @@ public class StagePart extends UIPart { var g:Graphics = outline.graphics; g.clear(); - drawTopBar(g, topBarColors, getTopBarPath(w - 1, topBarHeight), w, topBarHeight, CSS.borderColor); - g.lineStyle(1, CSS.borderColor, 1, true); + //drawTopBar(g, topBarColors, getTopBarPath(w - 1, topBarHeight), w, topBarHeight, CSS.borderColor); + g.clear(); + g.beginFill(CSS.grey100); + +g.drawRect(0,0,w,h); +g.lineStyle(1, CSS.borderColor, 1, true); g.drawRect(0, topBarHeight - 1, w - 1, h - topBarHeight); + g.beginFill(CSS.grey100); + g.lineStyle(1, CSS.borderColor, 1, true); +g.drawRect(0,h-1,w-1,16); + versionInfo.visible = !fullscreenButton.isOn(); } @@ -200,7 +216,7 @@ public class StagePart extends UIPart { // version info (only used on old website player) versionInfo.x = fullscreenButton.x + 1; - versionInfo.y = 27; + projectTitle.setWidth(runButton.x - projectTitle.x - 15); @@ -212,9 +228,9 @@ public class StagePart extends UIPart { yReadout.x = left + 60; var top:int = h + 1; - xReadout.y = yReadout.y = top; - xLabel.y = yLabel.y = top - 2; - + xReadout.y = yReadout.y = top-1; + xLabel.y = yLabel.y = top - 3; + versionInfo.y = yLabel.y+2; //recording tools stopRecordingButton.x=2; stopRecordingButton.y=top+2; @@ -225,18 +241,19 @@ public class StagePart extends UIPart { videoProgressBar.x = recordingTime.x+42; videoProgressBar.y=top+3; - stageSizeButton.x = w - 4; - stageSizeButton.y = h + 2; + stageSizeButton.x = w - 4-5; + stageSizeButton.y = h -1; if (playButton) playButton.scaleX = playButton.scaleY = app.stagePane.scaleX; + //redraw(); } - + private var lastTime:int=0; - + private function addRecordingTools():void { stopRecordingButton = new IconButton(app.stopVideo, 'stopVideo'); addChild(stopRecordingButton); - + videoProgressBar = new Shape(); var slotColor:int = CSS.overColor; var slotColor2:int = 0xBBBDBF; @@ -249,10 +266,10 @@ public class StagePart extends UIPart { g.drawRoundRect(0, .5, 300, 9,9,9); g.endFill(); addChild(videoProgressBar); - + const versionFormat:TextFormat = new TextFormat(CSS.font, 11, CSS.textColor); addChild(recordingTime = makeLabel(" 0 secs",versionFormat)); - + recordingIndicator = new Shape(); var k:Graphics = recordingIndicator.graphics; k.clear(); @@ -261,25 +278,25 @@ public class StagePart extends UIPart { k.endFill(); addChild(recordingIndicator); } - + private function resetTime():void { updateRecordingTools(0); - + removeChild(stopRecordingButton); - + stopRecordingButton = new IconButton(app.stopVideo, 'stopVideo'); addChild(stopRecordingButton); - + fixLayout(); } - + public function removeRecordingTools():void { stopRecordingButton.visible=false; videoProgressBar.visible=false; recordingTime.visible=false; recordingIndicator.visible=false; } - + public function updateRecordingTools(time:Number = -1.0):void { if (time<0) { time = Number(lastTime); @@ -305,7 +322,7 @@ public class StagePart extends UIPart { } g.drawRoundRect(0, .5, barWidth, 9,9,9); g.endFill(); - + if (lastTime!=int(time)) { var timeString:String = ""; if (int(time)<10) { @@ -325,9 +342,9 @@ public class StagePart extends UIPart { } } } - + private function addTitleAndInfo():void { - var fmt:TextFormat = app.isOffline ? new TextFormat(CSS.font, 16, CSS.textColor) : CSS.projectTitleFormat; + var fmt:TextFormat = app.isOffline ? new TextFormat(CSS.font, 16, 0x909090) : CSS.projectTitleFormat; projectTitle = getProjectTitle(fmt); addChild(projectTitle); @@ -436,7 +453,11 @@ public class StagePart extends UIPart { private function addFullScreenButton():void { function toggleFullscreen(b:IconButton):void { app.setPresentationMode(b.isOn()); + fixLayout(); drawOutline(); + fixLayout(); + app.toggleSmallStage(); + app.toggleSmallStage(); } fullscreenButton = new IconButton(toggleFullscreen, 'fullscreen'); fullscreenButton.disableMouseover(); @@ -458,26 +479,26 @@ public class StagePart extends UIPart { g.clear(); // draw tab - g.lineStyle(1, CSS.borderColor); + /*g.lineStyle(1, CSS.borderColor); g.beginFill(CSS.tabColor); g.moveTo(10, 0); g.lineTo(3, 0); g.lineTo(0, 3); g.lineTo(0, 13); g.lineTo(3, 15); - g.lineTo(10, 15); + g.lineTo(10, 15);*/ // draw arrow g.lineStyle(); g.beginFill(CSS.arrowColor); if (app.stageIsContracted) { g.moveTo(3, 3.5); - g.lineTo(9, 7.5); - g.lineTo(3, 12); + g.lineTo(10, 8.5); + g.lineTo(3, 14); } else { - g.moveTo(8, 3.5); - g.lineTo(2, 7.5); - g.lineTo(8, 12); + g.moveTo(9, 3.5); + g.lineTo(2, 8.5); + g.lineTo(9, 14); } g.endFill(); } diff --git a/src/ui/parts/TabsPart.as b/src/ui/parts/TabsPart.as index 5140c94..3ee0c83 100644 --- a/src/ui/parts/TabsPart.as +++ b/src/ui/parts/TabsPart.as @@ -27,12 +27,14 @@ package ui.parts { import flash.text.*; import translation.Translator; import uiwidgets.IconButton; - +import flash.filters.DropShadowFilter; +import com.greensock.TweenLite; public class TabsPart extends UIPart { private var scriptsTab:IconButton; private var imagesTab:IconButton; private var soundsTab:IconButton; + private var underline:Shape; public function TabsPart(app:Scratch) { function selectScripts(b:IconButton):void { app.setTab('scripts') } @@ -43,9 +45,21 @@ public class TabsPart extends UIPart { scriptsTab = makeTab('Scripts', selectScripts); imagesTab = makeTab('Images', selectImages); // changed to 'Costumes' or 'Scenes' by refresh() soundsTab = makeTab('Sounds', selectSounds); + underline=new Shape(); + var g:Graphics = underline.graphics; + + + + g.clear(); + g.beginFill(0x2196F3);//0x2196F3); +g.drawRect(0, 0, 80,4); + + addChild(scriptsTab); addChild(imagesTab); addChild(soundsTab); + addChild(underline); + scriptsTab.turnOn(); } @@ -63,20 +77,34 @@ public class TabsPart extends UIPart { scriptsTab.turnOff(); imagesTab.turnOff(); soundsTab.turnOff(); - if (tabName == 'scripts') scriptsTab.turnOn(); - if (tabName == 'images') imagesTab.turnOn(); - if (tabName == 'sounds') soundsTab.turnOn(); + if (tabName == 'scripts') { + scriptsTab.turnOn(); + TweenLite.to(underline, 0.2, {x:scriptsTab.x}); + //var myTween:Tween = new Tween(underline, "x", Regular.easeInOut, underline.x, scriptsTab.x, 0.5, true); + } + if (tabName == 'images') { + imagesTab.turnOn(); + TweenLite.to(underline, 0.2, {x:imagesTab.x}); + //var myTween:Tween = new Tween(underline, "x", Regular.easeInOut, underline.x, imagesTab.x, 0.5, true); + } + if (tabName == 'sounds'){ + soundsTab.turnOn(); + TweenLite.to(underline, 0.2, {x:soundsTab.x}); + //var myTween:Tween = new Tween(underline, "x", Regular.easeInOut, underline.x, soundsTab.x, 0.5, true); + + } } public function fixLayout():void { scriptsTab.x = 0; scriptsTab.y = 0; - imagesTab.x = scriptsTab.x + scriptsTab.width + 1; + imagesTab.x = scriptsTab.x + scriptsTab.width + 0; imagesTab.y = 0; - soundsTab.x = imagesTab.x + imagesTab.width + 1; + soundsTab.x = imagesTab.x + imagesTab.width + 0; soundsTab.y = 0; this.w = soundsTab.x + soundsTab.width; this.h = scriptsTab.height; + underline.y=this.h-4; } public function updateTranslation():void { @@ -90,22 +118,45 @@ public class TabsPart extends UIPart { } private function makeTabImg(label:String, isSelected:Boolean):Sprite { + var h:int = app.stagePane?this.app.stagePart.computeTopBarHeight():39; var img:Sprite = new Sprite(); var tf:TextField = new TextField(); - tf.defaultTextFormat = new TextFormat(CSS.font, 12, isSelected ? CSS.onColor : CSS.offColor, false); + var tFF:TextFormat=new TextFormat(CSS.font, 12, isSelected ? 0x2196F3 : 0x424242, false); + tFF.bold=true; + tf.defaultTextFormat = tFF; tf.text = Translator.map(label); tf.width = tf.textWidth + 5; tf.height = tf.textHeight + 5; - tf.x = 10; - tf.y = 4; + var w:int = Math.max(tf.width + 20,80); + tf.x = w/2-tf.width/2; + tf.y = h/2-tf.height /2; img.addChild(tf); var g:Graphics = img.graphics; - var w:int = tf.width + 20; - var h:int = 28; - var r:int = 9; - if (isSelected) drawTopBar(g, CSS.titleBarColors, getTopBarPath(w, h), w, h); - else drawSelected(g, [0xB1E0FF, 0x0077FF], getTopBarPath(w, h), w, h); + + + var r:int = 0;//9 + var pT:Array=[["m",0,h],["l",0,-h],["l",w,0],["l",0,h],["l",-w,0]]; + if (isSelected){ + g.clear(); + g.beginFill(CSS.grey100);//0x2196F3);//CSS.grey100); +g.drawRect(0, 0, w,h); +//g.beginFill(0x2196F3);//0x2196F3); +//g.drawRect(0,h-4,w,4); + //drawBoxBkgGradientShape(g, Math.PI / 2, colors,[0x00, 0xFF], path, w, h); + //g.lineStyle(0.5, borderColor, 1, true); + //DrawPath.drawPath(path, g); + }else{ + g.clear(); + g.beginFill(CSS.grey100);//0x2196F3);//CSS.grey100); +g.drawRect(0,0,w,h); +//g.beginFill(0xB3C1CC); +//g.drawRect(0,h-4,w,4); + //drawSelected(g, [0xB1E0FF, 0x0077FF], pT, w, h); + } + //this.height=h; + //if (isSelected) drawTopBar(g, CSS.titleBarColors, pT, w, h); + //else drawSelected(g, [0xB1E0FF, 0x0077FF], pT, w, h); return img; } diff --git a/src/ui/parts/TopBarPart.as b/src/ui/parts/TopBarPart.as index eee5c30..09a1163 100644 --- a/src/ui/parts/TopBarPart.as +++ b/src/ui/parts/TopBarPart.as @@ -43,7 +43,7 @@ import translation.Translator; import uiwidgets.*; import util.*; - +import flash.filters.DropShadowFilter; public class TopBarPart extends UIPart { private var shape:Shape; @@ -75,6 +75,13 @@ public class TopBarPart extends UIPart { addButtons(); addLogo(); refresh(); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 2; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 90; + //this.filters=[shadow]; } protected function addButtons():void { @@ -115,7 +122,7 @@ public class TopBarPart extends UIPart { removeChild(aboutButton); } } - + private function addLogo() : void { var logoClicked:Function = null; logoClicked = function(param1:*):void { @@ -140,7 +147,7 @@ public class TopBarPart extends UIPart { this.h = h; var g:Graphics = shape.graphics; g.clear(); - g.beginFill(CSS.topBarColor()); + g.beginFill(false?0x448AFF:0x2962FF); g.drawRect(0, 0, w, h); g.endFill(); fixLayout(); diff --git a/src/uiwidgets/Button.as b/src/uiwidgets/Button.as index 85777ce..15bf808 100644 --- a/src/uiwidgets/Button.as +++ b/src/uiwidgets/Button.as @@ -22,20 +22,29 @@ import flash.display.*; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.text.*; +import flash.filters.DropShadowFilter; +import flash.filters.BevelFilter; +import com.greensock.TweenLite; +import com.greensock.TweenMax; + public class Button extends Sprite { private var labelOrIcon:DisplayObject; - private var color:* = CSS.titleBarColors; + private var color:* = CSS.white;//CSS.titleBarColors; private var minWidth:int = 50; - private var paddingX:Number = 5.5; + private var paddingX:Number = 5; private var compact:Boolean; private var action:Function; // takes no arguments private var eventAction:Function; // like action, but takes the event as an argument private var tipName:String; + public var state:int=0; + public var raised:Boolean=true; + public var textColor:int = 0x2962FF; + public var raiseY:Number=1; - public function Button(label:String, action:Function = null, compact:Boolean = false, tipName:String = null) { + public function Button(label:String, action:Function = null, compact:Boolean = false, tipName:String = null,raised:Boolean = true) { this.action = action; this.compact = compact; this.tipName = tipName; @@ -45,7 +54,25 @@ public class Button extends Sprite { addEventListener(MouseEvent.MOUSE_OUT, mouseOut); addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); addEventListener(MouseEvent.MOUSE_UP, mouseUp); - setColor(CSS.titleBarColors); + setColor(CSS.white);//titleBarColors); + //setColor(0x4285f4); + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 1; +shadow.alpha=0.3; +shadow.blurX=6; +shadow.blurY=6; +shadow.angle = 90; + this.filters=[shadow]; + this.setRaised(raised); + } + public function dropShadow(e:Number):DropShadowFilter{ + var shadow:DropShadowFilter = new DropShadowFilter(); +shadow.distance = 1+e; +shadow.alpha=0.3; +shadow.blurX=e*2+2; +shadow.blurY=e*2+2; +shadow.angle = 90; +return shadow; } public function setLabel(s:String):void { @@ -76,7 +103,7 @@ public class Button extends Sprite { if (labelOrIcon != null) { if (labelOrIcon is TextField) { minW = Math.max(minWidth, labelOrIcon.width + paddingX * 2); - minH = compact ? 20 : 25; + minH = compact ? 20 : 26; } else { minW = Math.max(minWidth, labelOrIcon.width + 12); minH = Math.max(minH, labelOrIcon.height + 11); @@ -86,36 +113,101 @@ public class Button extends Sprite { } // outline graphics.clear(); - graphics.lineStyle(0.5, CSS.borderColor, 1, true); + //graphics.lineStyle(0.5, CSS.borderColor, 1, true); if (color is Array) { var matr:Matrix = new Matrix(); matr.createGradientBox(minW, minH, Math.PI / 2, 0, 0); - graphics.beginGradientFill(GradientType.LINEAR, CSS.titleBarColors, [100, 100], [0x00, 0xFF], matr); + graphics.beginGradientFill(GradientType.LINEAR, [0xFFFFFF,0xFFFFFF], [100, 100], [0x00, 0xFF], matr); } - else graphics.beginFill(color); - graphics.drawRoundRect(0, 0, minW, minH, 12); + else {graphics.beginFill(color);} + if(!raised){ + + graphics.beginFill(0,0); + }else{ + //graphics.beginFill(0x888AF3); + } + graphics.drawRoundRect(0, 0, minW, minH, 3); graphics.endFill(); } - + public function withTextColor(txtColor:int):Button{ + this.textColor=txtColor; + return this; + } public function setEventAction(newEventAction:Function):Function { var oldEventAction:Function = eventAction; eventAction = newEventAction; return oldEventAction; } - + public function addBevel():void{ + var f:BevelFilter = new BevelFilter(1); + f.blurX = f.blurY = 1; + f.highlightAlpha = 0.6; + f.shadowAlpha = 0.6; + //this.filters = [f].concat(filters || []); + //this.filters[this.filters.length]=f; + } private function mouseOver(evt:MouseEvent):void { - setColor(CSS.overColor) + //setColor(CSS.overColor) + //setColor(0x4285f4); + if(raised){ + if(this.filters.length<1){ + this.filters=[dropShadow(2)]; + addBevel(); + }else{ + TweenMax.to(this, 0.2, {dropShadowFilter:{distance:4,blurX:8,blurY:8}}); + } + }else{ + this.filters=[]; + if (labelOrIcon is TextField) { + TweenMax.to(labelOrIcon, 0.6, {hexColors:{textColor:this.textColor}}); + //(labelOrIcon as TextField).textColor=textColor; + } + } } private function mouseOut(evt:MouseEvent):void { - setColor(CSS.titleBarColors) + //setColor(CSS.titleBarColors) + //setColor(0x4285f4); + if(raised){ + if(this.filters.length<1){ + this.filters=[dropShadow(1)]; + addBevel(); + }else{ + TweenMax.to(this, 0.2, {dropShadowFilter:{distance:1,blurX:2,blurY:2}}); + } + }else{ + this.filters=[]; + if (labelOrIcon is TextField) { + TweenMax.to(labelOrIcon, 0.6, {hexColors:{textColor:0x424242}}); + //(labelOrIcon as TextField).textColor=0x424242; + } + } + } + public function setRaised(raised:Boolean):Button{ + this.raised=raised; + if(raised){ + this.filters=[dropShadow(1)]; + addBevel(); + }else{ + this.filters=[]; + if (labelOrIcon is TextField) { + (labelOrIcon as TextField).textColor=0x424242; + } + graphics.clear(); + graphics.beginFill(0,0); + graphics.drawRoundRect(0, 0, this.width, this.height, 3); + graphics.endFill(); + } + return this; } private function mouseDown(evt:MouseEvent):void { + //setColor(0x4285f4); Menu.removeMenusFrom(stage) } private function mouseUp(evt:MouseEvent):void { + setColor(CSS.titleBarColors); if (action != null) action(); if (eventAction != null) eventAction(evt); evt.stopImmediatePropagation(); @@ -129,6 +221,8 @@ public class Button extends Sprite { color = c; if (labelOrIcon is TextField) { (labelOrIcon as TextField).textColor = (c == CSS.overColor) ? CSS.white : CSS.buttonLabelColor; + //(labelOrIcon as TextField).textColor = textColor;//(state) ? CSS.white : CSS.buttonLabelColor; + } setMinWidthHeight(5, 5); } @@ -138,8 +232,12 @@ public class Button extends Sprite { label.autoSize = TextFieldAutoSize.LEFT; label.selectable = false; label.background = false; - label.defaultTextFormat = CSS.normalTextFormat; - label.textColor = CSS.buttonLabelColor; + var tF:TextFormat=new TextFormat(CSS.font, 12, textColor); + tF.bold=true; + label.defaultTextFormat = tF;//CSS.normalTextFormat; + //label.defaultTextFormat.bold=true; + + label.textColor=0x424242;// = textColor;//CSS.buttonLabelColor; label.text = s; labelOrIcon = label; setMinWidthHeight(0, 0); diff --git a/src/uiwidgets/ButtonInverted.as b/src/uiwidgets/ButtonInverted.as index e78fd70..9954671 100644 --- a/src/uiwidgets/ButtonInverted.as +++ b/src/uiwidgets/ButtonInverted.as @@ -87,7 +87,7 @@ public class ButtonInverted extends Sprite { graphics.beginGradientFill(GradientType.LINEAR, CSS.titleBarColors, [100, 100], [0x00, 0xFF], matr); } else graphics.beginFill(color); - graphics.drawRoundRect(0, 0, minW, minH, 12); + graphics.drawRoundRect(0, 0, minW, minH, 4); graphics.endFill(); } diff --git a/src/uiwidgets/DialogBox.as b/src/uiwidgets/DialogBox.as index 9ad0e0b..93eacbe 100644 --- a/src/uiwidgets/DialogBox.as +++ b/src/uiwidgets/DialogBox.as @@ -87,7 +87,7 @@ public class DialogBox extends Sprite { if (context) d.updateContext(context); d.showOnStage(stage ? stage : Scratch.app.stage); } - + public static function close(title:String, msg:String = null, widget:DisplayObject = null, button:String = "OK", stage:Stage = null, okFunction:Function = null, cancelFunction:Function = null, context:Dictionary = null,inverted:Boolean = false):void { var d:DialogBox = new DialogBox(okFunction, cancelFunction); d.leftJustify = false; @@ -191,10 +191,11 @@ public class DialogBox extends Sprite { if (action != null) action(); } var b:Button = new Button(Translator.map(label), doAction); + b.setRaised(false); addChild(b); buttons.push(b); } - + public function addInvertedButton(label:String, action:Function):void { function doAction():void { remove(); diff --git a/src/uiwidgets/ScriptsPane.as b/src/uiwidgets/ScriptsPane.as index 80a57cf..478e40d 100644 --- a/src/uiwidgets/ScriptsPane.as +++ b/src/uiwidgets/ScriptsPane.as @@ -71,8 +71,12 @@ public class ScriptsPane extends ScrollFrameContents { const bgColor:int = alpha | 0xD7D7D7; const c1:int = alpha | 0xCBCBCB; const c2:int = alpha | 0xC8C8C8; - texture = new BitmapData(23, 23, true, bgColor); - texture.setPixel(11, 0, c1); + texture = new BitmapData(23, 23, true, 0xFFFFFF); + for(var i:int=0;i<23;i++){ + texture.setPixel(i, 11, CSS.borderColor); + texture.setPixel(11,i, CSS.borderColor); + } + /*texture.setPixel(11, 0, c1); texture.setPixel(10, 1, c1); texture.setPixel(11, 1, c2); texture.setPixel(12, 1, c1); @@ -81,7 +85,7 @@ public class ScriptsPane extends ScrollFrameContents { texture.setPixel(1, 10, c1); texture.setPixel(1, 11, c2); texture.setPixel(1, 12, c1); - texture.setPixel(2, 11, c1); + texture.setPixel(2, 11, c1);*/ } public function viewScriptsFor(obj:ScratchObj):void { @@ -129,6 +133,16 @@ public class ScriptsPane extends ScrollFrameContents { } public function prepareToDrag(b:Block):void { + if (b.parent is Block){ + Block(b.parent).base.color=0x00FF00; + if (Block(b.parent).substacks.indexOf(b)>-1){ + Block(b.parent).base.color=0xFF0000; + Block(b.parent).substacks[Block(b.parent).substacks.indexOf(b)]=null; + Block(b.parent).fixArgLayout(); + //Block(b.parent).base.redraw(); + } + Block(b.parent).base.redraw(); + } findTargetsFor(b); nearestTarget = null; b.scaleX = b.scaleY = scaleX; @@ -201,6 +215,9 @@ public class ScriptsPane extends ScrollFrameContents { if (b.base.canHaveSubstack1() && !b.subStack1) { updateHeight(); } + if (b.base.substacks.length>0) { + //updateHeight(); + } } else { nearestTarget = null; @@ -220,6 +237,11 @@ public class ScriptsPane extends ScrollFrameContents { } private function blockDropped(b:Block):void { + if(b.parent!=null){ + if(b.parent is Block){ + Block(b.parent).removeBlock(b); + } + } if (nearestTarget == null) { b.cacheAsBitmap = true; } else { @@ -250,6 +272,9 @@ public class ScriptsPane extends ScrollFrameContents { targetCmd.insertBlockAround(b); break; } + if(nearestTarget[2]<0){ + targetCmd.insertBlockSubSpecial(-1-nearestTarget[2],b); + } } } if (b.op == Specs.PROCEDURE_DEF) app.updatePalette(); @@ -318,6 +343,17 @@ return true; // xxx disable this check for now; it was causing confusion at Scra } if (target.subStack1 != null) findCommandTargetsIn(target.subStack1, endsWithTerminal); if (target.subStack2 != null) findCommandTargetsIn(target.subStack2, endsWithTerminal); + + if (target.substacks.length>0) { + + for(var jim:int =0;jim0) { + + for(var jim:int =0;jim-1){ + //Block(b.parent).base.color=0xFF0000; + Block(b.parent).substacks[Block(b.parent).substacks.indexOf(b)]=null; + Block(b.parent).fixArgLayout(); + Block(b.parent).fixStackLayout(); + //Block(b.parent).base.redraw(); + }else{ + Block(b.parent).removeBlock(b); + } + Block(b.parent).base.redraw(); + } if (b.parent is MultiBlockArg) MultiBlockArg(b.parent).removeBlock(b); if (b.parent != null) b.parent.removeChild(b); app.scriptsPane.prepareToDrag(b); diff --git a/src/watchers/Watcher.as b/src/watchers/Watcher.as index dbb2783..6c4ece8 100644 --- a/src/watchers/Watcher.as +++ b/src/watchers/Watcher.as @@ -121,7 +121,9 @@ public class Watcher extends Sprite implements DragClient { setColor(Specs.variableColor); setLabel((target.isStage) ? varName : (target.objName + ": " + varName)); } - + public function updateForVarColor():void{ + setColor(this.target.lookupVar(this.param).color); + } public function changeVarName(varName:String):void { if (cmd != "getVar:") return; param = varName;