filter/source/svg/presentation_engine.js                     |  400 ++++++++---
 slideshow/source/engine/activities/activitiesfactory.cxx     |   75 +-
 slideshow/source/engine/animationnodes/animationbasenode.cxx |    4 
 slideshow/source/engine/animationnodes/basecontainernode.cxx |   40 +
 slideshow/source/engine/animationnodes/basecontainernode.hxx |    4 
 slideshow/source/engine/color.cxx                            |   25 
 slideshow/source/inc/hslcolor.hxx                            |    2 
 slideshow/source/inc/rgbcolor.hxx                            |    2 
 8 files changed, 452 insertions(+), 100 deletions(-)

New commits:
commit ed92cdd7f131bc37957d91d9ff171a2c5c2716c7
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Tue Jun 26 22:08:02 2012 +0200

    Bug fix: now skip effect work correctly with repeated animations

diff --git a/slideshow/source/engine/animationnodes/basecontainernode.cxx 
b/slideshow/source/engine/animationnodes/basecontainernode.cxx
index 6dab08e..e2a3a66 100644
--- a/slideshow/source/engine/animationnodes/basecontainernode.cxx
+++ b/slideshow/source/engine/animationnodes/basecontainernode.cxx
@@ -85,6 +85,7 @@ bool BaseContainerNode::init_children()
 
 void BaseContainerNode::deactivate_st( NodeState eDestState )
 {
+    mnLeftIterations = 0; // in order to make skip effect work correctly
     if (eDestState == FROZEN) {
         // deactivate all children that are not FROZEN or ENDED:
         forEachChildNode( boost::mem_fn(&AnimationNode::deactivate),
@@ -178,7 +179,7 @@ bool BaseContainerNode::notifyDeactivatedChild(
 
 bool BaseContainerNode::repeat()
 {
-    deactivate_st( ENDED );
+    forEachChildNode( boost::mem_fn(&AnimationNode::end), ~ENDED );
     sal_Bool bState = init_children();
     if( bState )
         activate_st();
commit 5525b212faadc83df458b0911d416a734357ea30
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Sat Jun 23 21:12:58 2012 +0200

    Modified the JavaScript implementation for repeated animations.
    
    That has been needed because of Firefox that does not compute the bounding 
box of a
    shape with zero width and height properly.

diff --git a/filter/source/svg/presentation_engine.js 
b/filter/source/svg/presentation_engine.js
index 4dba32e..5161780 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -1433,13 +1433,13 @@ var NAVDBG = new DebugPrinter();
 NAVDBG.off();
 
 var ANIMDBG = new DebugPrinter();
-ANIMDBG.on();
+ANIMDBG.off();
 
 var aRegisterEventDebugPrinter = new DebugPrinter();
 aRegisterEventDebugPrinter.off();
 
 var aTimerEventQueueDebugPrinter = new DebugPrinter();
-aTimerEventQueueDebugPrinter.on();
+aTimerEventQueueDebugPrinter.off();
 
 var aEventMultiplexerDebugPrinter = new DebugPrinter();
 aEventMultiplexerDebugPrinter.off();
@@ -4585,49 +4585,6 @@ function createStateTransitionTable()
 var aStateTransitionTable = createStateTransitionTable();
 
 
-
-
-
-// 
------------------------------------------------------------------------------------------
 //
-// Transition tables
-
-// transition table for restart=NEVER, fill=FREEZE
-var aStateTransitionTable_Never_Freeze =
-[
-    INVALID_NODE,
-    RESOLVED_NODE | ENDED_NODE,         // active successors for UNRESOLVED
-    ACTIVE_NODE | ENDED_NODE,           // active successors for RESOLVED
-    INVALID_NODE,
-    FROZEN_NODE | ENDED_NODE,           // active successors for ACTIVE: 
freeze object
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    ENDED_NODE,                         // active successors for FROZEN: end
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    INVALID_NODE,
-    ENDED_NODE                          // active successors for ENDED:
-    // this state is a sink here (cannot restart)
-];
-
-
-// Table guide
-var aTableGuide =
-[
-     null,
-     null,
-     null,
-     aStateTransitionTable_Never_Freeze,
-     null,
-     null
-];
-
-
-
 // 
------------------------------------------------------------------------------------------
 //
 function getTransitionTable( eRestartMode, eFillMode )
 {
@@ -5352,9 +5309,6 @@ BaseNode.prototype.resolve = function()
 
 BaseNode.prototype.activate = function()
 {
-    log( 'restart mode: ' + aRestartModeOutMap[ this.getRestartMode() ] );
-    log( 'fill mode: ' + aFillModeOutMap[ this.getFillMode() ] );
-
     if( ! this.checkValidNode() )
         return false;
 
@@ -8373,7 +8327,7 @@ function AnimatedElement( aElement )
     this.aClipPathContent = null;
 
     this.aPreviousElement = null;
-    this.aElementArray = new Array();
+    this.aStateArray = new Array();
     this.nCurrentState = -1;
     this.eAdditiveMode = ADDITIVE_MODE_REPLACE;
     this.bIsUpdated = true;
@@ -8486,7 +8440,8 @@ AnimatedElement.prototype.setToElement = function( 
aElement )
 
 AnimatedElement.prototype.notifySlideStart = function()
 {
-    this.setToFirst();
+    this.nCurrentState = -1;
+    //this.setToFirst();
     this.DBG( '.notifySlideStart invoked' );
 };
 
@@ -8529,9 +8484,15 @@ AnimatedElement.prototype.notifyNextEffectStart = 
function( nEffectIndex )
 AnimatedElement.prototype.saveState = function()
 {
     ++this.nCurrentState;
-    if( !this.aElementArray[ this.nCurrentState ] )
+    if( !this.aStateArray[ this.nCurrentState ] )
     {
-        this.aElementArray[ this.nCurrentState ] = 
this.aActiveElement.cloneNode( true );
+        this.aStateArray[ this.nCurrentState ] = new Object();
+        var aState = this.aStateArray[ this.nCurrentState ];
+        aState.aElement = this.aActiveElement.cloneNode( true );
+        aState.nCenterX = this.nCenterX;
+        aState.nCenterY = this.nCenterY;
+        aState.nScaleFactorX = this.nScaleFactorX;
+        aState.nScaleFactorY = this.nScaleFactorY;
     }
 };
 
@@ -8542,29 +8503,28 @@ AnimatedElement.prototype.setToFirst = function()
 
 AnimatedElement.prototype.setToLast = function()
 {
-    this.setTo( this.aElementArray.length - 1 );
+    this.setTo( this.aStateArray.length - 1 );
 };
 
 AnimatedElement.prototype.setTo = function( nNewState )
 {
-    if( !this.aElementArray[ nNewState ] )
+    if( !this.aStateArray[ nNewState ] )
     {
         log( 'AnimatedElement(' + this.getId() + ').setTo: state '
                  + nNewState + ' is not valid' );
         return false;
     }
 
-    var bRet = this.setToElement( this.aElementArray[ nNewState ] );
+    var aState = this.aStateArray[ nNewState ];
+    var bRet = this.setToElement( aState.aElement );
     if( bRet )
     {
         this.nCurrentState = nNewState;
 
-        var aBBox = this.getBBox();
-        var aBaseBBox = this.getBaseBBox();
-        this.nCenterX = aBBox.x + aBBox.width / 2;
-        this.nCenterY = aBBox.y + aBBox.height / 2;
-        this.nScaleFactorX = aBBox.width / aBaseBBox.width;
-        this.nScaleFactorY = aBBox.height / aBaseBBox.height;
+        this.nCenterX = aState.nCenterX;
+        this.nCenterY = aState.nCenterY;
+        this.nScaleFactorX = aState.nScaleFactorX;
+        this.nScaleFactorY = aState.nScaleFactorY;
     }
     return bRet;
 };
commit 06859010450505885a34313711a0d9d1fbaa30f2
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Sat Jun 23 18:58:07 2012 +0200

    Fixed a bug that prevented repeated animations to work properly when the 
repeatCount
    value is greater than 2.

diff --git a/slideshow/source/engine/animationnodes/basecontainernode.cxx 
b/slideshow/source/engine/animationnodes/basecontainernode.cxx
index 3ac076a..6dab08e 100644
--- a/slideshow/source/engine/animationnodes/basecontainernode.cxx
+++ b/slideshow/source/engine/animationnodes/basecontainernode.cxx
@@ -85,7 +85,6 @@ bool BaseContainerNode::init_children()
 
 void BaseContainerNode::deactivate_st( NodeState eDestState )
 {
-    mnLeftIterations = 0.0;
     if (eDestState == FROZEN) {
         // deactivate all children that are not FROZEN or ENDED:
         forEachChildNode( boost::mem_fn(&AnimationNode::deactivate),
commit 19da52d09b74a7de98be454fd48f807758e18c96
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Thu Jun 21 18:55:50 2012 +0200

    Now the rewind option is handled by the JavaScript engine too.

diff --git a/filter/source/svg/presentation_engine.js 
b/filter/source/svg/presentation_engine.js
index 13a0a3b..4dba32e 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5799,6 +5799,8 @@ AnimationBaseNode.prototype.deactivate_st = function( 
eDestState )
     {
         if( this.aActivity )
             this.aActivity.dispose();
+        if( ( this.getFillMode() == FILL_MODE_REMOVE ) && 
this.getAnimatedElement()  )
+            this.removeEffect();
     }
 };
 
@@ -6202,6 +6204,8 @@ BaseContainerNode.prototype.deactivate_st = function( 
eDestState )
     {
         // end all children that are not ENDED:
         this.forEachChildNode( mem_fn( 'end' ), ~ENDED_NODE );
+        if( this.getFillMode() == FILL_MODE_REMOVE )
+            this.forEachChildNode( mem_fn( 'removeEffect' ), ENDED_NODE );
     }
 };
 
@@ -8562,6 +8566,7 @@ AnimatedElement.prototype.setTo = function( nNewState )
         this.nScaleFactorX = aBBox.width / aBaseBBox.width;
         this.nScaleFactorY = aBBox.height / aBaseBBox.height;
     }
+    return bRet;
 };
 
 AnimatedElement.prototype.getBaseBBox = function()
commit 1ef95a7206979756a885b7bea4c788684f5e7b61
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Tue Jun 19 18:53:14 2012 +0200

    Added support for the repeatCount attribute for time containers.
    
    The support is limited to the case when the value of the repeatCount 
attribute is an integral
    number and the duration time is defined implicitly by the active duration 
of the time container
    children. The accumulate attribute is not handled.
    
    The support for such a feature has been implemented for both the C++ and 
the JavaScript
    presentation engine.

diff --git a/filter/source/svg/presentation_engine.js 
b/filter/source/svg/presentation_engine.js
index 0c566c8..13a0a3b 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -1439,7 +1439,7 @@ var aRegisterEventDebugPrinter = new DebugPrinter();
 aRegisterEventDebugPrinter.off();
 
 var aTimerEventQueueDebugPrinter = new DebugPrinter();
-aTimerEventQueueDebugPrinter.off();
+aTimerEventQueueDebugPrinter.on();
 
 var aEventMultiplexerDebugPrinter = new DebugPrinter();
 aEventMultiplexerDebugPrinter.off();
@@ -4522,7 +4522,7 @@ function createStateTransitionTable()
     aTable[FROZEN_NODE]         = INVALID_NODE;  // this state is unreachable 
here
     aTable[ENDED_NODE]          = ENDED_NODE;    // this state is a sink here 
(cannot restart)
 
-// transition table for restart=NEVER, fill=FREEZE
+    // transition table for restart=NEVER, fill=FREEZE
     aTable =
     aSTT[RESTART_MODE_NEVER][FILL_MODE_FREEZE] =
     aSTT[RESTART_MODE_NEVER][FILL_MODE_HOLD] =
@@ -5352,8 +5352,8 @@ BaseNode.prototype.resolve = function()
 
 BaseNode.prototype.activate = function()
 {
-//    log( 'restart mode: ' + aRestartModeOutMap[ this.getRestartMode() ] );
-//    log( 'fill mode: ' + aFillModeOutMap[ this.getFillMode() ] );
+    log( 'restart mode: ' + aRestartModeOutMap[ this.getRestartMode() ] );
+    log( 'fill mode: ' + aFillModeOutMap[ this.getFillMode() ] );
 
     if( ! this.checkValidNode() )
         return false;
@@ -5687,6 +5687,7 @@ function AnimationBaseNode( aAnimElem, aParentNode, 
aNodeContext )
     this.bIsContainer = false;
     this.aTargetElement = null;
     this.aAnimatedElement = null;
+    this.nAnimatedElementOriginalState = 0;
     this.aActivity = null;
 
     this.nMinFrameCount = undefined;
@@ -5734,7 +5735,7 @@ AnimationBaseNode.prototype.parseElement = function()
     if( this.aTargetElement )
     {
         // set up target element initial visibility
-        if( true && aAnimElem.getAttribute( 'attributeName' ) === 'visibility' 
)
+        if( aAnimElem.getAttribute( 'attributeName' ) === 'visibility' )
         {
             if( aAnimElem.getAttribute( 'to' ) === 'visible' )
                 this.aTargetElement.setAttribute( 'visibility', 'hidden' );
@@ -5774,6 +5775,7 @@ AnimationBaseNode.prototype.activate_st = function()
 {
     if( this.aActivity )
     {
+        this.nAnimatedElementOriginalState = 
this.getAnimatedElement().getCurrentState();
         this.aActivity.setTargets( this.getAnimatedElement() );
         this.getContext().aActivityQueue.addActivity( this.aActivity );
     }
@@ -5842,6 +5844,16 @@ AnimationBaseNode.prototype.hasPendingAnimation = 
function()
     return true;
 };
 
+AnimationBaseNode.prototype.saveStateOfAnimatedElement = function()
+{
+    this.getAnimatedElement().saveState();
+};
+
+AnimationBaseNode.prototype.removeEffect = function()
+{
+    this.getAnimatedElement().setTo( this.nAnimatedElementOriginalState );
+};
+
 AnimationBaseNode.prototype.getTargetElement = function()
 {
     return this.aTargetElement;
@@ -6096,6 +6108,7 @@ function BaseContainerNode( aAnimElem, aParentNode, 
aNodeContext )
     this.aChildrenArray = new Array();
     this.nFinishedChildren = 0;
     this.bDurationIndefinite = false;
+    this.nLeftIterations = 1;
 
     this.eImpressNodeType = undefined;
     this.ePresetClass =  undefined;
@@ -6157,6 +6170,13 @@ BaseContainerNode.prototype.appendChildNode = function( 
aAnimationNode )
 
 BaseContainerNode.prototype.init_st = function()
 {
+    this.nLeftIterations = this.getRepeatCount();
+
+    return this.init_children();
+};
+
+BaseContainerNode.prototype.init_children = function()
+{
     this.nFinishedChildren = 0;
     var nChildrenCount = this.aChildrenArray.length;
     var nInitChildren = 0;
@@ -6170,6 +6190,7 @@ BaseContainerNode.prototype.init_st = function()
     return ( nChildrenCount == nInitChildren );
 };
 
+
 BaseContainerNode.prototype.deactivate_st = function( eDestState )
 {
     if( eDestState == FROZEN_NODE )
@@ -6245,12 +6266,49 @@ BaseContainerNode.prototype.notifyDeactivatedChild = 
function( aChildNode )
 
     if( bFinished && this.isDurationIndefinite() )
     {
-        this.deactivate();
+        if( this.nLeftIterations >= 1.0 )
+        {
+            this.nLeftIterations -= 1.0;
+        }
+        if( this.nLeftIterations >= 1.0 )
+        {
+            bFinished = false;
+            var aRepetitionEvent = makeDelay( bind( this, this.repeat ), 0.0 );
+            this.aContext.aTimerEventQueue.addEvent( aRepetitionEvent );
+        }
+        else
+        {
+            this.deactivate();
+        }
     }
 
     return bFinished;
 };
 
+BaseContainerNode.prototype.repeat = function()
+{
+    this.deactivate_st( ENDED_NODE );
+    this.removeEffect();
+    var bInitialized = this.init_children();
+    if( bInitialized )
+        this.activate_st();
+    return bInitialized;
+};
+
+BaseContainerNode.prototype.removeEffect = function()
+{
+    this.forEachChildNode( mem_fn( 'removeEffect' ), FROZEN_NODE | ENDED_NODE 
);
+};
+
+BaseContainerNode.prototype.saveStateOfAnimatedElement = function()
+{
+    var nChildrenCount = this.aChildrenArray.length;
+    for( var i = 0; i < nChildrenCount; ++i )
+    {
+        this.aChildrenArray[i].saveStateOfAnimatedElement();
+    }
+}
+
 BaseContainerNode.prototype.forEachChildNode = function( aFunction, 
eNodeStateMask )
 {
     if( !eNodeStateMask )
@@ -6420,6 +6478,11 @@ SequentialTimeContainer.prototype.resolveChild = 
function( aChildNode )
 
     if( bResolved && this.isMainSequenceRootNode() )
     {
+        aChildNode.saveStateOfAnimatedElement();
+    }
+
+    if( bResolved && this.isMainSequenceRootNode() )
+    {
         // skip/rewind events handling
     }
     return bResolved;
@@ -8307,7 +8370,7 @@ function AnimatedElement( aElement )
 
     this.aPreviousElement = null;
     this.aElementArray = new Array();
-    this.nCurrentState = 0;
+    this.nCurrentState = -1;
     this.eAdditiveMode = ADDITIVE_MODE_REPLACE;
     this.bIsUpdated = true;
 
@@ -8316,7 +8379,7 @@ function AnimatedElement( aElement )
     this.aICTM = document.documentElement.createSVGMatrix();
     this.setCTM();
 
-    this.aElementArray[0] = this.aActiveElement.cloneNode( true );
+    //this.aElementArray[0] = this.aActiveElement.cloneNode( true );
 }
 
 AnimatedElement.prototype.initElement = function()
@@ -8382,6 +8445,11 @@ AnimatedElement.prototype.getId = function()
     return this.aActiveElement.getAttribute( 'id' );
 };
 
+AnimatedElement.prototype.getCurrentState = function()
+{
+    return this.nCurrentState;
+};
+
 AnimatedElement.prototype.isUpdated = function()
 {
     return this.bIsUpdated;
@@ -8432,26 +8500,35 @@ AnimatedElement.prototype.notifyAnimationEnd = 
function()
 
 AnimatedElement.prototype.notifyNextEffectStart = function( nEffectIndex )
 {
-    assert( this.nCurrentState === nEffectIndex,
-            'AnimatedElement(' + this.getId() + ').notifyNextEffectStart: 
assertion (current state == effect index) failed' );
+//    assert( this.nCurrentState === nEffectIndex,
+//            'AnimatedElement(' + this.getId() + ').notifyNextEffectStart: 
assertion (current state == effect index) failed' );
+//
+//    if( this.isUpdated() )
+//    {
+//        if( !this.aElementArray[ nEffectIndex ] )
+//        {
+//            this.aElementArray[ nEffectIndex ] =  this.aElementArray[ 
this.nCurrentState ];
+//            this.DBG( '.notifyNextEffectStart(' + nEffectIndex + '): new 
state set to previous one ' );
+//        }
+//    }
+//    else
+//    {
+//        if( !this.aElementArray[ nEffectIndex ] )
+//        {
+//            this.aElementArray[ nEffectIndex ] = 
this.aActiveElement.cloneNode( true );
+//            this.DBG( '.notifyNextEffectStart(' + nEffectIndex + '): cloned 
active state ' );
+//        }
+//    }
+//    ++this.nCurrentState;
+};
 
-    if( this.isUpdated() )
+AnimatedElement.prototype.saveState = function()
+{
+    ++this.nCurrentState;
+    if( !this.aElementArray[ this.nCurrentState ] )
     {
-        if( !this.aElementArray[ nEffectIndex ] )
-        {
-            this.aElementArray[ nEffectIndex ] =  this.aElementArray[ 
this.nCurrentState ];
-            this.DBG( '.notifyNextEffectStart(' + nEffectIndex + '): new state 
set to previous one ' );
-        }
+        this.aElementArray[ this.nCurrentState ] = 
this.aActiveElement.cloneNode( true );
     }
-    else
-    {
-        if( !this.aElementArray[ nEffectIndex ] )
-        {
-            this.aElementArray[ nEffectIndex ] = 
this.aActiveElement.cloneNode( true );
-            this.DBG( '.notifyNextEffectStart(' + nEffectIndex + '): cloned 
active state ' );
-        }
-    }
-    ++this.nCurrentState;
 };
 
 AnimatedElement.prototype.setToFirst = function()
@@ -8464,12 +8541,19 @@ AnimatedElement.prototype.setToLast = function()
     this.setTo( this.aElementArray.length - 1 );
 };
 
-AnimatedElement.prototype.setTo = function( nEffectIndex )
+AnimatedElement.prototype.setTo = function( nNewState )
 {
-    var bRet = this.setToElement( this.aElementArray[ nEffectIndex ] );
+    if( !this.aElementArray[ nNewState ] )
+    {
+        log( 'AnimatedElement(' + this.getId() + ').setTo: state '
+                 + nNewState + ' is not valid' );
+        return false;
+    }
+
+    var bRet = this.setToElement( this.aElementArray[ nNewState ] );
     if( bRet )
     {
-        this.nCurrentState = nEffectIndex;
+        this.nCurrentState = nNewState;
 
         var aBBox = this.getBBox();
         var aBaseBBox = this.getBaseBBox();
@@ -10613,7 +10697,7 @@ function FromToByActivityTemplate( BaseType ) // 
template parameter
     {
         if( this.aAnimation )
         {
-            if( this.isAutoreverse() )
+            if( this.isAutoReverse() )
                 this.aAnimation.perform( this.aStartValue );
             else
                 this.aAnimation.perform( this.aEndValue );
diff --git a/slideshow/source/engine/animationnodes/animationbasenode.cxx 
b/slideshow/source/engine/animationnodes/animationbasenode.cxx
index ed63495..719276d 100644
--- a/slideshow/source/engine/animationnodes/animationbasenode.cxx
+++ b/slideshow/source/engine/animationnodes/animationbasenode.cxx
@@ -422,16 +422,6 @@ AnimationBaseNode::fillCommonParameters() const
             else
                 aRepeats.reset( nRepeats / nDuration );
         }
-        // This is a temporary workaround:
-        // as the repeatCount attribute is defined on the <par> parent node
-        // and activities are created only for animation node leaves, that
-        // actual performs a shape effect, we get the repeatCount value
-        // from the parent node.
-        else if( ( getXAnimationNode()->getType() != 
animations::AnimationNodeType::SET )
-                 && (getParentNode()->getXAnimationNode()->getRepeatCount() 
>>= nRepeats) )
-        {
-            aRepeats.reset( nRepeats );
-        }
         else
         {
             // no double value for both values - Timing::INDEFINITE?
diff --git a/slideshow/source/engine/animationnodes/basecontainernode.cxx 
b/slideshow/source/engine/animationnodes/basecontainernode.cxx
index e9a53ac..3ac076a 100644
--- a/slideshow/source/engine/animationnodes/basecontainernode.cxx
+++ b/slideshow/source/engine/animationnodes/basecontainernode.cxx
@@ -32,10 +32,12 @@
 #include <canvas/verbosetrace.hxx>
 
 #include "basecontainernode.hxx"
+#include "eventqueue.hxx"
 #include "tools.hxx"
 #include "nodetools.hxx"
 #include "delayevent.hxx"
 
+#include <boost/bind.hpp>
 #include <boost/mem_fn.hpp>
 #include <algorithm>
 
@@ -65,7 +67,15 @@ void BaseContainerNode::dispose()
 
 bool BaseContainerNode::init_st()
 {
+   if( !(getXAnimationNode()->getRepeatCount() >>= mnLeftIterations) )
+        mnLeftIterations = 1.0;
+   return init_children();
+}
+
+bool BaseContainerNode::init_children()
+{
     mnFinishedChildren = 0;
+
     // initialize all children
     return (std::count_if(
                 maChildren.begin(), maChildren.end(),
@@ -75,6 +85,7 @@ bool BaseContainerNode::init_st()
 
 void BaseContainerNode::deactivate_st( NodeState eDestState )
 {
+    mnLeftIterations = 0.0;
     if (eDestState == FROZEN) {
         // deactivate all children that are not FROZEN or ENDED:
         forEachChildNode( boost::mem_fn(&AnimationNode::deactivate),
@@ -137,19 +148,44 @@ bool BaseContainerNode::notifyDeactivatedChild(
     std::size_t const nSize = maChildren.size();
     OSL_ASSERT( mnFinishedChildren < nSize );
     ++mnFinishedChildren;
-    bool const bFinished = (mnFinishedChildren >= nSize);
+    bool bFinished = (mnFinishedChildren >= nSize);
 
     // all children finished, and we've got indefinite duration?
     // think of ParallelTimeContainer::notifyDeactivating()
     // if duration given, we will be deactivated by some end event
     // @see fillCommonParameters()
     if (bFinished && isDurationIndefinite()) {
-        deactivate();
+        if( mnLeftIterations >= 1.0 )
+        {
+            mnLeftIterations -= 1.0;
+        }
+        if( mnLeftIterations >= 1.0 )
+        {
+            bFinished = false;
+            EventSharedPtr aRepetitionEvent =
+                    makeDelay( boost::bind( &BaseContainerNode::repeat, this ),
+                               0.0,
+                               "BaseContainerNode::repeat");
+            getContext().mrEventQueue.addEvent( aRepetitionEvent );
+        }
+        else
+        {
+            deactivate();
+        }
     }
 
     return bFinished;
 }
 
+bool BaseContainerNode::repeat()
+{
+    deactivate_st( ENDED );
+    sal_Bool bState = init_children();
+    if( bState )
+        activate_st();
+    return bState;
+}
+
 #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
 void BaseContainerNode::showState() const
 {
diff --git a/slideshow/source/engine/animationnodes/basecontainernode.hxx 
b/slideshow/source/engine/animationnodes/basecontainernode.hxx
index f4a0d78..4a9c6cd 100644
--- a/slideshow/source/engine/animationnodes/basecontainernode.hxx
+++ b/slideshow/source/engine/animationnodes/basecontainernode.hxx
@@ -51,6 +51,7 @@ protected:
 
 private:
     virtual bool init_st();
+    virtual bool init_children();
     virtual void deactivate_st( NodeState eDestState );
     virtual bool hasPendingAnimation() const;
     // force to be implemented by derived class:
@@ -66,6 +67,8 @@ protected:
     /// @return true: if all children have been deactivated
     bool notifyDeactivatedChild( AnimationNodeSharedPtr const& pChildNode );
 
+    bool repeat();
+
     template <typename FuncT>
     inline void forEachChildNode( FuncT const& func,
                                   int nodeStateMask = -1 ) const
@@ -83,6 +86,7 @@ protected:
     typedef ::std::vector<AnimationNodeSharedPtr> VectorOfNodes;
     VectorOfNodes       maChildren;
     ::std::size_t       mnFinishedChildren;
+    double       mnLeftIterations;
 
 private:
     const bool          mbDurationIndefinite;
commit 17ac2bb3b37d738488c12b26387822d62c4e8ded
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Mon Jun 18 18:49:30 2012 +0200

    Ported all transition tables. Now restart mode is set up as the SMIL spec 
requires.

diff --git a/filter/source/svg/presentation_engine.js 
b/filter/source/svg/presentation_engine.js
index 13efb60..0c566c8 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -1433,7 +1433,7 @@ var NAVDBG = new DebugPrinter();
 NAVDBG.off();
 
 var ANIMDBG = new DebugPrinter();
-ANIMDBG.off();
+ANIMDBG.on();
 
 var aRegisterEventDebugPrinter = new DebugPrinter();
 aRegisterEventDebugPrinter.off();
@@ -4503,6 +4503,94 @@ 
aTransitionInfoTable[FADE_TRANSITION][FADEOVERCOLOR_TRANS_SUBTYPE] =
 // 
------------------------------------------------------------------------------------------
 //
 // Transition tables
 
+function createStateTransitionTable()
+{
+    var aSTT = {}
+    var aTable = null;
+
+    aSTT[RESTART_MODE_NEVER] = {};
+    aSTT[RESTART_MODE_WHEN_NOT_ACTIVE] = {};
+    aSTT[RESTART_MODE_ALWAYS] = {};
+
+    // transition table for restart=NEVER, fill=REMOVE
+    aTable =
+    aSTT[RESTART_MODE_NEVER][FILL_MODE_REMOVE] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = ENDED_NODE;
+    aTable[FROZEN_NODE]         = INVALID_NODE;  // this state is unreachable 
here
+    aTable[ENDED_NODE]          = ENDED_NODE;    // this state is a sink here 
(cannot restart)
+
+// transition table for restart=NEVER, fill=FREEZE
+    aTable =
+    aSTT[RESTART_MODE_NEVER][FILL_MODE_FREEZE] =
+    aSTT[RESTART_MODE_NEVER][FILL_MODE_HOLD] =
+    aSTT[RESTART_MODE_NEVER][FILL_MODE_TRANSITION] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = FROZEN_NODE | ENDED_NODE;
+    aTable[FROZEN_NODE]         = ENDED_NODE;
+    aTable[ENDED_NODE]          = ENDED_NODE;   // this state is a sink here 
(cannot restart)
+
+    // transition table for restart=WHEN_NOT_ACTIVE, fill=REMOVE
+    aTable =
+    aSTT[RESTART_MODE_WHEN_NOT_ACTIVE][FILL_MODE_REMOVE] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = ENDED_NODE;
+    aTable[FROZEN_NODE]         = INVALID_NODE;  // this state is unreachable 
here
+    aTable[ENDED_NODE]          = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+
+    // transition table for restart=WHEN_NOT_ACTIVE, fill=FREEZE
+    aTable =
+    aSTT[RESTART_MODE_WHEN_NOT_ACTIVE][FILL_MODE_FREEZE] =
+    aSTT[RESTART_MODE_WHEN_NOT_ACTIVE][FILL_MODE_HOLD] =
+    aSTT[RESTART_MODE_WHEN_NOT_ACTIVE][FILL_MODE_TRANSITION] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = FROZEN_NODE | ENDED_NODE;
+    aTable[FROZEN_NODE]         = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+    aTable[ENDED_NODE]          = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+
+    // transition table for restart=ALWAYS, fill=REMOVE
+    aTable =
+    aSTT[RESTART_MODE_ALWAYS][FILL_MODE_REMOVE] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+    aTable[FROZEN_NODE]         = INVALID_NODE;  // this state is unreachable 
here
+    aTable[ENDED_NODE]          = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+
+    // transition table for restart=ALWAYS, fill=FREEZE
+    aTable =
+    aSTT[RESTART_MODE_ALWAYS][FILL_MODE_FREEZE] =
+    aSTT[RESTART_MODE_ALWAYS][FILL_MODE_HOLD] =
+    aSTT[RESTART_MODE_ALWAYS][FILL_MODE_TRANSITION] = {};
+    aTable[INVALID_NODE]        = INVALID_NODE;
+    aTable[UNRESOLVED_NODE]     = RESOLVED_NODE | ENDED_NODE;
+    aTable[RESOLVED_NODE]       = ACTIVE_NODE | ENDED_NODE;
+    aTable[ACTIVE_NODE]         = RESOLVED_NODE | ACTIVE_NODE | FROZEN_NODE | 
ENDED_NODE;
+    aTable[FROZEN_NODE]         = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+    aTable[ENDED_NODE]          = RESOLVED_NODE | ACTIVE_NODE | ENDED_NODE;  
// restart is possible
+
+
+    return aSTT;
+}
+
+var aStateTransitionTable = createStateTransitionTable();
+
+
+
+
+
+// 
------------------------------------------------------------------------------------------
 //
+// Transition tables
+
 // transition table for restart=NEVER, fill=FREEZE
 var aStateTransitionTable_Never_Freeze =
 [
@@ -4543,11 +4631,24 @@ var aTableGuide =
 // 
------------------------------------------------------------------------------------------
 //
 function getTransitionTable( eRestartMode, eFillMode )
 {
-    var nRestartValue = 0;  // never
+    // If restart mode has not been resolved we use 'never'.
+    // Note: RESTART_MODE_DEFAULT == RESTART_MODE_INHERIT.
+    if( eRestartMode == RESTART_MODE_DEFAULT )
+    {
+        log( 'getTransitionTable: unexpected restart mode: ' + eRestartMode
+                 + '. Used NEVER instead.');
+        eRestartMode = RESTART_MODE_NEVER;
+    }
 
-    var nFillValue = 1;     // frozen
+    // If fill mode has not been resolved we use 'remove'.
+    // Note: FILL_MODE_DEFAULT == FILL_MODE_INHERIT
+    if( eFillMode == FILL_MODE_DEFAULT ||
+        eFillMode == FILL_MODE_AUTO )
+    {
+        eFillMode = FILL_MODE_REMOVE;
+    }
 
-    return aTableGuide[ 3*nFillValue + nRestartValue ];
+    return aStateTransitionTable[eRestartMode][eFillMode];
 }
 
 
@@ -5178,8 +5279,7 @@ BaseNode.prototype.parseElement = function()
             this.eRestartMode = this.getParentNode().getRestartMode();
         else
             // SMIL recommendation document says to set it to 'always'
-            // but we follow the slideshow engine C++ implementation
-            this.eRestartMode = RESTART_MODE_NEVER;
+            this.eRestartMode = RESTART_MODE_ALWAYS;
 
     // resolve accelerate and decelerate attributes
     // from the SMIL recommendation document: if the individual values of the 
accelerate
@@ -5192,9 +5292,6 @@ BaseNode.prototype.parseElement = function()
         this.nDecelerate = 0.0;
     }
 
-    // TODO: at present we are able to manage only this case
-    this.eFillMode = FILL_MODE_FREEZE;
-    this.eRestartMode = RESTART_MODE_NEVER;
     this.aStateTransTable = getTransitionTable( this.getRestartMode(), 
this.getFillMode() );
 
     return true;
@@ -5255,6 +5352,9 @@ BaseNode.prototype.resolve = function()
 
 BaseNode.prototype.activate = function()
 {
+//    log( 'restart mode: ' + aRestartModeOutMap[ this.getRestartMode() ] );
+//    log( 'fill mode: ' + aFillModeOutMap[ this.getFillMode() ] );
+
     if( ! this.checkValidNode() )
         return false;
 
@@ -5289,7 +5389,7 @@ BaseNode.prototype.deactivate = function()
         var aStateTrans = new StateTransition( this );
         if( aStateTrans.enter( FROZEN_NODE, true /* FORCE */ ) )
         {
-            this.deactivate_st();
+            this.deactivate_st( FROZEN_NODE );
             aStateTrans.commit();
 
             this.notifyEndListeners();
commit 372c47309a192e67220913309fa1ccffdff8cde1
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Wed Jun 13 19:50:38 2012 +0200

    Now to animations and repeated to animations are handled by the C++ 
presentation engine
    as the SMIL spec describes.

diff --git a/slideshow/source/engine/activities/activitiesfactory.cxx 
b/slideshow/source/engine/activities/activitiesfactory.cxx
index f142456..a99a32a 100644
--- a/slideshow/source/engine/activities/activitiesfactory.cxx
+++ b/slideshow/source/engine/activities/activitiesfactory.cxx
@@ -167,6 +167,9 @@ public:
           mpFormula( rParms.mpFormula ),
           maStartValue(),
           maEndValue(),
+          maPreviousValue(),
+          maStartInterpolationValue(),
+          mnIteration( 0 ),
           mpAnim( rAnim ),
           maInterpolator( rInterpolator ),
           mbDynamicStartValue( false ),
@@ -220,6 +223,9 @@ public:
         }
         else
         {
+            maStartValue = aAnimationStartValue;
+            maStartInterpolationValue = maStartValue;
+
             // By or To animation. According to SMIL spec,
             // the To value takes precedence over the By
             // value, if both are specified
@@ -232,6 +238,7 @@ public:
                 // the to animation interpolates between
                 // the _running_ underlying value and the to value (as the end 
value)
                 mbDynamicStartValue = true;
+                maPreviousValue = maStartValue;
                 maEndValue = *maTo;
             }
             else if( maBy )
@@ -255,15 +262,61 @@ public:
     {
         if (this->isDisposed() || !mpAnim)
             return;
-        (*mpAnim)(
-            getPresentationValue(
-                accumulate( maEndValue,
-                            mbCumulative * nRepeatCount, // means: 
mbCumulative ? nRepeatCount : 0,
-                            maInterpolator( (mbDynamicStartValue
-                                             ? mpAnim->getUnderlyingValue()
-                                             : maStartValue),
-                                            maEndValue,
-                                            nModifiedTime ) ) ) );
+
+        // According to SMIL 3.0 spec 'to' animation if no other (lower 
priority)
+        // animations are active or frozen then a simple interpolation is 
performed.
+        // That is, the start interpolation value is constant while the 
animation
+        // is running, and is equal to the underlying value retrieved when
+        // the animation start.
+        // However if another animation is manipulating the underlying value,
+        // the 'to' animation will initially add to the effect of the lower 
priority
+        // animation, and increasingly dominate it as it nears the end of the
+        // simple duration, eventually overriding it completely.
+        // That is, each time the underlying value is changed between two
+        // computations of the animation function the new underlying value is 
used
+        // as start value for the interpolation.
+        // See:
+        // 
http://www.w3.org/TR/SMIL3/smil-animation.html#animationNS-ToAnimation
+        // (Figure 6 - Effect of Additive to animation example)
+        // Moreover when a 'to' animation is repeated, at each new iteration
+        // the start interpolation value is reset to the underlying value
+        // of the animated property when the animation started,
+        // as it is shown in the example provided by the SMIL 3.0 spec.
+        // This is exactly as Firefox performs SVG 'to' animations.
+        if( mbDynamicStartValue )
+        {
+            if( mnIteration != nRepeatCount )
+            {
+                mnIteration = nRepeatCount;
+                maStartInterpolationValue = maStartValue;
+            }
+            else
+            {
+                ValueType aActualValue = mpAnim->getUnderlyingValue();
+                if( aActualValue != maPreviousValue )
+                    maStartInterpolationValue = aActualValue;
+            }
+        }
+
+        ValueType aValue = maInterpolator( maStartInterpolationValue,
+                                           maEndValue, nModifiedTime );
+
+        // According to the SMIL spec:
+        // Because 'to' animation is defined in terms of absolute values of
+        // the target attribute, cumulative animation is not defined.
+        if( mbCumulative && !mbDynamicStartValue )
+        {
+            // aValue = this.aEndValue * nRepeatCount + aValue;
+            aValue = accumulate( maEndValue, nRepeatCount, aValue );
+        }
+
+        (*mpAnim)( getPresentationValue( aValue ) );
+
+        if( mbDynamicStartValue )
+        {
+            maPreviousValue = mpAnim->getUnderlyingValue();
+        }
+
     }
 
     using BaseType::perform;
@@ -316,6 +369,10 @@ private:
     ValueType                               maStartValue;
     ValueType                               maEndValue;
 
+    mutable ValueType                               maPreviousValue;
+    mutable ValueType                               maStartInterpolationValue;
+    mutable sal_uInt32                              mnIteration;
+
     ::boost::shared_ptr< AnimationType >    mpAnim;
     Interpolator< ValueType >               maInterpolator;
     bool                                    mbDynamicStartValue;
diff --git a/slideshow/source/engine/animationnodes/animationbasenode.cxx 
b/slideshow/source/engine/animationnodes/animationbasenode.cxx
index df1bcc5..ed63495 100644
--- a/slideshow/source/engine/animationnodes/animationbasenode.cxx
+++ b/slideshow/source/engine/animationnodes/animationbasenode.cxx
@@ -33,6 +33,7 @@
 #include <cppuhelper/exc_hlp.hxx>
 #include <comphelper/anytostring.hxx>
 #include <com/sun/star/presentation/ParagraphTarget.hpp>
+#include <com/sun/star/animations/AnimationNodeType.hpp>
 #include <com/sun/star/animations/Timing.hpp>
 #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
@@ -421,7 +422,18 @@ AnimationBaseNode::fillCommonParameters() const
             else
                 aRepeats.reset( nRepeats / nDuration );
         }
-        else {
+        // This is a temporary workaround:
+        // as the repeatCount attribute is defined on the <par> parent node
+        // and activities are created only for animation node leaves, that
+        // actual performs a shape effect, we get the repeatCount value
+        // from the parent node.
+        else if( ( getXAnimationNode()->getType() != 
animations::AnimationNodeType::SET )
+                 && (getParentNode()->getXAnimationNode()->getRepeatCount() 
>>= nRepeats) )
+        {
+            aRepeats.reset( nRepeats );
+        }
+        else
+        {
             // no double value for both values - Timing::INDEFINITE?
             animations::Timing eTiming;
 
diff --git a/slideshow/source/engine/color.cxx 
b/slideshow/source/engine/color.cxx
index dc5092b..c9ce4f2 100644
--- a/slideshow/source/engine/color.cxx
+++ b/slideshow/source/engine/color.cxx
@@ -217,6 +217,19 @@ namespace slideshow
             return maHSLTriple.mnLuminance;
         }
 
+
+        sal_Bool operator==( const HSLColor& rLHS, const HSLColor& rRHS )
+        {
+            return ( rLHS.getHue() == rRHS.getHue() &&
+                     rLHS.getSaturation() == rRHS.getSaturation() &&
+                     rLHS.getLuminance() == rRHS.getLuminance() );
+        }
+
+        sal_Bool operator!=( const HSLColor& rLHS, const HSLColor& rRHS )
+        {
+            return !( rLHS == rRHS );
+        }
+
         HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS )
         {
             return HSLColor( rLHS.getHue() + rRHS.getHue(),
@@ -346,6 +359,18 @@ namespace slideshow
                                            255 );
         }
 
+        sal_Bool operator==( const RGBColor& rLHS, const RGBColor& rRHS )
+        {
+            return ( rLHS.getRed() == rRHS.getRed() &&
+                     rLHS.getGreen() == rRHS.getGreen() &&
+                     rLHS.getBlue() == rRHS.getBlue() );
+        }
+
+        sal_Bool operator!=( const RGBColor& rLHS, const RGBColor& rRHS )
+        {
+            return !( rLHS == rRHS );
+        }
+
         RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS )
         {
             return RGBColor( rLHS.getRed() + rRHS.getRed(),
diff --git a/slideshow/source/inc/hslcolor.hxx 
b/slideshow/source/inc/hslcolor.hxx
index 6659923..15500c7 100644
--- a/slideshow/source/inc/hslcolor.hxx
+++ b/slideshow/source/inc/hslcolor.hxx
@@ -88,6 +88,8 @@ namespace slideshow
             double      mnMagicValue;
         };
 
+        sal_Bool operator==( const HSLColor& rLHS, const HSLColor& rRHS );
+        sal_Bool operator!=( const HSLColor& rLHS, const HSLColor& rRHS );
         HSLColor operator+( const HSLColor& rLHS, const HSLColor& rRHS );
         HSLColor operator*( const HSLColor& rLHS, const HSLColor& rRHS );
         HSLColor operator*( double nFactor, const HSLColor& rRHS );
diff --git a/slideshow/source/inc/rgbcolor.hxx 
b/slideshow/source/inc/rgbcolor.hxx
index fc161f7..844f324 100644
--- a/slideshow/source/inc/rgbcolor.hxx
+++ b/slideshow/source/inc/rgbcolor.hxx
@@ -84,6 +84,8 @@ namespace slideshow
             RGBTriple   maRGBTriple;
         };
 
+        sal_Bool operator==( const RGBColor& rLHS, const RGBColor& rRHS );
+        sal_Bool operator!=( const RGBColor& rLHS, const RGBColor& rRHS );
         RGBColor operator+( const RGBColor& rLHS, const RGBColor& rRHS );
         RGBColor operator*( const RGBColor& rLHS, const RGBColor& rRHS );
         RGBColor operator*( double nFactor, const RGBColor& rRHS );
commit b9cfef37900cea634dc6fb987498b840230399bd
Author: Marco Cecchetti <mrcek...@gmail.com>
Date:   Tue Jun 12 21:42:41 2012 +0200

    Now to animations and repeated to animations are handled by the JavaScript 
engine as the
    SMIL spec describes.

diff --git a/filter/source/svg/presentation_engine.js 
b/filter/source/svg/presentation_engine.js
index 1ce3e16..13efb60 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -3375,6 +3375,13 @@ RGBColor.prototype.clone = function()
     return new RGBColor( this.nRed, this.nGreen, this.nBlue );
 };
 
+RGBColor.prototype.equal = function( aRGBColor )
+{
+    return ( this.nRed == aRGBColor.nRed ) &&
+           ( this.nGreen == aRGBColor.nGreen ) &&
+           ( this.nBlue == aRGBColor.nBlue );
+};
+
 RGBColor.prototype.add = function( aRGBColor )
 {
     this.nRed += aRGBColor.nRed;
@@ -3488,6 +3495,13 @@ HSLColor.prototype.clone = function()
     return new HSLColor( this.nHue, this.nSaturation, this.nLuminance );
 };
 
+HSLColor.prototype.equal = function( aHSLColor )
+{
+    return ( this.nHue == aHSLColor.nHue ) &&
+           ( this.nSaturation += aHSLColor.nSaturation ) &&
+           ( this.nLuminance += aHSLColor.nLuminance );
+};
+
 HSLColor.prototype.add = function( aHSLColor )
 {
     this.nHue += aHSLColor.nHue;
@@ -3990,11 +4004,11 @@ aPresetIdInMap = {};
 
 
 // Restart Modes
-RESTART_MODE_DEFAULT            = 0;
-RESTART_MODE_INHERIT            = 0;
-RESTART_MODE_ALWAYS             = 1;
-RESTART_MODE_WHEN_NOT_ACTIVE    = 2;
-RESTART_MODE_NEVER              = 3;
+var RESTART_MODE_DEFAULT            = 0;
+var RESTART_MODE_INHERIT            = 0;
+var RESTART_MODE_ALWAYS             = 1;
+var RESTART_MODE_WHEN_NOT_ACTIVE    = 2;
+var RESTART_MODE_NEVER              = 3;
 
 aRestartModeInMap = {
     'inherit'       : RESTART_MODE_DEFAULT,
@@ -4168,7 +4182,7 @@ BOXWIPE_TRANSITION          = 2;
 FOURBOXWIPE_TRANSITION      = 3;
 ELLIPSEWIPE_TRANSITION      = 4; // 17
 CLOCKWIPE_TRANSITION        = 5; // 22
-PINWHEELWIPE_TRANSITION     = 6  // 23
+PINWHEELWIPE_TRANSITION     = 6;  // 23
 PUSHWIPE_TRANSITION         = 7; // 35
 SLIDEWIPE_TRANSITION        = 8; // 36
 FADE_TRANSITION             = 9; // 37
@@ -9613,6 +9627,11 @@ var aOperatorSetMap = new Array();
 // number operators
 aOperatorSetMap[ NUMBER_PROPERTY ] = new Object();
 
+aOperatorSetMap[ NUMBER_PROPERTY ].equal = function( a, b )
+{
+    return ( a === b );
+};
+
 aOperatorSetMap[ NUMBER_PROPERTY ].add = function( a, b )
 {
     return ( a + b );
@@ -9626,6 +9645,11 @@ aOperatorSetMap[ NUMBER_PROPERTY ].scale = function( k, 
v )
 // color operators
 aOperatorSetMap[ COLOR_PROPERTY ] = new Object();
 
+aOperatorSetMap[ COLOR_PROPERTY ].equal = function( a, b )
+{
+    return a.equal( b );
+};
+
 aOperatorSetMap[ COLOR_PROPERTY ].add = function( a, b )
 {
     var c = a.clone();
@@ -10318,11 +10342,15 @@ function FromToByActivityTemplate( BaseType ) // 
template parameter
         this.aBy = aByValue;
         this.aStartValue = null;
         this.aEndValue = null;
+        this.aPreviousValue = null;
+        this.aStartInterpolationValue = null;
         this.aAnimation = aAnimation;
         this.aInterpolator = aInterpolator;
+        this.equal = aOperatorSet.equal;
         this.add = aOperatorSet.add;
         this.scale = aOperatorSet.scale;
         this.bDynamicStartValue = false;
+        this.nIteration = 0;
         this.bCumulative = bAccumulate;
 
         this.initAnimatedElement();
@@ -10378,6 +10406,9 @@ function FromToByActivityTemplate( BaseType ) // 
template parameter
         }
         else
         {
+            this.aStartValue = aAnimationStartValue;
+            this.aStartInterpolationValue = this.aStartValue;
+
             // By or To animation. According to SMIL spec,
             // the To value takes precedence over the By
             // value, if both are specified
@@ -10390,6 +10421,7 @@ function FromToByActivityTemplate( BaseType ) // 
template parameter
                 // the to animation interpolates between
                 // the _running_ underlying value and the to value (as the end 
value)
                 this.bDynamicStartValue = true;
+                this.aPreviousValue = this.aStartValue;
                 this.aEndValue = this.aTo;
             }
             else if( this.aBy )
@@ -10420,18 +10452,61 @@ function FromToByActivityTemplate( BaseType ) // 
template parameter
             return;
         }
 
-        var aValue = this.bDynamicStartValue ? 
this.aAnimation.getUnderlyingValue()
-                                             : this.aStartValue;
 
-        aValue = this.aInterpolator( aValue, this.aEndValue, nModifiedTime );
+        // According to SMIL 3.0 spec 'to' animation if no other (lower 
priority)
+        // animations are active or frozen then a simple interpolation is 
performed.
+        // That is, the start interpolation value is constant while the 
animation
+        // is running, and is equal to the underlying value retrieved when
+        // the animation start.
+        // However if another animation is manipulating the underlying value,
+        // the 'to' animation will initially add to the effect of the lower 
priority
+        // animation, and increasingly dominate it as it nears the end of the
+        // simple duration, eventually overriding it completely.
+        // That is, each time the underlying value is changed between two
+        // computations of the animation function the new underlying value is 
used
+        // as start value for the interpolation.
+        // See:
+        // 
http://www.w3.org/TR/SMIL3/smil-animation.html#animationNS-ToAnimation
+        // (Figure 6 - Effect of Additive to animation example)
+        // Moreover when a 'to' animation is repeated, at each new iteration
+        // the start interpolation value is reset to the underlying value
+        // of the animated property when the animation started,
+        // as it is shown in the example provided by the SMIL 3.0 spec.
+        // This is exactly as Firefox performs SVG 'to' animations.
+        if( this.bDynamicStartValue )
+        {
+            if( this.nIteration != nRepeatCount )
+            {
+                this.nIteration = nRepeatCount;
+                this.aStartInterpolationValue =  this.aStartValue;
+            }
+            else
+            {
+                var aActualValue = this.aAnimation.getUnderlyingValue();
+                if( !this.equal( aActualValue, this.aPreviousValue ) )
+                    this.aStartInterpolationValue = aActualValue;
+            }
+        }
 
-        if( this.bCumulative )
+        var aValue = this.aInterpolator( this.aStartInterpolationValue,
+                                         this.aEndValue, nModifiedTime );
+
+        // According to the SMIL spec:
+        // Because 'to' animation is defined in terms of absolute values of
+        // the target attribute, cumulative animation is not defined.
+        if( this.bCumulative && !this.bDynamicStartValue )
         {
             // aValue = this.aEndValue * nRepeatCount + aValue;
             aValue = this.add( this.scale( nRepeatCount, this.aEndValue ), 
aValue );
         }
 
         this.aAnimation.perform( aValue );
+
+        if( this.bDynamicStartValue )
+        {
+            this.aPreviousValue = this.aAnimation.getUnderlyingValue();
+        }
+
     };
 
     FromToByActivity.prototype.performEnd = function()
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to