changeset 2018:95d672ac78a4

Moved multiple projects to open-source
author Pavel Safrata <pavel.safrata@oracle.com>
date Tue, 18 Dec 2012 09:15:15 +0000
parents d12924727cd8
children 834fd785f4ea
files build-defs.xml build.xml common.properties javafx-anim/build-closed.xml javafx-anim/build-common.xml javafx-anim/build.xml javafx-anim/javafx-anim.iml javafx-anim/nbproject/project.xml javafx-anim/project.properties javafx-anim/src/com/sun/javafx/animation/TickCalculation.java javafx-anim/src/com/sun/javafx/animation/TimingTarget.java javafx-anim/src/com/sun/scenario/DelayedRunnable.java javafx-anim/src/com/sun/scenario/Settings.java javafx-anim/src/com/sun/scenario/StandaloneAccessor.java javafx-anim/src/com/sun/scenario/ToolkitAccessor.java javafx-anim/src/com/sun/scenario/animation/AbstractMasterTimer.java javafx-anim/src/com/sun/scenario/animation/AnimationPulse.java javafx-anim/src/com/sun/scenario/animation/AnimationPulseMBean.java javafx-anim/src/com/sun/scenario/animation/FixedPulseTime.java javafx-anim/src/com/sun/scenario/animation/MilliCurrentTime.java javafx-anim/src/com/sun/scenario/animation/NanoCurrentTime.java javafx-anim/src/com/sun/scenario/animation/NumberTangentInterpolator.java javafx-anim/src/com/sun/scenario/animation/SplineInterpolator.java javafx-anim/src/com/sun/scenario/animation/shared/AnimationAccessor.java javafx-anim/src/com/sun/scenario/animation/shared/AnimationPulseReceiver.java javafx-anim/src/com/sun/scenario/animation/shared/ClipEnvelope.java javafx-anim/src/com/sun/scenario/animation/shared/ClipEnvelopeFactory.java javafx-anim/src/com/sun/scenario/animation/shared/ClipInterpolator.java javafx-anim/src/com/sun/scenario/animation/shared/CurrentTime.java javafx-anim/src/com/sun/scenario/animation/shared/FiniteClipEnvelope.java javafx-anim/src/com/sun/scenario/animation/shared/GeneralClipInterpolator.java javafx-anim/src/com/sun/scenario/animation/shared/InfiniteClipEnvelope.java javafx-anim/src/com/sun/scenario/animation/shared/InterpolationInterval.java javafx-anim/src/com/sun/scenario/animation/shared/PulseReceiver.java javafx-anim/src/com/sun/scenario/animation/shared/SimpleClipInterpolator.java javafx-anim/src/com/sun/scenario/animation/shared/SingleLoopClipEnvelope.java javafx-anim/src/com/sun/scenario/animation/shared/TimelineClipCore.java javafx-anim/src/javafx/animation/Animation.java javafx-anim/src/javafx/animation/AnimationAccessorImpl.java javafx-anim/src/javafx/animation/AnimationTimer.java javafx-anim/src/javafx/animation/Interpolatable.java javafx-anim/src/javafx/animation/Interpolator.java javafx-anim/src/javafx/animation/KeyFrame.java javafx-anim/src/javafx/animation/KeyValue.java javafx-anim/src/javafx/animation/Timeline.java javafx-anim/test/functional/javafx/animation/AnimationFunctionalTestBase.java javafx-anim/test/functional/javafx/animation/CanSkip_Test.java javafx-anim/test/functional/javafx/animation/JumpPastEndAR_Test.java javafx-anim/test/functional/javafx/animation/JumpPastEnd_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS101_02_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS102_01_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS103_01_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS103_02_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS104_01_Test.java javafx-anim/test/functional/javafx/animation/KeyFrame_TS105_01_Test.java javafx-anim/test/functional/javafx/animation/KeyValue_TS200_01_Test.java javafx-anim/test/functional/javafx/animation/KeyValue_TS200_02_Test.java javafx-anim/test/functional/javafx/animation/KeyValue_TS200_03_Test.java javafx-anim/test/functional/javafx/animation/KeyValue_TS200_04_Test.java javafx-anim/test/functional/javafx/animation/RateTestBackAR_Test.java javafx-anim/test/functional/javafx/animation/RateTestBack_Test.java javafx-anim/test/functional/javafx/animation/RateTestBase.java javafx-anim/test/functional/javafx/animation/RateTestFwdAR_Test.java javafx-anim/test/functional/javafx/animation/RateTestFwd_Test.java javafx-anim/test/functional/javafx/animation/RepeatAR_Test.java javafx-anim/test/functional/javafx/animation/RepeatBackAR_Test.java javafx-anim/test/functional/javafx/animation/RepeatBack_Test.java javafx-anim/test/functional/javafx/animation/Repeat_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS001_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS002_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS002_02_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS005_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS006_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS008_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS012_01_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS016_02_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS017_02_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS018_02_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS019_02_Test.java javafx-anim/test/functional/javafx/animation/Timeline_TS021_01_Test.java javafx-anim/test/functional/javafx/animation/ToggleAR_Test.java javafx-anim/test/functional/javafx/animation/ToggleBackAR_Test.java javafx-anim/test/functional/javafx/animation/ToggleBack_Test.java javafx-anim/test/functional/javafx/animation/ToggleStartNonEnd_Test.java javafx-anim/test/functional/javafx/animation/ToggleStartNonZero_Test.java javafx-anim/test/functional/javafx/animation/Toggle_Test.java javafx-anim/test/functional/javafx/animation/ZeroDur_Test.java javafx-anim/test/unit/com/sun/scenario/SettingsTest.java javafx-anim/test/unit/com/sun/scenario/ToolkitAccessorStub.java javafx-anim/test/unit/com/sun/scenario/animation/AbstractMasterTimerMock.java javafx-anim/test/unit/com/sun/scenario/animation/AbstractMasterTimerTest.java javafx-anim/test/unit/com/sun/scenario/animation/NumberTangentInterpolatorTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/AnimationPulseReceiverTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/ClipEnvelopeMock.java javafx-anim/test/unit/com/sun/scenario/animation/shared/ClipTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/FiniteClipEnvelopeTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/GeneralClipInterpolatorTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/InfiniteClipEnvelopeTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/SimpleClipInterpolatorTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/SingleLoopClipEnvelopeTest.java javafx-anim/test/unit/com/sun/scenario/animation/shared/TimelineClipCoreTest.java javafx-anim/test/unit/javafx/animation/AnimationImpl.java javafx-anim/test/unit/javafx/animation/AnimationMock.java javafx-anim/test/unit/javafx/animation/AnimationSetRateTest.java javafx-anim/test/unit/javafx/animation/AnimationTest.java javafx-anim/test/unit/javafx/animation/InterpolatorTest.java javafx-anim/test/unit/javafx/animation/KeyFrameTest.java javafx-anim/test/unit/javafx/animation/KeyValueTest.java javafx-anim/test/unit/javafx/animation/TimelineHelper.java javafx-anim/test/unit/javafx/animation/TimelinePlayTest.java javafx-anim/test/unit/javafx/animation/TimelineTest.java javafx-annotation-processor/.settings/org.eclipse.jdt.core.prefs javafx-annotation-processor/.settings/org.eclipse.jdt.launching.prefs javafx-annotation-processor/build-closed.xml javafx-annotation-processor/build-common.xml javafx-annotation-processor/build.xml javafx-annotation-processor/nbproject/project.xml javafx-annotation-processor/project.properties javafx-annotation-processor/src/META-INF/services/javax.annotation.processing.Processor javafx-annotation-processor/src/javafx/builder/processor/BuilderProcessor.java javafx-annotation-processor/src/javafx/builder/processor/BuilderProcessor.properties javafx-beans-dt/build-closed.xml javafx-common/build-closed.xml javafx-common/build-common.xml javafx-common/build-tools/VersionInfo.java javafx-common/build.xml javafx-common/javafx-common.iml javafx-common/nbproject/project.xml javafx-common/project.properties javafx-common/src/com/sun/javafx/PlatformUtil.java javafx-common/src/com/sun/javafx/UnmodifiableArrayList.java javafx-common/src/com/sun/javafx/event/BasicEventDispatcher.java javafx-common/src/com/sun/javafx/event/CompositeEventDispatcher.java javafx-common/src/com/sun/javafx/event/CompositeEventHandler.java javafx-common/src/com/sun/javafx/event/CompositeEventTarget.java javafx-common/src/com/sun/javafx/event/CompositeEventTargetImpl.java javafx-common/src/com/sun/javafx/event/DirectEvent.java javafx-common/src/com/sun/javafx/event/EventDispatchChainImpl.java javafx-common/src/com/sun/javafx/event/EventDispatchTree.java javafx-common/src/com/sun/javafx/event/EventDispatchTreeImpl.java javafx-common/src/com/sun/javafx/event/EventHandlerManager.java javafx-common/src/com/sun/javafx/event/EventQueue.java javafx-common/src/com/sun/javafx/event/EventRedirector.java javafx-common/src/com/sun/javafx/event/EventUtil.java javafx-common/src/com/sun/javafx/event/RedirectedEvent.java javafx-common/src/com/sun/javafx/runtime/SystemProperties.java javafx-common/src/javafx/event/ActionEvent.java javafx-common/src/javafx/event/Event.java javafx-common/src/javafx/event/EventDispatchChain.java javafx-common/src/javafx/event/EventDispatcher.java javafx-common/src/javafx/event/EventHandler.java javafx-common/src/javafx/event/EventTarget.java javafx-common/src/javafx/event/EventType.java javafx-common/src/javafx/event/WeakEventHandler.java javafx-common/src/javafx/event/package.html javafx-common/src/javafx/util/Builder.java javafx-common/src/javafx/util/BuilderFactory.java javafx-common/src/javafx/util/Callback.java javafx-common/src/javafx/util/Duration.java javafx-common/src/javafx/util/Pair.java javafx-common/src/javafx/util/StringConverter.java javafx-common/src/javafx/util/package.html javafx-common/test/unit/com/sun/javafx/event/CompositeEventDispatcherTest.java javafx-common/test/unit/com/sun/javafx/event/CompositeEventHandlerTest.java javafx-common/test/unit/com/sun/javafx/event/EmptyEvent.java javafx-common/test/unit/com/sun/javafx/event/EventChangingDispatcher.java javafx-common/test/unit/com/sun/javafx/event/EventChangingHandler.java javafx-common/test/unit/com/sun/javafx/event/EventConsumingHandler.java javafx-common/test/unit/com/sun/javafx/event/EventCountingDispatcher.java javafx-common/test/unit/com/sun/javafx/event/EventCountingHandler.java javafx-common/test/unit/com/sun/javafx/event/EventDispatchChainTest.java javafx-common/test/unit/com/sun/javafx/event/EventDispatchTreeTest.java javafx-common/test/unit/com/sun/javafx/event/EventHandlerManagerTest.java javafx-common/test/unit/com/sun/javafx/event/LabeledEventDispatcher.java javafx-common/test/unit/com/sun/javafx/event/Operation.java javafx-common/test/unit/com/sun/javafx/event/PathChangingDispatcher.java javafx-common/test/unit/com/sun/javafx/event/StubBasicEventDispatcher.java javafx-common/test/unit/com/sun/javafx/event/StubEventDispatchChain.java javafx-common/test/unit/com/sun/javafx/event/TestCompositeEventDispatcher.java javafx-common/test/unit/com/sun/javafx/event/ValueEvent.java javafx-common/test/unit/com/sun/javafx/runtime/VersionInfoTest.java javafx-common/test/unit/javafx/event/EventSerializationEventExists.java javafx-common/test/unit/javafx/event/EventSerializationTest.java javafx-common/test/unit/javafx/event/EventTest.java javafx-common/test/unit/javafx/event/WeakEventHandlerUtil.java javafx-common/test/unit/javafx/util/DurationTest.java javafx-common/test/unit/javafx/util/DurationValueOfTest.java javafx-concurrent/build-closed.xml javafx-designtime/build-closed.xml javafx-fxml/build-closed.xml javafx-fxml/nbproject/project.xml javafx-geom/build-closed.xml javafx-geom/build-common.xml javafx-geom/build.xml javafx-geom/cagsrc.double/com/sun/javafx/geom/Area.java javafx-geom/cagsrc.double/com/sun/javafx/geom/AreaOp.java javafx-geom/cagsrc.double/com/sun/javafx/geom/ChainEnd.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Crossings.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Curve.java javafx-geom/cagsrc.double/com/sun/javafx/geom/CurveLink.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Edge.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Order0.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Order1.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Order2.java javafx-geom/cagsrc.double/com/sun/javafx/geom/Order3.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Area.java javafx-geom/cagsrc.float/com/sun/javafx/geom/AreaOp.java javafx-geom/cagsrc.float/com/sun/javafx/geom/ChainEnd.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Crossings.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Curve.java javafx-geom/cagsrc.float/com/sun/javafx/geom/CurveLink.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Edge.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Order0.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Order1.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Order2.java javafx-geom/cagsrc.float/com/sun/javafx/geom/Order3.java javafx-geom/javafx-geom.iml javafx-geom/nbproject/project.xml javafx-geom/project.properties javafx-geom/src/com/sun/javafx/geom/Arc2D.java javafx-geom/src/com/sun/javafx/geom/ArcIterator.java javafx-geom/src/com/sun/javafx/geom/BaseBounds.java javafx-geom/src/com/sun/javafx/geom/BoxBounds.java javafx-geom/src/com/sun/javafx/geom/CameraImpl.java javafx-geom/src/com/sun/javafx/geom/ConcentricShapePair.java javafx-geom/src/com/sun/javafx/geom/CubicApproximator.java javafx-geom/src/com/sun/javafx/geom/CubicCurve2D.java javafx-geom/src/com/sun/javafx/geom/CubicIterator.java javafx-geom/src/com/sun/javafx/geom/Dimension2D.java javafx-geom/src/com/sun/javafx/geom/Ellipse2D.java javafx-geom/src/com/sun/javafx/geom/EllipseIterator.java javafx-geom/src/com/sun/javafx/geom/FlatteningPathIterator.java javafx-geom/src/com/sun/javafx/geom/GeneralShapePair.java javafx-geom/src/com/sun/javafx/geom/IllegalPathStateException.java javafx-geom/src/com/sun/javafx/geom/Line2D.java javafx-geom/src/com/sun/javafx/geom/LineIterator.java javafx-geom/src/com/sun/javafx/geom/ParallelCameraImpl.java javafx-geom/src/com/sun/javafx/geom/Path2D.java javafx-geom/src/com/sun/javafx/geom/PathConsumer2D.java javafx-geom/src/com/sun/javafx/geom/PathIterator.java javafx-geom/src/com/sun/javafx/geom/PerspectiveCameraImpl.java javafx-geom/src/com/sun/javafx/geom/PickRay.java javafx-geom/src/com/sun/javafx/geom/Point2D.java javafx-geom/src/com/sun/javafx/geom/QuadCurve2D.java javafx-geom/src/com/sun/javafx/geom/QuadIterator.java javafx-geom/src/com/sun/javafx/geom/RectBounds.java javafx-geom/src/com/sun/javafx/geom/Rectangle.java javafx-geom/src/com/sun/javafx/geom/RectangularShape.java javafx-geom/src/com/sun/javafx/geom/RoundRectIterator.java javafx-geom/src/com/sun/javafx/geom/RoundRectangle2D.java javafx-geom/src/com/sun/javafx/geom/Shape.java javafx-geom/src/com/sun/javafx/geom/ShapePair.java javafx-geom/src/com/sun/javafx/geom/TransformedShape.java javafx-geom/src/com/sun/javafx/geom/Vec2d.java javafx-geom/src/com/sun/javafx/geom/Vec2f.java javafx-geom/src/com/sun/javafx/geom/Vec3d.java javafx-geom/src/com/sun/javafx/geom/Vec3f.java javafx-geom/src/com/sun/javafx/geom/Vec4d.java javafx-geom/src/com/sun/javafx/geom/Vec4f.java javafx-geom/src/com/sun/javafx/geom/transform/Affine2D.java javafx-geom/src/com/sun/javafx/geom/transform/Affine3D.java javafx-geom/src/com/sun/javafx/geom/transform/AffineBase.java javafx-geom/src/com/sun/javafx/geom/transform/BaseTransform.java javafx-geom/src/com/sun/javafx/geom/transform/CanTransformVec3d.java javafx-geom/src/com/sun/javafx/geom/transform/GeneralTransform3D.java javafx-geom/src/com/sun/javafx/geom/transform/Identity.java javafx-geom/src/com/sun/javafx/geom/transform/NoninvertibleTransformException.java javafx-geom/src/com/sun/javafx/geom/transform/SingularMatrixException.java javafx-geom/src/com/sun/javafx/geom/transform/TransformHelper.java javafx-geom/src/com/sun/javafx/geom/transform/Translate2D.java javafx-geom/test/com/sun/javafx/geom/BaseBoundsTest.java javafx-geom/test/com/sun/javafx/geom/Path2DTest.java javafx-geom/test/com/sun/javafx/geom/TransformedShapeTest.java javafx-geom/test/com/sun/javafx/geom/Vec3dTest.java javafx-geom/test/com/sun/javafx/geom/transform/BaseTransformTest.java javafx-logging/build-before-profiler.xml javafx-logging/build-closed.xml javafx-logging/build-common.xml javafx-logging/build.xml javafx-logging/javafx-logging.iml javafx-logging/nbproject/build-impl.xml javafx-logging/nbproject/genfiles.properties javafx-logging/nbproject/profiler-build-impl.xml javafx-logging/nbproject/project.properties javafx-logging/nbproject/project.xml javafx-logging/src/com/sun/javafx/logging/LoggingProxy.java javafx-logging/src/com/sun/javafx/logging/LoggingSupport.java javafx-logging/src/com/sun/javafx/logging/PlatformLogger.java javafx-logging/src/com/sun/javafx/logging/PulseLogger.java javafx-logging/test/com/sun/javafx/logging/CreateButNotUse.java javafx-logging/test/com/sun/javafx/logging/ExampleUsage.java javafx-logging/test/com/sun/javafx/logging/JustImports.java javafx-logging/test/com/sun/javafx/logging/NoRefs.java javafx-logging/test/com/sun/javafx/logging/PlatformLoggerTest.java javafx-sg-common/build-closed.xml javafx-sg-common/build-common.xml javafx-sg-common/build.xml javafx-sg-common/javafx-sg-common.iml javafx-sg-common/nbproject/project.xml javafx-sg-common/project.properties javafx-sg-common/src/com/sun/javafx/sg/BaseCacheFilter.java javafx-sg-common/src/com/sun/javafx/sg/BaseEffectFilter.java javafx-sg-common/src/com/sun/javafx/sg/BaseNode.java javafx-sg-common/src/com/sun/javafx/sg/BaseNodeEffectInput.java javafx-sg-common/src/com/sun/javafx/sg/DirtyHint.java javafx-sg-common/src/com/sun/javafx/sg/DirtyRegionContainer.java javafx-sg-common/src/com/sun/javafx/sg/DirtyRegionPool.java javafx-sg-common/src/com/sun/javafx/sg/GrowableDataBuffer.java javafx-sg-common/src/com/sun/javafx/sg/NodePath.java javafx-sg-common/src/com/sun/javafx/sg/PGArc.java javafx-sg-common/src/com/sun/javafx/sg/PGCanvas.java javafx-sg-common/src/com/sun/javafx/sg/PGCircle.java javafx-sg-common/src/com/sun/javafx/sg/PGCubicCurve.java javafx-sg-common/src/com/sun/javafx/sg/PGEllipse.java javafx-sg-common/src/com/sun/javafx/sg/PGGroup.java javafx-sg-common/src/com/sun/javafx/sg/PGImageView.java javafx-sg-common/src/com/sun/javafx/sg/PGLine.java javafx-sg-common/src/com/sun/javafx/sg/PGMediaView.java javafx-sg-common/src/com/sun/javafx/sg/PGNode.java javafx-sg-common/src/com/sun/javafx/sg/PGPath.java javafx-sg-common/src/com/sun/javafx/sg/PGPolygon.java javafx-sg-common/src/com/sun/javafx/sg/PGPolyline.java javafx-sg-common/src/com/sun/javafx/sg/PGQuadCurve.java javafx-sg-common/src/com/sun/javafx/sg/PGRectangle.java javafx-sg-common/src/com/sun/javafx/sg/PGRegion.java javafx-sg-common/src/com/sun/javafx/sg/PGSVGPath.java javafx-sg-common/src/com/sun/javafx/sg/PGShape.java javafx-sg-common/src/com/sun/javafx/sg/PGSpan.java javafx-sg-common/src/com/sun/javafx/sg/PGText.java javafx-sg-common/src/com/sun/javafx/sg/PGTextHelper.java javafx-sg-common/src/com/sun/javafx/sg/PGWebView.java javafx-sg-common/test/com/sun/javafx/sg/ContentBoundsTest.java javafx-sg-common/test/com/sun/javafx/sg/CullingTest.java javafx-sg-common/test/com/sun/javafx/sg/DirtyRegionContainerTest.java javafx-sg-prism/build-closed.xml javafx-sg-prism/build-common.xml javafx-sg-prism/build.xml javafx-sg-prism/javafx-sg-prism.iml javafx-sg-prism/nbproject/project.xml javafx-sg-prism/project.properties javafx-sg-prism/src/com/sun/javafx/sg/prism/EffectUtil.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGArc.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCanvas.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCircle.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCubicCurve.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGEllipse.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGGroup.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGImageView.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGLine.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGMediaView.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGNode.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPath.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPolygon.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPolyline.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGQuadCurve.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRectangle.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGSVGPath.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGShape.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGSpan.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGText.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGTextHelper.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NodeEffectInput.java javafx-sg-prism/src/com/sun/javafx/sg/prism/PrismMediaFrameHandler.java javafx-sg-prism/src/com/sun/javafx/sg/prism/RegionImageCache.java javafx-sg-prism/src/com/sun/javafx/sg/prism/SceneChangeListener.java javafx-sg-prism/src/com/sun/javafx/sg/prism/ShapeEvaluator.java javafx-sg-prism/test/com/sun/javafx/sg/prism/DirtyRegionTestBase.java javafx-sg-prism/test/com/sun/javafx/sg/prism/GridDirtyRegionTest.java javafx-sg-prism/test/com/sun/javafx/sg/prism/NodeTestUtils.java javafx-sg-prism/test/com/sun/javafx/sg/prism/OcclusionCullingTest.java javafx-sg-prism/test/com/sun/javafx/sg/prism/TestGraphics.java javafx-sg-prism/test/com/sun/javafx/sg/prism/TestNGNode.java javafx-ui-charts/build-closed.xml javafx-ui-common/build-closed.xml javafx-ui-common/nbproject/project.xml javafx-ui-common/project.properties javafx-ui-controls/build-closed.xml javafx-ui-controls/build-common.xml javafx-ui-controls/project.properties javafx-util-converter/build-closed.xml pisces/build-closed.xml pisces/build-common.xml pisces/build.xml pisces/nbproject/project.xml pisces/pisces.iml pisces/project.properties pisces/src/com/sun/openpisces/AlphaConsumer.java pisces/src/com/sun/openpisces/Curve.java pisces/src/com/sun/openpisces/Dasher.java pisces/src/com/sun/openpisces/Helpers.java pisces/src/com/sun/openpisces/Renderer.java pisces/src/com/sun/openpisces/Stroker.java pisces/src/com/sun/openpisces/TransformingPathConsumer2D.java test-stub-toolkit/build-closed.xml
diffstat 395 files changed, 82686 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/build-defs.xml	Mon Dec 17 15:28:28 2012 -0800
+++ b/build-defs.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -357,7 +357,7 @@
                     <pathelement path="${javac.classpath}"/>
                     <pathelement path="${dist.dir}/@{name}.jar"/>
                     <pathelement location="${jfx.root.dir}/rt-closed/build/deps/javafx-beans.jar"/>
-                    <pathelement location="${jfx.root.dir}/rt-closed/build/deps/javafx-annotation-processor.jar"/>
+                    <pathelement location="${jfx.root.dir}/rt/build/deps/javafx-annotation-processor.jar"/>
                 </classpath>
                 <arg value="delombok"/>
                 <arg value="-d"/>
--- a/build.xml	Mon Dec 17 15:28:28 2012 -0800
+++ b/build.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -60,7 +60,15 @@
         <ant antfile="${rt.root.dir}/javafx-beans-dt/build.xml" target="jar" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-util-converter/build.xml" target="jar" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-concurrent/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-logging/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-anim/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-common/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-annotation-processor/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-common/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-geom/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-prism/build.xml" target="jar" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-common/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/pisces/build.xml" target="jar" inheritAll="false"/>
         <!--<ant antfile="${rt.root.dir}/javafx-designtime/build.xml" target="jar" inheritAll="false"/>-->
         <ant antfile="${rt.root.dir}/javafx-ui-controls/build.xml" target="jar" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="jar" inheritAll="false"/>
@@ -77,7 +85,15 @@
         <!--<ant antfile="${rt.root.dir}/javafx-designtime/build.xml" target="test" inheritAll="false"/>-->
         <ant antfile="${rt.root.dir}/javafx-ui-controls/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-logging/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-anim/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-common/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-annotation-processor/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-common/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-geom/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-prism/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-common/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/pisces/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-util-converter/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-fxml/build.xml" target="test" inheritAll="false"/>
     </target>
@@ -94,7 +110,15 @@
         <!--<ant antfile="${rt.root.dir}/javafx-designtime/build.xml" target="clean" inheritAll="false"/>-->
         <ant antfile="${rt.root.dir}/javafx-ui-controls/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-logging/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-anim/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-common/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-annotation-processor/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-common/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-geom/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-sg-prism/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-common/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/pisces/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-util-converter/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-fxml/build.xml" target="clean" inheritAll="false"/>
     </target>
--- a/common.properties	Mon Dec 17 15:28:28 2012 -0800
+++ b/common.properties	Tue Dec 18 09:15:15 2012 +0000
@@ -17,7 +17,7 @@
 javac.compilerargs=
 javac.processorpath=\
     ${runtime.dist.root.dir}/javafx-beans/dist/javafx-beans.jar:\
-    ${runtime.dist.root.dir}/javafx-annotation-processor/dist/javafx-annotation-processor.jar:\
+    ${rt.dist.root.dir}/javafx-annotation-processor/dist/javafx-annotation-processor.jar:\
     ${javac.classpath}
 javac.deprecation=false
 javac.source=1.6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/build-closed.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-anim" default="jar" basedir=".">
+    <description>Builds, tests, and jars the closed version of the javafx-anim project.</description>
+    
+    <property name="runtime.dist.root.dir" value="../../rt-closed" />    
+    <property name="rt.dist.root.dir" value="../../rt" />
+    <path id="javac.closed.classpath.path" path="
+        ${runtime.dist.root.dir}/javafx-beans/dist/javafx-beans.jar:
+        ${rt.dist.root.dir}/javafx-common/dist/javafx-common.jar" />
+    <property name="javac.classpath" refid="javac.closed.classpath.path"/>
+    
+    <import file="../build-defs.xml"/>
+    <import file="build-common.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/build-common.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-anim-common" default="jar" basedir=".">
+
+  <target name="debug-test-file" depends="jar">
+      <debug-selected-file-in-test/>
+  </target>
+    
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/build.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-anim" default="jar" basedir=".">
+    <description>Builds, tests, and runs the OpenJFX javafx-anim project.</description>
+
+    <import file="../build-defs.xml"/>
+    <import file="build-common.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/javafx-anim.iml	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/classes" />
+    <output-test url="file://$MODULE_DIR$/build/test/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test/functional" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/test/unit" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+      <excludeFolder url="file://$MODULE_DIR$/dist" />
+      <excludeFolder url="file://$MODULE_DIR$/nbproject" />
+      <excludeFolder url="file://$MODULE_DIR$/test/functional" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="javafx-beans" />
+    <orderEntry type="module" module-name="javafx-collections" />
+    <orderEntry type="module" module-name="javafx-common" />
+    <orderEntry type="library" scope="TEST" name="junit4" level="project" />
+  </component>
+  <component name="org.twodividedbyzero.idea.findbugs">
+    <option name="_basePreferences">
+      <map>
+        <entry key="property.analysisEffortLevel" value="default" />
+        <entry key="property.analyzeAfterCompile" value="false" />
+        <entry key="property.exportAsHtml" value="true" />
+        <entry key="property.exportAsXml" value="true" />
+        <entry key="property.exportBaseDir" value="" />
+        <entry key="property.exportCreateArchiveDir" value="false" />
+        <entry key="property.exportOpenBrowser" value="true" />
+        <entry key="property.minPriorityToReport" value="Medium" />
+        <entry key="property.runAnalysisInBackground" value="false" />
+        <entry key="property.showHiddenDetectors" value="false" />
+        <entry key="property.toolWindowToFront" value="true" />
+      </map>
+    </option>
+    <option name="_detectors">
+      <map>
+        <entry key="AppendingToAnObjectOutputStream" value="true" />
+        <entry key="BCPMethodReturnCheck" value="false" />
+        <entry key="BadAppletConstructor" value="false" />
+        <entry key="BadResultSetAccess" value="true" />
+        <entry key="BadSyntaxForRegularExpression" value="true" />
+        <entry key="BadUseOfReturnValue" value="true" />
+        <entry key="BadlyOverriddenAdapter" value="true" />
+        <entry key="BooleanReturnNull" value="true" />
+        <entry key="BuildInterproceduralCallGraph" value="false" />
+        <entry key="BuildObligationPolicyDatabase" value="true" />
+        <entry key="CallToUnsupportedMethod" value="false" />
+        <entry key="CalledMethods" value="true" />
+        <entry key="CheckCalls" value="false" />
+        <entry key="CheckExpectedWarnings" value="false" />
+        <entry key="CheckImmutableAnnotation" value="true" />
+        <entry key="CheckTypeQualifiers" value="true" />
+        <entry key="CloneIdiom" value="true" />
+        <entry key="ComparatorIdiom" value="true" />
+        <entry key="ConfusedInheritance" value="true" />
+        <entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" />
+        <entry key="CrossSiteScripting" value="true" />
+        <entry key="DoInsideDoPrivileged" value="true" />
+        <entry key="DontCatchIllegalMonitorStateException" value="true" />
+        <entry key="DontIgnoreResultOfPutIfAbsent" value="true" />
+        <entry key="DontUseEnum" value="true" />
+        <entry key="DroppedException" value="true" />
+        <entry key="DumbMethodInvocations" value="true" />
+        <entry key="DumbMethods" value="true" />
+        <entry key="DuplicateBranches" value="true" />
+        <entry key="EmptyZipFileEntry" value="true" />
+        <entry key="EqStringTest" value="false" />
+        <entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" />
+        <entry key="FieldItemSummary" value="true" />
+        <entry key="FinalizerNullsFields" value="true" />
+        <entry key="FindBadCast" value="false" />
+        <entry key="FindBadCast2" value="true" />
+        <entry key="FindBadEqualsImplementation" value="false" />
+        <entry key="FindBadForLoop" value="true" />
+        <entry key="FindBugsSummaryStats" value="true" />
+        <entry key="FindCircularDependencies" value="false" />
+        <entry key="FindDeadLocalStores" value="true" />
+        <entry key="FindDoubleCheck" value="true" />
+        <entry key="FindEmptySynchronizedBlock" value="true" />
+        <entry key="FindFieldSelfAssignment" value="true" />
+        <entry key="FindFinalizeInvocations" value="true" />
+        <entry key="FindFloatEquality" value="true" />
+        <entry key="FindFloatMath" value="false" />
+        <entry key="FindHEmismatch" value="true" />
+        <entry key="FindInconsistentSync2" value="true" />
+        <entry key="FindJSR166LockMonitorenter" value="true" />
+        <entry key="FindLocalSelfAssignment2" value="true" />
+        <entry key="FindMaskedFields" value="true" />
+        <entry key="FindMismatchedWaitOrNotify" value="true" />
+        <entry key="FindNakedNotify" value="true" />
+        <entry key="FindNonSerializableStoreIntoSession" value="true" />
+        <entry key="FindNonSerializableValuePassedToWriteObject" value="true" />
+        <entry key="FindNonShortCircuit" value="true" />
+        <entry key="FindNullDeref" value="true" />
+        <entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" />
+        <entry key="FindOpenStream" value="true" />
+        <entry key="FindPuzzlers" value="true" />
+        <entry key="FindRefComparison" value="true" />
+        <entry key="FindReturnRef" value="true" />
+        <entry key="FindRunInvocations" value="true" />
+        <entry key="FindSelfComparison" value="true" />
+        <entry key="FindSelfComparison2" value="true" />
+        <entry key="FindSleepWithLockHeld" value="true" />
+        <entry key="FindSpinLoop" value="true" />
+        <entry key="FindSqlInjection" value="true" />
+        <entry key="FindTwoLockWait" value="true" />
+        <entry key="FindUncalledPrivateMethods" value="true" />
+        <entry key="FindUnconditionalWait" value="true" />
+        <entry key="FindUninitializedGet" value="true" />
+        <entry key="FindUnrelatedTypesInGenericContainer" value="true" />
+        <entry key="FindUnreleasedLock" value="true" />
+        <entry key="FindUnsatisfiedObligation" value="true" />
+        <entry key="FindUnsyncGet" value="true" />
+        <entry key="FindUselessControlFlow" value="true" />
+        <entry key="FormatStringChecker" value="true" />
+        <entry key="HugeSharedStringConstants" value="true" />
+        <entry key="IDivResultCastToDouble" value="true" />
+        <entry key="IncompatMask" value="true" />
+        <entry key="InconsistentAnnotations" value="true" />
+        <entry key="InefficientMemberAccess" value="false" />
+        <entry key="InefficientToArray" value="true" />
+        <entry key="InfiniteLoop" value="true" />
+        <entry key="InfiniteRecursiveLoop" value="true" />
+        <entry key="InfiniteRecursiveLoop2" value="false" />
+        <entry key="InheritanceUnsafeGetResource" value="true" />
+        <entry key="InitializationChain" value="true" />
+        <entry key="InstantiateStaticClass" value="true" />
+        <entry key="InvalidJUnitTest" value="true" />
+        <entry key="IteratorIdioms" value="true" />
+        <entry key="LazyInit" value="true" />
+        <entry key="LoadOfKnownNullValue" value="true" />
+        <entry key="LockedFields" value="false" />
+        <entry key="LostLoggerDueToWeakReference" value="true" />
+        <entry key="MethodReturnCheck" value="true" />
+        <entry key="Methods" value="true" />
+        <entry key="MultithreadedInstanceAccess" value="true" />
+        <entry key="MutableLock" value="true" />
+        <entry key="MutableStaticFields" value="true" />
+        <entry key="Naming" value="true" />
+        <entry key="Noise" value="false" />
+        <entry key="NoiseNullDeref" value="false" />
+        <entry key="NoteAnnotationRetention" value="true" />
+        <entry key="NoteCheckReturnValue" value="true" />
+        <entry key="NoteCheckReturnValueAnnotations" value="true" />
+        <entry key="NoteDirectlyRelevantTypeQualifiers" value="true" />
+        <entry key="NoteJCIPAnnotation" value="true" />
+        <entry key="NoteNonNullAnnotations" value="true" />
+        <entry key="NoteNonnullReturnValues" value="true" />
+        <entry key="NoteSuppressedWarnings" value="true" />
+        <entry key="NoteUnconditionalParamDerefs" value="true" />
+        <entry key="NumberConstructor" value="true" />
+        <entry key="OverridingEqualsNotSymmetrical" value="true" />
+        <entry key="PreferZeroLengthArrays" value="true" />
+        <entry key="PublicSemaphores" value="false" />
+        <entry key="QuestionableBooleanAssignment" value="true" />
+        <entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" />
+        <entry key="ReadReturnShouldBeChecked" value="true" />
+        <entry key="RedundantInterfaces" value="true" />
+        <entry key="ReflectiveClasses" value="true" />
+        <entry key="RepeatedConditionals" value="true" />
+        <entry key="ResolveAllReferences" value="false" />
+        <entry key="RuntimeExceptionCapture" value="true" />
+        <entry key="SerializableIdiom" value="true" />
+        <entry key="StartInConstructor" value="true" />
+        <entry key="StaticCalendarDetector" value="true" />
+        <entry key="StringConcatenation" value="true" />
+        <entry key="SuperfluousInstanceOf" value="true" />
+        <entry key="SuspiciousThreadInterrupted" value="true" />
+        <entry key="SwitchFallthrough" value="true" />
+        <entry key="SynchronizationOnSharedBuiltinConstant" value="true" />
+        <entry key="SynchronizeAndNullCheckField" value="true" />
+        <entry key="SynchronizeOnClassLiteralNotGetClass" value="true" />
+        <entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" />
+        <entry key="TestASM" value="false" />
+        <entry key="TestDataflowAnalysis" value="false" />
+        <entry key="TestingGround" value="false" />
+        <entry key="TrainFieldStoreTypes" value="true" />
+        <entry key="TrainNonNullAnnotations" value="true" />
+        <entry key="TrainUnconditionalDerefParams" value="true" />
+        <entry key="URLProblems" value="true" />
+        <entry key="UncallableMethodOfAnonymousClass" value="true" />
+        <entry key="UnnecessaryMath" value="true" />
+        <entry key="UnreadFields" value="true" />
+        <entry key="UseObjectEquals" value="false" />
+        <entry key="UselessSubclassMethod" value="false" />
+        <entry key="VarArgsProblems" value="true" />
+        <entry key="VolatileUsage" value="true" />
+        <entry key="WaitInLoop" value="true" />
+        <entry key="WrongMapIterator" value="true" />
+        <entry key="XMLFactoryBypass" value="true" />
+      </map>
+    </option>
+    <option name="_reportCategories">
+      <map>
+        <entry key="BAD_PRACTICE" value="true" />
+        <entry key="CORRECTNESS" value="true" />
+        <entry key="EXPERIMENTAL" value="true" />
+        <entry key="I18N" value="true" />
+        <entry key="MALICIOUS_CODE" value="true" />
+        <entry key="MT_CORRECTNESS" value="true" />
+        <entry key="NOISE" value="false" />
+        <entry key="PERFORMANCE" value="true" />
+        <entry key="SECURITY" value="true" />
+        <entry key="STYLE" value="true" />
+      </map>
+    </option>
+  </component>
+</module>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/nbproject/project.xml	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>javafx-anim</name>
+            <properties>
+                <property-file>../base.properties</property-file>
+                <property-file>project.properties</property-file>
+                <property-file>../common.properties</property-file>
+            </properties>
+            <folders>
+                <source-folder>
+                    <label>Source Packages</label>
+                    <type>java</type>
+                    <location>${src.dir}</location>
+                </source-folder>
+                <source-folder>
+                    <label>Test Packages</label>
+                    <type>java</type>
+                    <location>${test.dir}</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>jar</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="test">
+                    <target>test</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>jar</target>
+                </action>
+                <action name="run.single">
+                    <target>test-single</target>
+                    <context>
+                        <property>run.file</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="test.single">
+                    <target>test-single</target>
+                    <context>
+                        <property>run.file</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <target>debug-test-file</target>
+                    <context>
+                        <property>debug.class</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+            </ide-actions>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>Source Packages</label>
+                        <location>${src.dir}</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>Test Packages</label>
+                        <location>${test.dir}</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                    <source-file>
+                        <location>project.properties</location>
+                    </source-file>
+                    <source-file>
+                        <location>nbproject/project.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="test"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
+            <compilation-unit>
+                <package-root>${src.dir}</package-root>
+                <classpath mode="compile">${javac.classpath}</classpath>
+                <built-to>dist/javafx-anim.jar</built-to>
+                <source-level>1.6</source-level>
+                <annotation-processing>
+                    <scan-trigger/>
+                    <editor-trigger/>
+                    <processor-path>${javac.processorpath}</processor-path>
+                </annotation-processing>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>${test.dir}</package-root>
+                <unit-tests/>
+                <classpath mode="compile">${javac.test.classpath}</classpath>
+                <built-to>${build.test.classes.dir}</built-to>
+                <source-level>1.6</source-level>
+                <annotation-processing>
+                    <scan-trigger/>
+                    <editor-trigger/>
+                    <processor-path>${javac.processorpath}</processor-path>
+                </annotation-processing>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/project.properties	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,8 @@
+JFXRT_HOME=\
+    ${runtime.dist.root.dir}/../artifacts/sdk/rt/lib
+javac.classpath=\
+    ${JFXRT_HOME}/jfxrt.jar
+build.dir=${basedir}/build
+build.classes.dir=${build.dir}/classes
+test.dir=test/unit
+ftest.dir=test/functional
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/javafx/animation/TickCalculation.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.animation;
+
+import javafx.util.Duration;
+
+public class TickCalculation {
+    public static final int TICKS_PER_SECOND = 6000;
+    private static final double TICKS_PER_MILI = TICKS_PER_SECOND / 1000.0;
+    private static final double TICKS_PER_NANO =  TICKS_PER_MILI * 1e-6;
+    
+    private TickCalculation() {}
+    
+    public static long add(long op1, long op2) {
+        assert (op1 >= 0);
+
+        if (op1 == Long.MAX_VALUE || op2 == Long.MAX_VALUE) {
+            return Long.MAX_VALUE;
+        } else if (op2 == Long.MIN_VALUE) {
+            return 0;
+        }
+
+        if (op2 >= 0) {
+            final long result = op1 + op2;
+            return (result < 0)? Long.MAX_VALUE : result;
+        } else {
+            return Math.max(0, op1 + op2);
+        }
+
+    }
+
+    public static long sub(long op1, long op2) {
+        assert (op1 >= 0);
+
+        if (op1 == Long.MAX_VALUE || op2 == Long.MIN_VALUE) {
+            return Long.MAX_VALUE;
+        } else if (op2 == Long.MAX_VALUE) {
+            return 0;
+        }
+
+        if (op2 >= 0) {
+            return Math.max(0, op1 - op2);
+        } else {
+            final long result = op1 - op2;
+            return result < 0 ? Long.MAX_VALUE : result;
+        }
+
+    }
+    
+    public static long fromMillis(double millis) {
+        return Math.round(TICKS_PER_MILI * millis);
+    }
+    
+    public static long fromNano(long nano) {
+        return Math.round(TICKS_PER_NANO * nano);
+    }
+    
+    public static long fromDuration(Duration duration) {
+        return fromMillis(duration.toMillis());
+    }
+
+    public static long fromDuration(Duration duration, double rate) {
+        return Math.round(TICKS_PER_MILI * duration.toMillis() / Math.abs(rate));
+    }
+    
+    public static Duration toDuration(long ticks) {
+        return Duration.millis(toMillis(ticks));
+    }
+
+    public static Duration toDuration(long ticks, double rate) {
+        return Duration.millis(ticks * Math.abs(rate) / TICKS_PER_MILI);
+    }
+    
+    public static double toMillis(long ticks) {
+        return ticks / TICKS_PER_MILI;
+    }
+    
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/javafx/animation/TimingTarget.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.animation;
+
+/**
+ * This interface provides the methods which are called by Clip during the
+ * course of a timing observableArrayList. Applications that wish to receive
+ * timing events will either create a subclass of TimingTargetAdapter and
+ * override or they can create or use an implementation of TimingTarget. A
+ * TimingTarget can be passed into the constructor of Clip or set later with the
+ * {@link TimelineClip#addTarget(TimingTarget)} method. Any Clip may have
+ * multiple TimingTargets.
+ */
+public interface TimingTarget {
+    /**
+     * This method will receive all of the timing events from a Clip during an
+     * animation.
+     * 
+     * @param totalElapsed
+     *            the amount of time that has elapsed relative to the start time
+     *            of a Clip, in milliseconds.
+     */
+    public void timingEvent(long totalElapsed);
+
+    /**
+     * Called when the Clip's animation begins. This provides a chance for
+     * targets to perform any setup required at animation start time.
+     */
+    public void begin();
+
+    /**
+     * Called when the Clip's animation ends.
+     */
+    public void end();
+
+    /**
+     * Called when the direction of a clip changes.
+     */
+    public void toggle();
+
+    /**
+     * Called when the Clip's animation is paused.
+     */
+    public void pause();
+
+    /**
+     * Called when the Clip's animation resumes from a paused state.
+     */
+    public void resume();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/DelayedRunnable.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario;
+
+public interface DelayedRunnable extends Runnable {
+    /**
+     * @return delay in millis
+     */
+    public long getDelay();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/Settings.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import javafx.util.Callback;
+
+/**
+ * A simple class to store and retrieve internal Scenario settings in the form
+ * of String key/value pairs. It is meant to be used in a similar way to System
+ * Properties, but without the security restrictions. This class is designed
+ * primarily to aid in testing and benchmarking Scenario itself.
+ * 
+ * If you are running in an environment that allows System Property access, this
+ * class will attempt to look for a key's value in the System Properties if none
+ * is found in Settings. This allows Settings to be set on the command line as
+ * well as via the Settings API.
+ * 
+ */
+public class Settings {
+
+    private final Map<String, String> settings = new HashMap<String, String>(5);
+    private final CopyOnWriteArrayList<Callback<String, Void>> listeners = new CopyOnWriteArrayList<Callback<String, Void>>();
+    private static final Object SETTINGS_KEY;
+    static {
+        SETTINGS_KEY = new StringBuilder("SettingsKey");
+
+        // It seems no longer necessary to force loading of MasterTimer to pick
+        // up the hi-res timer workaround. Also, this is causing some init
+        // order problems (RT-5572), so it's being commented out.
+        // Object obj = ToolkitAccessor.getMasterTimer();
+    }
+
+    private static synchronized Settings getInstance() {
+        Map<Object, Object> contextMap = ToolkitAccessor.getContextMap();
+        Settings instance = (Settings) contextMap.get(SETTINGS_KEY);
+        if (instance == null) {
+            instance = new Settings();
+            contextMap.put(SETTINGS_KEY, instance);
+        }
+        return instance;
+    }
+
+    /**
+     * Add a new key-value setting.
+     * 
+     * Passing a value of null indicates that the value for this key should be
+     * looked for in the System Properties.
+     * 
+     * If PropertyChangeListeners have been registered for the given key, they
+     * will be notified of a change in value.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException.
+     */
+    public static void set(String key, String value) {
+        getInstance().setImpl(key, value);
+    }
+
+    private void setImpl(String key, String value) {
+        checkKeyArg(key);
+        settings.put(key, value);
+        for (Callback<String, Void> l : listeners) {
+            l.call(key);
+        }
+     }
+
+    /**
+     * Retrieve the value for the given key.
+     * 
+     * If the key is not present in Settings or its value is null, this methods
+     * then checks to see if a value for this key is present in the System
+     * Properties (provided you have sufficient privileges).
+     * 
+     * If no value can be found for the given key, this method returns null.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException.
+     */
+    public static String get(String key) {
+        return getInstance().getImpl(key);
+    }
+
+    private String getImpl(String key) {
+        checkKeyArg(key);
+        String retVal = settings.get(key);
+        if (retVal == null) {
+            try {
+                retVal = System.getProperty(key);
+            } catch (SecurityException ignore) {
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Convenience method for boolean settings.
+     * 
+     * If the setting exists and its value is "true", true is returned.
+     * Otherwise, false is returned.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException.
+     */
+    public static boolean getBoolean(String key) {
+        return getInstance().getBooleanImpl(key);
+    }
+
+    private boolean getBooleanImpl(String key) {
+        // get() will call checkKeyArg(), so don't check it here
+        String value = getImpl(key);
+        return "true".equals(value);
+    }
+
+    /**
+     * Convenience method for boolean settings.
+     * 
+     * If the setting is set to "true", true is returned. If the setting is set
+     * to "false", false is returned. It the setting is set to anything else,
+     * defaultVal is returned.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException.
+     */
+    public static boolean getBoolean(String key, boolean defaultVal) {
+        return getInstance().getBooleanImpl(key, defaultVal);
+    }
+
+    private boolean getBooleanImpl(String key, boolean defaultVal) {
+        // get() will call checkKeyArg(), so don't check it here
+        String value = getImpl(key);
+        boolean retVal = defaultVal;
+        if (value != null) {
+            if ("false".equals(value)) {
+                retVal = false;
+            } else if ("true".equals(value)) {
+                retVal = true;
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Convenience method for int settings.
+     * 
+     * If the setting exists and its value can be parsed to an int, the int
+     * value is returned. Otherwise, the default value is returned.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException.
+     */
+    public static int getInt(String key, int defaultVal) {
+        return getInstance().getIntImpl(key, defaultVal);
+    }
+
+    private int getIntImpl(String key, int defaultVal) {
+        // get() will call checkKeyArg(), so don't check it here
+        String value = getImpl(key);
+        int retVal = defaultVal;
+        try {
+            retVal = Integer.parseInt(value);
+        } catch (NumberFormatException ignore) {
+            // ignore.printStackTrace();
+        }
+        return retVal;
+    }
+
+    /**
+     * Add a PropertyChangeListener for the specified setting
+     * 
+     * Note that the PropertyChangeEvent will contain old and new values as they
+     * would be returned from get(), meaning they may come from the System
+     * Properties.
+     * 
+     * If key is "" or null, this methods throws an IllegalArgumentException. If
+     * listener is null no exception is thrown and no action is taken.
+     */
+    public static void addPropertyChangeListener(Callback<String, Void> pcl) {
+        getInstance().addPropertyChangeListenerImpl(pcl);
+    }
+
+    private void addPropertyChangeListenerImpl(Callback<String, Void> pcl) {
+        listeners.add(pcl);
+    }
+
+    /**
+     * Remove the specified PropertyChangeListener.
+     * 
+     * If listener is null, or was never added, no exception is thrown and no
+     * action is taken.
+     */
+    public static void removePropertyChangeListener(Callback<String, Void> pcl) {
+        getInstance().removePropertyChangeListenerImpl(pcl);
+    }
+
+    private void removePropertyChangeListenerImpl(Callback<String, Void> pcl) {
+        listeners.remove(pcl);
+    }
+
+    /*
+     * Check that key is a valid Settings key. If not, throw an
+     * IllegalArgumentException.
+     */
+    private void checkKeyArg(String key) {
+        if (null == key || "".equals(key)) {
+            throw new IllegalArgumentException("null key not allowed");
+        }
+    }
+
+    private Settings() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/StandaloneAccessor.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.sun.scenario.animation.AbstractMasterTimer;
+
+/**
+ * This class is used in case Toolkit is not present. Usual use case is animaton
+ * testing for openjfx-compiler.
+ */
+public class StandaloneAccessor extends ToolkitAccessor {
+    private final Map<Object, Object> map = new HashMap<Object, Object>();
+    private final ScheduledExecutorService executor = Executors
+            .newSingleThreadScheduledExecutor(new ThreadFactory() {
+                private final ThreadFactory factory = Executors
+                        .defaultThreadFactory();
+
+                public Thread newThread(Runnable r) {
+                    Thread thread = factory.newThread(r);
+                    thread.setDaemon(true);
+                    return thread;
+                }
+            });
+
+    private AtomicReference<Future<?>> refFuture = new AtomicReference<Future<?>>();
+    private StandaloneMasterTimer standaloneMasterTimer = new StandaloneMasterTimer();
+
+    @Override
+    public Map<Object, Object> getContextMapImpl() {
+        return map;
+    }
+
+    @Override
+    public AbstractMasterTimer getMasterTimerImpl() {
+        return standaloneMasterTimer;
+    }
+
+    private class StandaloneMasterTimer extends AbstractMasterTimer {
+        @Override
+        protected boolean shouldUseNanoTime() {
+            return true;
+        }
+
+        @Override
+        protected void postUpdateAnimationRunnable(
+                final DelayedRunnable animationRunnable) {
+            if (animationRunnable == null) {
+                Future<?> future = refFuture.get();
+                if (future != null) {
+                    future.cancel(false);
+                    refFuture.set(null);
+                }
+                return;
+            }
+            if (refFuture.get() != null) {
+                return;
+            }
+            Future<?> future = executor.schedule(new Runnable() {
+                public void run() {
+                    refFuture.set(null);
+                    animationRunnable.run();
+                }
+            }, animationRunnable.getDelay(), TimeUnit.MILLISECONDS);
+            refFuture.set(future);
+        }
+
+        @Override
+        protected int getPulseDuration(int precision) {
+            int retVal = precision / 60;
+            return retVal;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/ToolkitAccessor.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario;
+
+import java.util.Map;
+
+import com.sun.scenario.animation.AbstractMasterTimer;
+
+/**
+ * A simple java accessor to the current Toolkit
+ * 
+ */
+public abstract class ToolkitAccessor {
+    private static ToolkitAccessor instance;
+
+    public static void setInstance(ToolkitAccessor accessor) {
+        instance = accessor;
+    }
+
+    private static ToolkitAccessor getInstance() {
+        if (instance == null) {
+            // running in stand alone case without toolkit
+            try {
+                Class<?> cl = Class
+                        .forName("com.sun.scenario.StandaloneAccessor");
+                setInstance((ToolkitAccessor) cl.newInstance());
+            } catch (Throwable th) {
+                // ignore
+            }
+        }
+        return instance;
+    }
+
+    public static Map<Object, Object> getContextMap() {
+        return getInstance().getContextMapImpl();
+    }
+
+    public static AbstractMasterTimer getMasterTimer() {
+        return getInstance().getMasterTimerImpl();
+    }
+
+    public abstract Map<Object, Object> getContextMapImpl();
+
+    public abstract AbstractMasterTimer getMasterTimerImpl();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/AbstractMasterTimer.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+import com.sun.javafx.animation.TickCalculation;
+import javafx.animation.AnimationTimer;
+
+import com.sun.scenario.DelayedRunnable;
+import com.sun.scenario.Settings;
+import com.sun.scenario.animation.shared.CurrentTime;
+import com.sun.scenario.animation.shared.PulseReceiver;
+import java.util.Arrays;
+import javafx.util.Callback;
+
+public abstract class AbstractMasterTimer {
+
+    // This PropertyChangeListener is added to Settings to listen for changes
+    // to the nogap and fullspeed properties.
+    private static Callback<String, Void> pcl = new Callback<String, Void>() {
+        public Void call(String key) {
+            if (key.equals(FULLSPEED_PROP)) {
+                fullspeed = Settings.getBoolean(FULLSPEED_PROP);
+            } else if (key.equals(ADAPTIVE_PULSE_PROP)) {
+                useAdaptivePulse = Settings.getBoolean(ADAPTIVE_PULSE_PROP);
+            } else if (key.equals(ANIMATION_MBEAN_ENABLED)) {
+                AnimationPulse.getDefaultBean()
+                  .setEnabled(Settings.getBoolean(ANIMATION_MBEAN_ENABLED));
+            }
+            return null;
+        }
+    };
+    
+    protected final static String FULLSPEED_PROP = "javafx.animation.fullspeed";
+    private static boolean fullspeed = Settings.getBoolean(FULLSPEED_PROP);
+
+    // enables the code path which estimates the next pulse time to be just
+    // enough in advance of the vsync to complete rendering before it happens
+    protected final static String ADAPTIVE_PULSE_PROP = "com.sun.scenario.animation.adaptivepulse";
+    private static boolean useAdaptivePulse = Settings.getBoolean(ADAPTIVE_PULSE_PROP);
+
+    // another property which is controlling whether vsync is enabled:
+    // "com.sun.scenario.animation.vsync". if true, JSGPanel will enable vsync
+    // for the toplevel it's in. See JSGPanel.
+
+    // properties to override the default pulse rate (set in hz - number of
+    // pulses per second)
+    protected final static String PULSE_PROP = "javafx.animation.pulse";
+    protected final static String FRAMERATE_PROP = "javafx.animation.framerate";
+    protected final static String FIXED_PULSE_LENGHT_PROP = "com.sun.scenario.animation.fixed.pulse.length";
+
+    // property to enable AnimationPulse data gathering
+    // note: it can be enabled via the MBean itself too
+    protected final static String ANIMATION_MBEAN_ENABLED = "com.sun.scenario.animation.AnimationMBean.enabled";
+    protected final static boolean enableAnimationMBean = false;
+
+    protected final int PULSE_DURATION = getPulseDuration(1000);
+    protected final int PULSE_DURATION_NS = getPulseDuration(1000000000);
+    protected final int PULSE_DURATION_TICKS = getPulseDuration((int)TickCalculation.fromMillis(1000));
+
+    private boolean paused = false;
+    private long totalPausedTime;
+    private long startPauseTime;
+
+    private PulseReceiver receivers[] = new PulseReceiver[2];
+    private int receiversLength;
+    private boolean receiversLocked;
+
+    // synchronize to update frameJobList and frameJobs
+    private AnimationTimer animationTimers[] = new AnimationTimer[2]; // frameJobList
+                                                                     // snapshot
+    private int animationTimersLength;
+    private boolean animationTimersLocked;
+
+    private Runnable activeAnimationHandler;
+    
+    // There is a memory leak possibility by having this be non-static, as
+    // Clip has a ref to this and this (may) have a ref to the enclosing class.
+    // But having it static has initialization issues (need to call
+    // shouldUseNanoTime() in the subclass during static init of the
+    // superclass).
+    // Moving to a true singleton model might help...
+    private final/* static */CurrentTime currentTime = createCurrentTime();
+
+    private final MainLoop theMaster = new MainLoop();
+
+
+    static {
+        Settings.addPropertyChangeListener(pcl);
+        int pulse = Settings.getInt(PULSE_PROP, -1);
+        if (pulse != -1) {
+            System.err.println("Setting PULSE_DURATION to " + pulse + " hz");
+        }
+    }
+
+    // Used by Clip.create() method that doesn't take a resolution argument
+    public int getDefaultResolution() {
+        return PULSE_DURATION_TICKS;
+    }
+    
+    protected CurrentTime createCurrentTime() {
+        if (Boolean.getBoolean(FIXED_PULSE_LENGHT_PROP)) { // Fixed time pulse is for debugging purposes only
+            FixedPulseTime time = new FixedPulseTime(PULSE_DURATION_NS);
+            return time;
+        }
+        return shouldUseNanoTime()? new NanoCurrentTime() : new MilliCurrentTime();
+    }
+    
+    public void pause() {
+        if (!paused) {
+            startPauseTime = nanos();
+            paused = true;
+        }
+    }
+    
+    public void resume() {
+        if (paused) {
+            paused = false;
+            totalPausedTime += nanos() - startPauseTime;
+        }
+    }
+    
+    public long nanos() {
+        return paused? startPauseTime : currentTime.nanos() - totalPausedTime;
+    }
+
+    public boolean isFullspeed() {
+        return fullspeed;
+    }
+
+    protected abstract boolean shouldUseNanoTime();
+    
+    /** Prevent external instantiation of MasterTimer */
+    protected AbstractMasterTimer() {
+        if (Boolean.getBoolean(FIXED_PULSE_LENGHT_PROP)) { // Fixed time pulse is for debugging purposes only
+            addPulseReceiver((FixedPulseTime)currentTime);
+        }
+    }
+    
+    // new methods to interact with Toolkit, will replace current interaction later
+    // for now they call the respecive methods of the old implementation
+    
+    // triggers an animation cycle, called by the platform
+    public void animationTrigger() {
+        theMaster.run();
+    }
+    
+    // return the delay to when the next pulse should be triggered
+    public long getNextPulseTime() {
+        return (theMaster.getDelay());
+    }
+    
+    public boolean isActive() {
+        return !theMaster.inactive;
+    }
+    
+    public void setActiveAnimationHandler(Runnable activeAnimationHandler) {
+        this.activeAnimationHandler = activeAnimationHandler;
+        
+    }
+
+    /**
+     * Adds a PulseReceiver to the list of targets being tracked against the
+     * global schedule. The target should already have an absolute start time
+     * recorded in it and that time will be used to start the clip at the
+     * appropriate wall clock time as defined by milliTime().
+     * 
+     * Note that pulseReceiver cannot be removed from the MasterTimer directly.
+     * It is removed automatically in the timePulse-iteration if timePulse
+     * returns true.
+     * 
+     * @param target
+     *            the Clip to be added to the scheduling queue
+     */
+    public void addPulseReceiver(PulseReceiver target) {
+        boolean needMoreSize = receiversLength == receivers.length;
+        if (receiversLocked || needMoreSize) {
+            receivers = Arrays.copyOf(receivers, needMoreSize ? receivers.length * 3 / 2 + 1 : receivers.length);
+            receiversLocked = false;
+        }
+        receivers[receiversLength++] = target;
+        if (receiversLength == 1) {
+            theMaster.updateAnimationRunnable();
+        }
+    }
+
+    public void removePulseReceiver(PulseReceiver target) {
+        if (receiversLocked) { 
+            receivers = receivers.clone();
+            receiversLocked = false;
+        }
+        for (int i = 0; i < receiversLength; ++i) {
+            if (target == receivers[i]) {
+                if (i == receiversLength - 1) {
+                    receivers[i] = null;
+                } else {
+                    System.arraycopy(receivers, i + 1, receivers, i, receiversLength - i - 1);
+                }
+                --receiversLength;
+                break;
+            }
+        }
+        if (receiversLength == 0) {
+            theMaster.updateAnimationRunnable();
+        }
+    }
+
+    public void addAnimationTimer(AnimationTimer timer) {
+        boolean needMoreSize = animationTimersLength == animationTimers.length;
+        if (animationTimersLocked || needMoreSize) {
+            animationTimers = Arrays.copyOf(animationTimers, needMoreSize ? animationTimers.length * 3 / 2 + 1 : animationTimers.length);
+            animationTimersLocked = false;
+        }
+        animationTimers[animationTimersLength++] = timer;
+        if (animationTimersLength == 1) {
+            theMaster.updateAnimationRunnable();
+        }
+    }
+
+    public void removeAnimationTimer(AnimationTimer timer) {
+        if (animationTimersLocked) { 
+            animationTimers = animationTimers.clone();
+            animationTimersLocked = false;
+        }
+        for (int i = 0; i < animationTimersLength; ++i) {
+            if (timer == animationTimers[i]) {
+                if (i == animationTimersLength - 1) {
+                    animationTimers[i] = null;
+                } else {
+                    System.arraycopy(animationTimers, i + 1, animationTimers, i, animationTimersLength - i - 1);
+                }
+                --animationTimersLength;
+                break;
+            }
+        }
+        if (animationTimersLength == 0) {
+            theMaster.updateAnimationRunnable();
+        }
+    }
+
+    @Deprecated
+    public void notifyJobsReady() {
+        postUpdateAnimationRunnable(theMaster);
+    }
+
+    /*
+     * methods to record times for different stages of a pulse overriden in
+     * MasterTimer to collect data for AnimationPulse Mbean
+     */
+    protected void recordStart(long shiftMillis) {
+    }
+
+    protected void recordEnd() {
+    }
+
+    protected void recordAnimationEnd() {
+    }
+
+    /**
+     * Hidden inner class to run the main timing loop. This is the
+     * "AnimationRunnable" for Desktop and TV
+     */
+    private final class MainLoop implements DelayedRunnable {
+
+        private boolean inactive = true;
+        
+        private long nextPulseTime = nanos();
+        private long lastPulseDuration = Integer.MIN_VALUE;
+
+        @Override
+        public void run() {
+            if (paused) {
+                return;
+            }
+            final long now = nanos();
+            recordStart((nextPulseTime - now) / 1000000);
+            timePulseImpl(now);
+            recordEnd();
+            updateNextPulseTime(now);
+            // reschedule animation runnable if needed
+            updateAnimationRunnable();
+        }
+
+        @Override
+        public long getDelay() {
+            final long now = nanos();
+            final long timeUntilPulse = (nextPulseTime - now) / 1000000;
+            return Math.max(0, timeUntilPulse);
+        }
+
+        private void updateNextPulseTime(long pulseStarted) {
+            final long now = nanos();
+            if (fullspeed) {
+                nextPulseTime = now;
+            } else {
+                if (useAdaptivePulse) {
+                    // Estimate the next pulse time such that we wake up just
+                    // early enough to finish up the painting and call swap
+                    // before vsync happens. We try to minimize the amount of
+                    // time we wait for vsync blocking the EDT thread.
+                    nextPulseTime += PULSE_DURATION_NS;
+                    long pulseDuration = now - pulseStarted;
+                    // if the new duration was smaller than the previous one
+                    // we don't need to do anything (we have decreased the
+                    // duration), but if it's longer to within 1/2ms then we
+                    // try to halve the next anticipated duration (but not
+                    // closer
+                    // than 2ms within the next expected pulse)
+                    if (pulseDuration - lastPulseDuration > 500000) {
+                        pulseDuration /= 2;
+                    }
+                    if (pulseDuration < 2000000) {
+                        pulseDuration = 2000000;
+                    }
+                    // if the pulse took longer than pulse_duration_ns we
+                    // probably missed the vsync
+                    if (pulseDuration >= PULSE_DURATION_NS) {
+                        pulseDuration = 3 * PULSE_DURATION_NS / 4;
+                    }
+                    lastPulseDuration = pulseDuration;
+                    nextPulseTime = nextPulseTime - pulseDuration;
+                } else {
+                    nextPulseTime = ((nextPulseTime + PULSE_DURATION_NS) / PULSE_DURATION_NS)
+                            * PULSE_DURATION_NS;
+                }
+            }
+        }
+
+        private void updateAnimationRunnable() {
+            final boolean newInactive = (animationTimersLength == 0 && receiversLength == 0);
+            if (inactive != newInactive) {
+                inactive = newInactive;
+                final DelayedRunnable animationRunnable = inactive? null : this;
+                if (activeAnimationHandler != null) {
+                    activeAnimationHandler.run();
+                }
+                postUpdateAnimationRunnable(animationRunnable);
+            }
+        }
+    }
+
+    protected abstract void postUpdateAnimationRunnable(
+            DelayedRunnable animationRunnable);
+
+    protected abstract int getPulseDuration(int precision);
+
+    protected void timePulseImpl(long now) {
+        final PulseReceiver receiversSnapshot[] = receivers;
+        final int rLength = receiversLength;
+        try {
+            receiversLocked = true;
+            for (int i = 0; i < rLength; i++) {
+                receiversSnapshot[i].timePulse(TickCalculation.fromNano(now));
+            }
+        } finally {
+            receiversLocked = false;
+        }
+        recordAnimationEnd();
+
+        final AnimationTimer animationTimersSnapshot[] = animationTimers;
+        final int aTLength = animationTimersLength;
+        try {
+            animationTimersLocked = true;
+            // After every frame, call any frame jobs
+            for (int i = 0; i < aTLength; i++) {
+                animationTimersSnapshot[i].handle(now);
+            }
+        } finally {
+            animationTimersLocked = false;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/AnimationPulse.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.sun.scenario.ToolkitAccessor;
+
+
+public class AnimationPulse implements AnimationPulseMBean {
+    public static AnimationPulse getDefaultBean() {
+        return AnimationPulseHolder.holder;
+    }
+    private static class AnimationPulseHolder {
+        private static final AnimationPulse holder = new AnimationPulse();
+    }
+    
+    private static class PulseData {
+        private final long startNanos;
+        private final long scheduledNanos;
+        
+        private long animationEndNanos = Long.MIN_VALUE;
+        private long paintingStartNanos = Long.MIN_VALUE;
+        private long paintingEndNanos = Long.MIN_VALUE;
+        private long scenePaintingStartNanos = Long.MIN_VALUE;
+        private long scenePaintingEndNanos = Long.MIN_VALUE;
+        private long endNanos = Long.MIN_VALUE;
+        
+        PulseData(long shiftNanos) {
+            startNanos = ToolkitAccessor.getMasterTimer().nanos();
+            scheduledNanos = startNanos + shiftNanos;
+        }
+        
+        //time from the scheduledNanos  
+        long getPulseStart(TimeUnit unit) {
+            return unit.convert(startNanos - scheduledNanos, TimeUnit.NANOSECONDS);            
+        }
+        
+        void recordAnimationEnd() {
+            animationEndNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        long getAnimationDuration(TimeUnit unit) {
+            return (animationEndNanos > Long.MIN_VALUE) 
+              ? unit.convert(animationEndNanos - startNanos, TimeUnit.NANOSECONDS)
+              : 0;
+        }
+        
+        void recordPaintingStart() {
+            paintingStartNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        void recordPaintingEnd() {
+            paintingEndNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        long getPaintingDuration(TimeUnit unit) {
+            return (paintingEndNanos > Long.MIN_VALUE && paintingStartNanos > Long.MIN_VALUE) 
+              ? unit.convert(paintingEndNanos - paintingStartNanos, TimeUnit.NANOSECONDS)
+              : 0;
+        }
+        
+        void recordScenePaintingStart() {
+            scenePaintingStartNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        void recordScenePaintingEnd() {
+            scenePaintingEndNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        long getScenePaintingDuration(TimeUnit unit) {
+            return (scenePaintingEndNanos > Long.MIN_VALUE && scenePaintingStartNanos > Long.MIN_VALUE) 
+              ? unit.convert(scenePaintingEndNanos - scenePaintingStartNanos, TimeUnit.NANOSECONDS)
+              : 0;
+        }
+        
+        long getPaintingPreparationDuration(TimeUnit unit) {
+            return (paintingStartNanos > Long.MIN_VALUE && scenePaintingStartNanos > Long.MIN_VALUE) 
+              ? unit.convert(scenePaintingStartNanos - paintingStartNanos, TimeUnit.NANOSECONDS)
+              : 0;
+        }
+        
+        long getPaintingFinalizationDuration(TimeUnit unit) {
+            return (scenePaintingEndNanos > Long.MIN_VALUE && paintingEndNanos > Long.MIN_VALUE) 
+              ? unit.convert(paintingEndNanos - scenePaintingEndNanos, TimeUnit.NANOSECONDS)
+              : 0;
+        }
+        
+        void recordEnd() {
+            endNanos = ToolkitAccessor.getMasterTimer().nanos();
+        }
+        
+        long getPulseDuration(TimeUnit unit) {
+            return unit.convert(endNanos - startNanos, TimeUnit.NANOSECONDS);
+        }
+        
+        //time from the scheduledNanos        
+        long getPulseEnd(TimeUnit unit) {
+            return unit
+                    .convert(endNanos - scheduledNanos, TimeUnit.NANOSECONDS);
+        }
+        
+        long getPulseStartFromNow(TimeUnit unit) {
+            return unit.convert(ToolkitAccessor.getMasterTimer().nanos() - startNanos,
+                    TimeUnit.NANOSECONDS);
+        }
+        
+        long getSkippedPulses() {
+            return getPulseEnd(TimeUnit.MILLISECONDS) 
+              / AnimationPulse.getDefaultBean().getPULSE_DURATION();
+        }
+        
+        static interface Accessor {
+            public long get(PulseData pulseData, TimeUnit unit);
+        }
+        
+        static final Accessor PulseStartAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+                return pulseData.getPulseStart(unit);
+            }
+        };
+        
+        static final Accessor AnimationDurationAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+                return pulseData.getAnimationDuration(unit);
+            }
+        };
+        
+        static final Accessor PaintingDurationAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+                return pulseData.getPaintingDuration(unit);
+            }
+        };
+        
+        static final Accessor ScenePaintingDurationAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+                return pulseData.getScenePaintingDuration(unit);
+            }
+        };
+        
+        static final Accessor PulseDurationAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+               return pulseData.getPulseDuration(unit);
+            }  
+        };
+        
+        static final Accessor PulseEndAccessor = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+               return pulseData.getPulseEnd(unit);
+            }  
+        };
+        
+        static final Accessor PaintingPreparationDuration = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+               return pulseData.getPaintingDuration(unit);
+            }  
+        };
+        
+        static final Accessor PaintingFinalizationDuration = new Accessor() {
+            @Override
+            public long get(PulseData pulseData, TimeUnit unit) {
+               return pulseData.getPaintingFinalizationDuration(unit);
+            }  
+        };
+        
+//        @Override
+//        public String toString() {
+//            StringBuilder sb = new StringBuilder(super.toString());
+//            TimeUnit unit = TimeUnit.MILLISECONDS;
+//            sb.append(" start: ").append(getPulseStart(unit))
+//            .append(" animation: ").append(getAnimationDuration(unit))
+//            .append(" painting: ").append(getPaintingDuration(unit))
+//            .append(" pulseDuration: ").append(getPulseDuration(unit))
+//            .append(" pulseEnd: ").append(getPulseEnd(unit));
+//            return sb.toString();
+//        }
+    }
+    
+    private final Queue<PulseData> pulseDataQueue = new ConcurrentLinkedQueue<PulseData>();
+    
+    //to be accessed from the EDT
+    private PulseData pulseData = null;
+    
+    
+
+    private volatile boolean isEnabled = false; 
+    @Override
+    public boolean getEnabled() {
+        return isEnabled;
+    }
+    
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (enabled == isEnabled) {
+            return;
+        }
+        isEnabled = enabled;
+        //we may want to clean the state on setEanbled(false)
+    }
+    
+    @Override
+    public long getPULSE_DURATION() {
+        return ToolkitAccessor.getMasterTimer().getPulseDuration(1000);
+    }
+    
+    
+    @Override
+    public long getSkippedPulses() {
+        return skippedPulses.get();
+    }
+    
+    @Override
+    public long getSkippedPulsesIn1Sec() {
+        long rv = 0;
+        for (PulseData pulseData : pulseDataQueue) {
+            if (pulseData.getPulseStartFromNow(TimeUnit.SECONDS) == 0) {
+                rv += pulseData.getSkippedPulses();
+            }
+        }
+        return rv;
+    }
+     
+    
+    public void recordStart(long shiftMillis) {
+        if (! getEnabled()) {
+            return;
+        }
+        pulseData = new PulseData(TimeUnit.MILLISECONDS.toNanos(shiftMillis));
+    }
+
+    // cleans items older than 1sec from the queue
+    private void purgeOldPulseData() {
+        Iterator<PulseData> iterator = pulseDataQueue.iterator();
+        while (iterator.hasNext() 
+                && iterator.next().getPulseStartFromNow(TimeUnit.SECONDS) > 1) {
+            iterator.remove();
+        }
+    }
+
+    private final AtomicLong pulseCounter = new AtomicLong();
+    
+    private final AtomicLong startMax = new AtomicLong();
+    private final AtomicLong startSum = new AtomicLong();
+    private final AtomicLong startAv = new AtomicLong();
+    
+    private final AtomicLong endMax = new AtomicLong();
+    private final AtomicLong endSum = new AtomicLong();
+    private final AtomicLong endAv = new AtomicLong();
+    
+    private final AtomicLong animationDurationMax = new AtomicLong(); 
+    private final AtomicLong animationDurationSum = new AtomicLong();
+    private final AtomicLong animationDurationAv = new AtomicLong();
+    
+    private final AtomicLong paintingDurationMax = new AtomicLong();
+    private final AtomicLong paintingDurationSum = new AtomicLong();
+    private final AtomicLong paintingDurationAv = new AtomicLong();
+    
+    private final AtomicLong pulseDurationMax = new AtomicLong();
+    private final AtomicLong pulseDurationSum = new AtomicLong();
+    private final AtomicLong pulseDurationAv = new AtomicLong();
+    
+    private final AtomicLong[] maxAndAv = new AtomicLong[] {
+            startMax, startSum, startAv,
+            endMax, endSum, endAv,
+            animationDurationMax, animationDurationSum, animationDurationAv,
+            paintingDurationMax, paintingDurationSum, paintingDurationAv,
+            pulseDurationMax, pulseDurationSum, pulseDurationAv
+    };
+    private final PulseData.Accessor[] maxAndAvAccessors = new PulseData.Accessor[] {
+            PulseData.PulseStartAccessor,
+            PulseData.PulseEndAccessor,
+            PulseData.AnimationDurationAccessor,
+            PulseData.PaintingDurationAccessor,
+            PulseData.PulseDurationAccessor
+    };
+    
+    private void updateMaxAndAv() {
+        long pulseCounterLong = pulseCounter.incrementAndGet();
+        for (int i = 0; i < maxAndAvAccessors.length; i++) {
+            int j = i * 3;
+            long tmpLong = maxAndAvAccessors[i].get(pulseData, TimeUnit.MILLISECONDS);
+            maxAndAv[j].set(Math.max(maxAndAv[j].get(), tmpLong));
+            maxAndAv[j + 1].addAndGet(tmpLong);
+            maxAndAv[j + 2].set(maxAndAv[j + 1].get() / pulseCounterLong);
+        }
+    }
+    
+    private final AtomicLong skippedPulses = new AtomicLong();
+    
+    private int skipPulses = 100;
+    public void recordEnd() {
+        if (! getEnabled()) {
+            return;
+        }
+        if (skipPulses > 0) {
+            //do not gather data for the first 'skipPulses' pulses
+            //let the application to warm up
+            skipPulses--;
+            pulseData = null;
+            return;
+        }
+        pulseData.recordEnd();
+        purgeOldPulseData();
+        updateMaxAndAv();
+        skippedPulses.addAndGet(pulseData.getSkippedPulses());
+        pulseDataQueue.add(pulseData);
+        pulseData = null;
+    }
+
+    /* 
+     * implementation detail: I wish we had deque in 1.5 but we do not so here we 
+     * iterate over the whole thing.
+     */
+    private long getAv(PulseData.Accessor accessor, long timeOut, TimeUnit unit) {
+        if (! getEnabled()) {
+            return 0;
+        }
+        long time = 0;
+        long items = 0;
+        for (PulseData currentPulseData : pulseDataQueue) {
+            if (currentPulseData.getPulseStartFromNow(unit) <= timeOut) {
+                time += accessor.get(currentPulseData, unit);
+                items++;
+            }
+        }
+        return (items == 0) ? 0 : time / items;
+    }
+    
+    private long getMax(PulseData.Accessor accessor, long timeOut, TimeUnit unit) {
+        if (! getEnabled()) {
+            return 0;
+        }
+        long max = 0;
+        for (PulseData currentPulseData : pulseDataQueue) {
+            if (currentPulseData.getPulseStartFromNow(unit) <= timeOut) {
+                max = Math.max(accessor.get(currentPulseData, unit), max);
+            }
+        }
+        return max;
+    }
+    
+    @Override
+    public long getStartMax() {
+        return startMax.get();
+    }
+    
+    @Override
+    public long getStartAv() {
+        return startAv.get();
+    }
+    
+    @Override
+    public long getStartMaxIn1Sec() {
+        return getMax(PulseData.PulseStartAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getStartAvIn100Millis() {
+        return getAv(PulseData.PulseStartAccessor, 100, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getEndMax() {
+        return endMax.get();
+    }
+    
+    @Override
+    public long getEndMaxIn1Sec() {
+        return getMax(PulseData.PulseEndAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getEndAv() {
+        return endAv.get();
+    }
+    
+    @Override
+    public long getEndAvIn100Millis() {
+        return getAv(PulseData.PulseEndAccessor, 100, TimeUnit.MILLISECONDS);
+    }
+    
+    public void recordAnimationEnd() {
+        if (getEnabled() && pulseData != null) {
+            pulseData.recordAnimationEnd();
+        }
+    }
+    
+    @Override
+    public long getAnimationDurationMax() {
+        return animationDurationMax.get();
+    }
+    
+    @Override
+    public long getAnimationMaxIn1Sec() {
+        return getMax(PulseData.AnimationDurationAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getAnimationDurationAv() {
+        return animationDurationAv.get();
+    }
+    
+    @Override
+    public long getAnimationDurationAvIn100Millis() {
+        return getAv(PulseData.AnimationDurationAccessor, 100, TimeUnit.MILLISECONDS);
+    }
+    
+    public void recordPaintingStart() {
+        if (getEnabled() && pulseData != null) {
+            pulseData.recordPaintingStart();
+        }
+    }
+    
+    public void recordPaintingEnd() {
+        if (getEnabled() && pulseData != null) {
+            pulseData.recordPaintingEnd();
+        }
+    }
+    
+    @Override
+    public long getPaintingDurationMax() {
+        return paintingDurationMax.get();
+    }
+    
+    @Override
+    public long getPaintingDurationMaxIn1Sec() {
+        return getMax(PulseData.PaintingDurationAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getPaintingDurationAv() {
+        return paintingDurationAv.get();
+    }
+    
+    @Override
+    public long getPaitningDurationAvIn100Millis() {
+        return getAv(PulseData.PaintingDurationAccessor, 100, TimeUnit.MILLISECONDS);
+    }
+    
+    public void recordScenePaintingStart() {
+        if (getEnabled() && pulseData != null) {
+            pulseData.recordScenePaintingStart();
+        }
+    }
+    
+    public void recordScenePaintingEnd() {
+        if (getEnabled() && pulseData != null) {
+            pulseData.recordScenePaintingEnd();
+        }
+    }
+    
+    @Override
+    public long getScenePaintingDurationMaxIn1Sec() {
+        return getMax(PulseData.ScenePaintingDurationAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getPulseDurationMax() {
+        return pulseDurationMax.get();
+    }
+    
+    @Override
+    public long getPulseDurationMaxIn1Sec() {
+        return getMax(PulseData.PulseDurationAccessor, 1000, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getPulseDurationAv() {
+        return pulseDurationAv.get();
+    }
+    
+    @Override
+    public long getPulseDurationAvIn100Millis() {
+        return getAv(PulseData.PulseDurationAccessor, 100, TimeUnit.MILLISECONDS);
+    }
+    
+    @Override
+    public long getPaintingPreparationDurationMaxIn1Sec() {
+        return getMax(PulseData.PaintingPreparationDuration, 1000, TimeUnit.MILLISECONDS);    
+    }
+    
+    @Override
+    public long getPaintingFinalizationDurationMaxIn1Sec() {
+        return getMax(PulseData.PaintingFinalizationDuration, 1000, TimeUnit.MILLISECONDS);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/AnimationPulseMBean.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+public interface AnimationPulseMBean {
+    public boolean getEnabled();
+
+    public void setEnabled(boolean enabled);
+
+    // we are using millis as time units here
+
+    public long getPULSE_DURATION();
+
+    public long getSkippedPulses();
+
+    public long getSkippedPulsesIn1Sec();
+
+    // from the scheduled time
+    public long getStartMax();
+
+    public long getStartMaxIn1Sec();
+
+    public long getStartAv();
+
+    public long getStartAvIn100Millis();
+
+    public long getEndMax();
+
+    public long getEndMaxIn1Sec();
+
+    public long getEndAv();
+
+    public long getEndAvIn100Millis();
+
+    public long getAnimationDurationMax();
+
+    public long getAnimationMaxIn1Sec();
+
+    public long getAnimationDurationAv();
+
+    public long getAnimationDurationAvIn100Millis();
+
+    public long getPaintingDurationMax();
+
+    public long getPaintingDurationMaxIn1Sec();
+
+    public long getPaintingDurationAv();
+
+    public long getPaitningDurationAvIn100Millis();
+
+    public long getScenePaintingDurationMaxIn1Sec();
+
+    public long getPaintingPreparationDurationMaxIn1Sec();
+
+    public long getPaintingFinalizationDurationMaxIn1Sec();
+
+    public long getPulseDurationMax();
+
+    public long getPulseDurationMaxIn1Sec();
+
+    public long getPulseDurationAv();
+
+    public long getPulseDurationAvIn100Millis();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/FixedPulseTime.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+import com.sun.scenario.animation.shared.CurrentTime;
+import com.sun.scenario.animation.shared.PulseReceiver;
+
+/**
+ *
+ */
+class FixedPulseTime implements CurrentTime, PulseReceiver {
+
+    public FixedPulseTime(long step) {
+        this.step = step;
+    }
+
+    private long currentNanos;
+    private final long step;
+
+    @Override
+    public long nanos() {
+        return currentNanos;
+    }
+
+    @Override
+    public void timePulse(long now) {
+        currentNanos += step;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/MilliCurrentTime.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+import com.sun.scenario.animation.shared.CurrentTime;
+
+final class MilliCurrentTime implements CurrentTime {
+
+    @Override
+    public long nanos() {
+        return System.currentTimeMillis() * 1000000;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/NanoCurrentTime.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation;
+
+import com.sun.scenario.animation.shared.CurrentTime;
+
+final class NanoCurrentTime implements CurrentTime {
+
+    @Override
+    public long nanos() {
+        return System.nanoTime();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/NumberTangentInterpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation;
+
+import javafx.animation.Interpolator;
+import javafx.util.Duration;
+
+public abstract class NumberTangentInterpolator extends Interpolator {
+
+    public abstract double getInValue();
+
+    public abstract double getOutValue();
+
+    public abstract double getInMillis();
+
+    public abstract double getOutMillis();
+
+    public static NumberTangentInterpolator create(final double inValue,
+            final Duration inDuration, final double outValue,
+            final Duration outDuration) {
+        return new NumberTangentInterpolator() {
+            @Override
+            public double getInValue() {
+                return inValue;
+            }
+
+            @Override
+            public double getOutValue() {
+                return outValue;
+            }
+
+            @Override
+            public double getInMillis() {
+                return inDuration.toMillis();
+            }
+
+            @Override
+            public double getOutMillis() {
+                return outDuration.toMillis();
+            }
+
+            @Override
+            public String toString() {
+                return "NumberTangentInterpolator [inValue=" + inValue
+                        + ", inDuration=" + inDuration + ", outValue="
+                        + outValue + ", outDuration=" + outDuration + "]";
+            }
+        };
+    }
+
+    public static NumberTangentInterpolator create(final double value,
+            final Duration duration) {
+        return new NumberTangentInterpolator() {
+            @Override
+            public double getInValue() {
+                return value;
+            }
+
+            @Override
+            public double getOutValue() {
+                return value;
+            }
+
+            @Override
+            public double getInMillis() {
+                return duration.toMillis();
+            }
+
+            @Override
+            public double getOutMillis() {
+                return duration.toMillis();
+            }
+
+            @Override
+            public String toString() {
+                return "NumberTangentInterpolator [inValue=" + value
+                        + ", inDuration=" + duration + ", outValue=" + value
+                        + ", outDuration=" + duration + "]";
+            }
+        };
+    }
+
+    @Override
+    protected double curve(double t) {
+        // Fallback: If NumberTangentInterpolator is used with a target, that is
+        // not a number,
+        // it behaves like linear interpolation.
+        return t;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/SplineInterpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation;
+
+import javafx.animation.Interpolator;
+
+/**
+ * An implementation of a spline interpolator for temporal interpolation that
+ * tries to follow the specification referenced by:
+ * http://www.w3.org/TR/SMIL/animation.html#animationNS-OverviewSpline
+ * <p>
+ * Basically, a cubic Bezier curve is created with start point (0,0) and
+ * endpoint (1,1). The other two control points (px1, py1) and (px2, py2) are
+ * given by the user, where px1, py1, px1, and px2 are all in the range [0,1]. A
+ * property of this specially constrained Bezier curve is that it is strictly
+ * monotonically increasing in both X and Y with t in range [0,1].
+ * <p>
+ * The interpolator works by giving it a value for X. It then finds what
+ * parameter t would generate this X value for the curve. Then this t parameter
+ * is applied to the curve to solve for Y. As X increases from 0 to 1, t also
+ * increases from 0 to 1, and correspondingly Y increases from 0 to 1. The
+ * X-to-Y mapping is not a function of path/curve length.
+ * 
+ */
+public class SplineInterpolator extends Interpolator {
+
+    /**
+     * The coordinates of the 2 2D control points for a cubic Bezier curve, with
+     * implicit start point (0,0) and end point (1,1) -- each individual
+     * coordinate value must be in range [0,1]
+     */
+    private final double x1, y1, x2, y2;
+
+    /**
+     * Do the input control points form a line with (0,0) and (1,1), i.e., x1 ==
+     * y1 and x2 == y2 -- if so, then all x(t) == y(t) for the curve
+     */
+    private final boolean isCurveLinear;
+
+    /**
+     * Power of 2 sample size for lookup table of x values
+     */
+    private static final int SAMPLE_SIZE = 16;
+
+    /**
+     * Difference in t used to calculate each of the xSamples values -- power of
+     * 2 sample size should provide exact representation of this value and its
+     * integer multiples (integer in range of [0..SAMPLE_SIZE]
+     */
+    private static final double SAMPLE_INCREMENT = 1.0 / SAMPLE_SIZE;
+
+    /**
+     * X values for the bezier curve, sampled at increments of 1/SAMPLE_SIZE --
+     * this is used to find the good initial guess for parameter t, given an x
+     */
+    private final double[] xSamples = new double[SAMPLE_SIZE + 1];
+
+    /**
+     * Creates a new instance with control points (0,0) (px1,py1) (px2,py2)
+     * (1,1) -- px1, py1, px2, py2 all in range [0,1]
+     * 
+     * @param px1
+     *            X coordinate of first control point, in range [0,1]
+     * @param py1
+     *            Y coordinate of first control point, in range [0,1]
+     * @param px2
+     *            X coordinate of second control point, in range [0,1]
+     * @param py2
+     *            Y coordinate of second control point, in range [0,1]
+     */
+    public SplineInterpolator(double px1, double py1, double px2, double py2) {
+        // check user input for precondition
+        if (px1 < 0 || px1 > 1 || py1 < 0 || py1 > 1 || px2 < 0 || px2 > 1
+                || py2 < 0 || py2 > 1) {
+            throw new IllegalArgumentException(
+                    "Control point coordinates must " + "all be in range [0,1]");
+        }
+
+        // save control point data
+        this.x1 = px1;
+        this.y1 = py1;
+        this.x2 = px2;
+        this.y2 = py2;
+
+        // calc linearity/identity curve
+        isCurveLinear = ((x1 == y1) && (x2 == y2));
+
+        // make the array of x value samples
+        if (!isCurveLinear) {
+            for (int i = 0; i < SAMPLE_SIZE + 1; ++i) {
+                xSamples[i] = eval(i * SAMPLE_INCREMENT, x1, x2);
+            }
+        }
+    }
+
+    /**
+     * Returns the y-value of the cubic bezier curve that corresponds to the x
+     * input.
+     * 
+     * @param x
+     *            is x-value of cubic bezier curve, in range [0,1]
+     * @return corresponding y-value of cubic bezier curve -- in range [0,1]
+     */
+    @Override
+    public double curve(double x) {
+        // check user input for precondition
+        if (x < 0 || x > 1) {
+            throw new IllegalArgumentException("x must be in range [0,1]");
+        }
+
+        // check quick exit identity cases (linear curve or curve endpoints)
+        if (isCurveLinear || x == 0 || x == 1) {
+            return x;
+        }
+
+        // find the t parameter for a given x value, and use this t to calculate
+        // the corresponding y value
+        return eval(findTForX(x), y1, y2);
+    }
+
+    /**
+     * Use Bernstein basis to evaluate 1D cubic Bezier curve (quicker and more
+     * numerically stable than power basis) -- 1D control coordinates are (0,
+     * p1, p2, 1), where p1 and p2 are in range [0,1], and there is no ordering
+     * constraint on p1 and p2, i.e., p1 &lt;= p2 does not have to be true.
+     * 
+     * @param t
+     *            is the paramaterized value in range [0,1]
+     * @param p1
+     *            is 1st control point coordinate in range [0,1]
+     * @param p2
+     *            is 2nd control point coordinate in range [0,1]
+     * @return the value of the Bezier curve at parameter t
+     */
+    private double eval(double t, double p1, double p2) {
+        // Use optimized version of the normal Bernstein basis form of Bezier:
+        // (3*(1-t)*(1-t)*t*p1)+(3*(1-t)*t*t*p2)+(t*t*t), since p0=0, p3=1.
+        // The above unoptimized version is best using -server, but since we
+        // are probably doing client-side animation, this is faster.
+        double compT = 1 - t;
+        return t * (3 * compT * (compT * p1 + t * p2) + (t * t));
+    }
+
+    /**
+     * Evaluates Bernstein basis derivative of 1D cubic Bezier curve, where 1D
+     * control points are (0, p1, p2, 1), where p1 and p2 are in range [0,1],
+     * and there is no ordering constraint on p1 and p2, i.e., p1 &lt;= p2 does
+     * not have to be true.
+     * 
+     * @param t
+     *            is the paramaterized value in range [0,1]
+     * @param p1
+     *            is 1st control point coordinate in range [0,1]
+     * @param p2
+     *            is 2nd control point coordinate in range [0,1]
+     * @return the value of the Bezier curve at parameter t
+     */
+    private double evalDerivative(double t, double p1, double p2) {
+        // use optimized version of Berstein basis Bezier derivative:
+        // (3*(1-t)*(1-t)*p1)+(6*(1-t)*t*(p2-p1))+(3*t*t*(1-p2)), since
+        // p0=0 and p3=1. The above unoptimized version is best using -server,
+        // but since we are probably doing client-side animation, this is
+        // faster.
+        double compT = 1 - t;
+        return 3 * (compT * (compT * p1 + 2 * t * (p2 - p1)) + t * t * (1 - p2));
+    }
+
+    /**
+     * Find an initial good guess for what parameter t might produce the x-value
+     * on the Bezier curve -- uses linear interpolation on the x-value sample
+     * array that was created on construction.
+     * 
+     * @param x
+     *            is x-value of cubic bezier curve, in range [0,1]
+     * @return a good initial guess for parameter t (in range [0,1]) that gives
+     *         x
+     */
+    private double getInitialGuessForT(double x) {
+        // find which places in the array that x would be sandwiched between,
+        // and then linearly interpolate a reasonable value of t -- array values
+        // are ascending (or at least never descending) -- binary search is
+        // probably more trouble than it is worth here
+        for (int i = 1; i < SAMPLE_SIZE + 1; ++i) {
+            if (xSamples[i] >= x) {
+                double xRange = xSamples[i] - xSamples[i - 1];
+                if (xRange == 0) {
+                    // no change in value between samples, so use earlier time
+                    return (i - 1) * SAMPLE_INCREMENT;
+                } else {
+                    // linearly interpolate the time value
+                    return ((i - 1) + ((x - xSamples[i - 1]) / xRange))
+                            * SAMPLE_INCREMENT;
+                }
+            }
+        }
+
+        // shouldn't get here since 0 <= x <= 1, and xSamples[0] == 0 and
+        // xSamples[SAMPLE_SIZE] == 1 (using power of 2 SAMPLE_SIZE for more
+        // exact increment arithmetic)
+        return 1;
+    }
+
+    /**
+     * Finds the parameter t that produces the given x-value for the curve --
+     * uses Newton-Raphson to refine the value as opposed to subdividing until
+     * we are within some tolerance.
+     * 
+     * @param x
+     *            is x-value of cubic bezier curve, in range [0,1]
+     * @return the parameter t (in range [0,1]) that produces x
+     */
+    private double findTForX(double x) {
+        // get an initial good guess for t
+        double t = getInitialGuessForT(x);
+
+        // use Newton-Raphson to refine the value for t -- for this constrained
+        // Bezier with float accuracy (7 digits), any value not converged by 4
+        // iterations is cycling between values, which can minutely affect the
+        // accuracy of the last digit
+        final int numIterations = 4;
+        for (int i = 0; i < numIterations; ++i) {
+            // stop if this value of t gives us exactly x
+            double xT = (eval(t, x1, x2) - x);
+            if (xT == 0) {
+                break;
+            }
+
+            // stop if derivative is 0
+            double dXdT = evalDerivative(t, x1, x2);
+            if (dXdT == 0) {
+                break;
+            }
+
+            // refine t
+            t -= xT / dXdT;
+        }
+
+        return t;
+    }
+
+    @Override
+    public String toString() {
+        return "SplineInterpolator [x1=" + x1 + ", y1=" + y1 + ", x2=" + x2
+                + ", y2=" + y2 + "]";
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/AnimationAccessor.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Animation;
+import javafx.animation.Timeline;
+
+public abstract class AnimationAccessor {
+ public static AnimationAccessor DEFAULT;
+
+    public static AnimationAccessor getDefault() {
+        if (DEFAULT != null) {
+            return DEFAULT;
+        }
+
+        // invokes static initializer of Item.class
+        // that will assign value to the DEFAULT field above
+        Class c = Animation.class;
+        try {
+            Class.forName(c.getName());
+        } catch (ClassNotFoundException ex) {
+            assert false : ex;
+        }
+        assert DEFAULT != null : "The DEFAULT field must be initialized";
+        return DEFAULT;
+    }
+
+    public abstract void timePulse(Animation animation, long elapsedTime);
+
+    public abstract void setCurrentRate(Animation animation, double currentRate);
+
+    public abstract void setCurrentTicks(Animation animation, long ticks);
+
+    public abstract void playTo(Animation animation, long pos, long cycleTicks);
+
+    public abstract void jumpTo(Animation animation, long pos, long cycleTicks, boolean forceJump);
+
+    public abstract void finished(Animation animation);
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/AnimationPulseReceiver.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import com.sun.javafx.animation.TickCalculation;
+import javafx.animation.Animation;
+import com.sun.scenario.animation.AbstractMasterTimer;
+
+public class AnimationPulseReceiver implements PulseReceiver {
+
+    private final Animation animation;
+    private final AbstractMasterTimer timer;
+
+    private long startTime;
+    private long pauseTime;
+    private boolean paused = false;
+
+    public AnimationPulseReceiver(Animation animation, AbstractMasterTimer timer) {
+        this.animation = animation;
+        this.timer = timer;
+    }
+
+    protected void addPulseReceiver() {
+        timer.addPulseReceiver(this);
+    }
+
+    protected void removePulseReceiver() {
+        timer.removePulseReceiver(this);
+    }
+
+    private long now() {
+        return TickCalculation.fromNano(timer.nanos());
+    }
+
+    public void start(long delay) {
+        paused = false;
+        startTime = now() + delay;
+        addPulseReceiver();
+    }
+
+    public void stop() {
+        if (!paused) {
+            removePulseReceiver();
+        }
+    }
+
+    public void pause() {
+        if (!paused) {
+            pauseTime = now();
+            paused = true;
+            removePulseReceiver();
+        }
+    }
+
+    public void resume() {
+        if (paused) {
+            final long deltaTime = now() - pauseTime;
+            startTime += deltaTime;
+            paused = false;
+            addPulseReceiver();
+        }
+    }
+
+    @Override
+    public void timePulse(long now) {
+        final long elapsedTime = now - startTime;
+        if (elapsedTime < 0) {
+            return;
+        }
+
+        AnimationAccessor.getDefault().timePulse(animation, elapsedTime);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/ClipEnvelope.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+import com.sun.javafx.animation.TickCalculation;
+import javafx.animation.Animation;
+import javafx.util.Duration;
+
+/**
+ * An instance of ClipEnvelope handles the loop-part of a clip.
+ * 
+ * The functionality to react on a pulse-signal from the timer is implemented in
+ * two classes: ClipEnvelope and ClipCore. ClipEnvelope is responsible for the
+ * "loop-part" (keeping track of the number of cycles, handling the direction of
+ * the clip etc.). ClipCore takes care of the inner part (interpolating the
+ * values, triggering the action-functions, ...)
+ * 
+ * Both classes have an abstract public definition and can only be created using
+ * the factory method create(). The intend is to provide a general
+ * implementation plus eventually some fast-track implementations for common use
+ * cases.
+ */
+
+public abstract class ClipEnvelope {
+
+    protected static final long INDEFINITE = Long.MAX_VALUE;
+    protected static final double EPSILON = 1e-12;
+
+    protected Animation animation;
+    protected double rate = 1;
+    protected long cycleTicks = 0;
+
+    // internal state-variables used by all implementations
+    protected long deltaTicks = 0;
+    protected long ticks = 0;
+    protected double currentRate = rate;
+    protected boolean inTimePulse = false;
+    protected boolean aborted = false;
+    
+    protected ClipEnvelope(Animation animation) {
+        this.animation = animation;
+        if (animation != null) {
+            final Duration cycleDuration = animation.getCycleDuration();
+            cycleTicks = TickCalculation.fromDuration(cycleDuration);
+            rate = animation.getRate();
+        }
+    }
+
+    public abstract ClipEnvelope setCycleDuration(Duration cycleDuration);
+    public abstract void setRate(double rate);
+    public abstract void setAutoReverse(boolean autoReverse);
+    public abstract ClipEnvelope setCycleCount(int cycleCount);
+
+    protected void updateCycleTicks(Duration cycleDuration) {
+        cycleTicks = TickCalculation.fromDuration(cycleDuration);
+    }
+
+    public boolean wasSynched() {
+        return cycleTicks != 0;
+    }
+
+    public void start() {
+        setCurrentRate(calculateCurrentRate());
+        deltaTicks = ticks;
+    }
+
+    public abstract void timePulse(long currentTick);
+
+    public abstract void jumpTo(long ticks);
+
+    public void abortCurrentPulse() {
+        if (inTimePulse) {
+            aborted = true;
+            inTimePulse = false;
+        }
+    }
+    
+    protected abstract double calculateCurrentRate();
+    
+    protected void setCurrentRate(double currentRate) {
+        this.currentRate = currentRate;
+        AnimationAccessor.getDefault().setCurrentRate(animation, currentRate);
+    }
+
+    protected static long checkBounds(long value, long max) {
+        return Math.max(0L, Math.min(value, max));
+    }
+
+    public double getCurrentRate() {
+        return currentRate;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/ClipEnvelopeFactory.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Animation;
+
+public class ClipEnvelopeFactory {
+
+    private ClipEnvelopeFactory() {}
+
+    public static ClipEnvelope create(Animation animation) {
+        if ((animation.getCycleCount() == 1) || (animation.getCycleDuration().isIndefinite())) {
+            return new SingleLoopClipEnvelope(animation);
+        } else if (animation.getCycleCount() == Animation.INDEFINITE) {
+            return new InfiniteClipEnvelope(animation);
+        } else {
+            return new FiniteClipEnvelope(animation);
+        }
+    }
+
+    
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/ClipInterpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.KeyFrame;
+import javafx.util.Duration;
+
+public abstract class ClipInterpolator {
+
+    static ClipInterpolator create(KeyFrame[] keyFrames, long[] keyFrameTicks) {
+        return (ClipInterpolator.getRealKeyFrameCount(keyFrames) == 2) ? (keyFrames.length == 1) ? new SimpleClipInterpolator(
+                keyFrames[0], keyFrameTicks[0]) : new SimpleClipInterpolator(keyFrames[0],
+                keyFrames[1], keyFrameTicks[1])
+                : new GeneralClipInterpolator(keyFrames, keyFrameTicks);
+    }
+
+    /**
+     * Returns keyFrames.length
+     * 
+     * @param keyFrames
+     * @return
+     */
+    static int getRealKeyFrameCount(KeyFrame[] keyFrames) {
+        final int length = keyFrames.length;
+        return (length == 0) ? 0 : (keyFrames[0].getTime()
+                .greaterThan(Duration.ZERO)) ? length + 1 : length;
+    }
+
+    /**
+     * Changes the keyframes.
+     * 
+     * The optimal implementation for the new keyFrames might be different. For
+     * this reason, setKeyFrames returns a ClipInterpolator implementation with
+     * the updated values. This can either be the same object or a newly created
+     * one.
+     * 
+     * @param keyFrames
+     *            The new sorted array of keyframes of this clip
+     * @param keyFrameTicks 
+     *            tick duration of corresponding keyFrames
+     * @return The ClipInterpolator implementation to use after changing the
+     *         keyframes.
+     */
+    abstract ClipInterpolator setKeyFrames(KeyFrame[] keyFrames, long[] keyFrameTicks);
+
+    abstract void interpolate(long ticks);
+
+    abstract void validate(boolean forceSync);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/CurrentTime.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+/**
+ * Query the current time in millis and nanos. Implementations can be based
+ * either on System.currentTimeMillis() or System.nanoTime() (but not both!).
+ * 
+ */
+public interface CurrentTime {
+    public long nanos();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/FiniteClipEnvelope.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Animation;
+import javafx.animation.Animation.Status;
+import javafx.util.Duration;
+
+public class FiniteClipEnvelope extends ClipEnvelope {
+    
+    private boolean autoReverse;
+    private int cycleCount;
+    private long totalTicks;
+    private long pos;
+    
+    protected FiniteClipEnvelope(Animation animation) {
+        super(animation);
+        if (animation != null) {
+            autoReverse = animation.isAutoReverse();
+            cycleCount = animation.getCycleCount();
+        }
+        updateTotalTicks();
+    }
+
+    @Override
+    public void setAutoReverse(boolean autoReverse) {
+        this.autoReverse = autoReverse;
+    }
+
+    @Override
+    protected double calculateCurrentRate() {
+        return !autoReverse? rate
+                : (ticks % (2 * cycleTicks) < cycleTicks) == (rate > 0)? rate : -rate;
+    }
+    
+    @Override
+    public ClipEnvelope setCycleDuration(Duration cycleDuration) {
+        if (cycleDuration.isIndefinite()) {
+            return ClipEnvelopeFactory.create(animation);
+        }
+        updateCycleTicks(cycleDuration);
+        updateTotalTicks();
+        return this;
+    }
+
+    @Override
+    public ClipEnvelope setCycleCount(int cycleCount) {
+        if ((cycleCount == 1) || (cycleCount == Animation.INDEFINITE)) {
+            return ClipEnvelopeFactory.create(animation);
+        }
+        this.cycleCount = cycleCount;
+        updateTotalTicks();
+        return this;
+    }
+    
+    @Override
+    public void setRate(double rate) {
+        final boolean toggled = rate * this.rate < 0;
+        final long newTicks = toggled? totalTicks - ticks : ticks;
+        final Status status = animation.getStatus();
+        if (status != Status.STOPPED) {
+            if (status == Status.RUNNING) {
+                setCurrentRate((Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate);
+            }
+            deltaTicks = newTicks - Math.round((ticks - deltaTicks) * Math.abs(rate / this.rate));
+            abortCurrentPulse();
+        }
+        ticks = newTicks;
+        this.rate = rate;
+    }
+    
+    private void updateTotalTicks() {
+        totalTicks = cycleCount * cycleTicks;
+    }
+
+    @Override
+    public void timePulse(long currentTick) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        aborted = false;
+        inTimePulse = true;
+
+        try {
+            final long oldTicks = ticks;
+            ticks = ClipEnvelope.checkBounds(deltaTicks + Math.round(currentTick * Math.abs(rate)), totalTicks);
+
+            final boolean reachedEnd = ticks >= totalTicks; 
+            
+            long overallDelta = ticks - oldTicks; // overall delta between current position and new position
+            if (overallDelta == 0) {
+                return;
+            }
+            
+            long cycleDelta = (currentRate > 0)? cycleTicks - pos : pos; // delta to reach end of cycle
+            
+            while (overallDelta >= cycleDelta) {
+                if (cycleDelta > 0) {
+                    pos = (currentRate > 0)? cycleTicks : 0;
+                    overallDelta -= cycleDelta;
+                    AnimationAccessor.getDefault().playTo(animation, pos, cycleTicks);
+                    if (aborted) {
+                        return;
+                    }
+                }
+
+                if (!reachedEnd || (overallDelta > 0)) {
+                    if (autoReverse) {
+                        setCurrentRate(-currentRate);
+                    } else {
+                        pos = (currentRate > 0)? 0 : cycleTicks;
+                        AnimationAccessor.getDefault().jumpTo(animation, pos, cycleTicks, false);
+                    }
+                }
+                cycleDelta = cycleTicks;
+            }
+
+            if (overallDelta > 0 && !reachedEnd) {
+                pos += (currentRate > 0)? overallDelta : -overallDelta;
+                AnimationAccessor.getDefault().playTo(animation, pos, cycleTicks);
+            }
+            
+            if(reachedEnd && !aborted) {
+                AnimationAccessor.getDefault().finished(animation);
+            }
+
+        } finally {
+            inTimePulse = false;
+        }
+    }
+
+    @Override
+    public void jumpTo(long newTicks) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        
+        final long oldTicks = ticks;
+        if (rate < 0) {
+            newTicks = totalTicks - newTicks;
+        }
+        ticks = ClipEnvelope.checkBounds(newTicks, totalTicks);
+        final long delta = ticks - oldTicks;
+        if (delta != 0) {
+            deltaTicks += delta;
+            if (autoReverse) {
+                final boolean forward = ticks % (2 * cycleTicks) < cycleTicks;
+                if (forward == (rate > 0)) {
+                    pos = ticks % cycleTicks;
+                    if (animation.getStatus() == Status.RUNNING) {
+                        setCurrentRate(Math.abs(rate));
+                    }
+                } else {
+                    pos = cycleTicks - (ticks % cycleTicks);
+                    if (animation.getStatus() == Status.RUNNING) {
+                        setCurrentRate(-Math.abs(rate));
+                    }
+                }
+            } else {
+                pos = ticks % cycleTicks;
+                if (rate < 0) {
+                    pos = cycleTicks - pos;
+                }
+                if ((pos == 0) && (ticks > 0)) {
+                    pos = cycleTicks;
+                }
+            }
+            
+            AnimationAccessor.getDefault().jumpTo(animation, pos, cycleTicks, false);
+            abortCurrentPulse();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/GeneralClipInterpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.beans.value.WritableValue;
+
+/**
+ * General implementation of ClipInterpolator, which covers all use-cases.
+ */
+
+// @@OPT:
+// - A binary search in interpolate might make sense.
+// - Prepare only first segment when starting timeline and do the rest later =>
+// improves startup time?
+// - Store 1 / (rightMillis - leftMillis) for each interval and multiply
+class GeneralClipInterpolator extends ClipInterpolator {
+
+    private KeyFrame[] keyFrames;
+    private long[] keyFrameTicks;
+
+    // List of interpolation-points associated with each target
+    private InterpolationInterval[][] interval = new InterpolationInterval[0][];
+
+    // List of indexes for targets with undefined start value
+    private int[] undefinedStartValues = new int[0];
+    // Is internal representation up-to-date?
+    private boolean invalid = true;
+
+    GeneralClipInterpolator(KeyFrame[] keyFrames, long[] keyFrameTicks) {
+        this.keyFrames = keyFrames;
+        this.keyFrameTicks = keyFrameTicks;
+    }
+
+    // See comment in ClipInterpolator
+    @Override
+    ClipInterpolator setKeyFrames(KeyFrame[] keyFrames, long[] keyFrameTicks) {
+        if (ClipInterpolator.getRealKeyFrameCount(keyFrames) == 2) {
+            return ClipInterpolator.create(keyFrames, keyFrameTicks);
+        }
+        this.keyFrames = keyFrames;
+        this.keyFrameTicks = keyFrameTicks;
+        invalid = true;
+        return this;
+    }
+
+    @Override
+    void validate(boolean forceSync) {
+        if (invalid) {
+            final Map<WritableValue<?>, KeyValue> lastKeyValues = new HashMap<WritableValue<?>, KeyValue>();
+            final int n = keyFrames.length;
+            int index = 0;
+            for (index = 0; index < n; index++) {
+                final KeyFrame keyFrame = keyFrames[index];
+                if (keyFrameTicks[index] == 0) {
+                    for (final KeyValue keyValue : keyFrame.getValues()) {
+                        lastKeyValues.put(keyValue.getTarget(), keyValue);
+                    }
+                } else {
+                    break;
+                }
+            }
+
+            final Map<WritableValue<?>, List<InterpolationInterval>> map = new HashMap<WritableValue<?>, List<InterpolationInterval>>();
+            final Set<WritableValue<?>> undefinedValues = new HashSet<WritableValue<?>>();
+            // iterate through all keyFrames
+            for (; index < n; index++) {
+                final KeyFrame keyFrame = keyFrames[index];
+                final long ticks = keyFrameTicks[index];
+                // iterate through all keyValues in this keyFrame
+                for (final KeyValue rightKeyValue : keyFrame.getValues()) {
+                    final WritableValue<?> target = rightKeyValue.getTarget();
+                    List<InterpolationInterval> list = map.get(target);
+                    final KeyValue leftKeyValue = lastKeyValues.get(target);
+                    if (list == null) {
+                        // first encounter of a particular target, generate a
+                        // new interval list
+                        list = new ArrayList<InterpolationInterval>();
+                        map.put(target, list);
+                        if (leftKeyValue == null) {
+                            list.add(InterpolationInterval.create(
+                                    rightKeyValue, ticks));
+                            undefinedValues.add(target);
+                        } else {
+                            list.add(InterpolationInterval
+                                    .create(rightKeyValue, ticks,
+                                            leftKeyValue, ticks));
+                        }
+                    } else {
+                        assert leftKeyValue != null;
+                        list.add(InterpolationInterval.create(rightKeyValue,
+                                ticks, leftKeyValue,
+                                ticks - list.get(list.size() - 1).ticks));
+                    }
+                    lastKeyValues.put(target, rightKeyValue);
+                }
+            }
+
+            // copy everything to arrays
+            final int targetCount = map.size();
+            if (interval.length != targetCount) {
+                interval = new InterpolationInterval[targetCount][];
+            }
+            final int undefinedStartValuesCount = undefinedValues.size();
+            if (undefinedStartValues.length != undefinedStartValuesCount) {
+                undefinedStartValues = new int[undefinedStartValuesCount];
+            }
+            int undefinedStartValuesIndex = 0;
+            final Iterator<Map.Entry<WritableValue<?>, List<InterpolationInterval>>> iterator = map
+                    .entrySet().iterator();
+            for (int i = 0; i < targetCount; i++) {
+                final Map.Entry<WritableValue<?>, List<InterpolationInterval>> entry = iterator
+                        .next();
+                interval[i] = new InterpolationInterval[entry.getValue().size()];
+                entry.getValue().toArray(interval[i]);
+                if (undefinedValues.contains(entry.getKey())) {
+                    undefinedStartValues[undefinedStartValuesIndex++] = i;
+                }
+            }
+            invalid = false;
+        } else if (forceSync) {
+            final int n = undefinedStartValues.length;
+            for (int i = 0; i < n; i++) {
+                final int index = undefinedStartValues[i];
+                interval[index][0].recalculateStartValue();
+            }
+        }
+    }
+
+    @Override
+    void interpolate(long ticks) {
+        final int targetCount = interval.length;
+        // iterate through all targets
+        targetLoop: for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
+            InterpolationInterval[] intervalList = interval[targetIndex];
+            final int intervalCount = intervalList.length;
+            // leftMillis keeps the timestamp of the left side of the interval
+            long leftTicks = 0;
+            // iterate through all intervals except the last one
+            for (int intervalIndex = 0; intervalIndex < intervalCount - 1; intervalIndex++) {
+                final InterpolationInterval i = intervalList[intervalIndex];
+                final long rightTicks = i.ticks;
+                if (ticks <= rightTicks) {
+                    // we found the current interval
+                    final double frac = (double)(ticks - leftTicks)
+                            / (rightTicks - leftTicks);
+                    i.interpolate(frac);
+                    continue targetLoop;
+                }
+                leftTicks = rightTicks;
+            }
+            // we did not find a current interval, use the last one
+            final InterpolationInterval i = intervalList[intervalCount - 1];
+            // the last interval may end before the timeline ends, make sure we
+            // set the end value
+            final double frac = Math.min(1.0, (double)(ticks - leftTicks)
+                    / (i.ticks - leftTicks));
+            i.interpolate(frac);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/InfiniteClipEnvelope.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Animation;
+import javafx.animation.Animation.Status;
+import javafx.util.Duration;
+
+public class InfiniteClipEnvelope extends ClipEnvelope {
+    
+    private boolean autoReverse;
+    private long pos;
+    
+    protected InfiniteClipEnvelope(Animation animation) {
+        super(animation);
+        if (animation != null) {
+            autoReverse = animation.isAutoReverse();
+        }
+    }
+
+    @Override
+    public void setAutoReverse(boolean autoReverse) {
+        this.autoReverse = autoReverse;
+    }
+
+    @Override
+    protected double calculateCurrentRate() {
+        return !autoReverse? rate
+                : (ticks % (2 * cycleTicks) < cycleTicks)? rate : -rate;
+    }
+    
+    @Override
+    public ClipEnvelope setCycleDuration(Duration cycleDuration) {
+        if (cycleDuration.isIndefinite()) {
+            return ClipEnvelopeFactory.create(animation);
+        }
+        updateCycleTicks(cycleDuration);
+        return this;
+    }
+
+    @Override
+    public ClipEnvelope setCycleCount(int cycleCount) {
+       return (cycleCount != Animation.INDEFINITE)? ClipEnvelopeFactory.create(animation) : this;
+    }
+
+    @Override
+    public void setRate(double rate) {
+        final Status status = animation.getStatus();
+        if (status != Status.STOPPED) {
+            if (status == Status.RUNNING) {
+                setCurrentRate((Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate);
+            }
+            deltaTicks = ticks - Math.round((ticks - deltaTicks) * Math.abs(rate / this.rate));
+            if (rate * this.rate < 0) {
+                final long delta = 2 * cycleTicks - pos;
+                deltaTicks += delta;
+                ticks += delta;
+            }
+            abortCurrentPulse();
+        }
+        this.rate = rate;
+    }
+    
+    @Override
+    public void timePulse(long currentTick) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        aborted = false;
+        inTimePulse = true;
+
+        try {
+            final long oldTicks = ticks;
+            ticks = Math.max(0, deltaTicks + Math.round(currentTick * Math.abs(rate)));
+
+            long overallDelta = ticks - oldTicks; // overall delta between current position and new position
+            if (overallDelta == 0) {
+                return;
+            }
+
+            long cycleDelta = (currentRate > 0)? cycleTicks - pos : pos; // delta to reach end of cycle
+
+            while (overallDelta >= cycleDelta) {
+                if (cycleDelta > 0) {
+                    pos = (currentRate > 0)? cycleTicks : 0;
+                    overallDelta -= cycleDelta;
+                    AnimationAccessor.getDefault().playTo(animation, pos, cycleTicks);
+                    if (aborted) {
+                        return;
+                    }
+                }
+                if (autoReverse) {
+                    setCurrentRate(-currentRate);
+                } else {
+                    pos = (currentRate > 0)? 0 : cycleTicks;
+                    AnimationAccessor.getDefault().jumpTo(animation, pos, cycleTicks, false);
+                }
+                cycleDelta = cycleTicks;
+            }
+
+            if (overallDelta > 0) {
+                pos += (currentRate > 0)? overallDelta : -overallDelta;
+                AnimationAccessor.getDefault().playTo(animation, pos, cycleTicks);
+            }
+
+        } finally {
+            inTimePulse = false;
+        }
+    }
+
+    @Override
+    public void jumpTo(long newTicks) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        final long oldTicks = ticks;
+        ticks = Math.max(0, newTicks) % (2 * cycleTicks);
+        final long delta = ticks - oldTicks;
+        if (delta != 0) {
+            deltaTicks += delta;
+            if (autoReverse) {
+                if (ticks > cycleTicks) {
+                    pos = 2 * cycleTicks - ticks;
+                    if (animation.getStatus() == Status.RUNNING) {
+                        setCurrentRate(-rate);
+                    }
+                } else {
+                    pos = ticks;
+                    if (animation.getStatus() == Status.RUNNING) {
+                        setCurrentRate(rate);
+                    }
+                }
+            } else {
+                pos = ticks % cycleTicks;
+                if (pos == 0) {
+                    pos = ticks;
+                }
+            }
+            AnimationAccessor.getDefault().jumpTo(animation, pos, cycleTicks, false);
+            abortCurrentPulse();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/InterpolationInterval.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Interpolator;
+import javafx.animation.KeyValue;
+import javafx.beans.value.WritableBooleanValue;
+import javafx.beans.value.WritableDoubleValue;
+import javafx.beans.value.WritableFloatValue;
+import javafx.beans.value.WritableIntegerValue;
+import javafx.beans.value.WritableLongValue;
+import javafx.beans.value.WritableValue;
+
+import com.sun.scenario.animation.NumberTangentInterpolator;
+
+public abstract class InterpolationInterval {
+
+    protected final long ticks;
+    protected final Interpolator rightInterpolator;
+
+    protected InterpolationInterval(long ticks,
+            Interpolator rightInterpolator) {
+        this.ticks = ticks;
+        this.rightInterpolator = rightInterpolator;
+    }
+
+    public abstract void interpolate(double frac);
+
+    public abstract void recalculateStartValue();
+
+    public static InterpolationInterval create(KeyValue rightKeyValue,
+            long ticks, KeyValue leftKeyValue, long duration) {
+        switch (rightKeyValue.getType()) {
+            case BOOLEAN:
+                return new BooleanInterpolationInterval(rightKeyValue, ticks,
+                        leftKeyValue.getEndValue());
+            case DOUBLE:
+                return (((Interpolator) leftKeyValue.getInterpolator() instanceof NumberTangentInterpolator) || ((Interpolator) rightKeyValue
+                        .getInterpolator() instanceof NumberTangentInterpolator)) ? new TangentDoubleInterpolationInterval(
+                        rightKeyValue, ticks, leftKeyValue, duration)
+                        : new DoubleInterpolationInterval(rightKeyValue,
+                                ticks, leftKeyValue.getEndValue());
+            case FLOAT:
+                return (((Interpolator) leftKeyValue.getInterpolator() instanceof NumberTangentInterpolator) || ((Interpolator) rightKeyValue
+                        .getInterpolator() instanceof NumberTangentInterpolator)) ? new TangentFloatInterpolationInterval(
+                        rightKeyValue, ticks, leftKeyValue, duration)
+                        : new FloatInterpolationInterval(rightKeyValue, ticks,
+                                leftKeyValue.getEndValue());
+            case INTEGER:
+                return (((Interpolator) leftKeyValue.getInterpolator() instanceof NumberTangentInterpolator) || ((Interpolator) rightKeyValue
+                        .getInterpolator() instanceof NumberTangentInterpolator)) ? new TangentIntegerInterpolationInterval(
+                        rightKeyValue, ticks, leftKeyValue, duration)
+                        : new IntegerInterpolationInterval(rightKeyValue,
+                                ticks, leftKeyValue.getEndValue());
+            case LONG:
+                return (((Interpolator) leftKeyValue.getInterpolator() instanceof NumberTangentInterpolator) || ((Interpolator) rightKeyValue
+                        .getInterpolator() instanceof NumberTangentInterpolator)) ? new TangentLongInterpolationInterval(
+                        rightKeyValue, ticks, leftKeyValue, duration)
+                        : new LongInterpolationInterval(rightKeyValue, ticks,
+                                leftKeyValue.getEndValue());
+            case OBJECT:
+                return new ObjectInterpolationInterval(rightKeyValue, ticks,
+                        leftKeyValue.getEndValue());
+        }
+        throw new RuntimeException("Should not reach here");
+    }
+
+    public static InterpolationInterval create(KeyValue rightKeyValue,
+            long ticks) {
+        switch (rightKeyValue.getType()) {
+            case BOOLEAN:
+                return new BooleanInterpolationInterval(rightKeyValue, ticks);
+            case DOUBLE:
+                return ((Interpolator) rightKeyValue.getInterpolator() instanceof NumberTangentInterpolator) ? new TangentDoubleInterpolationInterval(
+                        rightKeyValue, ticks)
+                        : new DoubleInterpolationInterval(rightKeyValue, ticks);
+            case FLOAT:
+                return ((Interpolator) rightKeyValue.getInterpolator() instanceof NumberTangentInterpolator) ? new TangentFloatInterpolationInterval(
+                        rightKeyValue, ticks)
+                        : new FloatInterpolationInterval(rightKeyValue, ticks);
+            case INTEGER:
+                return ((Interpolator) rightKeyValue.getInterpolator() instanceof NumberTangentInterpolator) ? new TangentIntegerInterpolationInterval(
+                        rightKeyValue, ticks)
+                        : new IntegerInterpolationInterval(rightKeyValue,
+                                ticks);
+            case LONG:
+                return ((Interpolator) rightKeyValue.getInterpolator() instanceof NumberTangentInterpolator) ? new TangentLongInterpolationInterval(
+                        rightKeyValue, ticks) : new LongInterpolationInterval(
+                        rightKeyValue, ticks);
+            case OBJECT:
+                return new ObjectInterpolationInterval(rightKeyValue, ticks);
+        }
+        throw new RuntimeException("Should not reach here");
+    }
+
+    private static abstract class TangentInterpolationInterval extends
+            InterpolationInterval {
+
+        private final double duration;
+        private final double p2;
+        protected final double p3;
+        private final NumberTangentInterpolator leftInterpolator;
+
+        protected double p0;
+        private double p1;
+
+        private TangentInterpolationInterval(KeyValue rightKeyValue,
+                long ticks, KeyValue leftKeyValue, long duration) {
+            super(ticks, rightKeyValue.getInterpolator());
+            assert (rightKeyValue.getEndValue() instanceof Number)
+                    && (leftKeyValue.getEndValue() instanceof Number);
+
+            this.duration = duration;
+            final Interpolator rawLeftInterpolator = leftKeyValue
+                    .getInterpolator();
+            leftInterpolator = (rawLeftInterpolator instanceof NumberTangentInterpolator) ? (NumberTangentInterpolator) rawLeftInterpolator
+                    : null;
+            recalculateStartValue(((Number) leftKeyValue.getEndValue())
+                    .doubleValue());
+
+            final NumberTangentInterpolator interpolator = (rightInterpolator instanceof NumberTangentInterpolator) ? (NumberTangentInterpolator) rightInterpolator
+                    : null;
+            p3 = ((Number) rightKeyValue.getEndValue()).doubleValue();
+            final double p2Delta = (interpolator == null) ? 0 : (interpolator
+                    .getInValue() - p3)
+                    * duration
+                    / interpolator.getInMillis()
+                    / 3;
+            p2 = p3 + p2Delta;
+        }
+
+        private TangentInterpolationInterval(KeyValue rightKeyValue,
+                long ticks) {
+            super(ticks, rightKeyValue.getInterpolator());
+            assert rightKeyValue.getEndValue() instanceof Number;
+
+            this.duration = ticks;
+            leftInterpolator = null;
+
+            final NumberTangentInterpolator interpolator = (rightInterpolator instanceof NumberTangentInterpolator) ? (NumberTangentInterpolator) rightInterpolator
+                    : null;
+            p3 = ((Number) rightKeyValue.getEndValue()).doubleValue();
+            final double p2Delta = (interpolator == null) ? 0 : (interpolator
+                    .getInValue() - p3)
+                    * duration
+                    / interpolator.getInMillis()
+                    / 3;
+            p2 = p3 + p2Delta;
+        }
+
+        protected double calculate(double t) {
+            final double oneMinusT = 1.0 - t;
+            final double tSquared = t * t;
+            final double oneMinusTSquared = oneMinusT * oneMinusT;
+
+            return oneMinusTSquared * oneMinusT * p0 + 3 * oneMinusTSquared * t
+                    * p1 + 3 * oneMinusT * tSquared * p2 + tSquared * t * p3;
+        };
+
+        protected final void recalculateStartValue(double leftValue) {
+            p0 = leftValue;
+            final double p1Delta = (leftInterpolator == null) ? 0
+                    : (leftInterpolator.getOutValue() - p0) * duration
+                            / leftInterpolator.getOutMillis() / 3;
+            p1 = p0 + p1Delta;
+        }
+    }
+
+    private static class BooleanInterpolationInterval extends
+            InterpolationInterval {
+
+        private final WritableBooleanValue target;
+        private boolean leftValue;
+        private final boolean rightValue;
+
+        private BooleanInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableBooleanValue)
+                    && (keyValue.getEndValue() instanceof Boolean)
+                    && (leftValue instanceof Boolean);
+            this.target = (WritableBooleanValue) keyValue.getTarget();
+            this.rightValue = (Boolean) keyValue.getEndValue();
+            this.leftValue = (Boolean) leftValue;
+        }
+
+        private BooleanInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableBooleanValue)
+                    && (keyValue.getEndValue() instanceof Boolean);
+            this.target = (WritableBooleanValue) keyValue.getTarget();
+            this.rightValue = (Boolean) keyValue.getEndValue();
+            this.leftValue = target.get();
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            final boolean value = rightInterpolator.interpolate(leftValue,
+                    rightValue, frac);
+            target.set(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.get();
+        }
+    }
+
+    private static class DoubleInterpolationInterval extends
+            InterpolationInterval {
+
+        private final WritableDoubleValue target;
+        private double leftValue;
+        private final double rightValue;
+
+        private DoubleInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableDoubleValue)
+                    && (keyValue.getEndValue() instanceof Number)
+                    && (leftValue instanceof Number);
+            this.target = (WritableDoubleValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).doubleValue();
+            this.leftValue = ((Number) leftValue).doubleValue();
+        }
+
+        private DoubleInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableDoubleValue)
+                    && (keyValue.getEndValue() instanceof Number);
+            this.target = (WritableDoubleValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).doubleValue();
+            this.leftValue = target.get();
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            final double value = rightInterpolator.interpolate(leftValue,
+                    rightValue, frac);
+            target.set(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.get();
+        }
+    }
+
+    private static class TangentDoubleInterpolationInterval extends
+            TangentInterpolationInterval {
+
+        private final WritableDoubleValue target;
+
+        private TangentDoubleInterpolationInterval(KeyValue rightKeyValue,
+                long ticks, KeyValue leftKeyValue, long duration) {
+            super(rightKeyValue, ticks, leftKeyValue, duration);
+            assert rightKeyValue.getTarget() instanceof WritableDoubleValue;
+            this.target = (WritableDoubleValue) rightKeyValue.getTarget();
+        }
+
+        private TangentDoubleInterpolationInterval(KeyValue rightKeyValue,
+                long ticks) {
+            super(rightKeyValue, ticks);
+            assert rightKeyValue.getTarget() instanceof WritableDoubleValue;
+            this.target = (WritableDoubleValue) rightKeyValue.getTarget();
+            recalculateStartValue(target.get());
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            target.set(calculate(frac));
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            recalculateStartValue(target.get());
+        }
+    }
+
+    private static class FloatInterpolationInterval extends
+            InterpolationInterval {
+
+        private final WritableFloatValue target;
+        private float leftValue;
+        private final float rightValue;
+
+        private FloatInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableFloatValue)
+                    && (keyValue.getEndValue() instanceof Number)
+                    && (leftValue instanceof Number);
+            this.target = (WritableFloatValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).floatValue();
+            this.leftValue = ((Number) leftValue).floatValue();
+        }
+
+        private FloatInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableFloatValue)
+                    && (keyValue.getEndValue() instanceof Number);
+            this.target = (WritableFloatValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).floatValue();
+            this.leftValue = target.get();
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            final float value = (float) rightInterpolator.interpolate(
+                    leftValue, rightValue, frac);
+            target.set(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.get();
+        }
+    }
+
+    private static class TangentFloatInterpolationInterval extends
+            TangentInterpolationInterval {
+
+        private final WritableFloatValue target;
+
+        private TangentFloatInterpolationInterval(KeyValue rightKeyValue,
+                long ticks, KeyValue leftKeyValue, long duration) {
+            super(rightKeyValue, ticks, leftKeyValue, duration);
+            assert rightKeyValue.getTarget() instanceof WritableFloatValue;
+            this.target = (WritableFloatValue) rightKeyValue.getTarget();
+        }
+
+        private TangentFloatInterpolationInterval(KeyValue rightKeyValue,
+                long ticks) {
+            super(rightKeyValue, ticks);
+            assert rightKeyValue.getTarget() instanceof WritableFloatValue;
+            this.target = (WritableFloatValue) rightKeyValue.getTarget();
+            recalculateStartValue(target.get());
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            target.set((float) calculate(frac));
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            recalculateStartValue(target.get());
+        }
+    }
+
+    private static class IntegerInterpolationInterval extends
+            InterpolationInterval {
+
+        private final WritableIntegerValue target;
+        private int leftValue;
+        private final int rightValue;
+
+        private IntegerInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableIntegerValue)
+                    && (keyValue.getEndValue() instanceof Number)
+                    && (leftValue instanceof Number);
+            this.target = (WritableIntegerValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).intValue();
+            this.leftValue = ((Number) leftValue).intValue();
+        }
+
+        private IntegerInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableIntegerValue)
+                    && (keyValue.getEndValue() instanceof Number);
+            this.target = (WritableIntegerValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).intValue();
+            this.leftValue = target.get();
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            final int value = rightInterpolator.interpolate(leftValue,
+                    rightValue, frac);
+            target.set(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.get();
+        }
+    }
+
+    private static class TangentIntegerInterpolationInterval extends
+            TangentInterpolationInterval {
+
+        private final WritableIntegerValue target;
+
+        private TangentIntegerInterpolationInterval(KeyValue rightKeyValue,
+                long ticks, KeyValue leftKeyValue, long duration) {
+            super(rightKeyValue, ticks, leftKeyValue, duration);
+            assert rightKeyValue.getTarget() instanceof WritableIntegerValue;
+            this.target = (WritableIntegerValue) rightKeyValue.getTarget();
+        }
+
+        private TangentIntegerInterpolationInterval(KeyValue rightKeyValue,
+                long ticks) {
+            super(rightKeyValue, ticks);
+            assert rightKeyValue.getTarget() instanceof WritableIntegerValue;
+            this.target = (WritableIntegerValue) rightKeyValue.getTarget();
+            recalculateStartValue(target.get());
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            target.set((int) Math.round(calculate(frac)));
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            recalculateStartValue(target.get());
+        }
+    }
+
+    private static class LongInterpolationInterval extends
+            InterpolationInterval {
+
+        private final WritableLongValue target;
+        private long leftValue;
+        private final long rightValue;
+
+        private LongInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableLongValue)
+                    && (keyValue.getEndValue() instanceof Number)
+                    && (leftValue instanceof Number);
+            this.target = (WritableLongValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).longValue();
+            this.leftValue = ((Number) leftValue).longValue();
+        }
+
+        private LongInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            assert (keyValue.getTarget() instanceof WritableLongValue)
+                    && (keyValue.getEndValue() instanceof Number);
+            this.target = (WritableLongValue) keyValue.getTarget();
+            this.rightValue = ((Number) keyValue.getEndValue()).longValue();
+            this.leftValue = target.get();
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            final long value = rightInterpolator.interpolate(leftValue,
+                    rightValue, frac);
+            target.set(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.get();
+        }
+    }
+
+    private static class TangentLongInterpolationInterval extends
+            TangentInterpolationInterval {
+
+        private final WritableLongValue target;
+
+        private TangentLongInterpolationInterval(KeyValue rightKeyValue,
+                long ticks, KeyValue leftKeyValue, long duration) {
+            super(rightKeyValue, ticks, leftKeyValue, duration);
+            assert rightKeyValue.getTarget() instanceof WritableLongValue;
+            this.target = (WritableLongValue) rightKeyValue.getTarget();
+        }
+
+        private TangentLongInterpolationInterval(KeyValue rightKeyValue,
+                long ticks) {
+            super(rightKeyValue, ticks);
+            assert rightKeyValue.getTarget() instanceof WritableLongValue;
+            this.target = (WritableLongValue) rightKeyValue.getTarget();
+            recalculateStartValue(target.get());
+        }
+
+        @Override
+        public void interpolate(double frac) {
+            target.set(Math.round(calculate(frac)));
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            recalculateStartValue(target.get());
+        }
+    }
+
+    private static class ObjectInterpolationInterval extends
+            InterpolationInterval {
+
+        @SuppressWarnings("rawtypes")
+        private final WritableValue target;
+        private Object leftValue;
+        private final Object rightValue;
+
+        private ObjectInterpolationInterval(KeyValue keyValue, long ticks,
+                Object leftValue) {
+            super(ticks, keyValue.getInterpolator());
+            this.target = keyValue.getTarget();
+            this.rightValue = keyValue.getEndValue();
+            this.leftValue = leftValue;
+        }
+
+        private ObjectInterpolationInterval(KeyValue keyValue, long ticks) {
+            super(ticks, keyValue.getInterpolator());
+            this.target = keyValue.getTarget();
+            this.rightValue = keyValue.getEndValue();
+            this.leftValue = target.getValue();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void interpolate(double frac) {
+            final Object value = rightInterpolator.interpolate(leftValue,
+                    rightValue, frac);
+            target.setValue(value);
+        };
+
+        @Override
+        public void recalculateStartValue() {
+            leftValue = target.getValue();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/PulseReceiver.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+/**
+ * A PulseReceiver can receive regular pulses from the MasterTimer. Removing
+ * receivers from the MasterTimer needs to be in-sync with the
+ * timePulse-iteration. The receiver is removed if timePulse returns true.
+ */
+public interface PulseReceiver {
+    /**
+     * Callback triggered to send regular pulses to the PulseReceiver
+     * 
+     * @param now
+     *            Timestamp of the pulse.
+     * @return true if PulseReceiver should be removed from the MasterTimer.
+     */
+    void timePulse(long now);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/SimpleClipInterpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.beans.value.WritableValue;
+import javafx.util.Duration;
+
+/**
+ * Simplified implementation of ClipCore, which is used for timelines with
+ * exactly two keyframes.
+ */
+
+class SimpleClipInterpolator extends ClipInterpolator {
+
+    private static final KeyFrame ZERO_FRAME = new KeyFrame(Duration.ZERO);
+
+    // The list of all targets in this clip
+    private KeyFrame startKeyFrame;
+    private KeyFrame endKeyFrame;
+    private long endTicks;
+    private InterpolationInterval[] interval;
+    private int undefinedStartValueCount;
+    private long ticks;
+
+    // Is internal representation uptodate?
+    private boolean invalid = true;
+
+    SimpleClipInterpolator(KeyFrame startKeyFrame, KeyFrame endKeyFrame, long ticks) {
+        this.startKeyFrame = startKeyFrame;
+        this.endKeyFrame = endKeyFrame;
+        this.endTicks = ticks;
+    }
+
+    SimpleClipInterpolator(KeyFrame endKeyFrame, long ticks) {
+        this.startKeyFrame = ZERO_FRAME;
+        this.endKeyFrame = endKeyFrame;
+        this.endTicks = ticks;
+    }
+
+    // See comment in ClipInterpolator
+    @Override
+    ClipInterpolator setKeyFrames(KeyFrame[] keyFrames, long[] keyFrameTicks) {
+        if (ClipInterpolator.getRealKeyFrameCount(keyFrames) != 2) {
+            return ClipInterpolator.create(keyFrames, keyFrameTicks);
+        }
+        if (keyFrames.length == 1) {
+            startKeyFrame = ZERO_FRAME;
+            endKeyFrame = keyFrames[0];
+            endTicks = keyFrameTicks[0];
+        } else {
+            startKeyFrame = keyFrames[0];
+            endKeyFrame = keyFrames[1];
+            endTicks = keyFrameTicks[1];
+        }
+        invalid = true;
+        return this;
+    }
+
+    @Override
+    void validate(boolean forceSync) {
+        if (invalid) {
+            ticks = endTicks;
+            
+            final Map<WritableValue<?>, KeyValue> map = new HashMap<WritableValue<?>, KeyValue>();
+            // create a map from target => keyValues of endFrame
+            for (final KeyValue keyValue : endKeyFrame.getValues()) {
+                map.put(keyValue.getTarget(), keyValue);
+            }
+
+            final int valueCount = map.size();
+            interval = new InterpolationInterval[valueCount];
+
+            // iterate through keyValues in startFrame and generate intervals
+            // if we find a matching keyValue in the startFrame, the entry is
+            // removed from the map
+            int i = 0;
+            for (final KeyValue startKeyValue : startKeyFrame.getValues()) {
+                final WritableValue<?> target = startKeyValue.getTarget();
+                final KeyValue endKeyValue = map.get(target);
+                if (endKeyValue != null) {
+                    interval[i++] = InterpolationInterval.create(endKeyValue,
+                            ticks, startKeyValue, ticks);
+                    map.remove(target);
+                }
+            }
+
+            // remaining entries in the map have no start value defined
+            undefinedStartValueCount = map.values().size();
+            for (final KeyValue endKeyValue : map.values()) {
+                interval[i++] = InterpolationInterval.create(endKeyValue,
+                        ticks);
+            }
+
+            invalid = false;
+        } else if (forceSync) {
+            // iterate through keyValues with undefined start value
+            final int n = interval.length;
+            for (int i = n - undefinedStartValueCount; i < n; i++) {
+                interval[i].recalculateStartValue();
+            }
+        }
+    }
+
+    @Override
+    void interpolate(long ticks) {
+        final double frac = ((double)ticks / this.ticks);
+        final int n = interval.length;
+        for (int i = 0; i < n; i++) {
+            interval[i].interpolate(frac);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/SingleLoopClipEnvelope.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.scenario.animation.shared;
+
+import javafx.animation.Animation;
+import javafx.animation.Animation.Status;
+import javafx.util.Duration;
+
+public class SingleLoopClipEnvelope extends ClipEnvelope {
+    
+    private int cycleCount;
+    
+    @Override
+    public void setRate(double rate) {
+        final Status status = animation.getStatus();
+        if (status != Status.STOPPED) {
+            if (status == Status.RUNNING) {
+                setCurrentRate((Math.abs(currentRate - this.rate) < EPSILON) ? rate : -rate);
+            }
+            deltaTicks = ticks - Math.round((ticks - deltaTicks) * rate / this.rate);
+            abortCurrentPulse();
+        }
+        this.rate = rate;
+    }
+
+    @Override
+    public void setAutoReverse(boolean autoReverse) {
+        // ignore autoReverse
+    }
+
+    @Override
+    protected double calculateCurrentRate() {
+        return rate;
+    }
+    
+    protected SingleLoopClipEnvelope(Animation animation) {
+        super(animation);
+        if (animation != null) {
+            cycleCount = animation.getCycleCount();
+        }
+    }
+
+    @Override
+    public boolean wasSynched() {
+        return super.wasSynched() && cycleCount != 0;
+    }
+
+    @Override
+    public ClipEnvelope setCycleDuration(Duration cycleDuration) {
+        if ((cycleCount != 1) && !cycleDuration.isIndefinite()) {
+            return ClipEnvelopeFactory.create(animation);
+        }
+        updateCycleTicks(cycleDuration);
+        return this;
+    }
+
+    @Override
+    public ClipEnvelope setCycleCount(int cycleCount) {
+        if ((cycleCount != 1) && (cycleTicks != ClipEnvelope.INDEFINITE)) {
+            return ClipEnvelopeFactory.create(animation);
+        }
+        this.cycleCount = cycleCount;
+        return this;
+    }
+
+    @Override
+    public void timePulse(long currentTick) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        aborted = false;
+        inTimePulse = true;
+
+        try {
+            ticks = ClipEnvelope.checkBounds(deltaTicks + Math.round(currentTick * currentRate), cycleTicks);
+            AnimationAccessor.getDefault().playTo(animation, ticks, cycleTicks);
+    
+            final boolean reachedEnd = (currentRate > 0)? (ticks == cycleTicks) : (ticks == 0);
+            if(reachedEnd && !aborted) {
+                AnimationAccessor.getDefault().finished(animation);
+            }
+        } finally {
+            inTimePulse = false;
+        }
+    }
+
+    @Override
+    public void jumpTo(long ticks) {
+        if (cycleTicks == 0L) {
+            return;
+        }
+        final long newTicks = ClipEnvelope.checkBounds(ticks, cycleTicks);
+        deltaTicks += (newTicks - this.ticks);
+        this.ticks = newTicks;
+
+        AnimationAccessor.getDefault().jumpTo(animation, newTicks, cycleTicks, false);
+
+        abortCurrentPulse();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/com/sun/scenario/animation/shared/TimelineClipCore.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.scenario.animation.shared;
+
+import com.sun.javafx.animation.TickCalculation;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import javafx.animation.Animation.Status;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.util.Duration;
+
+/**
+ * An instance of ClipCore handles the core part of a clip.
+ * 
+ * The functionality to react on a pulse-signal from the timer is implemented in
+ * two classes: ClipEnvelope and ClipCore. ClipEnvelope is responsible for the
+ * "loop-part" (keeping track of the number of cycles, handling the direction of
+ * the clip etc.). ClipCore takes care of the inner part (interpolating the
+ * values, triggering the action-functions, ...)
+ * 
+ * Both classes have an abstract public definition and can only be created using
+ * the factory method create(). The intend is to provide a general
+ * implementation plus eventually some fast-track implementations for common use
+ * cases.
+ */
+
+// @@OPT
+// - Use known information (kf) in visitKeyFrame to set values?
+
+public class TimelineClipCore {
+
+    private static final int UNDEFINED_KEYFRAME = -1;
+
+    /**
+     * Note: this comparator imposes orderings that are inconsistent with
+     * equals.
+     */
+    private static final Comparator<KeyFrame> KEY_FRAME_COMPARATOR = new Comparator<KeyFrame>() {
+        @Override
+        public int compare(KeyFrame kf1, KeyFrame kf2) {
+            return kf1.getTime().compareTo(kf2.getTime());
+        }
+    };
+
+    // The owner of this ClipCore
+    Timeline timeline;
+
+    // The sorted list of keyframes
+    private KeyFrame[] keyFrames = new KeyFrame[0];
+    private long[] keyFrameTicks = new long[0];
+
+    private ClipInterpolator clipInterpolator;
+
+    public TimelineClipCore(Timeline timeline) {
+        this.timeline = timeline;
+        this.clipInterpolator = ClipInterpolator.create(keyFrames, keyFrameTicks);
+    }
+
+    /**
+     * Changes the keyframes.
+     */
+    public Duration setKeyFrames(Collection<KeyFrame> keyFrames) {
+        final int n = keyFrames.size();
+        final KeyFrame[] sortedKeyFrames = new KeyFrame[n];
+        keyFrames.toArray(sortedKeyFrames);
+        Arrays.sort(sortedKeyFrames, KEY_FRAME_COMPARATOR);
+        
+        this.keyFrames = sortedKeyFrames;
+        keyFrameTicks = new long[n];
+        for (int i = 0; i < n; ++i) {
+            keyFrameTicks[i] = TickCalculation.fromDuration(this.keyFrames[i].getTime());
+        }
+        clipInterpolator = clipInterpolator.setKeyFrames(sortedKeyFrames, keyFrameTicks);
+        final Duration duration = (n == 0) ? Duration.ZERO
+                : sortedKeyFrames[n-1].getTime();
+        return duration;
+    }
+
+    public void notifyCurrentRateChanged() {
+        // special case: if clip is toggled while stopped, we want to revisit
+        // all key frames
+        if (timeline.getStatus() != Status.RUNNING) {
+            clearLastKeyFrame();
+        }
+    }
+
+    /**
+     * This method is called if while visiting a keyframe of a timeline the time
+     * or rate are changed, or if the timeline is stopped. In these cases
+     * visiting the keyframes must be aborted.
+     */
+    public void abort() {
+        aborted = true;
+    }
+
+    private boolean aborted = false;
+
+    // The index of the keyframe that was visited last
+    private int lastKF = UNDEFINED_KEYFRAME;
+
+    // The position where clip currently stands
+    private long curTicks = 0;
+
+    private void clearLastKeyFrame() {
+        lastKF = UNDEFINED_KEYFRAME;
+    }
+
+    public void jumpTo(long ticks, boolean forceJump) {
+        lastKF = UNDEFINED_KEYFRAME;
+        curTicks = ticks;
+        if (timeline.getStatus() != Status.STOPPED || forceJump) {
+            if (forceJump) {
+                clipInterpolator.validate(false);
+            }
+            clipInterpolator.interpolate(ticks);
+        }
+    }
+
+    public void start(boolean forceSync) {
+        clearLastKeyFrame();
+        clipInterpolator.validate(forceSync);
+        if (curTicks > 0) {
+            clipInterpolator.interpolate(curTicks);
+        }
+    }
+
+    /**
+     * Called to visit all keyframes within a specified time-interval
+     * 
+     * @param toTime
+     *            the stopping time of the interval (inclusive)
+     * @param catchingUp
+     *            true, if we are running behind and want to skip keyframe
+     *            visits (keyframe.canSkip is true)
+     * @param visitLast
+     *            true, if we want to enforce visiting the last keyframe within
+     *            toTime
+     */
+    public void playTo(long ticks) {
+        aborted = false;
+        final boolean forward = curTicks <= ticks;
+
+        if (forward) {
+            final int fromKF = (lastKF == UNDEFINED_KEYFRAME) ? 0
+                    : (keyFrameTicks[lastKF] <= curTicks) ? lastKF + 1
+                            : lastKF;
+            final int toKF = keyFrames.length;
+            for (int fi = fromKF; fi < toKF; fi++) {
+                final long kfTicks = keyFrameTicks[fi];
+                if (kfTicks > ticks) {
+                    lastKF = fi - 1;
+                    break;
+                }
+                if (kfTicks >= curTicks) {
+                    visitKeyFrame(fi, kfTicks);
+                    if (aborted) {
+                        break;
+                    }
+                }
+            }
+        } else {
+            final int fromKF = (lastKF == UNDEFINED_KEYFRAME) ? keyFrames.length - 1
+                    : (keyFrameTicks[lastKF] >= curTicks) ? lastKF - 1
+                            : lastKF;
+            for (int fi = fromKF; fi >= 0; fi--) {
+                final long kfTicks = keyFrameTicks[fi];
+                if (kfTicks < ticks) {
+                    lastKF = fi + 1;
+                    break;
+                }
+                if (kfTicks <= curTicks) {
+                    visitKeyFrame(fi, kfTicks);
+                    if (aborted) {
+                        break;
+                    }
+                }
+            }
+        }
+        if (!aborted
+                && ((lastKF == UNDEFINED_KEYFRAME)
+                        || keyFrameTicks[lastKF] != ticks || (keyFrames[lastKF]
+                        .getOnFinished() == null))) {
+            setTime(ticks);
+            clipInterpolator.interpolate(ticks);
+        }
+    }
+
+    private void setTime(long ticks) {
+        curTicks = ticks;
+        AnimationAccessor.getDefault().setCurrentTicks(timeline, ticks);
+    }
+
+    /**
+     * Visit a single keyframe
+     * 
+     * @param kfIndex
+     *            the index of the keyframe in the keyframe-array
+     * @param kfTicks
+     *            the time of that keyframe
+     */
+    private void visitKeyFrame(int kfIndex, long kfTicks) {
+        if (kfIndex != lastKF) { // suppress double visiting on toggle for
+                                 // autoReverse
+            lastKF = kfIndex;
+
+            final KeyFrame kf = keyFrames[kfIndex];
+            final EventHandler<ActionEvent> onFinished = kf.getOnFinished();
+
+            if (onFinished != null) {
+                // visit the action of this keyframe
+                setTime(kfTicks);
+                clipInterpolator.interpolate(kfTicks);
+                try {
+                    onFinished.handle(new ActionEvent(kf, null));
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/Animation.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+import com.sun.javafx.animation.TickCalculation;
+import java.util.HashMap;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.DoublePropertyBase;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.IntegerPropertyBase;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import javafx.beans.property.ReadOnlyDoublePropertyBase;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.ReadOnlyObjectPropertyBase;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableMap;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.util.Duration;
+
+import com.sun.scenario.ToolkitAccessor;
+import com.sun.scenario.animation.shared.AnimationPulseReceiver;
+import com.sun.scenario.animation.shared.ClipEnvelope;
+import com.sun.scenario.animation.shared.ClipEnvelopeFactory;
+
+import static com.sun.javafx.animation.TickCalculation.fromDuration;
+import com.sun.scenario.animation.AbstractMasterTimer;
+
+/**
+ * The class {@code Animation} provides the core functionality of all animations
+ * used in the JavaFX runtime.
+ * <p>
+ * An animation can run in a loop by setting {@link #cycleCount}. To make an
+ * animation run back and forth while looping, set the {@link #autoReverse}
+ * -flag.
+ * <p>
+ * Call {@link #play()} or {@link #playFromStart()} to play an {@code Animation}
+ * . The {@code Animation} progresses in the direction and speed specified by
+ * {@link #rate}, and stops when its duration is elapsed. An {@code Animation}
+ * with indefinite duration (a {@link #cycleCount} of {@link #INDEFINITE}) runs
+ * repeatedly until the {@link #stop()} method is explicitly called, which will
+ * stop the running {@code Animation} and reset its play head to the initial
+ * position.
+ * <p>
+ * An {@code Animation} can be paused by calling {@link #pause()}, and the next
+ * {@link #play()} call will resume the {@code Animation} from where it was
+ * paused.
+ * <p>
+ * An {@code Animation}'s play head can be randomly positioned, whether it is
+ * running or not. If the {@code Animation} is running, the play head jumps to
+ * the specified position immediately and continues playing from new position.
+ * If the {@code Animation} is not running, the next {@link #play()} will start
+ * the {@code Animation} from the specified position.
+ * <p>
+ * Inverting the value of {@link #rate} toggles the play direction.
+ * 
+ * @see Timeline
+ * @see Transition
+ * 
+ */
+public abstract class Animation {
+
+    static {
+        AnimationAccessorImpl.DEFAULT = new AnimationAccessorImpl();
+    }
+
+    /**
+     * Used to specify an animation that repeats indefinitely, until the
+     * {@code stop()} method is called.
+     */
+    public static final int INDEFINITE = -1;
+
+    /**
+     * The possible states for {@link Animation#statusProperty status}.
+     */
+    public static enum Status {
+        /**
+         * The paused state.
+         */
+        PAUSED,
+        /**
+         * The running state.
+         */
+        RUNNING,
+        /**
+         * The stopped state.
+         */
+        STOPPED
+    }
+
+    private static final double EPSILON = 1e-12;
+    
+    private class CurrentRateProperty extends ReadOnlyDoublePropertyBase {
+        private double value;
+        
+        @Override
+        public Object getBean() {
+            return Animation.this;
+        }
+
+        @Override
+        public String getName() {
+            return "currentRate";
+        }
+
+        @Override
+        public double get() {
+            return value;
+        }
+        
+        private void set(double value) {
+            this.value = value;
+            fireValueChangedEvent();
+        }
+    }
+    
+    private class AnimationReadOnlyProperty<T> extends ReadOnlyObjectPropertyBase<T> {
+        
+        private final String name;
+        private T value;
+        
+        private AnimationReadOnlyProperty(String name, T value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        @Override
+        public Object getBean() {
+            return Animation.this;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public T get() {
+            return value;
+        }
+        
+        private void set(T value) {
+            this.value = value;
+            fireValueChangedEvent();
+        }
+    }
+    
+
+    private final AnimationPulseReceiver pulseReceiver;
+    
+    /**
+     * The parent of this {@code Animation}. If this animation has not been
+     * added to another animation, such as {@link ParallelTransition} and
+     * {@link SequentialTransition}, then parent will be null.
+     *
+     * @defaultValue null
+     */
+    Animation parent = null;
+
+    /* Package-private for testing purposes */
+    ClipEnvelope clipEnvelope;
+
+    private boolean lastPlayedFinished = false;
+    
+    private boolean lastPlayedForward = true;
+    /**
+     * Defines the direction/speed at which the {@code Animation} is expected to
+     * be played.
+     * <p>
+     * The absolute value of {@code rate} indicates the speed which the
+     * {@code Animation} is to be played, while the sign of {@code rate}
+     * indicates the direction. A positive value of {@code rate} indicates
+     * forward play, a negative value indicates backward play and {@code 0.0} to
+     * stop a running {@code Animation}.
+     * <p>
+     * Rate {@code 1.0} is normal play, {@code 2.0} is 2 time normal,
+     * {@code -1.0} is backwards, etc...
+     * 
+     * <p>
+     * Inverting the rate of a running {@code Animation} will cause the
+     * {@code Animation} to reverse direction in place and play back over the
+     * portion of the {@code Animation} that has already elapsed.
+     * 
+     * @defaultValue 1.0
+     */
+    private DoubleProperty rate;
+    private static final double DEFAULT_RATE = 1.0;
+
+    public final void setRate(double value) {
+        if ((rate != null) || (Math.abs(value - DEFAULT_RATE) > EPSILON)) {
+            rateProperty().set(value);
+        }
+    }
+
+    public final double getRate() {
+        return (rate == null)? DEFAULT_RATE : rate.get();
+    }
+
+    public final DoubleProperty rateProperty() {
+        if (rate == null) {
+            rate = new DoublePropertyBase(DEFAULT_RATE) {
+
+                @Override
+                public void invalidated() {
+                    final double newRate = getRate();
+                    if (isRunningEmbedded()) {
+                        if (isBound()) {
+                            unbind();
+                        }
+                        set(oldRate);
+                        throw new IllegalArgumentException("Cannot set rate of embedded animation while running.");
+                    } else {
+                        if (Math.abs(newRate) < EPSILON) {
+                            if (getStatus() == Status.RUNNING) {
+                                lastPlayedForward = (Math.abs(getCurrentRate()
+                                        - oldRate) < EPSILON);
+                            }
+                            setCurrentRate(0.0);
+                            pulseReceiver.pause();
+                        } else {
+                            if (getStatus() == Status.RUNNING) {
+                                final double currentRate = getCurrentRate();
+                                if (Math.abs(currentRate) < EPSILON) {
+                                    setCurrentRate(lastPlayedForward ? newRate : -newRate);
+                                    pulseReceiver.resume();
+                                } else {
+                                    final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON;
+                                    setCurrentRate(playingForward ? newRate : -newRate);
+                                }
+                            }
+                            oldRate = newRate;
+                        }
+                        clipEnvelope.setRate(newRate);
+                    }
+                }
+
+                @Override
+                public Object getBean() {
+                    return Animation.this;
+                }
+
+                @Override
+                public String getName() { 
+                    return "rate";
+                }
+            };
+        }
+        return rate;
+    }
+
+    private boolean isRunningEmbedded() {
+        if (parent == null) {
+            return false;
+        }
+        return parent.getStatus() != Status.STOPPED || parent.isRunningEmbedded();
+    }
+
+    private double oldRate = 1.0;
+    /**
+     * Read-only variable to indicate current direction/speed at which the
+     * {@code Animation} is being played.
+     * <p>
+     * {@code currentRate} is not necessary equal to {@code rate}.
+     * {@code currentRate} is set to {@code 0.0} when animation is paused or
+     * stopped. {@code currentRate} may also point to different direction during
+     * reverse cycles when {@code autoReverse} is {@code true}
+     * 
+     * @defaultValue 0.0
+     */
+    private ReadOnlyDoubleProperty currentRate;
+    private static final double DEFAULT_CURRENT_RATE = 0.0;
+    
+    private void setCurrentRate(double value) {
+        if ((currentRate != null) || (Math.abs(value - DEFAULT_CURRENT_RATE) > EPSILON)) {
+            ((CurrentRateProperty)currentRateProperty()).set(value);
+        }
+    }
+
+    public final double getCurrentRate() {
+        return (currentRate == null)? DEFAULT_CURRENT_RATE : currentRate.get();
+    }
+
+    public final ReadOnlyDoubleProperty currentRateProperty() {
+        if (currentRate == null) {
+            currentRate = new CurrentRateProperty();
+        }
+        return currentRate;
+    }
+
+    /**
+     * Read-only variable to indicate the duration of one cycle of this
+     * {@code Animation}: the time it takes to play from time 0 to the
+     * {@code KeyFrame} with the largest time (at the default {@code rate} of
+     * 1.0).
+     * 
+     * <p>
+     * This is set to the largest time value of its keyFrames.
+     * 
+     * @defaultValue 0ms
+     */
+    private ReadOnlyObjectProperty<Duration> cycleDuration;
+    private static final Duration DEFAULT_CYCLE_DURATION = Duration.ZERO;
+
+    protected final void setCycleDuration(Duration value) {
+        if ((cycleDuration != null) || (!DEFAULT_CYCLE_DURATION.equals(value))) {
+            ((AnimationReadOnlyProperty<Duration>)cycleDurationProperty()).set(value);
+            updateTotalDuration();
+        }
+    }
+
+    public final Duration getCycleDuration() {
+        return (cycleDuration == null)? DEFAULT_CYCLE_DURATION : cycleDuration.get();
+    }
+
+    public final ReadOnlyObjectProperty<Duration> cycleDurationProperty() {
+        if (cycleDuration == null) {
+            cycleDuration = new AnimationReadOnlyProperty<Duration>("cycleDuration", DEFAULT_CYCLE_DURATION);
+        }
+        return cycleDuration;
+    }
+
+    /**
+     * Read-only variable to indicate the total duration of this
+     * {@code Animation}, including repeats. A {@code Animation} with a {@code cycleCount}
+     * of {@code Animation.INDEFINITE} will have a {@code totalDuration} of
+     * {@code Duration.INDEFINITE}.
+     * 
+     * <p>
+     * This is set to cycleDuration * cycleCount.
+     * 
+     * @defaultValue 0ms
+     */
+    private ReadOnlyObjectProperty<Duration> totalDuration;
+    private static final Duration DEFAULT_TOTAL_DURATION = Duration.ZERO;
+
+    public final Duration getTotalDuration() {
+        return (totalDuration == null)? DEFAULT_TOTAL_DURATION : totalDuration.get();
+    }
+
+    public final ReadOnlyObjectProperty<Duration> totalDurationProperty() {
+        if (totalDuration == null) {
+            totalDuration = new AnimationReadOnlyProperty<Duration>("totalDuration", DEFAULT_TOTAL_DURATION);
+        }
+        return totalDuration;
+    }
+
+    private void updateTotalDuration() {
+        // Implementing the bind eagerly, because cycleCount and
+        // cycleDuration should not change that often
+        final int cycleCount = getCycleCount();
+        final Duration cycleDuration = getCycleDuration();
+        final Duration newTotalDuration = Duration.ZERO.equals(cycleDuration) ? Duration.ZERO
+                : (cycleCount == Animation.INDEFINITE) ? Duration.INDEFINITE
+                        : (cycleCount <= 1) ? cycleDuration : cycleDuration
+                                .multiply(cycleCount);
+        if ((totalDuration != null) || (!DEFAULT_TOTAL_DURATION.equals(newTotalDuration))) {
+            ((AnimationReadOnlyProperty<Duration>)totalDurationProperty()).set(newTotalDuration);
+        }
+        if (newTotalDuration.lessThan(getCurrentTime())) {
+            jumpTo(newTotalDuration);
+        }
+    }
+
+    /**
+     * Defines the {@code Animation}'s play head position.
+     * 
+     * @defaultValue 0ms
+     */
+    private CurrentTimeProperty currentTime;
+    private long currentTicks;
+    private class CurrentTimeProperty extends ReadOnlyObjectPropertyBase<Duration> {
+        
+        @Override
+        public Object getBean() {
+            return Animation.this;
+        }
+
+        @Override
+        public String getName() {
+            return "currentTime";
+        }
+
+        @Override
+        public Duration get() {
+            return getCurrentTime();
+        }
+        
+        @Override
+        public void fireValueChangedEvent() {
+            super.fireValueChangedEvent();
+        }
+        
+    }
+    
+    public final Duration getCurrentTime() {
+        return TickCalculation.toDuration(currentTicks);
+    }
+
+    public final ReadOnlyObjectProperty<Duration> currentTimeProperty() {
+        if (currentTime == null) {
+            currentTime = new CurrentTimeProperty();
+        }
+        return currentTime;
+    }
+
+    /**
+     * Delays the start of an animation.
+     * 
+     * @defaultValue 0ms
+     */
+    private ObjectProperty<Duration> delay;
+    private static final Duration DEFAULT_DELAY = Duration.ZERO;
+
+    public final void setDelay(Duration value) {
+        if ((delay != null) || (!DEFAULT_DELAY.equals(value))) {
+            delayProperty().set(value);
+        }
+    }
+
+    public final Duration getDelay() {
+        return (delay == null)? DEFAULT_DELAY : delay.get();
+    }
+
+    public final ObjectProperty<Duration> delayProperty() {
+        if (delay == null) {
+            delay = new SimpleObjectProperty<Duration>(this, "delay", DEFAULT_DELAY);
+        }
+        return delay;
+    }
+
+    /**
+     * Defines the number of cycles in this animation. The {@code cycleCount}
+     * may be {@code INDEFINITE} for animations that repeat indefinitely, but
+     * must otherwise be > 0.
+     * <p>
+     * It is not possible to change the {@code cycleCount} of a running
+     * {@code Animation}. If the value of {@code cycleCount} is changed for a
+     * running {@code Animation}, the animation has to be stopped and started again to pick
+     * up the new value.
+     * 
+     * @defaultValue 1.0
+     * 
+     */
+    private IntegerProperty cycleCount;
+    private static final int DEFAULT_CYCLE_COUNT = 1;
+
+    public final void setCycleCount(int value) {
+        if ((cycleCount != null) || (value != DEFAULT_CYCLE_COUNT)) {
+            cycleCountProperty().set(value);
+        }
+    }
+
+    public final int getCycleCount() {
+        return (cycleCount == null)? DEFAULT_CYCLE_COUNT : cycleCount.get();
+    }
+
+    public final IntegerProperty cycleCountProperty() {
+        if (cycleCount == null) {
+            cycleCount = new IntegerPropertyBase(DEFAULT_CYCLE_COUNT) {
+
+                @Override
+                public void invalidated() {
+                    updateTotalDuration();
+                }
+
+                @Override
+                public Object getBean() {
+                    return Animation.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "cycleCount";
+                }
+            };
+        }
+        return cycleCount;
+    }
+
+    /**
+     * Defines whether this
+     * {@code Animation} reverses direction on alternating cycles. If
+     * {@code true}, the
+     * {@code Animation} will proceed forward on the first cycle,
+     * then reverses on the second cycle, and so on. Otherwise, animation will
+     * loop such that each cycle proceeds forward from the start.
+     * 
+     * It is not possible to change the {@code autoReverse} flag of a running
+     * {@code Animation}. If the value of {@code autoReverse} is changed for a
+     * running {@code Animation}, the animation has to be stopped and started again to pick
+     * up the new value.
+     * 
+     * @defaultValue false
+     */
+    private BooleanProperty autoReverse;
+    private static final boolean DEFAULT_AUTO_REVERSE = false;
+
+    public final void setAutoReverse(boolean value) {
+        if ((autoReverse != null) || (value != DEFAULT_AUTO_REVERSE)) {
+            autoReverseProperty().set(value);
+        }
+    }
+
+    public final boolean isAutoReverse() {
+        return (autoReverse == null)? DEFAULT_AUTO_REVERSE : autoReverse.get();
+    }
+
+    public final BooleanProperty autoReverseProperty() {
+        if (autoReverse == null) {
+            autoReverse = new SimpleBooleanProperty(this, "autoReverse", DEFAULT_AUTO_REVERSE);
+        }
+        return autoReverse;
+    }
+
+    /**
+     * The status of the {@code Animation}.
+     * 
+     * In {@code Animation} can be in one of three states:
+     * {@link Status#STOPPED}, {@link Status#PAUSED} or {@link Status#RUNNING}.
+     */
+    private ReadOnlyObjectProperty<Status> status;
+    private static final Status DEFAULT_STATUS = Status.STOPPED;
+
+    protected final void setStatus(Status value) {
+        if ((status != null) || (!DEFAULT_STATUS.equals(value))) {
+            ((AnimationReadOnlyProperty<Status>)statusProperty()).set(value);
+        }
+    }
+
+    public final Status getStatus() {
+        return (status == null)? DEFAULT_STATUS : status.get();
+    }
+
+    public final ReadOnlyObjectProperty<Status> statusProperty() {
+        if (status == null) {
+            status = new AnimationReadOnlyProperty<Status>("status", Status.STOPPED);
+        }
+        return status;
+    }
+    
+    private final double targetFramerate;
+    private final int resolution;
+    private long lastPulse;
+
+    /**
+     * The target framerate is the maximum framerate at which this {@code Animation}
+     * will run, in frames per second. This can be used, for example, to keep
+     * particularly complex {@code Animations} from over-consuming system resources.
+     * By default, an {@code Animation}'s framerate is not explicitly limited, meaning
+     * the {@code Animation} will run at an optimal framerate for the underlying platform.
+     *
+     * @return the target framerate
+     */
+    public final double getTargetFramerate() {
+        return targetFramerate;
+    }
+
+    /**
+     * The action to be executed at the conclusion of this {@code Animation}.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<EventHandler<ActionEvent>> onFinished;
+    private static final EventHandler<ActionEvent> DEFAULT_ON_FINISHED = null;
+
+    public final void setOnFinished(EventHandler<ActionEvent> value) {
+        if ((onFinished != null) || (value != null /* DEFAULT_ON_FINISHED */)) {
+            onFinishedProperty().set(value);
+        }
+    }
+
+    public final EventHandler<ActionEvent> getOnFinished() {
+        return (onFinished == null)? DEFAULT_ON_FINISHED : onFinished.get();
+    }
+
+    public final ObjectProperty<EventHandler<ActionEvent>> onFinishedProperty() {
+        if (onFinished == null) {
+            onFinished = new SimpleObjectProperty<EventHandler<ActionEvent>>(this, "onFinished", DEFAULT_ON_FINISHED);
+        }
+        return onFinished;
+    }
+
+    private final ObservableMap<String, Duration> cuePoints = FXCollections
+            .observableMap(new HashMap<String, Duration>(0));
+
+    /**
+     * The cue points can be
+     * used to mark important positions of the {@code Animation}. Once a cue
+     * point was defined, it can be used as an argument of
+     * {@link #jumpTo(String) jumpTo()} and {@link #playFrom(String) playFrom()}
+     * to move to the associated position quickly.
+     * <p>
+     * Every {@code Animation} has two predefined cue points {@code "start"} and
+     * {@code "end"}, which are set at the start respectively the end of the
+     * {@code Animation}. The predefined cuepoints do not appear in the map,
+     * attempts to override them have no effect.
+     * <p>
+     * Another option to define a cue point in a {@code Animation} is to set the
+     * {@link KeyFrame#name} property of a {@link KeyFrame}.
+     *
+     * @return {@link javafx.collections.ObservableMap} of cue points
+     */
+    public final ObservableMap<String, Duration> getCuePoints() {
+        return cuePoints;
+    }
+
+    /**
+     * Jumps to a given position in this {@code Animation}.
+     * 
+     * If the given time is less than {@link Duration#ZERO}, this method will
+     * jump to the start of the animation. If the given time is larger than the
+     * duration of this {@code Animation}, this method will jump to the end.
+     * 
+     * @param time
+     *            the new position
+     * @throws NullPointerException
+     *             if {@code time} is {@code null}
+     * @throws IllegalArgumentException
+     *             if {@code time} is {@link Duration#UNKNOWN}
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void jumpTo(Duration time) {
+        if (time == null) {
+            throw new NullPointerException("Time needs to be specified.");
+        }
+        if (time.isUnknown()) {
+            throw new IllegalArgumentException("The time is invalid");
+        }
+        if (parent != null) {
+            throw new IllegalStateException("Cannot jump when embedded in another animation");
+        }
+
+        lastPlayedFinished = false;
+        
+        final Duration totalDuration = getTotalDuration();
+        time = time.lessThan(Duration.ZERO) ? Duration.ZERO : time
+                .greaterThan(totalDuration) ? totalDuration : time;
+        final long ticks = fromDuration(time);
+
+        if (getStatus() == Status.STOPPED) {
+            syncClipEnvelope();
+        }
+        clipEnvelope.jumpTo(ticks);
+    }
+
+    /**
+     * Jumps to a predefined position in this {@code Animation}. This method
+     * looks for an entry in cue points and jumps to the associated
+     * position, if it finds one.
+     * <p>
+     * If the cue point is behind the end of this {@code Animation}, calling
+     * {@code jumpTo} will result in a jump to the end. If the cue point has a
+     * negative {@link javafx.util.Duration} it will result in a jump to the
+     * beginning. If the cue point has a value of
+     * {@link javafx.util.Duration#UNKNOWN} calling {@code jumpTo} will have no
+     * effect for this cue point.
+     * <p>
+     * There are two predefined cue points {@code "start"} and {@code "end"}
+     * which are defined to be at the start respectively the end of this
+     * {@code Animation}.
+     * 
+     * @param cuePoint
+     *            the name of the cue point
+     * @throws NullPointerException
+     *             if {@code cuePoint} is {@code null}
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     * @see #getCuePoints()
+     */
+    public void jumpTo(String cuePoint) {
+        if (cuePoint == null) {
+            throw new NullPointerException("CuePoint needs to be specified");
+        }
+        if ("start".equalsIgnoreCase(cuePoint)) {
+            jumpTo(Duration.ZERO);
+        } else if ("end".equalsIgnoreCase(cuePoint)) {
+            jumpTo(getTotalDuration());
+        } else {
+            final Duration target = getCuePoints().get(cuePoint);
+            if (target != null) {
+                jumpTo(target);
+            }
+        }
+    }
+
+    /**
+     * A convenience method to play this {@code Animation} from a predefined
+     * position. The position has to be predefined in cue points.
+     * Calling this method is equivalent to
+     * 
+     * <pre>
+     * <code>
+     * animation.jumpTo(cuePoint);
+     * animation.play();
+     * </code>
+     * </pre>
+     * 
+     * Note that unlike {@link #playFromStart()} calling this method will not
+     * change the playing direction of this {@code Animation}.
+     * 
+     * @param cuePoint
+     *            name of the cue point
+     * @throws NullPointerException
+     *             if {@code cuePoint} is {@code null}
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     * @see #getCuePoints()
+     */
+    public void playFrom(String cuePoint) {
+        jumpTo(cuePoint);
+        play();
+    }
+
+    /**
+     * A convenience method to play this {@code Animation} from a specific
+     * position. Calling this method is equivalent to
+     * 
+     * <pre>
+     * <code>
+     * animation.jumpTo(time);
+     * animation.play();
+     * </code>
+     * </pre>
+     * 
+     * Note that unlike {@link #playFromStart()} calling this method will not
+     * change the playing direction of this {@code Animation}.
+     * 
+     * @param time
+     *            position where to play from
+     * @throws NullPointerException
+     *             if {@code time} is {@code null}
+     * @throws IllegalArgumentException
+     *             if {@code time} is {@link Duration#UNKNOWN}
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void playFrom(Duration time) {
+        jumpTo(time);
+        play();
+    }
+
+    /**
+     * Plays {@code Animation} from current position in the direction indicated
+     * by {@code rate}. If the {@code Animation} is running, it has no effect.
+     * <p>
+     * When {@code rate} > 0 (forward play), if an {@code Animation} is already
+     * positioned at the end, the first cycle will not be played, it is
+     * considered to have already finished. This also applies to a backward (
+     * {@code rate} < 0) cycle if an {@code Animation} is positioned at the beginning.
+     * However, if the {@code Animation} has {@code cycleCount} > 1, following
+     * cycle(s) will be played as usual.
+     * <p>
+     * When the {@code Animation} reaches the end, the {@code Animation} is stopped and
+     * the play head remains at the end.
+     * <p>
+     * To play an {@code Animation} backwards from the end:<br>
+     * <code>
+     *  animation.setRate(negative rate);<br>
+     *  animation.jumpTo(overall duration of animation);<br>
+     *  animation.play();<br>
+     * </code>
+     * <p>
+     * Note: <ul>
+     * <li>{@code play()} is an asynchronous call, the {@code Animation} may not
+     * start immediately. </ul>
+     *
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void play() {
+        play(true);
+    }
+
+    private void play(boolean forceSync) {
+        if (parent != null) {
+            throw new IllegalStateException("Cannot start when embedded in another animation");
+        }
+        switch (getStatus()) {
+            case STOPPED:
+                if (impl_startable(forceSync)) {
+                    final double rate = getRate();
+                    if (lastPlayedFinished) {
+                        jumpTo((rate < 0)? getTotalDuration() : Duration.ZERO);
+                    }
+                    lastPlayedFinished = false;
+                    impl_start(forceSync);
+                    pulseReceiver.start(TickCalculation.fromDuration(getDelay()));
+                    if (Math.abs(rate) < EPSILON) {
+                        pulseReceiver.pause();
+                    } else {
+                        
+                    }
+                } else {
+                    final EventHandler<ActionEvent> handler = getOnFinished();
+                    if (handler != null) {
+                        handler.handle(new ActionEvent(this, null));
+                    }
+                }
+                break;
+            case PAUSED:
+                impl_resume();
+                if (Math.abs(getRate()) >= EPSILON) {
+                    pulseReceiver.resume();
+                }
+                break;
+        }
+    }
+
+    /**
+     * Plays an {@code Animation} from initial position in forward direction.
+     * <p>
+     * It is equivalent to
+     * <p>
+     * <code>
+     *      animation.stop();<br>
+     *      animation.setRate = setRate(Math.abs(animation.getRate())); </br>
+     *      animation.jumpTo(Duration.ZERO);<br>
+     *      animation.play();<br>
+     *  </code>
+     * 
+     * <p>
+     * Note: <ul>
+     * <li>{@code playFromStart()} is an asynchronous call, {@code Animation} may
+     * not start immediately. </ul>
+     * <p>
+     *
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void playFromStart() {
+        stop();
+        setRate(Math.abs(getRate()));
+        jumpTo(Duration.ZERO);
+        play(true);
+    }
+
+    /**
+     * Stops the animation and resets the play head to its initial position. If
+     * the animation is not currently running, this method has no effect.
+     * <p>
+     * Note: <ul>
+     * <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
+     * immediately. </ul>
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void stop() {
+        if (parent != null) {
+            throw new IllegalStateException("Cannot stop when embedded in another animation");
+        }
+        if (getStatus() != Status.STOPPED) {
+            clipEnvelope.abortCurrentPulse();
+            impl_stop();
+            jumpTo(Duration.ZERO);
+        }
+    }
+
+    /**
+     * Pauses the animation. If the animation is not currently running, this
+     * method has no effect.
+     * <p>
+     * Note: <ul>
+     * <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
+     * immediately. </ul>
+     * @throws IllegalStateException
+     *             if embedded in another animation,
+     *                such as {@link SequentialTransition} or {@link ParallelTransition}
+     */
+    public void pause() {
+        if (parent != null) {
+            throw new IllegalStateException("Cannot pause when embedded in another animation");
+        }
+        if (getStatus() == Status.RUNNING) {
+            clipEnvelope.abortCurrentPulse();
+            pulseReceiver.pause();
+            impl_pause();
+        }
+    }
+
+    /**
+     * The constructor of {@code Animation}.
+     * 
+     * This constructor allows to define a target framerate.
+     * 
+     * @param targetFramerate
+     *            The custom target frame rate for this {@code Animation}
+     * @see #getTargetFramerate()
+     */
+    protected Animation(double targetFramerate) {
+        this.targetFramerate = targetFramerate;
+        this.resolution = (int) Math.max(1, Math.round(TickCalculation.TICKS_PER_SECOND / targetFramerate));
+        this.pulseReceiver = new AnimationPulseReceiver(this, ToolkitAccessor
+                .getMasterTimer());
+        this.clipEnvelope = ClipEnvelopeFactory.create(this);
+    }
+
+    /**
+     * The constructor of {@code Animation}.
+     */
+    protected Animation() {
+        this.resolution = 1;
+        this.targetFramerate = TickCalculation.TICKS_PER_SECOND / ToolkitAccessor.getMasterTimer().getDefaultResolution();
+        this.pulseReceiver = new AnimationPulseReceiver(this, ToolkitAccessor
+                .getMasterTimer());
+        this.clipEnvelope = ClipEnvelopeFactory.create(this);
+    }
+
+    // These constructors are only for testing purposes
+    Animation(AbstractMasterTimer timer) {
+        this.resolution = 1;
+        this.targetFramerate = TickCalculation.TICKS_PER_SECOND / timer.getDefaultResolution();
+        this.pulseReceiver = new AnimationPulseReceiver(this, timer);
+        this.clipEnvelope = ClipEnvelopeFactory.create(this);
+    }
+
+    // These constructors are only for testing purposes
+    Animation(AnimationPulseReceiver pulseReceiver, ClipEnvelope clipEnvelope, int resolution) {
+        this.resolution = resolution;
+        this.targetFramerate = TickCalculation.TICKS_PER_SECOND / resolution;
+        this.pulseReceiver = pulseReceiver;
+        this.clipEnvelope = clipEnvelope;
+    }
+
+    boolean impl_startable(boolean forceSync) {
+        return (fromDuration(getCycleDuration()) > 0L)
+                || (!forceSync && clipEnvelope.wasSynched());
+    }
+
+    void impl_sync(boolean forceSync) {
+        if (forceSync || !clipEnvelope.wasSynched()) {
+            syncClipEnvelope();
+        }
+    }
+    
+    private void syncClipEnvelope() {
+        final int publicCycleCount = getCycleCount();
+        final int internalCycleCount = (publicCycleCount <= 0)
+                && (publicCycleCount != INDEFINITE) ? 1 : publicCycleCount;
+        clipEnvelope = clipEnvelope.setCycleCount(internalCycleCount);
+        clipEnvelope.setCycleDuration(getCycleDuration());
+        clipEnvelope.setAutoReverse(isAutoReverse());
+    }
+
+    void impl_start(boolean forceSync) {
+        impl_sync(forceSync);
+        setStatus(Status.RUNNING);
+        clipEnvelope.start();
+        setCurrentRate(clipEnvelope.getCurrentRate());
+        lastPulse = 0;
+    }
+
+    void impl_pause() {
+        final double currentRate = getCurrentRate();
+        if (Math.abs(currentRate) >= EPSILON) {
+            lastPlayedForward = Math.abs(getCurrentRate() - getRate()) < EPSILON;
+        }
+        setCurrentRate(0.0);
+        setStatus(Status.PAUSED);
+    }
+
+    void impl_resume() {
+        setStatus(Status.RUNNING);
+        setCurrentRate(lastPlayedForward ? getRate() : -getRate());
+    }
+
+    void impl_stop() {
+        pulseReceiver.stop();
+        setStatus(Status.STOPPED);
+        setCurrentRate(0.0);
+    }
+
+    void impl_timePulse(long elapsedTime) {
+        if (resolution == 1) { // fullspeed
+            clipEnvelope.timePulse(elapsedTime);
+        } else if (elapsedTime - lastPulse >= resolution) {
+            lastPulse = (elapsedTime / resolution) * resolution;
+            clipEnvelope.timePulse(elapsedTime);
+        }
+    }
+
+    abstract void impl_playTo(long currentTicks, long cycleTicks);
+
+    abstract void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump);
+
+    void impl_setCurrentTicks(long ticks) {
+        currentTicks = ticks;
+        if (currentTime != null) {
+            currentTime.fireValueChangedEvent();
+        }
+    }
+
+    void impl_setCurrentRate(double currentRate) {
+//        if (getStatus() == Status.RUNNING) {
+            setCurrentRate(currentRate);
+//        }
+    }
+
+    final void impl_finished() {
+        lastPlayedFinished = true;
+        impl_stop();
+        final EventHandler<ActionEvent> handler = getOnFinished();
+        if (handler != null) {
+            try {
+                handler.handle(new ActionEvent(this, null));
+            } catch (Exception ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/AnimationAccessorImpl.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import com.sun.scenario.animation.shared.AnimationAccessor;
+
+final class AnimationAccessorImpl extends AnimationAccessor{
+
+    @Override
+    public void timePulse(Animation animation, long elapsedTime) {
+        animation.impl_timePulse(elapsedTime);
+    }
+
+    @Override
+    public void setCurrentRate(Animation animation, double currentRate) {
+        animation.impl_setCurrentRate(currentRate);
+    }
+
+    @Override
+    public void playTo(Animation animation, long pos, long cycleTicks) {
+        animation.impl_playTo(pos, cycleTicks);
+    }
+
+    @Override
+    public void jumpTo(Animation animation, long pos, long cycleTicks, boolean forceJump) {
+        animation.impl_jumpTo(pos, cycleTicks, forceJump);
+    }
+
+    @Override
+    public void finished(Animation animation) {
+        animation.impl_finished();
+    }
+
+    @Override
+    public void setCurrentTicks(Animation animation, long ticks) {
+        animation.impl_setCurrentTicks(ticks);
+    }
+
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/AnimationTimer.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import com.sun.scenario.ToolkitAccessor;
+import com.sun.scenario.animation.AbstractMasterTimer;
+
+/**
+ * The class {@code AnimationTimer} allows to create a timer, that is called in
+ * each frame while it is active.
+ * 
+ * An extending class has to override the method {@link #handle(long)} which
+ * will be called in every frame.
+ * 
+ * The methods {@link AnimationTimer#start()} and {@link #stop()} allow to start
+ * and stop the timer.
+ * 
+ * 
+ */
+public abstract class AnimationTimer {
+    
+    private final AbstractMasterTimer timer;
+    private boolean active;
+    
+    public AnimationTimer() {
+        timer = ToolkitAccessor.getMasterTimer();
+    }
+    
+    // For testing only
+    AnimationTimer(AbstractMasterTimer timer) {
+        this.timer = timer;
+    }
+    
+    /**
+     * This method needs to be overridden by extending classes. It is going to
+     * be called in every frame while the {@code AnimationTimer} is active.
+     * 
+     * @param now
+     *            The timestamp of the current frame given in nanoseconds. This
+     *            value will be the same for all {@code AnimationTimers} called
+     *            during one frame.
+     */
+    public abstract void handle(long now);
+
+    /**
+     * Starts the {@code AnimationTimers}. Once it is started, the
+     * {@link #handle(long)} method of this {@code AnimationTimers} will be
+     * called in every frame.
+     * 
+     * The {@code AnimationTimers} can be stopped by calling {@link #stop()}.
+     */
+    public void start() {
+        if (!active) {
+            timer.addAnimationTimer(this);
+            active = true;
+        }
+    }
+
+    /**
+     * Stops the {@code AnimationTimers}. It can be activated again by calling
+     * {@link #start()}.
+     */
+    public void stop() {
+        if (active) {
+            timer.removeAnimationTimer(this);
+            active = false;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/Interpolatable.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+/**
+ * A value that can be interpolated. It defines single
+ * {@link #interpolate(Object, double)} method, which returns interpolated value
+ * of given fraction.
+ */
+public interface Interpolatable<T> {
+
+    /**
+     * The function calculates an interpolated value along the fraction
+     * {@code t} between {@code 0.0} and {@code 1.0}. When {@code t} = 1.0,
+     * {@code endVal} is returned.
+     * 
+     * @param endValue
+     *            target value
+     * @param t
+     *            fraction between {@code 0.0} and {@code 1.0}
+     * @return interpolated value
+     */
+    public T interpolate(T endValue, double t);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/Interpolator.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+import javafx.util.Duration;
+
+import com.sun.scenario.animation.NumberTangentInterpolator;
+import com.sun.scenario.animation.SplineInterpolator;
+
+/**
+ * The abstract class defines several {@code interpolate} methods, which are
+ * used to calculate interpolated values. Various built-in implementations of
+ * this class are offered. Applications may choose to implement their own
+ * {@code Interpolator} to get custom interpolation behavior.
+ * <p>
+ * A custom {@code Interpolator} has to be defined in terms of a "
+ * {@link #curve(double) curve()}".
+ */
+public abstract class Interpolator {
+
+    private static final double EPSILON = 1e-12;
+
+    /**
+     * The constructor of {@code Interpolator}.
+     */
+    protected Interpolator() {
+    }
+
+    /**
+     * Built-in interpolator that provides discrete time interpolation. The
+     * return value of {@code interpolate()} is {@code endValue} only when the
+     * input {@code fraction} is 1.0, and {@code startValue} otherwise.
+     */
+    public static final Interpolator DISCRETE = new Interpolator() {
+        @Override
+        protected double curve(double t) {
+            return (Math.abs(t - 1.0) < EPSILON) ? 1.0 : 0.0;
+        }
+
+        @Override
+        public String toString() {
+            return "Interpolator.DISCRETE";
+        }
+    };
+
+    /**
+     * Built-in interpolator that provides linear time interpolation. The return
+     * value of {@code interpolate()} is {@code startValue} + ({@code endValue}
+     * - {@code startValue}) * {@code fraction}.
+     */
+    public static final Interpolator LINEAR = new Interpolator() {
+        @Override
+        protected double curve(double t) {
+            return t;
+        }
+
+        @Override
+        public String toString() {
+            return "Interpolator.LINEAR";
+        }
+    };
+
+    /*
+     * Easing is calculated with the following algorithm (taken from SMIL 3.0
+     * specs). The result is clamped because of possible rounding errors.
+     * 
+     * double runRate = 1.0 / (1.0 - acceleration/2.0 - deceleration/2.0); if
+     * (fraction < acceleration) { double averageRunRate = runRate * (fraction /
+     * acceleration) / 2; fraction *= averageRunRate; } else if (fraction > (1.0
+     * - deceleration)) { // time spent in deceleration portion double tdec =
+     * fraction - (1.0 - deceleration); // proportion of tdec to total
+     * deceleration time double pdec = tdec / deceleration; fraction = runRate *
+     * (1.0 - ( acceleration / 2) - deceleration + tdec * (2 - pdec) / 2); }
+     * else { fraction = runRate * (fraction - (acceleration / 2)); }
+     */
+
+    /**
+     * Built-in interpolator instance that provides ease in/out behavior.
+     * <p>
+     * An ease-both interpolator will make an animation start slow, then
+     * accelerate and slow down again towards the end, all in a smooth manner.
+     * <p>
+     * The implementation uses the algorithm for easing defined in SMIL 3.0
+     * with an acceleration and deceleration factor of 0.2, respectively.
+     */
+    public static final Interpolator EASE_BOTH = new Interpolator() {
+        @Override
+        protected double curve(double t) {
+            // See the SMIL 3.1 specification for details on this calculation
+            // acceleration = 0.2, deceleration = 0.2
+            return Interpolator.clamp((t < 0.2) ? 3.125 * t * t
+                    : (t > 0.8) ? -3.125 * t * t + 6.25 * t - 2.125
+                            : 1.25 * t - 0.125);
+        }
+
+        @Override
+        public String toString() {
+            return "Interpolator.EASE_BOTH";
+        }
+    };
+    /**
+     * Built-in interpolator instance that provides ease in behavior.
+     * <p>
+     * An ease-in interpolator will make an animation start slow and then
+     * accelerate smoothly.
+     * <p>
+     * The implementation uses the algorithm for easing defined in SMIL 3.0
+     * with an acceleration factor of 0.2.
+     */
+    public static final Interpolator EASE_IN = new Interpolator() {
+        private static final double S1 = 25.0 / 9.0;
+        private static final double S3 = 10.0 / 9.0;
+        private static final double S4 = 1.0 / 9.0;
+
+        @Override
+        protected double curve(double t) {
+            // See the SMIL 3.1 specification for details on this calculation
+            // acceleration = 0.2, deceleration = 0.0
+            return Interpolator.clamp((t < 0.2) ? S1 * t * t : S3 * t - S4);
+        }
+
+        @Override
+        public String toString() {
+            return "Interpolator.EASE_IN";
+        }
+    };
+
+    /**
+     * Built-in interpolator instance that provides ease out behavior.
+     * <p>
+     * An ease-out interpolator will make an animation slow down toward the
+     * end smoothly.
+     * <p>
+     * The implementation uses the algorithm for easing defined in SMIL 3.0 
+     * with an deceleration factor of 0.2.
+     */
+    public static final Interpolator EASE_OUT = new Interpolator() {
+        private static final double S1 = -25.0 / 9.0;
+        private static final double S2 = 50.0 / 9.0;
+        private static final double S3 = -16.0 / 9.0;
+        private static final double S4 = 10.0 / 9.0;
+
+        @Override
+        protected double curve(double t) {
+            // See the SMIL 3.1 specification for details on this calculation
+            // acceleration = 0.2, deceleration = 0.0
+            return Interpolator.clamp((t > 0.8) ? S1 * t * t + S2 * t + S3 : S4
+                    * t);
+        }
+
+        @Override
+        public String toString() {
+            return "Interpolator.EASE_OUT";
+        }
+    };
+
+    /**
+     * Creates an {@code Interpolator}, which {@link #curve(double) curve()} is
+     * shaped using the spline control points defined by ({@code x1}, {@code y1}
+     * ) and ({@code x2}, {@code y2}). The anchor points of the spline are
+     * implicitly defined as ({@code 0.0}, {@code 0.0}) and ({@code 1.0},
+     * {@code 1.0}).
+     * 
+     * @param x1
+     *            x coordinate of the first control point
+     * @param y1
+     *            y coordinate of the first control point
+     * @param x2
+     *            x coordinate of the second control point
+     * @param y2
+     *            y coordinate of the second control point
+     * @return A spline interpolator
+     */
+    public static Interpolator SPLINE(double x1, double y1, double x2, double y2) {
+        return new SplineInterpolator(x1, y1, x2, y2);
+    }
+
+    /**
+     * Create a tangent interpolator. A tangent interpolator allows to define
+     * the behavior of an animation curve very precisely by defining the
+     * tangents close to a key frame.
+     * 
+     * A tangent interpolator defines the behavior to the left and to the right
+     * of a key frame, therefore it is only useful within a {@link Timeline}.
+     * 
+     * The parameters define the tangent of the animation curve for the in
+     * tangent (before a key frame) and out tangent (after a key frame). Each
+     * tangent is specified with a pair, the distance to the key frame and the
+     * value of the tangent at this moment.
+     * 
+     * @param t1
+     *            The delta time of the in-tangent
+     * @param v1
+     *            The value of the in-tangent, in degrees
+     * @param t2
+     *            The delta time of the out-tangent
+     * @param v2
+     *            The value of the out-tangent, in degrees
+     * @return the new tangent interpolator
+     */
+    public static Interpolator TANGENT(Duration t1, double v1, Duration t2,
+            double v2) {
+        return NumberTangentInterpolator.create(v1, t1, v2, t2);
+    }
+
+    /**
+     * Creates a tangent interpolator, for which in-tangent and out-tangent are
+     * identical. This is especially useful for the first and the last key frame
+     * of a {@link Timeline}, because for these key frames only one tangent is
+     * used.
+     * 
+     * @see #TANGENT(Duration, double, Duration, double)
+     * 
+     * @param t
+     *            The delta time of the tangent
+     * @param v
+     *            The value of the tangent, in degrees
+     * @return the new Tangent interpolator
+     */
+    public static Interpolator TANGENT(Duration t, double v) {
+        return NumberTangentInterpolator.create(v, t);
+    }
+
+    /**
+     * This method takes two {@code Objects} along with a {@code fraction}
+     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
+     * <p>
+     * If both {@code Objects} implement {@code Number}, their values are
+     * interpolated. If {@code startValue} implements {@link Interpolatable} the
+     * calculation defined in {@link Interpolatable#interpolate(Object, double)
+     * interpolate()} is used. If neither of these conditions are met, a
+     * discrete interpolation is used, i.e. {@code endValue} is returned if and
+     * only if {@code fraction} is {@code 1.0}, otherwise {@code startValue} is
+     * returned.
+     * <p>
+     * Before calculating the interpolated value, the fraction is altered
+     * according to the function defined in {@link #curve(double) curve()}.
+     * 
+     * @param startValue
+     *            start value
+     * @param endValue
+     *            end value
+     * @param fraction
+     *            a value between 0.0 and 1.0
+     * @return interpolated value
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Object interpolate(Object startValue, Object endValue,
+            double fraction) {
+        if ((startValue instanceof Number) && (endValue instanceof Number)) {
+            final double start = ((Number) startValue).doubleValue();
+            final double end = ((Number) endValue).doubleValue();
+            final double val = start + (end - start) * curve(fraction);
+            if ((startValue instanceof Double) || (endValue instanceof Double)) {
+                return Double.valueOf(val);
+            }
+            if ((startValue instanceof Float) || (endValue instanceof Float)) {
+                return Float.valueOf((float) val);
+            }
+            if ((startValue instanceof Long) || (endValue instanceof Long)) {
+                return Long.valueOf(Math.round(val));
+            }
+            return Integer.valueOf((int) Math.round(val));
+        } else if ((startValue instanceof Interpolatable) && (endValue instanceof Interpolatable)) {
+            return ((Interpolatable) startValue).interpolate(endValue,
+                    curve(fraction));
+        } else {
+            // discrete
+            return (curve(fraction) == 1.0) ? endValue : startValue;
+        }
+    }
+
+    /**
+     * This method takes two {@code boolean} values along with a
+     * {@code fraction} between {@code 0.0} and {@code 1.0} and returns the
+     * interpolated value.
+     * <p>
+     * Before calculating the interpolated value, the fraction is altered
+     * according to the function defined in {@link #curve(double) curve()}.
+     * 
+     * @param startValue
+     *            the first data point
+     * @param endValue
+     *            the second data point
+     * @param fraction
+     *            the fraction in {@code [0.0...1.0]}
+     */
+    public boolean interpolate(boolean startValue, boolean endValue,
+            double fraction) {
+        return (Math.abs(curve(fraction) - 1.0) < EPSILON) ? endValue
+                : startValue;
+    }
+
+    /**
+     * This method takes two {@code double} values along with a {@code fraction}
+     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
+     * <p>
+     * Before calculating the interpolated value, the fraction is altered
+     * according to the function defined in {@link #curve(double) curve()}.
+     * 
+     * @param startValue
+     *            the first data point
+     * @param endValue
+     *            the second data point
+     * @param fraction
+     *            the fraction in {@code [0.0...1.0]}
+     */
+    public double interpolate(double startValue, double endValue,
+            double fraction) {
+        return startValue + (endValue - startValue) * curve(fraction);
+    }
+
+    /**
+     * This method takes two {@code int} values along with a {@code fraction}
+     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
+     * <p>
+     * Before calculating the interpolated value, the fraction is altered
+     * according to the function defined in {@link #curve(double) curve()}.
+     * 
+     * @param startValue
+     *            the first data point
+     * @param endValue
+     *            the second data point
+     * @param fraction
+     *            the fraction in {@code [0.0...1.0]}
+     */
+    public int interpolate(int startValue, int endValue, double fraction) {
+        return startValue
+                + (int) Math.round((endValue - startValue) * curve(fraction));
+    }
+
+    /**
+     * This method takes two {@code int} values along with a {@code fraction}
+     * between {@code 0.0} and {@code 1.0} and returns the interpolated value.
+     * <p>
+     * Before calculating the interpolated value, the fraction is altered
+     * according to the function defined in {@link #curve(double) curve()}.
+     * 
+     * @param startValue
+     *            the first data point
+     * @param endValue
+     *            the second data point
+     * @param fraction
+     *            the fraction in {@code [0.0...1.0]}
+     */
+    public long interpolate(long startValue, long endValue,
+            double fraction) {
+        return startValue
+                + Math.round((endValue - startValue) * curve(fraction));
+    }
+
+    private static double clamp(double t) {
+        return (t < 0.0) ? 0.0 : (t > 1.0) ? 1.0 : t;
+    }
+
+    /**
+     * Mapping from [0.0..1.0] to itself.
+     * 
+     * @param t
+     *            time, but normalized to the range [0.0..1.0], where 0.0 is the
+     *            start of the current interval, while 1.0 is the end of the
+     *            current interval. Usually a function that increases
+     *            monotonically.
+     */
+    protected abstract double curve(double t);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/KeyFrame.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.util.Duration;
+
+/**
+ * Defines target values at a specified point in time for a set of variables
+ * that are interpolated along a {@link Timeline}.
+ * <p>
+ * The developer controls the interpolation of a set of variables for the
+ * interval between successive key frames by providing a target value and an
+ * {@link Interpolator} associated with each variable. The variables are
+ * interpolated such that they will reach their target value at the specified
+ * time. An {@link #onFinished} function is invoked on each {@code KeyFrame} if one
+ * is provided. A {@code KeyFrame} can optionally have a {@link #name}, which
+ * will result in a cuepoint that is automatically added to the {@code Timeline}.
+ * 
+ * @see Timeline
+ * @see KeyValue
+ * @see Interpolator
+ * 
+ */
+public final class KeyFrame {
+
+    private static final EventHandler<ActionEvent> DEFAULT_ON_FINISHED = null;
+    private static final String DEFAULT_NAME = null;
+
+    /**
+     * Returns the time offset of this {@code KeyFrame}.
+     * 
+     * The returned {@link javafx.util.Duration} defines the time offset within
+     * a single cycle of a {@link Timeline} at which the {@link KeyValue
+     * KeyValues} will be set and at which the {@link #onFinished} function
+     * variable will be called.
+     * <p>
+     * The {@code time} of a {@code KeyFrame} has to be greater than or equal to
+     * {@link javafx.util.Duration#ZERO} and it cannot be
+     * {@link javafx.util.Duration#UNKNOWN}.
+     * 
+     * Note: While the unit of {@code time} is a millisecond, the granularity
+     * depends on the underlying operating system and will in general be larger.
+     * For example animations on desktop systems usually run with a maximum of
+     * 60fps which gives a granularity of ~17 ms.
+     */
+    public Duration getTime() {
+        return time;
+    }
+    private final Duration time;
+
+    /**
+     * Returns an immutable {@code Set} of {@link KeyValue} instances. 
+     * 
+     * A {@code KeyValue} defines a target and the desired value that should be
+     * interpolated at the specified time of this {@code KeyFrame}.
+     */
+    public Set<KeyValue> getValues() {
+        return values;
+    }
+    private final Set<KeyValue> values;
+
+    /**
+     * Returns the {@code onFinished} event handler of this {@code KeyFrame}.
+     * 
+     * The {@code onFinished} event handler is a function that is called when
+     * the elapsed time on a cycle passes the specified time of this
+     * {@code KeyFrame}. The {@code onFinished} function variable will be called
+     * if the elapsed time passes the indicated value, even if it never equaled
+     * the time value exactly.
+     */
+    public EventHandler<ActionEvent> getOnFinished() {
+        return onFinished;
+    }
+    private final EventHandler<ActionEvent> onFinished;
+
+    /**
+     * Returns the {@code name} of this {@code KeyFrame}.
+     * 
+     * If a named {@code KeyFrame} is added to a {@link Timeline}, a cuepoint
+     * with the {@code name} and the {@link #time} of the {@code KeyFrame} will
+     * be added automatically. If the {@code KeyFrame} is removed, the cuepoint
+     * will also be removed.
+     */
+    public String getName() {
+        return name;
+    }
+    private final String name;
+
+    /**
+     * Constructor of {@code KeyFrame}
+     * <p>
+     * If a passed in {@code KeyValue} is {@code null} or a duplicate, it will
+     * be ignored.
+     * 
+     * @param time
+     *            the {@link #time}
+     * @param name
+     *            the {@link #name}
+     * @param onFinished
+     *            the {@link #onFinished onFinished-handler}
+     * @param values
+     *            a {@link javafx.collections.ObservableList} of
+     *            {@link KeyValue} instances
+     * @throws NullPointerException
+     *             if {@code time} is null
+     * @throws IllegalArgumentException
+     *             if {@code time} is invalid (see {@link #time})
+     */
+    public KeyFrame(Duration time, String name,
+            EventHandler<ActionEvent> onFinished, Collection<KeyValue> values) {
+        if (time == null) {
+            throw new NullPointerException("The time has to be specified");
+        }
+        if (time.lessThan(Duration.ZERO) || time.equals(Duration.UNKNOWN)) {
+            throw new IllegalArgumentException("The time is invalid.");
+        }
+        this.time = time;
+        this.name = name;
+        if (values != null) {
+            final Set<KeyValue> set = new CopyOnWriteArraySet<KeyValue>(values);
+            set.remove(null);
+            this.values = (set.size() == 0) ? Collections.<KeyValue> emptySet()
+                    : (set.size() == 1) ? Collections.<KeyValue> singleton(set
+                            .iterator().next()) : Collections
+                            .unmodifiableSet(set);
+        } else {
+            this.values = Collections.<KeyValue> emptySet();
+        }
+        this.onFinished = onFinished;
+    }
+
+    /**
+     * Constructor of {@code KeyFrame}
+     * <p>
+     * If a passed in {@code KeyValue} is {@code null} or a duplicate, it will
+     * be ignored.
+     * 
+     * @param time
+     *            the {@link #time}
+     * @param name
+     *            the {@link #name}
+     * @param onFinished
+     *            the {@link #onFinished onFinished-handler}
+     * @param values
+     *            the {@link KeyValue} instances
+     * @throws NullPointerException
+     *             if {@code time} is null
+     * @throws IllegalArgumentException
+     *             if {@code time} is invalid (see {@link #time})
+     */
+    public KeyFrame(Duration time, String name,
+            EventHandler<ActionEvent> onFinished, KeyValue... values) {
+        if (time == null) {
+            throw new NullPointerException("The time has to be specified");
+        }
+        if (time.lessThan(Duration.ZERO) || time.equals(Duration.UNKNOWN)) {
+            throw new IllegalArgumentException("The time is invalid.");
+        }
+        this.time = time;
+        this.name = name;
+        if (values != null) {
+            final Set<KeyValue> set = new CopyOnWriteArraySet<KeyValue>();
+            for (final KeyValue keyValue : values) {
+                if (keyValue != null) {
+                    set.add(keyValue);
+                }
+            }
+            this.values = (set.size() == 0) ? Collections.<KeyValue> emptySet()
+                    : (set.size() == 1) ? Collections.<KeyValue> singleton(set
+                            .iterator().next()) : Collections
+                            .unmodifiableSet(set);
+        } else {
+            this.values = Collections.emptySet();
+        }
+        this.onFinished = onFinished;
+    }
+
+    /**
+     * Constructor of {@code KeyFrame}
+     * 
+     * @param time
+     *            the {@link #time}
+     * @param onFinished
+     *            the {@link #onFinished onFinished-handler}
+     * @param values
+     *            the {@link KeyValue} instances
+     * @throws NullPointerException
+     *             if {@code time} is null
+     * @throws IllegalArgumentException
+     *             if {@code time} is invalid (see {@link #time})
+     */
+    public KeyFrame(Duration time, EventHandler<ActionEvent> onFinished,
+            KeyValue... values) {
+        this(time, DEFAULT_NAME, onFinished, values);
+    }
+
+    /**
+     * Constructor of {@code KeyFrame}
+     * 
+     * @param time
+     *            the {@link #time}
+     * @param name
+     *            the {@link #name}
+     * @param values
+     *            the {@link KeyValue} instances
+     * @throws NullPointerException
+     *             if {@code time} is null
+     * @throws IllegalArgumentException
+     *             if {@code time} is invalid (see {@link #time})
+     */
+    public KeyFrame(Duration time, String name, KeyValue... values) {
+        this(time, name, DEFAULT_ON_FINISHED, values);
+    }
+
+    /**
+     * Constructor of {@code KeyFrame}
+     * 
+     * @param time
+     *            the {@link #time}
+     * @param values
+     *            the {@link KeyValue} instances
+     * @throws NullPointerException
+     *             if {@code time} is null
+     * @throws IllegalArgumentException
+     *             if {@code time} is invalid (see {@link #time})
+     */
+    public KeyFrame(Duration time, KeyValue... values) {
+        this(time, DEFAULT_NAME, DEFAULT_ON_FINISHED, values);
+    }
+
+    /** 
+     * Returns a string representation of this {@code KeyFrame} object. 
+     * @return a string representation of this {@code KeyFrame} object. 
+     */ 
+    @Override
+    public String toString() {
+        return "KeyFrame [time=" + time + ", values=" + values
+                + ", onFinished=" + onFinished + ", name=" + name + "]";
+    }
+
+    /** 
+     * Returns a hash code for this {@code KeyFrame} object. 
+     * @return a hash code for this {@code KeyFrame} object. 
+     */ 
+    @Override
+    public int hashCode() {
+        assert (time != null) && (values != null);
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + time.hashCode();
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result
+                + ((onFinished == null) ? 0 : onFinished.hashCode());
+        result = prime * result + values.hashCode();
+        return result;
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this one. 
+     * Two {@code KeyFrames} are considered equal, if their {@link #getTime()
+     * time}, {@link #onFinished onFinished}, and {@link #getValues() values}
+     * are equal.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof KeyFrame) {
+            final KeyFrame kf = (KeyFrame) obj;
+            assert (time != null) && (values != null) && (kf.time != null)
+                    && (kf.values != null);
+            return time.equals(kf.time)
+                    && ((name == null) ? kf.name == null : name.equals(kf.name))
+                    && ((onFinished == null) ? kf.onFinished == null
+                            : onFinished.equals(kf.onFinished))
+                    && values.equals(kf.values);
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/KeyValue.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+import javafx.beans.value.WritableBooleanValue;
+import javafx.beans.value.WritableDoubleValue;
+import javafx.beans.value.WritableFloatValue;
+import javafx.beans.value.WritableIntegerValue;
+import javafx.beans.value.WritableLongValue;
+import javafx.beans.value.WritableNumberValue;
+import javafx.beans.value.WritableValue;
+
+/**
+ * Defines a key value to be interpolated for a particular interval along the
+ * animation. A {@link KeyFrame}, which defines a specific point on a timeline,
+ * can hold multiple {@code KeyValues}. {@code KeyValue} is an immutable class.
+ * <p>
+ * A {@code KeyValue} is defined by a target, which is an implementation of
+ * {@link javafx.beans.value.WritableValue}, an end value and an
+ * {@link Interpolator}.
+ * <p>
+ * Most interpolators define the interpolation between two {@code KeyFrames}.
+ * (The only exception are tangent-interpolators.)
+ * The {@code KeyValue} of the second {@code KeyFrame} (in forward
+ * direction) specifies the interpolator to be used in the interval.
+ * <p>
+ * Tangent-interpolators define the interpolation to the left and to the right of
+ * a {@code KeyFrame} (see {@link  Interpolator#TANGENT(javafx.util.Duration, double, javafx.util.Duration, double)
+ * Interpolator.TANGENT}).
+ * <p>
+ * By default, {@link Interpolator#LINEAR} is used in the interval.
+ * 
+ * @see Timeline
+ * @see KeyFrame
+ * @see Interpolator
+ * 
+ */
+public final class KeyValue {
+
+    private static final Interpolator DEFAULT_INTERPOLATOR = Interpolator.LINEAR;
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public static enum Type {
+        BOOLEAN, DOUBLE, FLOAT, INTEGER, LONG, OBJECT
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public Type getType() {
+        return type;
+    }
+
+    private final Type type;
+
+    /**
+     * Returns the target of this {@code KeyValue}
+     * 
+     * @return the target
+     */
+    public WritableValue<?> getTarget() {
+        return target;
+    }
+
+    private final WritableValue<?> target;
+
+    /**
+     * Returns the end value of this {@code KeyValue}
+     * 
+     * @return the end value
+     */
+    public Object getEndValue() {
+        return endValue;
+    }
+
+    private final Object endValue;
+
+    /**
+     * {@link Interpolator} to be used for calculating the key value along the
+     * particular interval. By default, {@link Interpolator#LINEAR} is used.
+     */
+    public Interpolator getInterpolator() {
+        return interpolator;
+    }
+
+    private final Interpolator interpolator;
+
+    /**
+     * Creates a {@code KeyValue}.
+     * 
+     * @param target
+     *            the target
+     * @param endValue
+     *            the end value
+     * @param interpolator
+     *            the {@link Interpolator}
+     * @throws NullPointerException
+     *             if {@code target} or {@code interpolator} are {@code null}
+     */
+    public <T> KeyValue(WritableValue<T> target, T endValue,
+            Interpolator interpolator) {
+        if (target == null) {
+            throw new NullPointerException("Target needs to be specified");
+        }
+        if (interpolator == null) {
+            throw new NullPointerException("Interpolator needs to be specified");
+        }
+
+        this.target = target;
+        this.endValue = endValue;
+        this.interpolator = interpolator;
+        this.type = (target instanceof WritableNumberValue) ? (target instanceof WritableDoubleValue) ? Type.DOUBLE
+                : (target instanceof WritableIntegerValue) ? Type.INTEGER
+                        : (target instanceof WritableFloatValue) ? Type.FLOAT
+                                : (target instanceof WritableLongValue) ? Type.LONG
+                                        : Type.OBJECT
+                : (target instanceof WritableBooleanValue) ? Type.BOOLEAN
+                        : Type.OBJECT;
+    }
+
+    /**
+     * Creates a {@code KeyValue} that uses {@link Interpolator#LINEAR}.
+     * 
+     * @param target
+     *            the target
+     * @param endValue
+     *            the end value
+     * @throws NullPointerException
+     *             if {@code target} or {@code interpolator} are {@code null}
+     */
+    public <T> KeyValue(WritableValue<T> target, T endValue) {
+        this(target, endValue, DEFAULT_INTERPOLATOR);
+    }
+
+    /**
+     * Returns a string representation of this {@code KeyValue} object.
+     * @return a string representation of this {@code KeyValue} object.
+     */ 
+    @Override
+    public String toString() {
+        return "KeyValue [target=" + target + ", endValue=" + endValue
+                + ", interpolator=" + interpolator + "]";
+    }
+
+    /**
+     * Returns a hash code for this {@code KeyValue} object.
+     * @return a hash code for this {@code KeyValue} object.
+     */ 
+    @Override
+    public int hashCode() {
+        assert (target != null) && (interpolator != null);
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + target.hashCode();
+        result = prime * result
+                + ((endValue == null) ? 0 : endValue.hashCode());
+        result = prime * result + interpolator.hashCode();
+        return result;
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this one.
+     * Two {@code KeyValues} are considered equal, if their {@link #getTarget()
+     * target}, {@link #getEndValue() endValue}, and {@link #getInterpolator()
+     * interpolator} are equal.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof KeyValue) {
+            final KeyValue keyValue = (KeyValue) obj;
+            assert (target != null) && (interpolator != null)
+                    && (keyValue.target != null)
+                    && (keyValue.interpolator != null);
+            return target.equals(keyValue.target)
+                    && ((endValue == null) ? (keyValue.endValue == null)
+                            : endValue.equals(keyValue.endValue))
+                    && interpolator.equals(keyValue.interpolator);
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/src/javafx/animation/Timeline.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.animation;
+
+import javafx.collections.ListChangeListener.Change;
+import javafx.collections.ObservableList;
+import javafx.util.Duration;
+
+import com.sun.javafx.collections.TrackableObservableList;
+import com.sun.scenario.animation.AbstractMasterTimer;
+import com.sun.scenario.animation.shared.TimelineClipCore;
+
+/**
+ * A {@code Timeline} can be used to define a free from animation of any
+ * {@link javafx.beans.value.WritableValue}, e.g. all
+ * {@link javafx.beans.property.Property JavaFX Properties}.
+ * <p>
+ * A {@code Timeline}, defined by one or more {@link KeyFrame}s, processes
+ * individual {@code KeyFrame} sequentially, in the order specified by
+ * {@code KeyFrame.time}. The animated properties, defined as key values in
+ * {@code KeyFrame.values}, are interpolated 
+ * to/from the targeted key values at the specified time of the {@code KeyFrame}
+ * to {@code Timeline}'s initial position, depends on {@code Timeline}'s
+ * direction.
+ * <p>
+ * {@code Timeline} processes individual {@code KeyFrame} at or after specified
+ * time interval elapsed, it does not guarantee the timing when {@code KeyFrame}
+ * is processed.
+ * <p>
+ * If a {@code KeyFrame} is not provided for the {@code time==0s} instant, one
+ * will be synthesized using the target values that are current at the time
+ * {@link #play()} or {@link #playFromStart()} is called.
+ * <p>
+ * It is not possible to change the {@code keyFrames} of a running {@code Timeline}.
+ * If the value of {@code keyFrames} is changed for a running {@code Timeline}, it 
+ * has to be stopped and started again to pick up the new value.
+ * 
+ * @see Animation
+ * @see KeyFrame
+ * @see KeyValue
+ * 
+ */
+public final class Timeline extends Animation {
+    /* Package-private for testing purposes */
+    final TimelineClipCore clipCore;
+    
+    /**
+     * Returns the {@link KeyFrame KeyFrames} of this {@code Timeline}.
+     */
+    public final ObservableList<KeyFrame> getKeyFrames() {
+        return keyFrames;
+    }
+    private final ObservableList<KeyFrame> keyFrames = new TrackableObservableList<KeyFrame>() {
+        @Override
+        protected void onChanged(Change<KeyFrame> c) {
+            while (c.next()) {
+                if (!c.wasPermutated()) {
+                    for (final KeyFrame keyFrame : c.getRemoved()) {
+                        final String cuePoint = keyFrame.getName();
+                        if (cuePoint != null) {
+                            getCuePoints().remove(cuePoint);
+                        }
+                    }
+                    for (final KeyFrame keyFrame : c.getAddedSubList()) {
+                        final String cuePoint = keyFrame.getName();
+                        if (cuePoint != null) {
+                            getCuePoints().put(cuePoint, keyFrame.getTime());
+                        }
+                    }
+                    final Duration duration = clipCore.setKeyFrames(getKeyFrames());
+                    setCycleDuration(duration);
+                }
+            }
+        }
+    };
+
+    /**
+     * The constructor of {@code Timeline}.
+     * 
+     * This constructor allows to define a {@link Animation#targetFramerate}.
+     * 
+     * @param targetFramerate
+     *            The custom target frame rate for this {@code Timeline}
+     * @param keyFrames
+     *            The keyframes of this {@code Timeline}
+     */
+    public Timeline(double targetFramerate, KeyFrame... keyFrames) {
+        super(targetFramerate);
+        clipCore = new TimelineClipCore(this);
+        getKeyFrames().setAll(keyFrames);
+    }
+
+    /**
+     * The constructor of {@code Timeline}.
+     * 
+     * @param keyFrames
+     *            The keyframes of this {@code Timeline}
+     */
+    public Timeline(KeyFrame... keyFrames) {
+        super();
+        clipCore = new TimelineClipCore(this);
+        getKeyFrames().setAll(keyFrames);
+    }
+
+    /**
+     * The constructor of {@code Timeline}.
+     * 
+     * This constructor allows to define a {@link Animation#targetFramerate}.
+     * 
+     * @param targetFramerate
+     *            The custom target frame rate for this {@code Timeline}
+     */
+    public Timeline(double targetFramerate) {
+        super(targetFramerate);
+        clipCore = new TimelineClipCore(this);
+    }
+
+    /**
+     * The constructor of {@code Timeline}.
+     */
+    public Timeline() {
+        super();
+        clipCore = new TimelineClipCore(this);
+    }
+
+    // This constructor is only for testing purposes
+    Timeline(final AbstractMasterTimer timer) {
+        super(timer);
+        clipCore = new TimelineClipCore(this);
+    }
+
+    @Override
+    void impl_playTo(long currentTicks, long cycleTicks) {
+        clipCore.playTo(currentTicks);
+    }
+
+    @Override
+    void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump) {
+        impl_sync(false);
+        impl_setCurrentTicks(currentTicks);
+        clipCore.jumpTo(currentTicks, forceJump);
+    }
+
+    @Override
+    void impl_setCurrentRate(double currentRate) {
+        super.impl_setCurrentRate(currentRate);
+        clipCore.notifyCurrentRateChanged();
+    }
+
+    @Override
+    void impl_start(boolean forceSync) {
+        super.impl_start(forceSync);
+        clipCore.start(forceSync);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void stop() {
+        if (parent != null) {
+            throw new IllegalStateException("Cannot stop when embedded in another animation");
+        }
+        if (getStatus() == Status.RUNNING) {
+            clipCore.abort();
+        }
+        super.stop();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/AnimationFunctionalTestBase.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import com.sun.javafx.functions.Function0;
+
+import javafx.util.Duration;
+
+public abstract class AnimationFunctionalTestBase {
+    private Thread delayedThread;
+    private Error failError;
+
+    private String name = "";
+
+    protected AnimationFunctionalTestBase() {}
+
+    protected AnimationFunctionalTestBase(String name) {
+        this.name = name;
+    }
+
+    // used by subclasses for delayed function invocation
+    protected final void delay(long ms) {
+        delayedThread = Thread.currentThread();
+        failError = null;
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ignore) {}
+        finally {
+            delayedThread = null;
+            if (failError != null) {
+                throw failError; // re-throw the error on the main thread
+            }
+        }
+    }
+
+    protected final void delayFor(Timeline t) {
+        delay((long) t.getTotalDuration().toMillis() + 300);
+    }
+
+    protected final void fail(String message) {
+        failError = new AssertionError(name + " FAILED: " + message);
+        if (delayedThread != null && delayedThread != Thread.currentThread()) {
+            // error happened on the timeline thread: should re-throw it on the main thread
+            // so that JUnit could catch it
+            delayedThread.interrupt();
+        } else {
+            throw failError;
+        }
+    }
+
+    protected class SimpleKeyFrame extends KeyFrame {
+        public SimpleKeyFrame(double time) {
+            this(time, false);
+        }
+
+        public SimpleKeyFrame(double time, boolean canSkip, KeyValue... values) {
+            super(Duration.valueOf(time), values);
+            setAction(
+                    new Function0<Void>() {
+                        @Override public Void invoke() {
+                            action();
+                            return null;
+                        }
+                    }
+            );
+            setCanSkip(canSkip);
+        }
+
+        protected void action() {}
+    }
+
+    protected class SimpleTimeline extends Timeline {
+        public SimpleTimeline(KeyFrame... frames) {
+            this(1, frames);
+        }
+
+        public SimpleTimeline(int repeatCount, KeyFrame... frames) {
+            init(repeatCount, frames);
+        }
+        public SimpleTimeline(double framerate, int repeatCount, KeyFrame... frames) {
+            super(framerate);
+            init(repeatCount, frames);
+        }
+
+        public void playBackward() {
+            setRate(-Math.abs(getRate()));
+            playFromEnd();
+        }
+
+        public void playFromEnd() {
+            getCurrentTime(getCycleDuration());
+            play();
+        }
+
+
+        private void init(int repeatCount, KeyFrame... frames) {
+            setCycleCount(repeatCount);
+            if (frames != null) {
+                getKeyFrames().addAll(frames);
+            }
+        }
+    }
+
+    public class SimpleKeyValueTarget implements KeyValueTarget {
+        private Object value;
+        private Type type;
+
+        public SimpleKeyValueTarget(Object value) {
+            this.value = value;
+            final Class<?> clazz = value.getClass();
+            type = ((clazz == boolean.class) || (clazz == Boolean.class))? Type.BOOLEAN :
+            ((clazz == byte.class) || (clazz == Byte.class))? Type.BYTE :
+            ((clazz == double.class) || (clazz == Double.class))? Type.DOUBLE :
+            ((clazz == float.class) || (clazz == Float.class))? Type.FLOAT :
+            ((clazz == int.class) || (clazz == Integer.class))? Type.INTEGER :
+            ((clazz == long.class) || (clazz == Long.class))? Type.LONG :
+            ((clazz == short.class) || (clazz == Short.class))? Type.SHORT :
+            Type.OBJECT;
+        }
+        @Override public Object get() {return getValue();}
+        @Override public KeyValueTarget.Type getType() {return type;}
+        @Override public Object getValue() {return value;}
+        @Override public void set(Object value) {setValue(value);}
+        @Override public void setValue(Object o) {value = o;}
+        @Override public KeyValueTarget unwrap() {return this;}
+    };
+
+
+    public class TestSet {
+        public final double TARGET_VALUE = 700;
+        public final double TIME_VALUE = 1000;
+        public final Duration TIME_DURATION = Duration.valueOf(TIME_VALUE);
+        protected SimpleKeyValueTarget target = new SimpleKeyValueTarget(0);
+        protected SimpleTimeline timeline;
+        protected int[] count = {0, 0};
+
+        public TestSet() {
+            this(Double.MAX_VALUE);
+        }
+
+        public TestSet(double frameRate) {
+            timeline = new SimpleTimeline(frameRate, 1,
+                    new SimpleKeyFrame(0) {
+                        @Override protected void action() {
+                            count[0]++;
+                        }
+                    },
+                    new SimpleKeyFrame(TIME_VALUE, false, new KeyValue(target, TARGET_VALUE)) {
+                        @Override protected void action() {
+                            count[1]++;
+                        }
+                    }
+            );
+        }
+
+        protected void resetCount() {
+            count[0] = 0;
+            count[1] = 0;
+        }
+
+        protected final void check(int count0, int count1, boolean running) {
+            checkVisitCount(0, count0);
+            checkVisitCount(1, count1);
+            checkRunning(running);
+        }
+
+        protected final void check(int count0, int count1, boolean running, double time, double target) {
+            check(count0, count1, running);
+            checkTime(time);
+            checkTarget(target);
+        }
+
+        protected void checkVisitCount(int frameIndex, int expected) {
+            if (count[frameIndex] != expected) {
+                fail("KeyFrame #" + frameIndex + " visited " + count[frameIndex]
+                        + " times instead of " + expected + signature());
+            }
+        }
+
+        protected final void checkRunning(boolean running) {
+            if (running && !timeline.isRunning()) {
+                fail("should be running");
+            } else if (!running && timeline.isRunning()) {
+                fail("should not be running");
+            }
+        }
+
+        protected void checkValue(String message, double value, double expected) {
+            if (value != expected) {
+                fail(message + " actual: " + value + ", expected: " + expected + signature());
+            }
+        }
+
+        protected void checkTime(double expected) {
+            checkValue("time ", timeline.getCurrentTime().toMillis(), expected);
+        }
+
+        protected void checkTarget(double expected) {
+            checkValue("target ", Double.valueOf(target.getValue().toString()), expected);
+        }
+
+        String signature() {
+            return ", rate=" + timeline.getRate() + ", repeatCount=" + timeline.getRepeatCount();
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/CanSkip_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Test behavior of canSkip (though the pass/fail of the test only depends on
+ * the non-canSkip KeyFrame being executed the correct number of times).
+ *
+ * Ideally, test should be run with a few different animation timer values,
+ * by setting -Dcom.sun.scenario.animation=
+ * 5, 60 & 500
+ * One must also run using the Swing toolkit, at least until RT-5647 gets
+ * fixed.
+ * Best is to compare behavior to 1.3.1.  The 2ms KeyFrame should execute
+ * roughly the same number of times.
+ *
+ */
+
+public class CanSkip_Test extends AnimationFunctionalTestBase {
+    public static final int N = 100;
+    private Timeline t;
+    private int count2ms = 0;
+    private int count4ms = 0;
+
+    public CanSkip_Test() {
+        super("Should not skip KeyFrames");
+    }
+
+    @Before public void setUp() {
+        t = new SimpleTimeline(N,
+                new SimpleKeyFrame(2, true) {
+                    @Override protected void action() {
+                        count2ms++;
+                    }
+                },
+                new SimpleKeyFrame(4) {
+                    @Override protected void action() {
+                        count4ms++;
+                    }
+                }
+        );
+    }
+
+    @Test public void test() {
+        t.play();
+        delayFor(t);
+        if (count4ms != N) {
+            fail("visited only " + count4ms + " out of " + N);
+        }
+    }
+
+
+    @After public void cleanUp() {
+        t.stop();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/JumpPastEndAR_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import javafx.util.Duration;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class JumpPastEndAR_Test extends AnimationFunctionalTestBase {
+    private TestSet set = new TestSet();
+    private Timeline tt;
+
+    public JumpPastEndAR_Test() {
+        super("Test of behavior when jumping beyond Timeline.cycleDuration with autoReverse=true");
+    }
+
+    @Before public void setUp() {
+        set.timeline.setCycleCount(4);
+        set.timeline.setAutoReverse(true);
+
+        tt = new SimpleTimeline(
+                new SimpleKeyFrame(2100) {
+                    @Override protected void action() {
+                        set.timeline.getCurrentTime(Duration.valueOf(2000));
+                    }
+                },
+                new SimpleKeyFrame(2200) {
+                    @Override protected void action() {
+                        set.checkVisitCount(0, 2);
+                        if (!set.timeline.isRunning()) {
+                            fail("Should be running");
+                        }
+                        if (((Integer)set.target.getValue()) < set.TARGET_VALUE * 0.85) {
+                            fail("Did not skip to beginning " + set.target.getValue());
+                        }
+                        if (set.timeline.getCurrentTime().toMillis() < set.timeline.getCycleDuration().toMillis() * 0.85) {
+                            fail("Time should be under 850ms while it's actually " + set.timeline.getCurrentTime());
+                        }
+                    }
+                },
+                new SimpleKeyFrame(3000) {
+                    @Override protected void action() {
+                        if (!set.timeline.isRunning()) {
+                            throw new AssertionError("Should still be running");
+                        }
+                    }
+                },
+                new SimpleKeyFrame(3150) {
+                    @Override protected void action() {
+                        if (set.timeline.isRunning()) {
+                            throw new AssertionError("Should not be running");
+                        }
+                    }
+                }
+        );
+    }
+
+    @Test public void test() {
+        set.timeline.play();
+        tt.play();
+        delayFor(tt);
+    }
+
+
+    @After public void cleanUp() {
+        set.timeline.stop();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/JumpPastEnd_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import javafx.util.Duration;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+public class JumpPastEnd_Test extends AnimationFunctionalTestBase {
+    private TestSet set = new TestSet();
+    private Timeline tt;
+
+    public JumpPastEnd_Test() {
+        super("Test of behavior when jumping beyond Timeline.cycleDuration");
+    }
+
+    @Before public void setUp() {
+        set.timeline.setCycleCount(4);
+        
+        tt = new SimpleTimeline(
+                new SimpleKeyFrame(2100) {
+                    @Override protected void action() {
+                        set.timeline.getCurrentTime(Duration.valueOf(2000));
+                    }
+                },
+                new SimpleKeyFrame(2200) {
+                    @Override protected void action() {
+                        set.checkVisitCount(0, 4);
+                        if (!set.timeline.isRunning()) {
+                            fail("Should be running");
+                        }
+                        if (((Integer)set.target.getValue()) > set.TARGET_VALUE * 0.15) {
+                            fail("Did not skip to beginning");
+                        }
+                        if (set.timeline.getCurrentTime().toMillis() > set.timeline.getCycleDuration().toMillis() * 0.15) {
+                            fail("Time should be under 100ms while it's actually " + set.timeline.getCurrentTime());
+                        }
+                    }
+                },
+                new SimpleKeyFrame(3000) {
+                    @Override protected void action() {
+                        if (!set.timeline.isRunning()) {
+                            throw new AssertionError("Should still be running");
+                        }
+                    }
+                },
+                new SimpleKeyFrame(3150) {
+                    @Override protected void action() {
+                        if (set.timeline.isRunning()) {
+                            throw new AssertionError("Should not be running");
+                        }
+                    }
+                },
+                new SimpleKeyFrame(3500) {
+                    @Override protected void action() {
+                        // testing jump past end from final cycle
+                        set.resetCount();
+                        set.timeline.playFromStart();
+                    }
+                },
+                new SimpleKeyFrame(7000) {
+                    @Override protected void action() {
+                        set.timeline.getCurrentTime(Duration.valueOf(2000));
+                    }
+                },
+                new SimpleKeyFrame(7100) {
+                    @Override protected void action() {
+                        set.checkVisitCount(0, 4);
+                        if (set.timeline.isRunning()) {
+                            fail("Should not be running");
+                        }
+                        set.checkTarget(set.TARGET_VALUE);
+                        set.checkTime(set.timeline.getCycleDuration().toMillis());
+                    }
+                },
+                new SimpleKeyFrame(7500) {
+                    @Override protected void action() {
+                        // test jumping to end of Timeline with repeatCount=1
+                        set.resetCount();
+                        set.timeline.setCycleCount(1);
+                        set.timeline.playFromStart();
+                    }
+                },
+                new SimpleKeyFrame(7800) {
+                    @Override protected void action() {
+                        set.timeline.getCurrentTime(set.TIME_DURATION);
+                    }
+                },
+                new SimpleKeyFrame(8300) {
+                    @Override protected void action() {
+                        set.checkVisitCount(0, 1);
+                        if (set.timeline.isRunning()) {
+                            fail("Should not be running");
+                        }
+                    }
+                }
+        );
+    }
+
+    @Test public void test() {
+        set.timeline.play();
+        tt.play();
+        delayFor(tt);
+    }
+
+
+    @After public void cleanUp() {
+        set.timeline.stop();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/KeyFrame_TS101_02_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class KeyFrame_TS101_02_Test extends AnimationFunctionalTestBase {
+    private boolean ea = false;
+    private boolean eb = false;
+    private Timeline t;
+
+    public KeyFrame_TS101_02_Test() {
+        super("Should visit very close KeyFrames");
+    }
+
+    @Before public void setUp() {
+        t = new SimpleTimeline(
+                new SimpleKeyFrame(0) {
+                    @Override protected void action() {
+                        ea = true;
+                    }
+                },
+                new SimpleKeyFrame(0.00001) {
+                    @Override protected void action() {
+                        eb = true;
+                    }
+                }
+        );
+
+    }
+
+    @Test public void test() {
+        t.playFromStart();
+        delay(100);
+
+        if(!ea || !eb) {
+            fail(" visited at 0 ms = " + ea + ", visited at 0.00001 ms = " + eb);
+        }
+    }
+
+    @After public void cleanUp() {
+        t.stop();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/KeyFrame_TS102_01_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.animation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+public class KeyFrame_TS102_01_Test extends AnimationFunctionalTestBase {
+//    private boolean ea = false;
+    private boolean eb = false;
+    private boolean ec = false;
+
+    private long s1;
+//    private long t0;
+    private long t1;
+    private long t2;
+    private Timeline t;
+
+    public KeyFrame_TS102_01_Test() {
+        super("Should visit all KeyFrames in right order");
+    }
+
+    private long getDuration(long begin) {
+        return TimeUnit.NANOSECONDS.toMillis((System.nanoTime()-begin));
+    }
+
+
+    @Before public void setUp() {
+        t = new SimpleTimeline(
+//                // Negative time - should never be visited
+//                // COMMENTED OUT: this causes assertion error in Timeline
+//                new SimpleKeyFrame(-1000) {
+//                    @Override protected void action() {
+//                        ea = true;
+//                        t0 = getDuration(s1);
+//                    }
+//                },
+                new SimpleKeyFrame(1000) {
+                    @Override protected void action() {
+                        ec = true;
+                        t2 = getDuration(s1);
+                    }
+                },
+                new SimpleKeyFrame(0) {
+                    @Override protected void action() {
+                        eb = true;
+                        t1 = getDuration(s1);
+                    }
+                }
+        );
+    }
+
+    @Test public void test() {
+        s1 = System.nanoTime();
+        t.playFromStart();
+        delay(5000);
+
+        if(/*t0 != 0 || t0 > t2 ||*/ t1 > t2) {
+            fail("visited in wrong order");
+        }
+
+        if(/*ea ||*/ !eb || !ec) {
+            fail("didn'timeline visit all KeyFrames");
+        }
+    }
+
+    @After public void cleanUp() {
+        t.stop();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-anim/test/functional/javafx/animation/KeyFrame_TS103_01_Test.java	Tue Dec 18 09:15:15 2012 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as