diff options
Diffstat (limited to 'filter/source/svg/presentation_engine.js')
-rw-r--r-- | filter/source/svg/presentation_engine.js | 829 |
1 files changed, 692 insertions, 137 deletions
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js index 9bd2936a019d..fa42b89b2f8e 100644 --- a/filter/source/svg/presentation_engine.js +++ b/filter/source/svg/presentation_engine.js @@ -32,6 +32,15 @@ var abs = Math.abs; var now = Date.now; /** + * polyfill for IE11 + */ +if (!Math.trunc) { + Math.trunc = function (v) { + return v < 0 ? Math.ceil(v) : Math.floor(v); + }; +} + +/** * set a timeout with a given scope * @param {Function} fn * @param {Number} timeout @@ -359,7 +368,7 @@ function uniqueArray(src, key, sort) { * @returns {String|Undefined} prefixed */ function prefixed(obj, property) { - // tml: Have to check for obj being undefined + // tml: Have to check for obj being undefined if (obj === undefined) { return undefined; } @@ -754,7 +763,7 @@ function getDistance(p1, p2, props) { var x = p2[props[0]] - p1[props[0]], y = p2[props[1]] - p1[props[1]]; - return Math.sqrt((x * x) + (y * y)); + return Math.hypot(x, y); } /** @@ -2747,6 +2756,56 @@ function getElementsByProperty( node, name ) return elements; } +// User can hide / show the presentation content. +// For that purpose, we change the background color of root node to either black or white. +// To set it back to its original color (for showing the content back), we should have the initial background color saved somewhere to read back. +// There may be no initial color at all, so the initial value of the saved initial is undefined. +var rootNodeInitialBackgroundColor = undefined; + +function changeRootNodeBackgroundTo(color) { + if (rootNodeInitialBackgroundColor === undefined) + rootNodeInitialBackgroundColor = ROOT_NODE.style.backgroundColor; + + if (color === 'initial') + ROOT_NODE.style.backgroundColor = rootNodeInitialBackgroundColor; + else + ROOT_NODE.style.backgroundColor = color; +} + +var isContentHidden = false; +var contentInitialVisibilityValues = null; + +function getInitialVisibilityValues() { + var list = ROOT_NODE.querySelectorAll('g'); + contentInitialVisibilityValues = []; + for (var i = 0; i < list.length; i++) { + var temp = {}; + temp.object = list[i]; + temp.visibility = list[i].style.visibility; + contentInitialVisibilityValues.push(temp); + } +} + +function hideShowContent(color) { + if (contentInitialVisibilityValues === null) + getInitialVisibilityValues(); + + if (isContentHidden) { + for (var i = 0; i < contentInitialVisibilityValues.length; i++) + contentInitialVisibilityValues[i].object.style.visibility = contentInitialVisibilityValues[i].visibility; + + changeRootNodeBackgroundTo('initial'); + isContentHidden = false; + } + else { + for (var i = 0; i < contentInitialVisibilityValues.length; i++) + contentInitialVisibilityValues[i].object.style.visibility = 'hidden'; + + changeRootNodeBackgroundTo(color); + isContentHidden = true; + } +} + /** Event handler for key press. * * @param aEvt the event @@ -2756,7 +2815,7 @@ function onKeyDown( aEvt ) if ( !aEvt ) aEvt = window.event; - var code = aEvt.keyCode || aEvt.charCode; + var code = aEvt.keyCode || aEvt.charCode || aEvt.code; // console.log('===> onKeyDown: ' + code); @@ -2779,6 +2838,20 @@ function onKeyDown( aEvt ) // console.log(' now: ' + code); } + else if (code === P_KEY) { + aEvt.preventDefault(); + if (ROOT_NODE.style.cursor === 'pointer') + ROOT_NODE.style.cursor = 'default'; + else + ROOT_NODE.style.cursor = 'pointer'; + } + else if (code === W_KEY) { + hideShowContent('white'); + } + else if (code === B_KEY) { + hideShowContent('black'); + } + if( !processingEffect && keyCodeDictionary[currentMode] && keyCodeDictionary[currentMode][code] ) { @@ -4434,8 +4507,10 @@ var aOOOAttrUsePositionedChars = 'use-positioned-chars'; var aOOOAttrSlide = 'slide'; var aOOOAttrMaster = 'master'; +var aOOOAttrDisplayName = 'display-name'; var aOOOAttrSlideDuration = 'slide-duration'; var aOOOAttrHasTransition = 'has-transition'; +var aOOOAttrHasCustomBackground = 'has-custom-background'; var aOOOAttrBackgroundVisibility = 'background-visibility'; var aOOOAttrMasterObjectsVisibility = 'master-objects-visibility'; var aOOOAttrPageNumberVisibility = 'page-number-visibility'; @@ -4453,10 +4528,13 @@ var aOOOAttrTextAdjust = 'text-adjust'; // element class names var aClipPathGroupClassName = 'ClipPathGroup'; var aPageClassName = 'Page'; -var aSlideNumberClassName = 'Slide_Number'; -var aDateTimeClassName = 'Date/Time'; +var aSlideNumberClassName = 'PageNumber'; +var aDateTimeClassName = 'DateTime'; var aFooterClassName = 'Footer'; var aHeaderClassName = 'Header'; +var aDateClassName = 'Date'; +var aTimeClassName = 'Time'; +var aSlideNameClassName='PageName'; // Creating a namespace dictionary. var NSS = {}; @@ -4491,7 +4569,10 @@ var END_KEY = 35; // end keycode var ENTER_KEY = 13; var SPACE_KEY = 32; var ESCAPE_KEY = 27; +var B_KEY = 66; +var P_KEY = 80; var Q_KEY = 81; +var W_KEY = 87; // Visibility Values var HIDDEN = 0; @@ -4771,15 +4852,6 @@ function getRandomInt( nMax ) return Math.floor( Math.random() * nMax ); } -function isTextFieldElement( aElement ) // eslint-disable-line no-unused-vars -{ - var sClassName = aElement.getAttribute( 'class' ); - return ( sClassName === aSlideNumberClassName ) || - ( sClassName === aFooterClassName ) || - ( sClassName === aHeaderClassName ) || - ( sClassName === aDateTimeClassName ); -} - /********************* ** Debug Utilities ** @@ -4908,6 +4980,8 @@ function MetaDocument() this.aTextFieldHandlerSet = {}; this.aTextFieldContentProviderSet = []; this.aSlideNumberProvider = new SlideNumberProvider( this.nStartSlideNumber + 1, this.sPageNumberingType ); + this.aCurrentDateProvider = new CurrentDateTimeProvider( null, '<date>' ); + this.aCurrentTimeProvider = new CurrentDateTimeProvider( null, '<time>' ); // We create a map with key an id and value the svg element containing // the animations performed on the slide with such an id. @@ -5025,6 +5099,8 @@ function MetaSlide( sMetaSlideId, aMetaDoc ) else this.nSlideNumber= -1; + this.slideName = this.element.getAttributeNS( NSS['ooo'], aOOOAttrDisplayName ); + // Each slide element is double wrapped by <g> elements. // The outer <g> element is responsible for // the slide element visibility. In fact the visibility attribute has @@ -5056,10 +5132,23 @@ function MetaSlide( sMetaSlideId, aMetaDoc ) assert( this.pageElement, 'MetaSlide: page element <' + this.slideId + '> not found.' ); + // The slide custom background element and its id attribute. + this.backgroundElement = getElementByClassName( this.pageElement, 'Background' ); + if( this.backgroundElement ) + { + this.backgroundId = this.backgroundElement.getAttribute( 'id' ); + } + + // We initialize text fields + this.initPlaceholderElements(); + // We initialize the MasterPage object that provides direct access to // the target master page element. this.masterPage = this.initMasterPage(); + // We check if the slide has a custom background which overrides the one of the targeted master page + this.bHasCustomBackground = this.initHasCustomBackground(); + // We initialize visibility properties of the target master page elements. this.nAreMasterObjectsVisible = this.initVisibilityProperty( aOOOAttrMasterObjectsVisibility, VISIBLE ); this.nIsBackgroundVisible = this.initVisibilityProperty( aOOOAttrBackgroundVisibility, VISIBLE ); @@ -5079,6 +5168,9 @@ function MetaSlide( sMetaSlideId, aMetaDoc ) this.aTextFieldContentProviderSet[aDateTimeClassName] = this.initDateTimeFieldContentProvider( aOOOAttrDateTimeField ); this.aTextFieldContentProviderSet[aFooterClassName] = this.initFixedTextFieldContentProvider( aOOOAttrFooterField ); this.aTextFieldContentProviderSet[aHeaderClassName] = this.initFixedTextFieldContentProvider( aOOOAttrHeaderField ); + this.aTextFieldContentProviderSet[aDateClassName] = this.theMetaDoc.aCurrentDateProvider; + this.aTextFieldContentProviderSet[aTimeClassName] = this.theMetaDoc.aCurrentTimeProvider; + this.aTextFieldContentProviderSet[aSlideNameClassName] = new FixedTextProvider( this.slideName ); // We init the slide duration when automatic slide transition is enabled this.fDuration = this.initSlideDuration(); @@ -5149,6 +5241,24 @@ updateMasterPageView : function() }, /*** private methods ***/ + +// It handles a text field inserted on a slide, not on a master page. +initPlaceholderElements : function() +{ + var aPlaceholderList = getElementsByClassName(this.pageElement , 'PlaceholderText' ); + var i = 0; + for( ; i < aPlaceholderList.length; ++i ) + { + var aPlaceholderElem = aPlaceholderList[i]; + var sClass = aPlaceholderElem.getAttribute('class'); + var sFieldType = sClass.split(' ')[1]; + if( sFieldType === aDateClassName) + aPlaceholderElem.textContent = new Date().toLocaleDateString(); + else if( sFieldType === aTimeClassName ) + aPlaceholderElem.textContent = new Date().toLocaleTimeString(); + } +}, + initMasterPage : function() { var sMasterPageId = this.element.getAttributeNS( NSS['ooo'], aOOOAttrMaster ); @@ -5181,6 +5291,12 @@ initHasTransition : function() return ( sHasTransition === 'true' ); }, +initHasCustomBackground : function() +{ + var sHasCustomBackground = this.element.getAttributeNS( NSS['ooo'], aOOOAttrHasCustomBackground ); + return ( sHasCustomBackground === 'true' ); +}, + initVisibilityProperty : function( aVisibilityAttribute, nDefaultValue ) { var nVisibility = nDefaultValue; @@ -5211,7 +5327,7 @@ initDateTimeFieldContentProvider : function( aOOOAttrDateTimeField ) var sClassName = getClassAttribute( aTextFieldElem ); if( sClassName == 'FixedDateTimeField' ) { - aTextField = new FixedTextProvider( aTextFieldElem ); + aTextField = new FixedTextByElementProvider( aTextFieldElem ); this.bIsDateTimeVariable = false; } else if( sClassName == 'VariableDateTimeField' ) @@ -5241,7 +5357,7 @@ initFixedTextFieldContentProvider : function( aOOOAttribute ) { var aTextFieldElem = document.getElementById( sTextFieldId ); this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ] - = new FixedTextProvider( aTextFieldElem ); + = new FixedTextByElementProvider( aTextFieldElem ); } return this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ]; }, @@ -5319,6 +5435,56 @@ getSlideAnimationsRoot : function() }; // end MetaSlide prototype +function removeRedundantParagraphFromTextFieldShape( aObject ) +{ + var aTextElem = getElementByClassName( aObject, 'SVGTextShape' ); + if( aTextElem ) + { + var aPlaceholderElement = getElementsByClassName(aTextElem, 'PlaceholderText'); + if( aPlaceholderElement ) + { + var aTextParagraphSet = getElementsByClassName(aTextElem, 'TextParagraph'); + // When the text field width is too small, the placeholder text spans several lines. + // We remove all text lines but the first one which is used as a placeholder. + // This is a workaround but it should work in the majority of cases. + // A complete solution needs to support svg text wrapping. + if( aTextParagraphSet.length > 1 ) + { + var i = aTextParagraphSet.length; + while( i > 1 ) + { + aTextElem.removeChild(aTextParagraphSet[i - 1]); + --i; + } + } + } + } +} + +function getTextFieldType ( elem ) { + var sFieldType = null; + var sClass = elem.getAttribute('class'); + if( sClass === 'TextShape' ) + { + var aPlaceholderElement = getElementByClassName(elem, 'PlaceholderText'); + if( aPlaceholderElement ) + { + var sClassAttr = aPlaceholderElement.getAttribute('class'); + var classes = sClassAttr.split(' '); + sFieldType = classes[1]; + } + } + return sFieldType; +} + +function isTextFieldByClassName ( sClassName ) +{ + return sClassName.indexOf( aDateTimeClassName ) == 0 || sClassName.indexOf( aFooterClassName ) == 0 + || sClassName.indexOf( aHeaderClassName ) == 0 || sClassName.indexOf( aSlideNumberClassName ) == 0 + || sClassName.indexOf( aDateClassName ) == 0 || sClassName.indexOf( aTimeClassName ) == 0 + || sClassName.indexOf( aSlideNameClassName ) == 0; +} + /** Class MasterPage * This class gives direct access to a master page element and to the following * elements included in the master page: @@ -5333,7 +5499,7 @@ getSlideAnimationsRoot : function() * background image * </g> * <g class='BackgroundObjects'> - * <g class='Date/Time'> + * <g class='DateTime'> * date/time placeholder * </g> * <g class='Header'> @@ -5342,7 +5508,7 @@ getSlideAnimationsRoot : function() * <g class='Footer'> * footer placeholder * </g> - * <g class='Slide_Number'> + * <g class='PageNumber'> * slide number placeholder * </g> * shapes @@ -5381,6 +5547,7 @@ function MasterPage( sMasterPageId, aMetaSlide ) // 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.aBackgroundObjectSubGroupIdList = []; if( this.backgroundObjects ) { this.backgroundObjectsId = this.backgroundObjects.getAttribute( 'id' ); @@ -5394,13 +5561,33 @@ function MasterPage( sMasterPageId, aMetaSlide ) var nSubGroupId = 1; var sClass; var sId = ''; - this.aBackgroundObjectSubGroupIdList = []; var i = 0; for( ; i < aBackgroundObjectList.length; ++i ) { - sClass = aBackgroundObjectList[i].getAttribute( 'class' ); - if( !sClass || ( ( sClass !== aDateTimeClassName ) && ( sClass !== aFooterClassName ) - && ( sClass !== aHeaderClassName ) && ( sClass !== aSlideNumberClassName ) ) ) + var aObject = aBackgroundObjectList[i]; + removeRedundantParagraphFromTextFieldShape( aObject ); + sClass = null; + var sFieldType = getTextFieldType( aObject ); + if( sFieldType && aObject.firstElementChild ) + { + var sObjId = aObject.firstElementChild.getAttribute( 'id' ); + if( sObjId ) + { + sClass = sFieldType + '.' + sObjId; + aObject.setAttribute('class', sClass); + } + } + if( !sClass ) + { + sClass = aBackgroundObjectList[i].getAttribute('class'); + if( sClass === aDateTimeClassName || sClass === aFooterClassName + || sClass === aHeaderClassName || sClass === aSlideNumberClassName) + { + sClass += '.Default'; + aObject.setAttribute('class', sClass); + } + } + if( !sClass || !isTextFieldByClassName( sClass ) ) { if( nCount === 0 ) { @@ -5444,10 +5631,14 @@ MasterPage.prototype = 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 ); + var sClassName; + var i = 0; + for( ; i < this.aBackgroundObjectSubGroupIdList.length; ++i ) + { + sClassName = this.aBackgroundObjectSubGroupIdList[i]; + if( isTextFieldByClassName( sClassName ) ) + this.aPlaceholderShapeSet[ sClassName ] = new PlaceholderShape( this, sClassName ); + } } }; // end MasterPage prototype @@ -5492,70 +5683,101 @@ PlaceholderShape.prototype.isValid = function() */ PlaceholderShape.prototype.init = function() { - var aTextFieldElement = getElementByClassName( this.masterPage.backgroundObjects, this.className ); if( aTextFieldElement ) { - var aPlaceholderElement = getElementByClassName( aTextFieldElement, 'PlaceholderText' ); - if( aPlaceholderElement ) + var aTextElem = getElementByClassName( aTextFieldElement, 'SVGTextShape' ); + if( aTextElem ) { - // 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 aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' ); - if( aSVGRectElem ) + var aPlaceholderElement = getElementByClassName(aTextElem, 'PlaceholderText'); + if( aPlaceholderElement ) { - var aRect = new Rectangle( aSVGRectElem ); - 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' ) + // SVG 1.1 does not support text wrapping wrt a rectangle. + // When a text shape contains a placeholder, setting up the position + // of each text line doesn't work since the position is computed + // before replacing the placeholder text. + // Anyway each text shape has an invisible rectangle that can be + // regarded as the text shape 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 text shape content. + // We assume that once the real value has been substituted to + // the placeholder the resulting content is no more than a single line. + // So we remove from <tspan> elements used for setting up the + // position of text lines (class TextPosition) the 'x' and 'y' attribute. + // In the general case we would need to implement a function + // which is able to compute at which words the text shape content has + // to be wrapped. + var aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' ); + if( aSVGRectElem ) { - sTextAnchor = 'middle'; - var nMiddle = ( aRect.left + aRect.right ) / 2; - sX = String( parseInt( String( nMiddle ) ) ); + var aRect = new Rectangle( aSVGRectElem ); + var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust ); + // the bbox of the text shape is indeed a bit larger, there is a bit of internal padding + var nMargin = 250; // 1000th mm + var sTextAnchor, sX; + if( sTextAdjust == 'left' ) + { + sTextAnchor = 'start'; + sX = String( Math.trunc( aRect.left + nMargin ) ); + } + else if( sTextAdjust == 'right' ) + { + sTextAnchor = 'end'; + sX = String( Math.trunc( aRect.right - nMargin ) ); + } + else if( sTextAdjust == 'center' ) + { + sTextAnchor = 'middle'; + var nMiddle = ( aRect.left + aRect.right ) / 2; + sX = String( parseInt( String( nMiddle ) ) ); + } + if( sTextAnchor ) + { + aTextElem.setAttribute( 'text-anchor', sTextAnchor ); + if( sX ) + aTextElem.setAttribute( 'x', sX ); + + var aTSpanElements = getElementsByClassName( aTextElem, 'TextPosition' ); + if( aTSpanElements ) + { + var i = 0; + for( ; i < aTSpanElements.length; ++i ) + { + var aTSpanElem = aTSpanElements[i]; + aTSpanElem.removeAttribute( 'x' ); + if( i !== 0 ) + aTSpanElem.removeAttribute( 'y' ); + } + } + } } - if( sTextAnchor ) - aPlaceholderElement.setAttribute( 'text-anchor', sTextAnchor ); - if( sX ) - aPlaceholderElement.setAttribute( 'x', sX ); - } - // date/time fields were not exported correctly when positioned chars are used - if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars ) - { - // We remove all text lines but the first one used as placeholder. - var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode; - if( aTextLineGroupElem ) + // date/time fields were not exported correctly when positioned chars are used + if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars ) { - // Just to be sure it is the element we are looking for. - var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' ); - if( sFontFamilyAttr ) + // We remove all text lines but the first one used as placeholder. + var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode; + if( aTextLineGroupElem ) { - var aChildSet = getElementChildren( aTextLineGroupElem ); - if( aChildSet.length > 1 ) - var i = 1; - for( ; i < aChildSet.length; ++i ) + // Just to be sure it is the element we are looking for. + var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' ); + if( sFontFamilyAttr ) { - aTextLineGroupElem.removeChild( aChildSet[i] ); + var aChildSet = getElementChildren( aTextLineGroupElem ); + if( aChildSet.length > 1 ) + var i = 1; + for( ; i < aChildSet.length; ++i ) + { + aTextLineGroupElem.removeChild( aChildSet[i] ); + } } } } + this.textElement = aPlaceholderElement; } - this.element = aTextFieldElement; - this.textElement = aPlaceholderElement; } + this.element = aTextFieldElement; } }; @@ -5570,10 +5792,10 @@ PlaceholderShape.prototype.init = function() * <use class='Background'> // reference to master page background element * <g class='BackgroundObjects'> * <use class='BackgroundObjectSubGroup'> // reference to the group of shapes on the master page that are below text fields - * <g class='Slide_Number'> // a cloned element + * <g class='PageNumber'> // a cloned element * ... * </g> - * <use class='Date/Time'> // reference to a clone + * <use class='DateTime'> // reference to a clone * <use class='Footer'> * <use class='Header'> * <use class='BackgroundObjectSubGroup'> // reference to the group of shapes on the master page that are above text fields @@ -5660,10 +5882,11 @@ MasterPageView.prototype.createElement = function() // init the Background element if( this.aMetaSlide.nIsBackgroundVisible ) { + var nBackgroundId = this.aMetaSlide.bHasCustomBackground ? this.aMetaSlide.backgroundId : this.aMasterPage.backgroundId; this.aBackgroundElement = theDocument.createElementNS( NSS['svg'], 'use' ); this.aBackgroundElement.setAttribute( 'class', 'Background' ); setNSAttribute( 'xlink', this.aBackgroundElement, - 'href', '#' + this.aMasterPage.backgroundId ); + 'href', '#' + nBackgroundId ); // node linking aMasterPageViewElement.appendChild( this.aBackgroundElement ); @@ -5690,57 +5913,71 @@ MasterPageView.prototype.createElement = function() for( ; i < aBackgroundObjectSubGroupIdList.length; ++i ) { sId = aBackgroundObjectSubGroupIdList[i]; - if( sId === aSlideNumberClassName ) + if( sId.indexOf( aSlideNumberClassName ) == 0 ) { // 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 && + var nIsPageNumberVisible = sId === (aSlideNumberClassName + '.Default') ? this.aMetaSlide.nIsPageNumberVisible : true; + if( aPlaceholderShapeSet[sId] && + aPlaceholderShapeSet[sId].isValid() && + nIsPageNumberVisible && aTextFieldContentProviderSet[aSlideNumberClassName] ) { - this.aSlideNumberFieldHandler = - new SlideNumberFieldHandler( aPlaceholderShapeSet[aSlideNumberClassName], - aTextFieldContentProviderSet[aSlideNumberClassName] ); - this.aSlideNumberFieldHandler.update( this.aMetaSlide.nSlideNumber ); - this.aSlideNumberFieldHandler.appendTo( this.aBackgroundObjectsElement ); + var aSlideNumberFieldHandler = + new SlideNumberFieldHandler( aPlaceholderShapeSet[sId], + aTextFieldContentProviderSet[aSlideNumberClassName] ); + aSlideNumberFieldHandler.update( this.aMetaSlide.nSlideNumber ); + aSlideNumberFieldHandler.appendTo( this.aBackgroundObjectsElement ); + if ( sId === aSlideNumberClassName + '.Default' ) + this.aSlideNumberFieldHandler = aSlideNumberFieldHandler; } } - else if( sId === aDateTimeClassName ) + else if( sId === aDateTimeClassName + '.Default' ) { - // Date/Time field + // DateTime field if( this.aMetaSlide.nIsDateTimeVisible ) { this.aDateTimeFieldHandler = - this.initTextFieldHandler( aDateTimeClassName, aPlaceholderShapeSet, + this.initTextFieldHandler( sId, aPlaceholderShapeSet, aTextFieldContentProviderSet, aDefsElement, aTextFieldHandlerSet, sMasterSlideId ); } } - else if( sId === aFooterClassName ) + else if( sId === aFooterClassName + '.Default' ) { // Footer Field if( this.aMetaSlide.nIsFooterVisible ) { this.aFooterFieldHandler = - this.initTextFieldHandler( aFooterClassName, aPlaceholderShapeSet, + this.initTextFieldHandler( sId, aPlaceholderShapeSet, aTextFieldContentProviderSet, aDefsElement, aTextFieldHandlerSet, sMasterSlideId ); } } - else if( sId === aHeaderClassName ) + else if( sId === aHeaderClassName + '.Default' ) { // Header Field if( this.aMetaSlide.nIsHeaderVisible ) { this.aHeaderFieldHandler = - this.initTextFieldHandler( aHeaderClassName, aPlaceholderShapeSet, + this.initTextFieldHandler( sId, aPlaceholderShapeSet, aTextFieldContentProviderSet, aDefsElement, aTextFieldHandlerSet, sMasterSlideId ); } } + else if( sId.indexOf( aDateTimeClassName ) == 0 + || sId.indexOf( aFooterClassName ) == 0 + || sId.indexOf( aHeaderClassName ) == 0 + || sId.indexOf( aDateClassName ) == 0 + || sId.indexOf( aTimeClassName ) == 0 + || sId.indexOf( aSlideNameClassName ) == 0 ) + { + this.initTextFieldHandler( sId, aPlaceholderShapeSet, + aTextFieldContentProviderSet, aDefsElement, + aTextFieldHandlerSet, sMasterSlideId ); + } else { // init BackgroundObjectSubGroup elements @@ -5762,37 +5999,49 @@ MasterPageView.prototype.createElement = function() }; MasterPageView.prototype.initTextFieldHandler = - function( sClassName, aPlaceholderShapeSet, aTextFieldContentProviderSet, + function( sId, aPlaceholderShapeSet, aTextFieldContentProviderSet, aDefsElement, aTextFieldHandlerSet, sMasterSlideId ) { + var sRefId = null; var aTextFieldHandler = null; - if( aPlaceholderShapeSet[sClassName] && - aPlaceholderShapeSet[sClassName].isValid() - && aTextFieldContentProviderSet[sClassName] ) + var sClassName = sId.split('.')[0]; + var aPlaceholderShape = aPlaceholderShapeSet[sId]; + var aTextFieldContentProvider = aTextFieldContentProviderSet[sClassName]; + if( aPlaceholderShape && aPlaceholderShape.isValid() + && aTextFieldContentProvider ) { - var sTextFieldContentProviderId = aTextFieldContentProviderSet[sClassName].sId; + var sTextFiedHandlerKey = aTextFieldContentProvider.sId + '.' + 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 ] ) + if ( !aTextFieldHandlerSet[ sMasterSlideId ][ sTextFiedHandlerKey ] ) { - aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] = - new TextFieldHandler( aPlaceholderShapeSet[sClassName], - aTextFieldContentProviderSet[sClassName] ); - aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ]; + aTextFieldHandlerSet[ sMasterSlideId ][ sTextFiedHandlerKey ] = + new TextFieldHandler( aPlaceholderShape, + aTextFieldContentProvider ); + aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFiedHandlerKey ]; aTextFieldHandler.update(); aTextFieldHandler.appendTo( aDefsElement ); } else { - aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ]; + aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFiedHandlerKey ]; } + sRefId = aTextFieldHandler.sId; + } + else if( aPlaceholderShape && aPlaceholderShape.element && aPlaceholderShape.element.firstElementChild + && !aPlaceholderShape.textElement && !aTextFieldContentProvider ) + { + sRefId = aPlaceholderShape.element.firstElementChild.getAttribute('id'); + } + if( sRefId ) + { // 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 ); + var aTextFieldElement = document.createElementNS(NSS['svg'], 'use'); + aTextFieldElement.setAttribute('class', sClassName); + setNSAttribute('xlink', aTextFieldElement, + 'href', '#' + sRefId); // node linking this.aBackgroundObjectsElement.appendChild( aTextFieldElement ); } @@ -5955,25 +6204,32 @@ SlideNumberFieldHandler.prototype.update = function( nPageNumber ) * The svg element that contains the text content for one or more * master slide text field. */ -function TextFieldContentProvider( aTextFieldContentElement ) +function TextFieldContentProvider() { - // This id is used as key for the theMetaDoc.aTextFieldHandlerSet object. - if( aTextFieldContentElement ) - this.sId = aTextFieldContentElement.getAttribute( 'id' ); + this.sId = TextFieldContentProvider.getUniqueId(); } +/*** private methods ***/ + +TextFieldContentProvider.CURR_UNIQUE_ID = 0; + +TextFieldContentProvider.getUniqueId = function() +{ + ++TextFieldContentProvider.CURR_UNIQUE_ID; + return TextFieldContentProvider.CURR_UNIQUE_ID; +}; + /** 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. + * @param aText + * a string containing the text to be substituted. */ -function FixedTextProvider( aTextFieldContentElement ) +function FixedTextProvider( aText ) { - FixedTextProvider.superclass.constructor.call( this, aTextFieldContentElement ); - this.text = aTextFieldContentElement.textContent; + FixedTextProvider.superclass.constructor.call( this ); + this.text = aText; } extend( FixedTextProvider, TextFieldContentProvider ); @@ -5991,6 +6247,20 @@ FixedTextProvider.prototype.update = function( aFixedTextField ) aFixedTextField.setTextContent( this.text ); }; +/** Class FixedTextByElementProvider + * 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 FixedTextByElementProvider( aTextFieldContentElement ) +{ + FixedTextByElementProvider.superclass.constructor.call( this, aTextFieldContentElement.textContent ); +} +extend( FixedTextByElementProvider, FixedTextProvider ); + /** 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. @@ -5999,10 +6269,15 @@ FixedTextProvider.prototype.update = function( aFixedTextField ) * The svg element that contains the date/time format for one or more * master slide date/time field. */ -function CurrentDateTimeProvider( aTextFieldContentElement ) +function CurrentDateTimeProvider( aTextFieldContentElement, sDateTimeFormat ) { CurrentDateTimeProvider.superclass.constructor.call( this, aTextFieldContentElement ); - this.dateTimeFormat = getOOOAttribute( aTextFieldContentElement, aOOOAttrDateTimeFormat ); + if( aTextFieldContentElement ) + this.dateTimeFormat = getOOOAttribute( aTextFieldContentElement, aOOOAttrDateTimeFormat ); + else + { + this.dateTimeFormat = sDateTimeFormat; + } } extend( CurrentDateTimeProvider, TextFieldContentProvider ); @@ -6017,17 +6292,22 @@ extend( CurrentDateTimeProvider, TextFieldContentProvider ); */ CurrentDateTimeProvider.prototype.update = function( aDateTimeField ) { - var sText = this.createDateTimeText( this.dateTimeFormat ); + var sText = this.createDateTimeText(); aDateTimeField.setTextContent( sText ); }; /*** private methods ***/ -CurrentDateTimeProvider.prototype.createDateTimeText = function( /*sDateTimeFormat*/ ) +CurrentDateTimeProvider.prototype.createDateTimeText = function() { // TODO handle date/time format - var aDate = new Date(); - var sDate = aDate.toLocaleString(); + var sDate; + if( this.dateTimeFormat === '<date>' ) + sDate = new Date().toLocaleDateString(); + else if( this.dateTimeFormat === '<time>' ) + sDate = new Date().toLocaleTimeString(); + else + sDate = new Date().toLocaleDateString(); return sDate; }; @@ -6037,7 +6317,7 @@ CurrentDateTimeProvider.prototype.createDateTimeText = function( /*sDateTimeForm */ function SlideNumberProvider( nInitialSlideNumber, sPageNumberingType ) { - SlideNumberProvider.superclass.constructor.call( this, null ); + SlideNumberProvider.superclass.constructor.call( this ); this.nInitialSlideNumber = nInitialSlideNumber; this.pageNumberingType = sPageNumberingType; @@ -6841,6 +7121,8 @@ function matrixToString( aSVGMatrix ) // eslint-disable-next-line no-unused-vars function numberParser( sValue ) { + if( typeof sValue !== 'string' ) + return undefined; if( sValue === '.' ) return undefined; var reFloatNumber = /^[+-]?[0-9]*[.]?[0-9]*$/; @@ -6853,6 +7135,9 @@ function numberParser( sValue ) function booleanParser( sValue ) { + if( typeof sValue !== 'string' ) + return undefined; + sValue = sValue.toLowerCase(); if( sValue === 'true' ) return true; @@ -6864,6 +7149,9 @@ function booleanParser( sValue ) function colorParser( sValue ) { + if( typeof sValue !== 'string' ) + return undefined; + // The following 3 color functions are used in evaluating sValue string // so don't remove them. @@ -7797,6 +8085,7 @@ var ENUM_PROPERTY = 2; var COLOR_PROPERTY = 3; var STRING_PROPERTY = 4; var BOOL_PROPERTY = 5; +var TUPLE_NUMBER_PROPERTY = 6; var aValueTypeOutMap = [ 'unknown', 'number', 'enum', 'color', 'string', 'boolean' ]; @@ -7814,6 +8103,14 @@ var aAttributeMap = 'get': 'getOpacity', 'set': 'setOpacity' }, + 'scale': { 'type': TUPLE_NUMBER_PROPERTY, + 'get': 'getSize', + 'set': 'setSize' }, + + 'translate': { 'type': TUPLE_NUMBER_PROPERTY, + 'get': 'getPos', + 'set': 'setPos' }, + 'rotate': { 'type': NUMBER_PROPERTY, 'get': 'getRotationAngle', 'set': 'setRotationAngle' }, @@ -11382,6 +11679,70 @@ PropertyAnimationNode.prototype.createActivity = function() +function isValidTransformation( sType ) +{ + return ( sType === 'translate' || sType === 'scale' || sType === 'rotate' + || sType === 'skewX' || sType === 'skewY' ); +} + +function AnimationTransformNode( aAnimElem, aParentNode, aNodeContext ) +{ + AnimationTransformNode.superclass.constructor.call( this, aAnimElem, aParentNode, aNodeContext ); + + this.sClassName = 'AnimationTransformNode'; +} +extend( AnimationTransformNode, AnimationBaseNode3 ); + + +AnimationTransformNode.prototype.parseElement = function() +{ + var bRet = AnimationTransformNode.superclass.parseElement.call(this); + + var aAnimElem = this.aElement; + + // transformation type + var sTransformType = aAnimElem.getAttribute( 'svg:type' ); + if( !isValidTransformation( sTransformType ) ) + { + this.eCurrentState = INVALID_NODE; + log( 'AnimationTransformNode.parseElement: transformation type not found: ' + sTransformType ); + } + else + { + this.sAttributeName = sTransformType; + } + + return bRet; +} + +AnimationTransformNode.prototype.createActivity = function() +{ + var aActivityParamSet = this.fillActivityParams(); + var aAnimation; + + if( this.getAttributeName() === 'scale' || this.getAttributeName() === 'translate' ) + { + aAnimation = createPairPropertyAnimation( this.getAttributeName(), + this.getAnimatedElement(), + this.aNodeContext.aSlideWidth, + this.aNodeContext.aSlideHeight ); + + } + else + { + aAnimation = createPropertyAnimation( this.getAttributeName(), + this.getAnimatedElement(), + this.aNodeContext.aSlideWidth, + this.aNodeContext.aSlideHeight ); + } + + var aInterpolator = null; // createActivity will compute it; + return createActivity( aActivityParamSet, this, aAnimation, aInterpolator ); +}; + + + + function AnimationSetNode( aAnimElem, aParentNode, aNodeContext ) { AnimationSetNode.superclass.constructor.call( this, aAnimElem, aParentNode, aNodeContext ); @@ -11680,10 +12041,8 @@ function createAnimationNode( aElement, aParentNode, aNodeContext ) aCreatedNode = new AnimationColorNode( aElement, aParentNode, aNodeContext ); break; case ANIMATION_NODE_ANIMATETRANSFORM: - //aCreatedNode = new AnimationTransformNode( aElement, aParentNode, aNodeContext ); - //break; - log( 'createAnimationNode: ANIMATETRANSFORM not implemented' ); - return null; + aCreatedNode = new AnimationTransformNode( aElement, aParentNode, aNodeContext ); + break; case ANIMATION_NODE_TRANSITIONFILTER: aCreatedNode = new AnimationTransitionFilterNode( aElement, aParentNode, aNodeContext ); break; @@ -11806,6 +12165,41 @@ function createPropertyAnimation( sAttrName, aAnimatedElement, nWidth, nHeight ) +function createPairPropertyAnimation( sTransformType, aAnimatedElement, nWidth, nHeight ) +{ + var aFunctorSet = aAttributeMap[ sTransformType ]; + var sGetValueMethod = aFunctorSet.get; + var sSetValueMethod = aFunctorSet.set; + + var aDefaultValue = []; + var aSizeReference = []; + if( sTransformType === 'scale' ) + { + aDefaultValue[0] = aSizeReference[0] = aAnimatedElement.getBaseBBox().width; + aDefaultValue[1] = aSizeReference[1] = aAnimatedElement.getBaseBBox().height; + } + else if( sTransformType === 'translate' ) + { + aDefaultValue[0] = aAnimatedElement.getBaseCenterX(); + aDefaultValue[1] = aAnimatedElement.getBaseCenterY(); + aSizeReference[0] = nWidth; + aSizeReference[1] = nHeight; + } + else + { + log( 'createPairPropertyAnimation: transform type is not handled' ); + return null; + } + + return new TupleAnimation( bind( aAnimatedElement, aAnimatedElement[ sGetValueMethod ] ), + bind( aAnimatedElement, aAnimatedElement[ sSetValueMethod ] ), + aDefaultValue, + aSizeReference ); +} + + + + /** createShapeTransition * * @param aActivityParamSet @@ -12005,6 +12399,45 @@ GenericAnimation.prototype.getUnderlyingValue = function() +function TupleAnimation( aGetValueFunc, aSetValueFunc, aDefaultValue, aReferenceSize ) +{ + TupleAnimation.superclass.constructor.call( this, aGetValueFunc, aSetValueFunc ); + assert( aDefaultValue && aReferenceSize, + 'TupleAnimation constructor: default value functor and/or reference size are not valid' ); + + this.aDefaultValue = aDefaultValue; + this.aReferenceSize = aReferenceSize; +} +extend( TupleAnimation, GenericAnimation ); + +TupleAnimation.prototype.perform = function( aNormValue ) +{ + assert(aNormValue.length === this.aReferenceSize.length); + + var aValue = []; + for( var i = 0; i < aNormValue.length; ++i ) + { + aValue.push( aNormValue[i] * this.aReferenceSize[i] ); + } + + this.aSetValueFunc( aValue ); +}; + +TupleAnimation.prototype.getUnderlyingValue = function() +{ + var aValue = this.aGetValueFunc(); + assert(aValue.length === this.aReferenceSize.length); + + var aNormValue = []; + for( var i = 0; i < aValue.length; ++i ) + { + aNormValue.push( aValue[i] / this.aReferenceSize[i] ); + } + + return aNormValue; +}; + + function HSLAnimationWrapper( aColorAnimation ) { @@ -14322,6 +14755,11 @@ AnimatedElement.prototype.getY = function() return this.nCenterY; }; +AnimatedElement.prototype.getPos = function() +{ + return [this.getX(), this.getY()]; +}; + AnimatedElement.prototype.getWidth = function() { return this.nScaleFactorX * this.getBaseBBox().width; @@ -14332,6 +14770,11 @@ AnimatedElement.prototype.getHeight = function() return this.nScaleFactorY * this.getBaseBBox().height; }; +AnimatedElement.prototype.getSize = function() +{ + return [this.getWidth(), this.getHeight()]; +}; + AnimatedElement.prototype.updateTransformAttribute = function() { this.aTransformAttrList = this.aActiveElement.transform.baseVal; @@ -14361,12 +14804,27 @@ AnimatedElement.prototype.setY = function( nNewCenterY ) this.nCenterY = nNewCenterY; }; +AnimatedElement.prototype.setPos = function( aNewPos ) +{ + var nNewCenterX = aNewPos[0]; + var nNewCenterY = aNewPos[1]; + + if( nNewCenterX === this.nCenterX && nNewCenterY === this.nCenterY ) return; + + this.aTransformAttrList = this.aActiveElement.transform.baseVal; + this.aTransformAttr = this.aTransformAttrList.getItem( 0 ); + this.aTMatrix = this.aTransformAttr.matrix.translate( nNewCenterX - this.nCenterX, nNewCenterY - this.nCenterY ); + this.aTransformAttr.setMatrix( this.aTMatrix ); + this.nCenterX = nNewCenterX; + this.nCenterY = nNewCenterY; +}; + AnimatedElement.prototype.setWidth = function( nNewWidth ) { ANIMDBG.print( 'AnimatedElement.setWidth: nNewWidth = ' + nNewWidth ); if( nNewWidth < 0 ) { - log('AnimatedElement(' + this.getId() + ').setWidth: negative height!'); + log('AnimatedElement(' + this.getId() + ').setWidth: negative width!'); nNewWidth = 0; } @@ -14411,6 +14869,43 @@ AnimatedElement.prototype.setHeight = function( nNewHeight ) this.nScaleFactorY = nScaleFactorY; }; +AnimatedElement.prototype.setSize= function( aNewSize ) +{ + var nNewWidth = aNewSize[0]; + var nNewHeight = aNewSize[1]; + ANIMDBG.print( 'AnimatedElement.setSize: = [' + nNewWidth + ',' + nNewHeight + ']'); + if( nNewWidth < 0 ) + { + log('AnimatedElement(' + this.getId() + ').setSize: negative width!'); + nNewWidth = 0; + } + if( nNewHeight < 0 ) + { + log('AnimatedElement(' + this.getId() + ').setSize: negative height!'); + nNewHeight = 0; + } + + var nBaseWidth = this.getBaseBBox().width; + var nScaleFactorX = nNewWidth / nBaseWidth; + if( nScaleFactorX < 1e-5 ) nScaleFactorX = 1e-5; + + var nBaseHeight = this.getBaseBBox().height; + var nScaleFactorY = nNewHeight / nBaseHeight; + if( nScaleFactorY < 1e-5 ) nScaleFactorY = 1e-5; + + if( nScaleFactorX == this.nScaleFactorX && nScaleFactorY == this.nScaleFactorY ) return; + + this.aTMatrix = document.documentElement.createSVGMatrix() + .translate( this.nCenterX, this.nCenterY ) + .rotate(this.nRotationAngle) + .scaleNonUniform( nScaleFactorX, nScaleFactorY ) + .translate( -this.nBaseCenterX, -this.nBaseCenterY ); + this.updateTransformAttribute(); + + this.nScaleFactorX = nScaleFactorX; + this.nScaleFactorY = nScaleFactorY; +}; + AnimatedElement.prototype.getOpacity = function() { return this.aActiveElement.getAttribute( 'opacity' ); @@ -14573,7 +15068,7 @@ function AnimatedTextElement( aElement, aEventMultiplexer ) } var aTextShapeElement = aElement.parentNode; sTextType = aTextShapeElement.getAttribute( 'class' ); - if( sTextType !== 'TextShape' ) + if( sTextType !== 'SVGTextShape' ) { log( 'AnimatedTextElement: element parent is not a text shape.' ); return; @@ -14615,7 +15110,7 @@ function AnimatedTextElement( aElement, aEventMultiplexer ) if( aBulletPlaceholderElem ) { var sId = aBulletPlaceholderElem.getAttribute( 'id' ); - sId = 'bullet-char(' + sId + ')'; + sId = 'bullet-char-' + sId; aBulletCharElem = theDocument.getElementById( sId ); if( aBulletCharElem ) { @@ -16144,6 +16639,17 @@ aInterpolatorHandler.aLerpFunctorMap[ CALC_MODE_LINEAR ][ COLOR_PROPERTY ][ COLO }; }; +aInterpolatorHandler.aLerpFunctorMap[ CALC_MODE_LINEAR ][ TUPLE_NUMBER_PROPERTY ] = + function ( aFrom, aTo, nT ) + { + var aRes = []; + for( var i = 0; i < aFrom.length; ++i ) + { + aRes.push( ( 1.0 - nT )* aFrom[i] + nT * aTo[i] ); + } + return aRes; + }; + @@ -16311,6 +16817,36 @@ aOperatorSetMap[ STRING_PROPERTY ] = aOperatorSetMap[ ENUM_PROPERTY ]; // bool operators aOperatorSetMap[ BOOL_PROPERTY ] = aOperatorSetMap[ ENUM_PROPERTY ]; +// tuple number operators +aOperatorSetMap[ TUPLE_NUMBER_PROPERTY ] = {}; + +aOperatorSetMap[ TUPLE_NUMBER_PROPERTY ].equal = function( a, b ) +{ + assert( a.length === b.length, 'Tuples length mismatch.' ); + return ( a.toString() === b.toString() ); +}; + +aOperatorSetMap[ TUPLE_NUMBER_PROPERTY ].add = function( a, b ) +{ + assert( a.length === b.length, 'Tuples length mismatch.' ); + var r = []; + for( var i = 0; i < a.length; ++i ) + { + r.push(a[i] + b[i]); + } + return r; +}; + +aOperatorSetMap[ TUPLE_NUMBER_PROPERTY ].scale = function( k, v ) +{ + var r = []; + for( var i = 0; i < v.length; ++i ) + { + r.push(k * v[i]); + } + return r; +}; + @@ -17720,6 +18256,22 @@ function extractAttributeValues( eValueType, aValueList, aValueSet, aBBox, nSlid aValueList.push( aValue ); } break; + case TUPLE_NUMBER_PROPERTY : + for( i = 0; i < aValueSet.length; ++i ) + { + if( typeof aValueSet[i] === 'string' ) + { + var aTuple = aValueSet[i].split(','); + aValue = []; + evalValuesAttribute(aValue, aTuple, aBBox, nSlideWidth, nSlideHeight); + aValueList.push(aValue); + } + else + { + aValueList.push( undefined ); + } + } + break; default: log( 'createValueListActivity: unexpected value type: ' + eValueType ); } @@ -17742,9 +18294,12 @@ function evalValuesAttribute( aValueList, aValueSet, aBBox, nSlideWidth, nSlideH for( var i = 0; i < aValueSet.length; ++i ) { var sValue = aValueSet[i]; - sValue = sValue.replace(reMath, 'Math.$&'); - sValue = sValue.replace(/pi(?!\w)/g, 'Math.PI'); - sValue = sValue.replace(/e(?!\w)/g, 'Math.E'); + if(sValue) + { + sValue = sValue.replace(reMath, 'Math.$&'); + sValue = sValue.replace(/pi(?!\w)/g, 'Math.PI'); + sValue = sValue.replace(/e(?!\w)/g, 'Math.E'); + } var aValue = eval( sValue ); aValueList.push( aValue ); } @@ -18430,8 +18985,8 @@ SlideShow.prototype.exitSlideShowInApp = function() { if (window.webkit !== undefined && window.webkit.messageHandlers !== undefined && - window.webkit.messageHandlers.lool !== undefined) - window.webkit.messageHandlers.lool.postMessage('EXITSLIDESHOW', '*'); + window.webkit.messageHandlers.lok !== undefined) + window.webkit.messageHandlers.lok.postMessage('EXITSLIDESHOW', '*'); } SlideShow.prototype.displaySlide = function( nNewSlide, bSkipSlideTransition ) |