[Libreoffice-commits] .: Branch 'feature/svg-export' - filter/source

Marco Cecchetti mcecchetti at kemper.freedesktop.org
Sat May 26 03:59:36 PDT 2012


 filter/source/svg/presentation_engine.js | 1387 +++++++++++++++++++++----------
 1 file changed, 984 insertions(+), 403 deletions(-)

New commits:
commit 4c7f9585d5ac71c6bfa3ff4b819b8f2939ccde15
Author: Marco Cecchetti <mrcekets at gmail.com>
Date:   Sat May 26 12:53:19 2012 +0200

    In order to enable future support for slide transitions has been needed
    to
    modify the part of the presentation engine handling the slides
    navigation feature.
    
    A new structure for slide and master page elements has been designed.
    The following new classes have been implemented: MasterPageView,
    TextFieldHandler, SlideNumberFieldHandler.
    The following classes have been modified: MetaDocument, MetaSlide,
    MasterPage, PlaceholderShape, Thumbnail.
    The following classes have been modified and renamed: TextField ->
    TextFieldContentProvider, FixedTextField -> FixedTextProvider,
    VariableDateTimeField -> CurrentDateTimeProvider, SlideNumberField ->
    SlideNumberProvider.

diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 98eab2f..6aeabe1 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -332,6 +332,8 @@ function getDefaultMouseHandlerDictionary()
     // index mode
     mouseHandlerDict[INDEX_MODE][MOUSE_DOWN]
         = function( aEvt ) { return toggleSlideIndex(); };
+//    mouseHandlerDict[INDEX_MODE][MOUSE_MOVE]
+//        = function( aEvt ) { return theSlideIndexPage.updateSelection( aEvt ); };
 
     return mouseHandlerDict;
 }
@@ -646,7 +648,8 @@ var aOOOAttrDateTimeFormat = 'date-time-format';
 
 var aOOOAttrTextAdjust = 'text-adjust';
 
-// Placeholder class names
+// element class names
+var aPageClassName = 'Page';
 var aSlideNumberClassName = 'Slide_Number';
 var aDateTimeClassName = 'Date/Time';
 var aFooterClassName = 'Footer';
@@ -801,6 +804,13 @@ function log( message )
     }
 }
 
+function warning( bCondition, sMessage )
+{
+    if( bCondition )
+        log( sMessage );
+    return bCondition;
+}
+
 function getNSAttribute( sNSPrefix, aElem, sAttrName )
 {
     if( !aElem ) return null;
@@ -910,6 +920,105 @@ function getSafeIndex( nIndex, nMin, nMax )
         return nIndex;
 }
 
+function isTextFieldElement( aElement )
+{
+    var sClassName = aElement.getAttribute( 'class' );
+    return ( sClassName === aSlideNumberClassName ) ||
+           ( sClassName === aFooterClassName ) ||
+           ( sClassName === aHeaderClassName ) ||
+           ( sClassName === aDateTimeClassName );
+}
+
+
+
+function tempWrapMasterPages()
+{
+    var aSlideGroupElement = document.createElementNS( NSS['svg'], 'g' );
+    aSlideGroupElement.setAttribute( 'class', 'SlideGroup' );
+    //aSlideGroupElement.onmousedown = function( aEvt ) { return mouseHandlerDispatch( aEvt, MOUSE_DOWN ); };
+    //aSlideGroupElement.setAttribute( 'visibility', 'hidden' );
+
+
+    var aDrawPageSet = getElementsByClassName(ROOT_NODE, 'Slide');
+    ROOT_NODE.insertBefore( aSlideGroupElement, aDrawPageSet[0] );
+
+    var aMasterPageSet = getElementsByClassName(ROOT_NODE, 'Master_Slide');
+    if( aMasterPageSet )
+    {
+        var aDefsElement = document.createElementNS( NSS['svg'], 'defs' );
+
+        ROOT_NODE.insertBefore( aDefsElement, aMasterPageSet[0] );
+        var i;
+        for( i = 0; i < aMasterPageSet.length; ++i)
+        {
+            var aMasterPage = ROOT_NODE.removeChild( aMasterPageSet[i] );
+            aDefsElement.appendChild( aMasterPage );
+        }
+    }
+}
+
+function tempCreateSlideView( aPageElement )
+{
+    if( !aPageElement )
+        return;
+
+    var aSlideGroupElement = getElementByClassName( ROOT_NODE, 'SlideGroup' );
+
+    var sId = aPageElement.getAttribute( 'id' );
+    var sName = aPageElement.getAttributeNS( NSS['ooo'], 'name' );
+    var sClipPath = aPageElement.getAttribute( 'clip-path' );
+
+    aPageElement.removeAttribute( 'id' );
+    aPageElement.removeAttributeNS( NSS['ooo'], 'name' );
+    aPageElement.removeAttribute( 'visibility' );
+    aPageElement.removeAttribute( 'clip-path' );
+    aPageElement.setAttribute( 'class', aPageClassName );
+
+    var aVisibilityStatusElement = document.createElementNS( NSS['svg'], 'g' );
+    aVisibilityStatusElement.setAttribute( 'visibility', 'hidden' );
+
+    var aSlideElement = document.createElementNS( NSS['svg'], 'g' );
+    aSlideElement.setAttribute( 'id', sId );
+    aSlideElement.setAttributeNS( NSS['ooo'], 'name', sName );
+    aSlideElement.setAttribute( 'clip-path', sClipPath );
+    aSlideElement.setAttribute( 'class', 'Slide' );
+    aVisibilityStatusElement.appendChild( aSlideElement );
+
+    aPageElement.parentNode.removeChild( aPageElement );
+    aSlideElement.appendChild( aPageElement );
+    aSlideGroupElement.appendChild( aVisibilityStatusElement );
+}
+
+function tempModMasterPage( aMasterPageElement, sId )
+{
+    if( !aMasterPageElement )
+        return;
+
+
+    var aBackgroundObjectsElement =
+        getElementByClassName( aMasterPageElement, 'BackgroundObjects' );
+
+    var aBackgroundShapesElement = document.createElementNS( NSS['svg'], 'g' );
+    aBackgroundShapesElement.setAttribute( 'id', 'bs-' + sId );
+    aBackgroundShapesElement.setAttribute( 'class', 'BackgroundShapes' );
+
+
+    if( aBackgroundObjectsElement.hasChildNodes() )
+    {
+        var aChildNode = aBackgroundObjectsElement.firstElementChild;
+        while( aChildNode )
+        {
+            var aNextChildNode= aChildNode.nextElementSibling;
+            if( !isTextFieldElement( aChildNode ) )
+            {
+                aBackgroundObjectsElement.removeChild( aChildNode );
+                aBackgroundShapesElement.appendChild( aChildNode );
+            }
+            aChildNode = aNextChildNode;
+        }
+    }
+    aBackgroundObjectsElement.appendChild( aBackgroundShapesElement );
+}
 
 
 // ------------------------------------------------------------------------------------------ //
@@ -978,31 +1087,59 @@ aAnimatedElementDebugPrinter.off();
 
 
 // ------------------------------------------------------------------------------------------ //
-/******************
- ** Core Classes **
- ******************/
-
-/** Class MetaDocument **
- *  This class provides a pool of properties related to the whole presentation and
- *  it is responsible for initializing the set of MetaSlide objects that handle
- *  the meta information for each slide.
+/************************
+ ***   Core Classes   ***
+ ************************/
+
+/** Class MetaDocument
+ *  This class provides a pool of properties related to the whole presentation.
+ *  Moreover it is responsible for:
+ *  - initializing the set of MetaSlide objects that handle the meta information
+ *    for each slide;
+ *  - creating a map with key an id and value the svg element containing
+ *    the animations performed on the slide with such an id.
+ *
  */
-function MetaDocument( aMetaDocElem )
+function MetaDocument()
 {
+    // TODO to be implemented in C++
+    tempWrapMasterPages();
+
+    // We look for the svg element that provides the following presentation
+    // properties:
+    // - the number of slides in the presentation;
+    // - the type of numbering used in the presentation.
+    // Moreover it wraps svg elements providing meta information on each slide
+    // and svg elements providing content and properties of each text field.
+    var aMetaDocElem = document.getElementById( aOOOElemMetaSlides );
+    assert( aMetaDocElem, 'MetaDocument: the svg element with id:' + aOOOElemMetaSlides + 'is not valid.');
+
+    // We initialize general presentation properties:
+    // - the number of slides in the presentation;
     this.nNumberOfSlides = parseInt( aMetaDocElem.getAttributeNS( NSS['ooo'], aOOOAttrNumberOfSlides ) );
     assert( typeof this.nNumberOfSlides == 'number' && this.nNumberOfSlides > 0,
             'MetaDocument: number of slides is zero or undefined.' );
-    this.startSlideNumber = 0;
+    // - the index of the slide to show when the presentation starts;
+    this.nStartSlideNumber = 0;
+    // - the numbering type used in the presentation, default type is arabic.
     this.sPageNumberingType = aMetaDocElem.getAttributeNS( NSS['ooo'], aOOOAttrNumberingType ) || 'arabic';
+
+    // The collections for handling properties of each slide, svg elements
+    // related to master pages and content and properties of text fields.
     this.aMetaSlideSet = new Array();
     this.aMasterPageSet = new Object();
-    this.aTextFieldSet = new Array();
-    this.slideNumberField =  new SlideNumberField( this.startSlideNumber + 1, this.sPageNumberingType );
+    this.aTextFieldHandlerSet = new Object();
+    this.aTextFieldContentProviderSet = new Array();
+    this.aSlideNumberProvider =  new SlideNumberProvider( this.nStartSlideNumber + 1, this.sPageNumberingType );
 
+    // We create a map with key an id and value the svg element containing
+    // the animations performed on the slide with such an id.
+    this.bIsAnimated = false;
     this.aSlideAnimationsMap = new Object();
     this.initSlideAnimationsMap();
 
-
+    // We initialize the set of MetaSlide objects that handle the meta
+    // information for each slide.
     for( var i = 0; i < this.nNumberOfSlides; ++i )
     {
         var sMetaSlideId = aOOOElemMetaSlide + '_' + i;
@@ -1010,21 +1147,55 @@ function MetaDocument( aMetaDocElem )
     }
     assert( this.aMetaSlideSet.length == this.nNumberOfSlides,
             'MetaDocument: aMetaSlideSet.length != nNumberOfSlides.' );
-    //this.aMetaSlideSet[ this.startSlideNumber ].show();
 }
 
-MetaDocument.prototype.initPlaceholderShapes = function()
+MetaDocument.prototype =
 {
-    this.aMetaSlideSet[0].initPlaceholderShapes();
-};
+/*** public methods ***/
+
+/** getCurrentSlide
+ *
+ *  @return
+ *      The MetaSlide object handling the current slide.
+ */
+getCurrentSlide : function()
+{
+    return this.aMetaSlideSet[nCurSlide];
+},
+
+/** setCurrentSlide
+ *
+ *  @param nSlideIndex
+ *      The index of the slide to show.
+ */
+setCurrentSlide : function( nSlideIndex )
+{
+    if( nSlideIndex >= 0 &&  nSlideIndex < this.nNumberOfSlides )
+    {
+        if( nCurSlide !== undefined )
+            this.aMetaSlideSet[nCurSlide].hide();
+        this.aMetaSlideSet[nSlideIndex].show();
+        nCurSlide = nSlideIndex;
+    }
+    else
+    {
+        log('MetaDocument.setCurrentSlide: slide index out of range: ' + nSlideIndex );
+    }
+},
+
+/*** private methods ***/
 
-MetaDocument.prototype.initSlideAnimationsMap = function()
+initSlideAnimationsMap : function()
 {
     var aAnimationsSection = document.getElementById( 'presentation-animations' );
     if( aAnimationsSection )
     {
         var aAnimationsDefSet = aAnimationsSection.getElementsByTagName( 'defs' );
 
+        // we have at least one slide with animations ?
+        this.bIsAnimated = ( typeof aAnimationsDefSet.length =='number' &&
+                             aAnimationsDefSet.length > 0 );
+
         for( var i = 0; i < aAnimationsDefSet.length; ++i )
         {
             var sSlideId = aAnimationsDefSet[i].getAttributeNS( NSS['ooo'], aOOOAttrSlide );
@@ -1035,433 +1206,880 @@ MetaDocument.prototype.initSlideAnimationsMap = function()
             }
         }
     }
-};
-
+}
 
+}; // end MetaDocument prototype
 
-/** Class MetaSlide **
- *  This class is responsible for managing the visibility of all master page shapes
- *  and background related to a given slide element; it performs the creation and
- *  the initialization of each Text Field object.
+/** Class MetaSlide
+ *  This class is responsible for:
+ *  - parsing and initializing slide properties;
+ *  - creating a MasterSlide object that provides direct access to the target
+ *    master slide and its sub-elements;
+ *  - initializing text field content providers;
+ *  - initializing the slide animation handler.
+ *
+ *  @param sMetaSlideId
+ *      The string representing the id attribute of the meta-slide element.
+ *  @param aMetaDoc
+ *      The MetaDocument global object.
  */
 function MetaSlide( sMetaSlideId, aMetaDoc )
 {
     this.theDocument = document;
     this.id = sMetaSlideId;
     this.theMetaDoc = aMetaDoc;
+
+    // We get a reference to the meta-slide element.
     this.element = this.theDocument.getElementById( this.id );
-    assert( this.element, 'MetaSlide: meta_slide element <' + this.id + '> not found.' );
-    // - Initialize the Slide Element -
+    assert( this.element,
+            'MetaSlide: meta_slide element <' + this.id + '> not found.' );
+
+    // We get a reference to the slide element.
     this.slideId = this.element.getAttributeNS( NSS['ooo'], aOOOAttrSlide );
     this.slideElement = this.theDocument.getElementById( this.slideId );
+    assert( this.slideElement,
+            'MetaSlide: slide element <' + this.slideId + '> not found.' );
+    this.nSlideNumber = parseInt( this.slideId.substr(2) );
+
+    // ------------------------------
+    // TODO: to be implemented in C++
+    tempCreateSlideView(this.slideElement);
+    this.slideElement = this.theDocument.getElementById( this.slideId );
     assert( this.slideElement, 'MetaSlide: slide element <' + this.slideId + '> not found.' );
-    // - Initialize the Target Master Page Element -
+    // ------------------------------
+
+    // Each slide element is wrapped by a <g> element that is responsible for
+    // the slide element visibility. In fact the visibility attribute has
+    // to be set on the parent of the slide element and not directly on
+    // the slide element. The reason is that in index mode each slide
+    // rendered in a thumbnail view is targeted by a <use> element, however
+    // when the visibility attribute is set directly on the referred slide
+    // element its visibility is not overridden by the visibility attribute
+    // defined by the targeting <use> element. The previous solution was,
+    // when the user switched to index mode, to set up the visibility attribute
+    // of all slides rendered in a thumbnail to 'visible'.
+    // Obviously the slides were not really visible because the grid of
+    // thumbnails was above them, anyway Firefox performance was really bad.
+    // The workaround of setting up the visibility attribute on the slide
+    // parent element let us to make visible a slide in a <use> element
+    // even if the slide parent element visibility is set to 'hidden'.
+    this.aVisibilityStatusElement = this.slideElement.parentNode;
+
+    // We get a reference to the draw page element, where all shapes specific
+    // of this slide live.
+    this.pageElement = getElementByClassName( this.slideElement, aPageClassName );
+    assert( this.pageElement,
+            'MetaSlide: page element <' + this.slideId + '> not found.' );
+
+    // We initialize the MasterPage object that provides direct access to
+    // the target master page element.
     this.masterPage = this.initMasterPage();
-    // - Initialize Background -
-    //this.aBackground                 = getElementByClassName( this.aSlide, 'Background' );
-    // - Initialize Visibility Properties -
+
+    // We initialize visibility properties of the target master page elements.
     this.nAreMasterObjectsVisible     = this.initVisibilityProperty( aOOOAttrMasterObjectsVisibility,  VISIBLE );
     this.nIsBackgroundVisible         = this.initVisibilityProperty( aOOOAttrBackgroundVisibility,     VISIBLE );
     this.nIsPageNumberVisible         = this.initVisibilityProperty( aOOOAttrPageNumberVisibility,     HIDDEN );
     this.nIsDateTimeVisible           = this.initVisibilityProperty( aOOOAttrDateTimeVisibility,       VISIBLE );
     this.nIsFooterVisible             = this.initVisibilityProperty( aOOOAttrFooterVisibility,         VISIBLE );
     this.nIsHeaderVisible             = this.initVisibilityProperty( aOOOAttrHeaderVisibility,         VISIBLE );
-    // - Initialize Master Page Text Fields (Placeholders)-
-    this.aMPTextFieldSet = new Object();
-    this.aMPTextFieldSet[aSlideNumberClassName]   = this.initSlideNumberField();
-    this.aMPTextFieldSet[aDateTimeClassName]      = this.initDateTimeField( aOOOAttrDateTimeField );
-    this.aMPTextFieldSet[aFooterClassName]        = this.initFixedTextField( aOOOAttrFooterField );
-    this.aMPTextFieldSet[aHeaderClassName]        = this.initFixedTextField( aOOOAttrHeaderField );
-
-    // - Initialize Slide Animations Handler
+
+    // This property tell us if the date/time field need to be updated
+    // each time the slide is shown. It is initialized in
+    // the initDateTimeFieldContentProvider method.
+    this.bIsDateTimeVariable = undefined;
+
+    // We initialize the objects responsible to provide the content to text field.
+    this.aTextFieldContentProviderSet = new Object();
+    this.aTextFieldContentProviderSet[aSlideNumberClassName]   = this.initSlideNumberFieldContentProvider();
+    this.aTextFieldContentProviderSet[aDateTimeClassName]      = this.initDateTimeFieldContentProvider( aOOOAttrDateTimeField );
+    this.aTextFieldContentProviderSet[aFooterClassName]        = this.initFixedTextFieldContentProvider( aOOOAttrFooterField );
+    this.aTextFieldContentProviderSet[aHeaderClassName]        = this.initFixedTextFieldContentProvider( aOOOAttrHeaderField );
+
+    // We initialize the SlideAnimationsHandler object
     this.aSlideAnimationsHandler = new SlideAnimations( aSlideShow.getContext() );
     this.aSlideAnimationsHandler.importAnimations( this.getSlideAnimationsRoot() );
     this.aSlideAnimationsHandler.parseElements();
+
+    // this statement is used only for debugging
     if( false && this.aSlideAnimationsHandler.aRootNode )
         log( this.aSlideAnimationsHandler.aRootNode.info( true ) );
+
 }
 
-/*** MetaSlide methods ***/
 MetaSlide.prototype =
 {
-    /*** public methods ***/
-    hide : function()
-    {
-        checkElemAndSetAttribute( this.slideElement, 'visibility', 'hidden' );
-
-        this.masterPage.hide();
-        this.masterPage.hideBackground();
+/*** public methods ***/
 
-        var aFieldSet = this.aMPTextFieldSet;
-        var aShapeSet = this.masterPage.aPlaceholderShapeSet;
-        if( aFieldSet[aSlideNumberClassName] )         aFieldSet[aSlideNumberClassName].hide( aShapeSet[aSlideNumberClassName] );
-        if( aFieldSet[aDateTimeClassName] )            aFieldSet[aDateTimeClassName].hide( aShapeSet[aDateTimeClassName] );
-        if( aFieldSet[aFooterClassName] )              aFieldSet[aFooterClassName].hide( aShapeSet[aFooterClassName] );
-        if( aFieldSet[aHeaderClassName] )              aFieldSet[aHeaderClassName].hide( aShapeSet[aHeaderClassName] );
-    },
+/** show
+ *  Set the visibility property of the slide to 'inherit'.
+ */
+show : function()
+{
+    this.updateMasterPageView();
+    this.aVisibilityStatusElement.setAttribute( 'visibility', 'inherit' );
+},
 
-    hideExceptMaster : function()
-    {
-        checkElemAndSetAttribute( this.slideElement, 'visibility', 'hidden' );
-    },
+/** hide
+ *  Set the visibility property of the slide to 'hidden'.
+ */
+hide : function()
+{
+    this.aVisibilityStatusElement.setAttribute( 'visibility', 'hidden' );
+},
 
-    show : function()
+/** updateMasterPageView
+ *  On first call it creates a master page view element and insert it at
+ *  the begin of the slide element. Moreover it updates the text fields
+ *  included in the master page view.
+ */
+updateMasterPageView : function()
+{
+    // The master page view element is generated and attached on first time
+    // the slide is shown.
+    if( !this.aMasterPageView )
     {
-        checkElemAndSetAttribute( this.slideElement, 'visibility', 'visible' );
-
-        this.masterPage.setVisibility( this.nAreMasterObjectsVisible );
-        this.masterPage.setVisibilityBackground( this.nIsBackgroundVisible );
-
-
-        this.setTextFieldVisibility( aSlideNumberClassName, this.nIsPageNumberVisible );
-        this.setTextFieldVisibility( aDateTimeClassName, this.nIsDateTimeVisible );
-        this.setTextFieldVisibility( aFooterClassName, this.nIsFooterVisible );
-        this.setTextFieldVisibility( aHeaderClassName, this.nIsHeaderVisible );
-    },
+        this.aMasterPageView = new MasterPageView( this );
+        this.aMasterPageView.attachToSlide();
+    }
+    this.aMasterPageView.update();
+},
 
-    getMasterPageId : function()
-    {
-        return this.masterPage.id;
-    },
+/*** private methods ***/
+initMasterPage : function()
+{
+    var sMasterPageId = this.element.getAttributeNS( NSS['ooo'], aOOOAttrMaster );
 
-    getMasterPageElement : function()
+    // Check that the master page handler object has not already been
+    // created by an other slide that target the same master page.
+    if( !this.theMetaDoc.aMasterPageSet.hasOwnProperty( sMasterPageId ) )
     {
-        return this.masterPage.element;
-    },
+        this.theMetaDoc.aMasterPageSet[ sMasterPageId ] = new MasterPage( sMasterPageId );
 
-    getBackground : function()
-    {
-        return getElementByClassName( this.slideElement, 'Background' );
-    },
+        // We initialize aTextFieldHandlerSet[ sMasterPageId ] to an empty
+        // collection.
+        this.theMetaDoc.aTextFieldHandlerSet[ sMasterPageId ] = new Object();
+    }
+    return this.theMetaDoc.aMasterPageSet[ sMasterPageId ];
+},
 
-    getMasterPageBackground : function()
-    {
-        return this.masterPage.background;
-    },
+initVisibilityProperty : function( aVisibilityAttribute, nDefaultValue )
+{
+    var nVisibility = nDefaultValue;
+    var sVisibility = getOOOAttribute( this.element, aVisibilityAttribute );
+    if( sVisibility )
+        nVisibility = aVisibilityValue[ sVisibility ];
+    return nVisibility;
+},
 
-    /*** private methods ***/
-    initMasterPage : function()
-    {
-        var sMasterPageId = this.element.getAttributeNS( NSS['ooo'], aOOOAttrMaster );
-        if( !this.theMetaDoc.aMasterPageSet.hasOwnProperty( sMasterPageId ) )
-            this.theMetaDoc.aMasterPageSet[ sMasterPageId ] = new MasterPage( sMasterPageId );
-        return this.theMetaDoc.aMasterPageSet[ sMasterPageId ];
-    },
+initSlideNumberFieldContentProvider : function()
+{
+    return this.theMetaDoc.aSlideNumberProvider;
+},
 
-    initVisibilityProperty : function( aVisibilityAttribute, nDefaultValue )
-    {
-        var nVisibility = nDefaultValue;
-        var sVisibility = getOOOAttribute( this.element, aVisibilityAttribute );
-        if( sVisibility )
-            nVisibility = aVisibilityValue[ sVisibility ];
-        return nVisibility;
-    },
+initDateTimeFieldContentProvider : function( aOOOAttrDateTimeField )
+{
+    var sTextFieldId = getOOOAttribute( this.element, aOOOAttrDateTimeField );
+    if( !sTextFieldId )  return null;
 
-    initSlideNumberField : function()
-    {
-        return this.theMetaDoc.slideNumberField;
-    },
+    var nLength = aOOOElemTextField.length + 1;
+    var nIndex = parseInt(sTextFieldId.substring( nLength ) );
+    if( typeof nIndex != 'number') return null;
 
-    initDateTimeField : function( aOOOAttrDateTimeField )
+    if( !this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ] )
     {
-        var sTextFieldId = getOOOAttribute( this.element, aOOOAttrDateTimeField );
-        if( !sTextFieldId )  return null;
-
-        var nLength = aOOOElemTextField.length + 1;
-        var nIndex = parseInt(sTextFieldId.substring( nLength ) );
-        if( typeof nIndex != 'number') return null;
-
-        if( !this.theMetaDoc.aTextFieldSet[ nIndex ] )
+        var aTextField;
+        var aTextFieldElem = document.getElementById( sTextFieldId );
+        var sClassName = getClassAttribute( aTextFieldElem );
+        if( sClassName == 'FixedDateTimeField' )
         {
-            var aTextField;
-            var aTextFieldElem = document.getElementById( sTextFieldId );
-            var sClassName = getClassAttribute( aTextFieldElem );
-            if( sClassName == 'FixedDateTimeField' )
-            {
-                aTextField = new FixedTextField( aTextFieldElem );
-            }
-            else if( sClassName == 'VariableDateTimeField' )
-            {
-                aTextField = new VariableDateTimeField( aTextFieldElem );
-            }
-            else
-            {
-                aTextField = null;
-            }
-            this.theMetaDoc.aTextFieldSet[ nIndex ] = aTextField;
+            aTextField = new FixedTextProvider( aTextFieldElem );
+            this.bIsDateTimeVariable = false;
         }
-        return this.theMetaDoc.aTextFieldSet[ nIndex ];
-    },
-
-    initFixedTextField : function( aOOOAttribute )
-    {
-        var sTextFieldId = getOOOAttribute( this.element, aOOOAttribute );
-        if( !sTextFieldId ) return null;
-
-        var nLength = aOOOElemTextField.length + 1;
-        var nIndex = parseInt( sTextFieldId.substring( nLength ) );
-        if( typeof nIndex != 'number') return null;
-
-        if( !this.theMetaDoc.aTextFieldSet[ nIndex ] )
+        else if( sClassName == 'VariableDateTimeField' )
         {
-            var aTextFieldElem = document.getElementById( sTextFieldId );
-            this.theMetaDoc.aTextFieldSet[ nIndex ]
-                = new FixedTextField( aTextFieldElem );
+            aTextField = new CurrentDateTimeProvider( aTextFieldElem );
+            this.bIsDateTimeVariable = true;
         }
-        return this.theMetaDoc.aTextFieldSet[ nIndex ];
-    },
+        else
+        {
+            aTextField = null;
+        }
+        this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ] = aTextField;
+    }
+    return this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ];
+},
 
-    setTextFieldVisibility : function( sClassName, nVisible )
-    {
-        var aTextField = this.aMPTextFieldSet[ sClassName ];
-        var aPlaceholderShape = this.masterPage.aPlaceholderShapeSet[ sClassName ];
-        if( !aTextField ) return;
-        aTextField.setVisibility( this.nAreMasterObjectsVisible & nVisible, aPlaceholderShape );
-    },
+initFixedTextFieldContentProvider : function( aOOOAttribute )
+{
+    var sTextFieldId = getOOOAttribute( this.element, aOOOAttribute );
+    if( !sTextFieldId ) return null;
 
-    getSlideAnimationsRoot : function()
+    var nLength = aOOOElemTextField.length + 1;
+    var nIndex = parseInt( sTextFieldId.substring( nLength ) );
+    if( typeof nIndex != 'number') return null;
+
+    if( !this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ] )
     {
-        return this.theMetaDoc.aSlideAnimationsMap[ this.slideId ];
+        var aTextFieldElem = document.getElementById( sTextFieldId );
+        this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ]
+            = new FixedTextProvider( aTextFieldElem );
     }
+    return this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ];
+},
 
-};
+getSlideAnimationsRoot : function()
+{
+    return this.theMetaDoc.aSlideAnimationsMap[ this.slideId ];
+}
+
+}; // end MetaSlide prototype
 
 /** Class MasterPage **
- *  This class gives access to a master page element, its background and
- *  each placeholder shape present in the master page element.
+ *  This class gives direct access to a master page element and to the following
+ *  elements included in the master page:
+ *  - the background element,
+ *  - the background objects group element,
+ *  - the background shapes group element.
+ *  Moreover for each text field element a Placeholder object is created which
+ *  manages the text field element itself.
+ *
+ *  The master page element structure is the following:
+ *  <g class='Master_Slide'>
+ *      <g class='Background'>
+ *          background image
+ *      </g>
+ *      <g class='BackgroundObjects'>
+ *          <g class='BackgroundFields'>
+ *              <g class='Date/Time'>
+ *                  date/time placeholder
+ *              </g>
+ *              <g class='Header'>
+ *                  header placeholder
+ *              </g>
+ *              <g class='Footer'>
+ *                  footer placeholder
+ *              </g>
+ *              <g class='Slide_Number'>
+ *                  slide number placeholder
+ *              </g>
+ *          </g>
+ *          <g class='BackgroundShapes'>
+ *              shapes
+ *          </g>
+ *      </g>
+ *  </g>
+ *
+ *  @param sMasterPageId
+ *      A string representing the value of the id attribute of the master page
+ *      element to be handled.
  */
 function MasterPage( sMasterPageId )
 {
     this.id = sMasterPageId;
+
+    // The master page element to be handled.
+    this.element = document.getElementById( this.id );
+    assert( this.element,
+            'MasterPage: master page element <' + this.id + '> not found.' );
+
+    // ------------------------------
+    // TODO: to be implemented in C++
+    tempModMasterPage( this.element, this.id );
     this.element = document.getElementById( this.id );
     assert( this.element, 'MasterPage: master page element <' + this.id + '> not found.' );
+    // ------------------------------
+
+    // The master page background element and its id attribute.
     this.background = getElementByClassName( this.element, 'Background' );
-    this.backgroundId = this.background.getAttribute( 'id' );
-    this.backgroundVisibility = initVisibilityProperty( this.background );
+    if( this.background )
+    {
+        this.backgroundId = this.background.getAttribute( 'id' );
+//      this.backgroundVisibility = initVisibilityProperty( this.background );
+    }
+    else
+    {
+        this.backgroundId = '';
+        log( 'MasterPage: the background element is not valid.' );
+    }
+
+    // The background objects group element that contains every element presents
+    // on the master page except the background element.
     this.backgroundObjects = getElementByClassName( this.element, 'BackgroundObjects' );
-    this.backgroundObjectsId = this.backgroundObjects.getAttribute( 'id' );
-    this.backgroundObjectsVisibility = initVisibilityProperty( this.backgroundObjects );
+    if( this.backgroundObjects )
+    {
+        this.backgroundObjectsId = this.backgroundObjects.getAttribute( 'id' );
+//      this.backgroundObjectsVisibility = initVisibilityProperty( this.backgroundObjects );
+    }
+    else
+    {
+        this.backgroundObjectsId = '';
+        log( 'MasterPage: the background objects element is not valid.' );
+    }
+
+    // The background shapes group element that contains all the shape of
+    // the master page that are not text fields.
+    this.backgroundShapes = getElementByClassName( this.backgroundObjects, 'BackgroundShapes' );
+    if( this.backgroundShapes )
+    {
+        this.backgroundShapesId = this.backgroundShapes.getAttribute( 'id' );
+    }
+    else
+    {
+        this.backgroundShapesId = '';
+        log( 'MasterPage: the background shapes element is not valid.' );
+    }
+
+    // We populate the collection of placeholders.
     this.aPlaceholderShapeSet = new Object();
     this.initPlaceholderShapes();
+
+    this.removeVisibilityAttributes();
 }
 
-/*** MasterPage methods ***/
 MasterPage.prototype =
 {
-    /*** public method ***/
-    setVisibility : function( nVisibility )
-    {
-        this.backgroundObjectsVisibility = setElementVisibility( this.backgroundObjects, this.backgroundObjectsVisibility, nVisibility );
-    },
-
-    setVisibilityBackground : function( nVisibility )
-    {
-        this.backgroundVisibility = setElementVisibility( this.background, this.backgroundVisibility, nVisibility );
-    },
+/*** private methods ***/
 
-    hide : function()
-    {
-        this.setVisibility( HIDDEN );
-    },
-
-    show : function()
-    {
-        this.setVisibility( VISIBLE );
-    },
+initPlaceholderShapes : function()
+{
+    this.aPlaceholderShapeSet[ aSlideNumberClassName ] = new PlaceholderShape( this, aSlideNumberClassName );
+    this.aPlaceholderShapeSet[ aDateTimeClassName ] = new PlaceholderShape( this, aDateTimeClassName );
+    this.aPlaceholderShapeSet[ aFooterClassName ] = new PlaceholderShape( this, aFooterClassName );
+    this.aPlaceholderShapeSet[ aHeaderClassName ] = new PlaceholderShape( this, aHeaderClassName );
+},
 
-    hideBackground : function()
-    {
-        this.setVisibilityBackground( HIDDEN );
-    },
+removeVisibilityAttributes : function()
+{
+    this.element.removeAttribute( 'visibility' );
+    this.background.removeAttribute( 'visibility' );
+    this.backgroundObjects.removeAttribute( 'visibility' );
+}
 
-    showBackground : function()
-    {
-        this.setVisibilityBackground( VISIBLE );
-    },
+}; // end MasterPage prototype
 
-    /*** private method ***/
-    initPlaceholderShapes : function()
-    {
-        this.aPlaceholderShapeSet[ aSlideNumberClassName ] = new PlaceholderShape( this, aSlideNumberClassName );
-        this.aPlaceholderShapeSet[ aDateTimeClassName ] = new PlaceholderShape( this, aDateTimeClassName );
-        this.aPlaceholderShapeSet[ aFooterClassName ] = new PlaceholderShape( this, aFooterClassName );
-        this.aPlaceholderShapeSet[ aHeaderClassName ] = new PlaceholderShape( this, aHeaderClassName );
-    }
-};
-
-/** Class PlaceholderShape **
- *  This class manages the visibility and the text content of a placeholder shape.
+/** Class PlaceholderShape
+ *  This class provides direct access to a text field element and
+ *  to the embedded placeholder element.
+ *  Moreover it set up the text adjustment and position for the placeholder
+ *  element.
+ *  Note: the text field element included in a master page is used only as
+ *  a template element, it is cloned for each specific text content
+ *  (see the TextFieldContentProvider class and its derived classes).
+ *
+ *  @param aMasterPage
+ *      The master page object to which the text field to be handled belongs.
+ *  @param sClassName
+ *      A string representing the value of the class attribute of the text
+ *      field element to be handled.
  */
 function PlaceholderShape( aMasterPage, sClassName )
 {
     this.masterPage = aMasterPage;
     this.className = sClassName;
+
     this.element = null;
     this.textElement = null;
-
     this.init();
 }
 
 /* public methods */
-PlaceholderShape.prototype.setTextContent = function( sText )
+PlaceholderShape.prototype.isValid = function()
 {
-    if( !this.textElement )
+    return ( this.element && this.textElement );
+};
+
+/* private methods */
+
+/** init
+ *  In case a text field element of class type 'className' exists and such
+ *  an element embeds a placeholder element, the text adjustment and position
+ *  of the placeholder element is set up.
+ */
+PlaceholderShape.prototype.init = function()
+{
+
+    var aTextFieldElement = getElementByClassName( this.masterPage.backgroundObjects, this.className );
+    if( aTextFieldElement )
     {
-        log( 'error: PlaceholderShape.setTextContent: text element is not valid in placeholder of type '
-                + this.className + ' that belongs to master slide ' + this.masterPage.id );
-        return;
+        aTextFieldElement.removeAttribute( 'visibility' ); // TODO to be handled in C++ ?
+
+        var aPlaceholderElement = getElementByClassName( aTextFieldElement, 'PlaceholderText' );
+        if( aPlaceholderElement )
+        {
+            // Each text field element has an invisible rectangle that can be
+            // regarded as the text field bounding box.
+            // We exploit such a feature and the exported text adjust attribute
+            // value in order to set up correctly the position and text
+            // adjustment for the placeholder element.
+            var aSVGRectElemSet = aTextFieldElement.getElementsByTagName( 'rect' );
+            // As far as text field element exporting is implemented it should
+            // be only one <rect> element!
+            if( aSVGRectElemSet.length === 1)
+            {
+                var aRect = new Rectangle( aSVGRectElemSet[0] );
+                var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust ) || 'left';
+                var sTextAnchor, sX;
+                if( sTextAdjust == 'left' )
+                {
+                    sTextAnchor = 'start';
+                    sX = String( aRect.left );
+                }
+                else if( sTextAdjust == 'right' )
+                {
+                    sTextAnchor = 'end';
+                    sX = String( aRect.right );
+                }
+                else if( sTextAdjust == 'center' )
+                {
+                    sTextAnchor = 'middle';
+                    var nMiddle = ( aRect.left + aRect.right ) / 2;
+                    sX = String( parseInt( String( nMiddle ) ) );
+                }
+                if( sTextAnchor )
+                    aPlaceholderElement.setAttribute( 'text-anchor', sTextAnchor );
+                if( sX )
+                    aPlaceholderElement.setAttribute( 'x', sX );
+
+                this.element = aTextFieldElement;
+                this.textElement = aPlaceholderElement;
+            }
+        }
     }
-    this.textElement.textContent = sText;
 };
 
-PlaceholderShape.prototype.setVisibility = function( nVisibility )
+/** Class MasterPageView
+ *  This class is used to creates a svg element of class MasterPageView and its
+ *  sub-elements.
+ *  It is also responsible for updating the content of the included text fields.
+ *
+ *  MasterPageView element structure:
+ *
+ *  <g class='MasterPageView'>
+ *      <use class='Background'>               // reference to master page background element
+ *      <g class='BackgroundObjects'>
+ *          <g class='BackgroundFields'>
+ *              <g class='Slide_Number'>       // a cloned element
+ *                  ...
+ *              </g>
+ *              <use class='Date/Time'>        // reference to a clone
+ *              <use class='Footer'>
+ *              <use class='Header'>
+ *          </g>
+ *          <use class='BackgroundShapes'>     // reference to the group of shapes on the master page
+ *      </g>
+ *  </g>
+ *
+ *  Sub-elements are present only if they are visible.
+ *
+ *  @param aMetaSlide
+ *      The MetaSlide object managing the slide element that targets
+ *      the master page view element created by an instance of MasterPageView.
+ */
+function MasterPageView( aMetaSlide )
+{
+    this.aMetaSlide = aMetaSlide;
+    this.aSlideElement = aMetaSlide.slideElement;
+    this.aPageElement = aMetaSlide.pageElement;
+    this.aMasterPage = aMetaSlide.masterPage;
+    this.aMPVElement = this.createElement();
+    this.bIsAttached = false;
+}
+
+/*** public methods ***/
+
+/** attachToSlide
+ *  Prepend the master slide view element to the slide element.
+ */
+MasterPageView.prototype.attachToSlide = function()
 {
-    if( !this.element )
+    if( !this.bIsAttached )
     {
-        return;
+        var aInsertedElement = this.aSlideElement.insertBefore( this.aMPVElement, this.aPageElement );
+        assert( aInsertedElement === this.aMPVElement,
+                'MasterPageView.attachToSlide: aInsertedElement != this.aMPVElement' );
+
+        this.bIsAttached = true;
     }
-    this.element.setAttribute( 'visibility', aVisibilityAttributeValue[nVisibility] );
 };
 
-PlaceholderShape.prototype.show = function()
+/** detachFromSlide
+ *  Remove the master slide view element from the slide element.
+ */
+MasterPageView.prototype.detachFromSlide = function()
 {
-    this.setVisibility( VISIBLE );
+    if( this.bIsAttached )
+    {
+        this.aSlideElement.removeChild( this.aMPVElement );
+        this.bIsAttached = false;
+    }
 };
 
-PlaceholderShape.prototype.hide = function()
+/** update
+ *  Update the content of text fields placed on the master page.
+ */
+MasterPageView.prototype.update = function()
 {
-    this.setVisibility( HIDDEN );
+    if( this.aDateTimeFieldHandler && this.aMetaSlide.bIsDateTimeVariable )
+        this.aDateTimeFieldHandler.update();
 };
 
-/* private methods */
-PlaceholderShape.prototype.init = function()
+/*** private methods ***/
+
+MasterPageView.prototype.createElement = function()
 {
-    var aShapeElem = getElementByClassName( this.masterPage.backgroundObjects, this.className );
-    if( !aShapeElem ) return;
+    var theDocument = document;
+    var aMasterPageViewElement = theDocument.createElementNS( NSS['svg'], 'g' );
+    assert( aMasterPageViewElement,
+            'MasterPageView.createElement: failed to create a master page view element.' );
+    aMasterPageViewElement.setAttribute( 'class', 'MasterPageView' );
 
-    this.element = aShapeElem;
-    this.element.setAttribute( 'visibility', 'hidden' );
+    // init the Background element
+    if( this.aMetaSlide.nIsBackgroundVisible )
+    {
+        this.aBackgroundElement = theDocument.createElementNS( NSS['svg'], 'use' );
+        this.aBackgroundElement.setAttribute( 'class', 'Background' );
+        setNSAttribute( 'xlink', this.aBackgroundElement,
+                        'href', '#' + this.aMasterPage.backgroundId );
 
-    this.textElement = getElementByClassName( this.element , 'PlaceholderText' );
-    if( !this.textElement )  return;
+        // node linking
+        aMasterPageViewElement.appendChild( this.aBackgroundElement );
+    }
 
+    // init the BackgroundObjects element
+    if( this.aMetaSlide.nAreMasterObjectsVisible )
+    {
+        this.aBackgroundObjectsElement = theDocument.createElementNS( NSS['svg'], 'g' );
+        this.aBackgroundObjectsElement.setAttribute( 'class', 'BackgroundObjects' );
 
-    var aSVGRectElemSet = this.element.getElementsByTagName( 'rect' );
-    if( aSVGRectElemSet.length != 1) return;
+        // create background fields group
+        this.aBackgroundFieldsElement = theDocument.createElementNS( NSS['svg'], 'g' );
+        this.aBackgroundFieldsElement.setAttribute( 'class', 'BackgroundFields' );
 
-    var aRect = new Rectangle( aSVGRectElemSet[0] );
+        // clone and initialize text field elements
+        var aPlaceholderShapeSet = this.aMasterPage.aPlaceholderShapeSet;
+        var aTextFieldContentProviderSet = this.aMetaSlide.aTextFieldContentProviderSet;
+        // where cloned elements are appended
+        var aDefsElement = this.aMetaSlide.element.parentNode;
+        var aTextFieldHandlerSet = this.aMetaSlide.theMetaDoc.aTextFieldHandlerSet;
+        var sMasterSlideId = this.aMasterPage.id;
 
-    var sTextAdjust = getOOOAttribute( this.element, aOOOAttrTextAdjust ) || 'left';
-    var sTextAnchor, sX;
-    if( sTextAdjust == 'left' )
-    {
-        sTextAnchor = 'start';
-        sX = String( aRect.left );
-    }
-    else if( sTextAdjust == 'right' )
-    {
-        sTextAnchor = 'end';
-        sX = String( aRect.right );
-    }
-    else if( sTextAdjust == 'center' )
-    {
-        sTextAnchor = 'middle';
-        var nMiddle = ( aRect.left + aRect.right ) / 2;
-        sX = String( parseInt( String( nMiddle ) ) );
-    }
+        // Slide Number Field
+        // The cloned element is appended directly to the field group element
+        // since there is no slide number field content shared between two slide
+        // (because the slide number of two slide is always different).
+        if( aPlaceholderShapeSet[aSlideNumberClassName] &&
+            aPlaceholderShapeSet[aSlideNumberClassName].isValid() &&
+            this.aMetaSlide.nIsPageNumberVisible )
+        {
+            this.aSlideNumberFieldHandler =
+            new SlideNumberFieldHandler( aPlaceholderShapeSet[aSlideNumberClassName],
+                                         aTextFieldContentProviderSet[aSlideNumberClassName] );
+            this.aSlideNumberFieldHandler.update( this.aMetaSlide.nSlideNumber );
+            this.aSlideNumberFieldHandler.appendTo( this.aBackgroundFieldsElement );
+        }
 
+        // Date/Time field
+        if( this.aMetaSlide.nIsDateTimeVisible )
+        {
+            this.aDateTimeFieldHandler =
+            this.initTextFieldHandler( aDateTimeClassName, aPlaceholderShapeSet,
+                                       aTextFieldContentProviderSet, aDefsElement,
+                                       aTextFieldHandlerSet, sMasterSlideId );
+        }
+
+        // Footer Field
+        if( this.aMetaSlide.nIsFooterVisible )
+        {
+            this.aFooterFieldHandler =
+            this.initTextFieldHandler( aFooterClassName, aPlaceholderShapeSet,
+                                       aTextFieldContentProviderSet, aDefsElement,
+                                       aTextFieldHandlerSet, sMasterSlideId );
+        }
+
+        // Header Field
+        if( this.aMetaSlide.nIsHeaderVisible )
+        {
+            this.aHeaderFieldHandler =
+            this.initTextFieldHandler( aHeaderClassName, aPlaceholderShapeSet,
+                                       aTextFieldContentProviderSet, aDefsElement,
+                                       aTextFieldHandlerSet, sMasterSlideId );
+        }
+
+        // init BackgroundShapes element
+        this.aBackgroundShapesElement = theDocument.createElementNS( NSS['svg'], 'use' );
+        this.aBackgroundShapesElement.setAttribute( 'class', 'BackgroundShapes' );
+        setNSAttribute( 'xlink', this.aBackgroundShapesElement,
+                        'href', '#' + this.aMasterPage.backgroundShapesId );
 
-    this.textElement.setAttribute( 'text-anchor', sTextAnchor );
-    this.textElement.setAttribute( 'x', sX );
+        // node linking
+        this.aBackgroundObjectsElement.appendChild( this.aBackgroundFieldsElement );
+        this.aBackgroundObjectsElement.appendChild( this.aBackgroundShapesElement );
+        aMasterPageViewElement.appendChild( this.aBackgroundObjectsElement );
+    }
+
+    return aMasterPageViewElement;
 };
 
+MasterPageView.prototype.initTextFieldHandler =
+function( sClassName, aPlaceholderShapeSet, aTextFieldContentProviderSet,
+          aDefsElement, aTextFieldHandlerSet, sMasterSlideId )
+{
+    var aTextFieldHandler = null;
+    if( aPlaceholderShapeSet[sClassName] &&
+        aPlaceholderShapeSet[sClassName].isValid() )
+    {
+        var sTextFieldContentProviderId = aTextFieldContentProviderSet[sClassName].sId;
+        // We create only one single TextFieldHandler object (and so one only
+        // text field clone) per master slide and text content.
+        if ( !aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] )
+        {
+            aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] =
+            new TextFieldHandler( aPlaceholderShapeSet[sClassName],
+                                  aTextFieldContentProviderSet[sClassName] );
+            aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ];
+            aTextFieldHandler.update();
+            aTextFieldHandler.appendTo( aDefsElement );
+        }
+        else
+        {
+            aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ];
+        }
 
-// ------------------------------------------------------------------------------------------ //
-/********************************
- ** Text Field Class Hierarchy **
- ********************************/
+        // We create a <use> element referring to the cloned text field and
+        // append it to the field group element.
+        var aTextFieldElement = document.createElementNS( NSS['svg'], 'use' );
+        aTextFieldElement.setAttribute( 'class', sClassName );
+        setNSAttribute( 'xlink', aTextFieldElement,
+                        'href', '#' + aTextFieldHandler.sId );
+        // node linking
+        this.aBackgroundFieldsElement.appendChild( aTextFieldElement );
+    }
+    return aTextFieldHandler;
+};
 
-/** Class TextField **
- *  This class is the root abstract class of the hierarchy.
- *  The 'shapeElement' property is the shape element to which
- *  this TextField object provides the text content.
+/** Class TextFieldHandler
+ *  This class clone a text field field of a master page and set up
+ *  the content of the cloned element on demand.
+ *
+ *  @param aPlaceholderShape
+ *      A PlaceholderShape object that provides the text field to be cloned.
+ *  @param aTextContentProvider
+ *      A TextContentProvider object to which the actual content updating is
+ *      demanded.
  */
-function TextField( aTextFieldElem )
-{
-    this.bIsUpdated = false;
+function TextFieldHandler( aPlaceholderShape, aTextContentProvider )
+{
+    this.aPlaceHolderShape = aPlaceholderShape;
+    this.aTextContentProvider = aTextContentProvider;
+    assert( this.aTextContentProvider,
+            'TextFieldHandler: text content provider not defined.' );
+    this.sId = 'tf' + String( TextFieldHandler.getUniqueId() );
+    // The cloned text field element to be handled.
+    this.aTextFieldElement = null;
+    // The actual <text> element where the field content has to be placed.
+    this.aTextPlaceholderElement = null;
+    this.cloneElement();
 }
 
-/*** TextField public methods ***/
-TextField.prototype.getShapeElement = function()
+/*** private methods ***/
+
+TextFieldHandler.CURR_UNIQUE_ID = 0;
+
+TextFieldHandler.getUniqueId = function()
+{
+    ++TextFieldHandler.CURR_UNIQUE_ID;
+    return TextFieldHandler.CURR_UNIQUE_ID;
+};
+
+TextFieldHandler.prototype.cloneElement = function()
 {
-    return this.shapeElement;
+    assert( this.aPlaceHolderShape && this.aPlaceHolderShape.isValid(),
+            'TextFieldHandler.cloneElement: placeholder shape is not valid.' );
+    // The cloned text field element.
+    this.aTextFieldElement = this.aPlaceHolderShape.element.cloneNode( true /* deep clone */ );
+    assert( this.aTextFieldElement,
+            'TextFieldHandler.cloneElement: aTextFieldElement is not defined' );
+    this.aTextFieldElement.setAttribute( 'id', this.sId );
+    // The actual <text> element where the field content has to be placed.
+    this.aTextPlaceholderElement = getElementByClassName( this.aTextFieldElement, 'PlaceholderText' );
+    assert( this.aTextPlaceholderElement,
+            'TextFieldHandler.cloneElement: aTextPlaceholderElement is not defined' );
 };
 
-TextField.prototype.setVisibility = function( nVisibility, aPlaceholderShape )
+/*** public methods ***/
+
+/** appendTo
+ *  Append the cloned text field element to a svg element.
+ *
+ *  @param aParentNode
+ *      The svg element to which the cloned text field has to be appended.
+ */
+TextFieldHandler.prototype.appendTo = function( aParentNode )
 {
-    if( !this.bIsUpdated )
+    if( !this.aTextFieldElement )
     {
-        if( nVisibility )
-        {
-            this.update( aPlaceholderShape );
-            this.bIsUpdated = true;
-        }
-        aPlaceholderShape.setVisibility( nVisibility );
+        log( 'TextFieldHandler.appendTo: aTextFieldElement is not defined' );
+        return;
     }
-    else if( !nVisibility )
+    if( !aParentNode )
     {
-        aPlaceholderShape.hide();
-        this.bIsUpdated = false;
+        log( 'TextFieldHandler.appendTo: parent node is not defined' );
+        return;
     }
+
+    aParentNode.appendChild( this.aTextFieldElement );
 };
 
-TextField.prototype.show = function( aPlaceholderShape )
+/** setTextContent
+ *  Modify the content of the cloned text field.
+ *
+ *  @param sText
+ *      A string representing the new content of the cloned text field.
+ */
+TextFieldHandler.prototype.setTextContent = function( sText )
 {
-    this.setVisibility( VISIBLE, aPlaceholderShape );
+    if( !this.aTextPlaceholderElement )
+    {
+        log( 'TextFieldHandler.setTextContent: text element is not valid in placeholder of type '
+                + this.className + ' that belongs to master slide ' + this.masterPage.id );
+        return;
+    }
+    this.aTextPlaceholderElement.textContent = sText;
 };
 
-TextField.prototype.hide = function( aPlaceholderShape )
+/** update
+ *  Update the content of the handled text field. The new content is provided
+ *  directly from the TextContentProvider data member.
+ */
+TextFieldHandler.prototype.update = function()
+{
+    if( !this.aTextContentProvider )
+        log('TextFieldHandler.update: text content provider not defined.');
+    else
+        this.aTextContentProvider.update( this );
+};
+
+/** SlideNumberFieldHandler
+ *  This class clone the slide number field of a master page and set up
+ *  the content of the cloned element on demand.
+ *
+ *  @param aPlaceholderShape
+ *      A PlaceholderShape object that provides the slide number field
+ *      to be cloned.
+ *  @param aTextContentProvider
+ *      A SlideNumberProvider object to which the actual content updating is
+ *      demanded.
+ */
+function SlideNumberFieldHandler( aPlaceholderShape, aTextContentProvider )
+{
+    SlideNumberFieldHandler.superclass.constructor.call( this, aPlaceholderShape, aTextContentProvider );
+}
+extend( SlideNumberFieldHandler, TextFieldHandler );
+
+/*** public methods ***/
+
+/** update
+ *  Update the content of the handled slide number field with the passed number.
+ *
+ * @param nPageNumber
+ *      The number representing the new content of the slide number field.
+ */
+SlideNumberFieldHandler.prototype.update = function( nPageNumber )
 {
-    this.setVisibility( HIDDEN, aPlaceholderShape );
+    // The actual content updating is demanded to the related
+    // SlideNumberProvider instance that have the needed info on
+    // the numbering type.
+    if( !this.aTextContentProvider )
+        log('TextFieldHandler.update: text content provider not defined.');
+    else
+        this.aTextContentProvider.update( this, nPageNumber );
 };
 
+// ------------------------------------------------------------------------------------------ //
+/******************************************************************************
+ * Text Field Content Provider Class Hierarchy
+ *
+ * The following classes are responsible to format and set the text content
+ * of text fields.
+ *
+ ******************************************************************************/
+
+/** Class TextFieldContentProvider
+ *  This class is the root abstract class of the hierarchy.
+ *
+ *  @param aTextFieldContentElement
+ *      The svg element that contains the text content for one or more
+ *      master slide text field.
+ */
+function TextFieldContentProvider( aTextFieldContentElement )
+{
+    // This id is used as key for the theMetaDoc.aTextFieldHandlerSet object.
+    if( aTextFieldContentElement )
+        this.sId = aTextFieldContentElement.getAttribute( 'id' );
+}
 
-/** Class FixedTextField **
+/** Class FixedTextProvider
  *  This class handles text field with a fixed text.
  *  The text content is provided by the 'text' property.
+ *
+ *  @param aTextFieldContentElement
+ *      The svg element that contains the text content for one or more
+ *      master slide text field.
  */
-function FixedTextField( aTextFieldElem )
+function FixedTextProvider( aTextFieldContentElement )
 {
-    TextField.call( this, aTextFieldElem );
-    this.text = aTextFieldElem.textContent;
+    FixedTextProvider.superclass.constructor.call( this, aTextFieldContentElement );
+    this.text = aTextFieldContentElement.textContent;
 }
-extend( FixedTextField, TextField );
+extend( FixedTextProvider, TextFieldContentProvider );
 
-FixedTextField.prototype.update = function( aPlaceholderShape )
+/*** public methods ***/
+
+/** update
+ *  Set up the content of a fixed text field.
+ *
+ *  @param aFixedTextField
+ *      An object that implement a setTextContent( String ) method in order
+ *      to set the content of a given text field.
+ */
+FixedTextProvider.prototype.update = function( aFixedTextField )
 {
-    aPlaceholderShape.setTextContent( this.text );
+    aFixedTextField.setTextContent( this.text );
 };
 
-
-/** Class VariableDateTimeField **
- *  Provide the text content for the related shape by generating the current
+/** Class CurrentDateTimeProvider
+ *  Provide the text content to a date/time field by generating the current
  *  date/time in the format specified by the 'dateTimeFormat' property.
+ *
+ *  @param aTextFieldContentElement
+ *      The svg element that contains the date/time format for one or more
+ *      master slide date/time field.
  */
-function VariableDateTimeField( aTextFieldElem )
+function CurrentDateTimeProvider( aTextFieldContentElement )
 {
-    VariableDateTimeField.superclass.constructor.call( this, aTextFieldElem );
-    this.dateTimeFormat = getOOOAttribute( aTextFieldElem, aOOOAttrDateTimeFormat );
+    CurrentDateTimeProvider.superclass.constructor.call( this, aTextFieldContentElement );
+    this.dateTimeFormat = getOOOAttribute( aTextFieldContentElement, aOOOAttrDateTimeFormat );
 }
-extend( VariableDateTimeField, TextField );
+extend( CurrentDateTimeProvider, TextFieldContentProvider );
 
 /*** public methods ***/
-VariableDateTimeField.prototype.update = function( aPlaceholderShape )
+
+/** update
+ *  Set up the content of a variable date/time field.
+ *
+ *  @param aDateTimeField
+ *      An object that implement a setTextContent( String ) method in order
+ *      to set the content of a given text field.
+ */
+CurrentDateTimeProvider.prototype.update = function( aDateTimeField )
 {
     var sText = this.createDateTimeText( this.dateTimeFormat );
-    aPlaceholderShape.setTextContent( sText );
+    aDateTimeField.setTextContent( sText );
 };
 
-VariableDateTimeField.prototype.createDateTimeText = function( sDateTimeFormat )
+/*** private methods ***/
+
+CurrentDateTimeProvider.prototype.createDateTimeText = function( sDateTimeFormat )
 {
     // TODO handle date/time format
     var aDate = new Date();
@@ -1469,37 +2087,57 @@ VariableDateTimeField.prototype.createDateTimeText = function( sDateTimeFormat )
     return sDate;
 };
 
-/** Class SlideNumberField **
- *  Provides the text content to the related shape by generating
+/** Class SlideNumberProvider
+ *  Provides the text content to the related text field by generating
  *  the current page number in the given page numbering type.
  */
-function SlideNumberField( nInitialSlideNumber, sPageNumberingType )
+function SlideNumberProvider( nInitialSlideNumber, sPageNumberingType )
 {
-    SlideNumberField.superclass.constructor.call( this, null );
+    SlideNumberProvider.superclass.constructor.call( this, null );
     this.nInitialSlideNumber = nInitialSlideNumber;
     this.pageNumberingType = sPageNumberingType;
 
 }
-extend( SlideNumberField, TextField );
+extend( SlideNumberProvider, TextFieldContentProvider );
 
 /*** public methods ***/
-SlideNumberField.prototype.getNumberingType = function()
+
+/** getNumberingType
+ *
+ *  @return
+ *      The page numbering type.
+ */
+SlideNumberProvider.prototype.getNumberingType = function()
 {
     return this.pageNumberingType;
 };
 
-SlideNumberField.prototype.update = function( aPlaceholderShape )
+/** update
+ *  Set up the content of a slide number field.
+ *
+ *  @param aSlideNumberField
+ *      An object that implement a setTextContent( String ) method in order
+ *      to set the content of a given text field.
+ *  @param nSlideNumber
+ *      An integer representing the slide number.
+ */
+
+SlideNumberProvider.prototype.update = function( aSlideNumberField, nSlideNumber )
 {
-    var nSlideNumber;
-    if( nCurSlide === undefined )
-        nSlideNumber = this.nInitialSlideNumber;
-    else
-        nSlideNumber = nCurSlide + 1;
+    if( nSlideNumber === undefined )
+    {
+        if( nCurSlide === undefined )
+            nSlideNumber = this.nInitialSlideNumber;
+        else
+            nSlideNumber = nCurSlide + 1;
+    }
     var sText = this.createSlideNumberText( nSlideNumber, this.getNumberingType() );
-    aPlaceholderShape.setTextContent( sText );
+    aSlideNumberField.setTextContent( sText );
 };
 
-SlideNumberField.prototype.createSlideNumberText = function( nSlideNumber, sNumberingType )
+/*** private methods ***/
+
+SlideNumberProvider.prototype.createSlideNumberText = function( nSlideNumber, sNumberingType )
 {
     // TODO handle page numbering type
     return String( nSlideNumber );
@@ -1601,6 +2239,7 @@ SlideIndexPage.prototype.createPageElement = function()
     var aPageElement = document.createElementNS( NSS['svg'], 'g' );
     aPageElement.setAttribute( 'id', this.pageElementId );
     aPageElement.setAttribute( 'display', 'none' );
+    aPageElement.setAttribute( 'visibility', 'visible' );
 
     // the slide index page background
     var sPageBgColor = this.pageBgColor + ';';
@@ -1747,9 +2386,10 @@ function Thumbnail( aSlideIndexPage, nIndex )
     this.thumbnailId = 'thumbnail' + this.index;
     this.thumbnailElement = this.createThumbnailElement();
     this.slideElement = getElementByClassName( this.thumbnailElement, 'Slide' );
-    this.backgroundElement = getElementByClassName( this.thumbnailElement, 'Background' );
-    this.backgroundObjectsElement = getElementByClassName( this.thumbnailElement, 'BackgroundObjects' );
     this.borderElement = getElementByClassName( this.thumbnailElement, 'Border' );
+    this.mouseAreaElement = getElementByClassName( this.thumbnailElement, 'MouseArea' );
+    //this.mouseAreaElement.setAttribute( 'onmouseover', 'theSlideIndexPage.aThumbnailSet[' + this.index  + '].onMouseOver()' );
+    //this.mouseAreaElement.onmousedown = mouseHandlerDictionary[INDEX_MODE][MOUSE_DOWN];
     this.aTransformSet = new Array( 3 );
     this.visibility = VISIBLE;
     this.isSelected = false;
@@ -1821,7 +2461,7 @@ Thumbnail.prototype.updateView = function()
     this.aTransformSet[1] = 'scale(' + this.container.scaleFactor + ')';
     var sTransformAttrValue = this.computeTransform();
     this.thumbnailElement.setAttribute( 'transform', sTransformAttrValue );
-    this.thumbnailElement.setAttribute( 'onmouseover', 'theSlideIndexPage.aThumbnailSet[' + this.index  + '].onMouseOver()' );
+    this.mouseAreaElement.setAttribute( 'onmouseover', 'theSlideIndexPage.aThumbnailSet[' + this.index  + '].onMouseOver()' );
 };
 
 /** update
@@ -1835,33 +2475,14 @@ Thumbnail.prototype.update = function( nIndex )
     if( this.slideIndex == nIndex )  return;
 
     var aMetaSlide = theMetaDoc.aMetaSlideSet[nIndex];
+    aMetaSlide.updateMasterPageView();
     setNSAttribute( 'xlink', this.slideElement, 'href', '#' + aMetaSlide.slideId );
-    if( aMetaSlide.nIsBackgroundVisible )
-    {
-        setNSAttribute( 'xlink', this.backgroundElement, 'href', '#' + aMetaSlide.masterPage.backgroundId );
-        this.backgroundElement.setAttribute( 'visibility', 'inherit' );
-    }
-    else
-    {
-        this.backgroundElement.setAttribute( 'visibility', 'hidden' );
-    }
-    if( aMetaSlide.nAreMasterObjectsVisible )
-    {
-        setNSAttribute( 'xlink',  this.backgroundObjectsElement, 'href', '#' + aMetaSlide.masterPage.backgroundObjectsId );
-        this.backgroundObjectsElement.setAttribute( 'visibility', 'inherit' );
-    }
-    else
-    {
-        this.backgroundObjectsElement.setAttribute( 'visibility', 'hidden' );
-    }
     this.slideIndex = nIndex;
 };
 
 Thumbnail.prototype.clear = function( nIndex )
 {
     setNSAttribute( 'xlink', this.slideElement, 'href', '' );
-    setNSAttribute( 'xlink', this.backgroundElement, 'href', '' );
-    setNSAttribute( 'xlink', this.backgroundObjectsElement, 'href', '' );
 };
 
 /* private methods */
@@ -1871,28 +2492,17 @@ Thumbnail.prototype.createThumbnailElement = function()
     aThumbnailElement.setAttribute( 'id', this.thumbnailId );
     aThumbnailElement.setAttribute( 'display', 'inherit' );
 
-    var aMouseAreaElement = document.createElementNS( NSS['svg'], 'use' );
-    setNSAttribute( 'xlink', aMouseAreaElement, 'href', '#' + this.container.thumbnailMouseAreaTemplateId );
-    aMouseAreaElement.setAttribute( 'class', 'MouseArea' );
-    aThumbnailElement.appendChild( aMouseAreaElement );
-
-    var aBackgroundElement = document.createElementNS( NSS['svg'], 'use' );
-    setNSAttribute( 'xlink', aBackgroundElement, 'href', '' );
-    aBackgroundElement.setAttribute( 'visibility', 'inherit');
-    aBackgroundElement.setAttribute( 'class', 'Background' );
-    aThumbnailElement.appendChild( aBackgroundElement );
-
-    var aBackgroundObjectsElement = document.createElementNS( NSS['svg'], 'use' );
-    setNSAttribute( 'xlink', aBackgroundObjectsElement, 'href', '' );
-    aBackgroundObjectsElement.setAttribute( 'visibility', 'inherit');
-    aBackgroundObjectsElement.setAttribute( 'class', 'BackgroundObjects' );
-    aThumbnailElement.appendChild( aBackgroundObjectsElement );
-
     var aSlideElement = document.createElementNS( NSS['svg'], 'use' );
     setNSAttribute( 'xlink', aSlideElement, 'href', '' );
     aSlideElement.setAttribute( 'class', 'Slide' );
     aThumbnailElement.appendChild( aSlideElement );
 
+    var aMouseAreaElement = document.createElementNS( NSS['svg'], 'use' );
+    setNSAttribute( 'xlink', aMouseAreaElement, 'href', '#' + this.container.thumbnailMouseAreaTemplateId );
+    aMouseAreaElement.setAttribute( 'class', 'MouseArea' );
+    aMouseAreaElement.setAttribute( 'opacity', 0.0 );
+    aThumbnailElement.appendChild( aMouseAreaElement );
+
     var aBorderElement = document.createElementNS( NSS['svg'], 'use' );
     setNSAttribute( 'xlink', aBorderElement, 'href', '#' + this.container.thumbnailBorderTemplateId );
     aBorderElement.setAttribute( 'stroke', this.sNormalBorderColor );
@@ -1943,12 +2553,11 @@ function init()
         HEIGHT = ROOT_NODE.viewBox.animVal.height;
     }
 
-    var aMetaDocElem = document.getElementById( aOOOElemMetaSlides );
-    assert( aMetaDocElem, 'init: meta document element not found' );
     aSlideShow = new SlideShow();
-    theMetaDoc =  new MetaDocument( aMetaDocElem );
+    theMetaDoc =  new MetaDocument();
+    aSlideShow.bIsEnabled = theMetaDoc.bIsAnimated;
     theSlideIndexPage = new SlideIndexPage();
-    aSlideShow.displaySlide( theMetaDoc.startSlideNumber );
+    aSlideShow.displaySlide( theMetaDoc.nStartSlideNumber, false );
 
     //=====================================//
     //      ===== timing test =====        //
@@ -2005,12 +2614,12 @@ function dispatchEffects(dir)
 
         if( !bRet )
         {
-            switchSlide( 1 );
+            switchSlide( 1, false );
         }
     }
     else
     {
-        switchSlide( dir );
+        switchSlide( dir, false );
     }
 }
 
@@ -2059,19 +2668,12 @@ function displayIndex( offsetNumber )
  */
 function toggleSlideIndex()
 {
-    var suspendHandle = ROOT_NODE.suspendRedraw(500);
-    var aMetaSlideSet = theMetaDoc.aMetaSlideSet;
+    //var suspendHandle = ROOT_NODE.suspendRedraw(500);
 
     if( currentMode == SLIDE_MODE )
     {
-        aMetaSlideSet[nCurSlide].hide();
-        var counter;
-        for( counter = 0; counter < aMetaSlideSet.length; ++counter )
-        {
-            checkElemAndSetAttribute( aMetaSlideSet[counter].slideElement, 'visibility', 'inherit' );
-            aMetaSlideSet[counter].masterPage.setVisibilityBackground( INHERIT );
-            aMetaSlideSet[counter].masterPage.setVisibility( INHERIT );
-        }
+
+        theMetaDoc.getCurrentSlide().hide();
         INDEX_OFFSET = -1;
         indexSetPageSlide( nCurSlide );
         theSlideIndexPage.show();
@@ -2082,20 +2684,12 @@ function toggleSlideIndex()
         theSlideIndexPage.hide();
         var nNewSlide = theSlideIndexPage.selectedSlideIndex;
 
-        for( counter = 0; counter < aMetaSlideSet.length; ++counter )
-        {
-            var aMetaSlide = aMetaSlideSet[counter];
-            aMetaSlide.slideElement.setAttribute( 'visibility', 'hidden' );
-            aMetaSlide.masterPage.setVisibilityBackground( HIDDEN );
-            aMetaSlide.masterPage.setVisibility( HIDDEN );
-        }
-
         aSlideShow.displaySlide( nNewSlide, true );
         currentMode = SLIDE_MODE;
     }
 
-    ROOT_NODE.unsuspendRedraw(suspendHandle);
-    ROOT_NODE.forceRedraw();
+    //ROOT_NODE.unsuspendRedraw(suspendHandle);
+    //ROOT_NODE.forceRedraw();
 }
 
 /** Function that exit from the index mode without changing the shown slide
@@ -7716,11 +8310,9 @@ SlideShow.prototype.displaySlide = function( nNewSlide, bSkipSlideTransition )
     else if( nNewSlide >= nSlides )
         nNewSlide = 0;
 
-    var newMetaSlide = null;
     if( ( currentMode === INDEX_MODE ) && ( nNewSlide === nCurSlide ) )
     {
-        newMetaSlide = aMetaDoc.aMetaSlideSet[nNewSlide];
-        newMetaSlide.show();
+        aMetaDoc.getCurrentSlide().show();
         return;
     }
 
@@ -7731,8 +8323,6 @@ SlideShow.prototype.displaySlide = function( nNewSlide, bSkipSlideTransition )
         var oldMetaSlide = aMetaDoc.aMetaSlideSet[nOldSlide];
         if( this.isEnabled() )
         {
-            // hide current slide
-            oldMetaSlide.hide();
             if( oldMetaSlide.aSlideAnimationsHandler.isAnimated() )
             {
                 // force end animations
@@ -7742,36 +8332,27 @@ SlideShow.prototype.displaySlide = function( nNewSlide, bSkipSlideTransition )
                 this.dispose();
             }
         }
-        else
-        {
-            oldMetaSlide.hide();
-        }
+    }
+
+    if( !bSkipSlideTransition )
+    {
+        // create slide transition and add to activity queue
+        // to be implemented
+        aMetaDoc.setCurrentSlide( nNewSlide );
+    }
+    else
+    {
+        aMetaDoc.setCurrentSlide( nNewSlide );
     }
 
     // handle new slide
-    nCurSlide = nNewSlide;
-    newMetaSlide = aMetaDoc.aMetaSlideSet[nNewSlide];
     if( this.isEnabled() )
     {
-        // prepare to show a new slide
         this.notifySlideStart( nNewSlide );
 
-        if( !bSkipSlideTransition )
-        {
-            // create slide transition and add to activity queue
-            // to be implemented
-        }
-
-        // show next slide and start animations
-        newMetaSlide.show();
-        newMetaSlide.aSlideAnimationsHandler.start();
+        aMetaDoc.getCurrentSlide().aSlideAnimationsHandler.start();
         this.update();
     }
-    else
-    {
-        newMetaSlide.show();
-    }
-
 
 
     /*
@@ -7987,7 +8568,7 @@ TimerEventQueue.prototype.addEvent = function( aEvent )
     this.DBG( 'TimerEventQueue.addEvent invoked' );
     if( !aEvent )
     {
-        log( 'error: TimerEventQueue.addEvent: null event' );
+        log( 'TimerEventQueue.addEvent: null event' );
         return false;
     }
 


More information about the Libreoffice-commits mailing list