1 /* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4 * - Presentation Engine - *
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
8 * WARNING: any comment that should not be striped out by the script
9 * generating the C++ header file must start with a '/' and exactly 5 '*'
10 * not striped examples: '/*****', '/***** *'
11 * striped examples: '/** ***' (not contiguous), '/******' (more than 5)
13 * NOTE: This file combines several works, under different
14 * licenses. See the @licstart / @licend sections below.
22 * The following is the license notice for the part of JavaScript code of this
23 * page included between the '@jessyinkstart' and the '@jessyinkend' notes.
26 /***** ******************************************************************
28 * Copyright 2008-2013 Hannes Hochreiner
30 * The JavaScript code included between the start note '@jessyinkstart'
31 * and the end note '@jessyinkend' is subject to the terms of the Mozilla
32 * Public License, v. 2.0. If a copy of the MPL was not distributed with
33 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
35 * Alternatively, you can redistribute and/or that part of this file
36 * under the terms of the GNU General Public License as published by
37 * the Free Software Foundation, either version 3 of the License, or
38 * (at your option) any later version.
40 * This program is distributed in the hope that it will be useful,
41 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 * GNU General Public License for more details.
45 * You should have received a copy of the GNU General Public License
46 * along with this program. If not, see http://www.gnu.org/licenses/.
50 * You can find the complete source code of the JessyInk project at:
51 * @source http://code.google.com/p/jessyink/
57 * The above is the license notice for the part of JavaScript code of this
58 * page included between the '@jessyinkstart' and the '@jessyinkend' notes.
66 * The following code is a derivative work of some parts of the JessyInk
68 * @source http://code.google.com/p/jessyink/
72 /** Convenience function to get an element depending on whether it has a
73 * property with a particular name.
75 * @param node element of the document
76 * @param name attribute name
78 * @returns an array containing all the elements of the tree with root
79 * 'node' that own the property 'name'
81 function getElementsByProperty( node
, name
)
83 var elems
= new Array();
85 if( node
.getAttribute( name
) )
88 for( var counter
= 0; counter
< node
.childNodes
.length
; ++counter
)
90 if( node
.childNodes
[counter
].nodeType
== 1 )
92 var subElems
= getElementsByProperty( node
.childNodes
[counter
], name
);
93 elems
= elems
.concat( subElems
);
99 /** Event handler for key press.
101 * @param aEvt the event
103 function onKeyDown( aEvt
)
108 var code
= aEvt
.keyCode
|| aEvt
.charCode
;
110 if( !processingEffect
&& keyCodeDictionary
[currentMode
] && keyCodeDictionary
[currentMode
][code
] )
112 return keyCodeDictionary
[currentMode
][code
]();
116 document
.onkeypress
= onKeyPress
;
120 //Set event handler for key down.
121 document
.onkeydown
= onKeyDown
;
123 /** Event handler for key press.
125 * @param aEvt the event
127 function onKeyPress( aEvt
)
129 document
.onkeypress
= null;
134 var str
= String
.fromCharCode( aEvt
.keyCode
|| aEvt
.charCode
);
136 if ( !processingEffect
&& charCodeDictionary
[currentMode
] && charCodeDictionary
[currentMode
][str
] )
137 return charCodeDictionary
[currentMode
][str
]();
142 /** Function to supply the default key code dictionary.
144 * @returns default key code dictionary
146 function getDefaultKeyCodeDictionary()
148 var keyCodeDict
= new Object();
150 keyCodeDict
[SLIDE_MODE
] = new Object();
151 keyCodeDict
[INDEX_MODE
] = new Object();
154 keyCodeDict
[SLIDE_MODE
][LEFT_KEY
]
155 = function() { return aSlideShow
.rewindEffect(); };
156 keyCodeDict
[SLIDE_MODE
][RIGHT_KEY
]
157 = function() { return dispatchEffects(1); };
158 keyCodeDict
[SLIDE_MODE
][UP_KEY
]
159 = function() { return aSlideShow
.rewindEffect(); };
160 keyCodeDict
[SLIDE_MODE
][DOWN_KEY
]
161 = function() { return skipEffects(1); };
162 keyCodeDict
[SLIDE_MODE
][PAGE_UP_KEY
]
163 = function() { return aSlideShow
.rewindAllEffects(); };
164 keyCodeDict
[SLIDE_MODE
][PAGE_DOWN_KEY
]
165 = function() { return skipAllEffects(); };
166 keyCodeDict
[SLIDE_MODE
][HOME_KEY
]
167 = function() { return aSlideShow
.displaySlide( 0, true ); };
168 keyCodeDict
[SLIDE_MODE
][END_KEY
]
169 = function() { return aSlideShow
.displaySlide( theMetaDoc
.nNumberOfSlides
- 1, true ); };
170 keyCodeDict
[SLIDE_MODE
][SPACE_KEY
]
171 = function() { return dispatchEffects(1); };
174 keyCodeDict
[INDEX_MODE
][LEFT_KEY
]
175 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
- 1 ); };
176 keyCodeDict
[INDEX_MODE
][RIGHT_KEY
]
177 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
+ 1 ); };
178 keyCodeDict
[INDEX_MODE
][UP_KEY
]
179 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
- theSlideIndexPage
.indexColumns
); };
180 keyCodeDict
[INDEX_MODE
][DOWN_KEY
]
181 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
+ theSlideIndexPage
.indexColumns
); };
182 keyCodeDict
[INDEX_MODE
][PAGE_UP_KEY
]
183 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
- theSlideIndexPage
.getTotalThumbnails() ); };
184 keyCodeDict
[INDEX_MODE
][PAGE_DOWN_KEY
]
185 = function() { return indexSetPageSlide( theSlideIndexPage
.selectedSlideIndex
+ theSlideIndexPage
.getTotalThumbnails() ); };
186 keyCodeDict
[INDEX_MODE
][HOME_KEY
]
187 = function() { return indexSetPageSlide( 0 ); };
188 keyCodeDict
[INDEX_MODE
][END_KEY
]
189 = function() { return indexSetPageSlide( theMetaDoc
.nNumberOfSlides
- 1 ); };
190 keyCodeDict
[INDEX_MODE
][ENTER_KEY
]
191 = function() { return toggleSlideIndex(); };
192 keyCodeDict
[INDEX_MODE
][SPACE_KEY
]
193 = function() { return toggleSlideIndex(); };
194 keyCodeDict
[INDEX_MODE
][ESCAPE_KEY
]
195 = function() { return abandonIndexMode(); };
200 /** Function to supply the default char code dictionary.
202 * @returns default char code dictionary
204 function getDefaultCharCodeDictionary()
206 var charCodeDict
= new Object();
208 charCodeDict
[SLIDE_MODE
] = new Object();
209 charCodeDict
[INDEX_MODE
] = new Object();
212 charCodeDict
[SLIDE_MODE
]['i']
213 = function () { return toggleSlideIndex(); };
216 charCodeDict
[INDEX_MODE
]['i']
217 = function () { return toggleSlideIndex(); };
218 charCodeDict
[INDEX_MODE
]['-']
219 = function () { return theSlideIndexPage
.decreaseNumberOfColumns(); };
220 charCodeDict
[INDEX_MODE
]['=']
221 = function () { return theSlideIndexPage
.increaseNumberOfColumns(); };
222 charCodeDict
[INDEX_MODE
]['+']
223 = function () { return theSlideIndexPage
.increaseNumberOfColumns(); };
224 charCodeDict
[INDEX_MODE
]['0']
225 = function () { return theSlideIndexPage
.resetNumberOfColumns(); };
231 function slideOnMouseUp( aEvt
)
238 if( aEvt
.button
== 0 )
240 else if( aEvt
.button
== 2 )
244 dispatchEffects( nOffset
);
245 return true; // the click has been handled
248 document
.handleClick
= slideOnMouseUp
;
251 /** Event handler for mouse wheel events in slide mode.
252 * based on http://adomas.org/javascript-mouse-wheel/
254 * @param aEvt the event
256 function slideOnMouseWheel(aEvt
)
265 delta
= aEvt
.wheelDelta
/120;
267 else if (aEvt
.detail
)
269 delta
= -aEvt
.detail
/3;
277 if (aEvt
.preventDefault
)
278 aEvt
.preventDefault();
280 aEvt
.returnValue
= false;
284 if( window
.addEventListener
)
286 window
.addEventListener( 'DOMMouseScroll', function( aEvt
) { return mouseHandlerDispatch( aEvt
, MOUSE_WHEEL
); }, false );
289 //Opera Safari OK - may not work in IE
291 = function( aEvt
) { return mouseHandlerDispatch( aEvt
, MOUSE_WHEEL
); };
293 /** Function to handle all mouse events.
296 * @param anAction type of event (e.g. mouse up, mouse wheel)
298 function mouseHandlerDispatch( aEvt
, anAction
)
305 if ( mouseHandlerDictionary
[currentMode
] && mouseHandlerDictionary
[currentMode
][anAction
] )
307 var subRetVal
= mouseHandlerDictionary
[currentMode
][anAction
]( aEvt
);
309 if( subRetVal
!= null && subRetVal
!= undefined )
313 if( aEvt
.preventDefault
&& !retVal
)
314 aEvt
.preventDefault();
316 aEvt
.returnValue
= retVal
;
321 //Set mouse event handler.
322 document
.onmouseup = function( aEvt
) { return mouseHandlerDispatch( aEvt
, MOUSE_UP
); };
323 //document.onmousemove = function( aEvt ) { return mouseHandlerDispatch( aEvt, MOUSE_MOVE ); };
329 * a mouse click handler
331 function mouseClickHelper( aEvt
)
333 // In case text is selected we stay on the current slide.
334 // Anyway if we are dealing with Firefox there is an issue:
335 // Firefox supports a naive way of selecting svg text, if you click
336 // on text the current selection is set to the whole text fragment
337 // wrapped by the related <tspan> element.
338 // That means until you click on text you never move to the next slide.
339 // In order to avoid this case we do not test the status of current
340 // selection, when the presentation is running on a mozilla browser.
341 if( !Detect
.isMozilla
)
343 var aWindowObject
= document
.defaultView
;
346 var aTextSelection
= aWindowObject
.getSelection();
347 var sSelectedText
= aTextSelection
.toString();
350 DBGLOG( 'text selection: ' + sSelectedText
);
351 if( sLastSelectedText
!== sSelectedText
)
353 bTextHasBeenSelected
= true;
354 sLastSelectedText
= sSelectedText
;
358 bTextHasBeenSelected
= false;
362 else if( bTextHasBeenSelected
)
364 bTextHasBeenSelected
= false;
365 sLastSelectedText
= '';
371 log( 'error: HyperlinkElement.handleClick: invalid window object.' );
375 var aSlideAnimationsHandler
= theMetaDoc
.aMetaSlideSet
[nCurSlide
].aSlideAnimationsHandler
;
376 if( aSlideAnimationsHandler
)
378 var aCurrentEventMultiplexer
= aSlideAnimationsHandler
.aEventMultiplexer
;
379 if( aCurrentEventMultiplexer
)
381 if( aCurrentEventMultiplexer
.hasRegisteredMouseClickHandlers() )
383 return aCurrentEventMultiplexer
.notifyMouseClick( aEvt
);
387 return slideOnMouseUp( aEvt
);
391 /** Function to supply the default mouse handler dictionary.
393 * @returns default mouse handler dictionary
395 function getDefaultMouseHandlerDictionary()
397 var mouseHandlerDict
= new Object();
399 mouseHandlerDict
[SLIDE_MODE
] = new Object();
400 mouseHandlerDict
[INDEX_MODE
] = new Object();
403 mouseHandlerDict
[SLIDE_MODE
][MOUSE_UP
]
405 //= function( aEvt ) { return slideOnMouseDown( aEvt ); };
406 // = function( aEvt ) { return ( aSlideShow.aEventMultiplexer ) ?
407 // aSlideShow.aEventMultiplexer.notifyMouseClick( aEvt )
408 // : slideOnMouseUp( aEvt ); };
410 mouseHandlerDict
[SLIDE_MODE
][MOUSE_WHEEL
]
411 = function( aEvt
) { return slideOnMouseWheel( aEvt
); };
414 mouseHandlerDict
[INDEX_MODE
][MOUSE_UP
]
415 = function( aEvt
) { return toggleSlideIndex(); };
416 // mouseHandlerDict[INDEX_MODE][MOUSE_MOVE]
417 // = function( aEvt ) { return theSlideIndexPage.updateSelection( aEvt ); };
419 return mouseHandlerDict
;
422 /** Function to set the page and active slide in index view.
424 * @param nIndex index of the active slide
426 * NOTE: To force a redraw,
427 * set INDEX_OFFSET to -1 before calling indexSetPageSlide().
429 * This is necessary for zooming (otherwise the index might not
430 * get redrawn) and when switching to index mode.
433 * indexSetPageSlide(activeSlide);
435 function indexSetPageSlide( nIndex
)
437 var aMetaSlideSet
= theMetaDoc
.aMetaSlideSet
;
438 nIndex
= getSafeIndex( nIndex
, 0, aMetaSlideSet
.length
- 1 );
440 //calculate the offset
441 var nSelectedThumbnailIndex
= nIndex
% theSlideIndexPage
.getTotalThumbnails();
442 var offset
= nIndex
- nSelectedThumbnailIndex
;
447 //if different from kept offset, then record and change the page
448 if( offset
!= INDEX_OFFSET
)
450 INDEX_OFFSET
= offset
;
451 displayIndex( INDEX_OFFSET
);
454 //set the selected thumbnail and the current slide
455 theSlideIndexPage
.setSelection( nSelectedThumbnailIndex
);
462 * The above code is a derivative work of some parts of the JessyInk project.
463 * @source http://code.google.com/p/jessyink/
473 * The following is the license notice for the part of JavaScript code of this
474 * page included between the '@dojostart' and the '@dojoend' notes.
477 /***** **********************************************************************
479 * The 'New' BSD License:
480 * **********************
481 * Copyright (c) 2005-2012, The Dojo Foundation
482 * All rights reserved.
484 * Redistribution and use in source and binary forms, with or without
485 * modification, are permitted provided that the following conditions are met:
487 * * Redistributions of source code must retain the above copyright notice,
488 * this list of conditions and the following disclaimer.
489 * * Redistributions in binary form must reproduce the above copyright notice,
490 * this list of conditions and the following disclaimer in the documentation
491 * and/or other materials provided with the distribution.
492 * * Neither the name of the Dojo Foundation nor the names of its contributors
493 * may be used to endorse or promote products derived from this software
494 * without specific prior written permission.
496 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
497 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
498 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
499 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
500 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
501 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
502 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
503 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
504 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
505 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
507 ****************************************************************************/
513 * The above is the license notice for the part of JavaScript code of this
514 * page included between the '@dojostart' and the '@dojoend' notes.
522 * The following code is a derivative work of some part of the dojox.gfx library.
523 * @source http://svn.dojotoolkit.org/src/dojox/trunk/_base/sniff.js
528 return has
.cache
[name
];
533 has
.add = function( name
, test
)
535 has
.cache
[name
] = test
;
538 function configureDetectionTools()
542 log( 'error: configureDetectionTools: configuration failed' );
549 tv
= parseFloat(dav
);
551 has
.add('air', dua
.indexOf('AdobeAIR') >= 0),
552 has
.add('khtml', dav
.indexOf('Konqueror') >= 0 ? tv
: undefined);
553 has
.add('webkit', parseFloat(dua
.split('WebKit/')[1]) || undefined);
554 has
.add('chrome', parseFloat(dua
.split('Chrome/')[1]) || undefined);
555 has
.add('safari', dav
.indexOf('Safari')>=0 && !has('chrome') ? parseFloat(dav
.split('Version/')[1]) : undefined);
556 has
.add('mac', dav
.indexOf('Macintosh') >= 0);
557 has
.add('quirks', document
.compatMode
== 'BackCompat');
558 has
.add('ios', /iPhone|iPod|iPad/.test(dua
));
559 has
.add('android', parseFloat(dua
.split('Android ')[1]) || undefined);
563 if(dua
.indexOf('Opera') >= 0){
564 // see http://dev.opera.com/articles/view/opera-ua-string-changes and http://www.useragentstring.com/pages/Opera/
565 // 9.8 has both styles; <9.8, 9.9 only old style
566 has
.add('opera', tv
>= 9.8 ? parseFloat(dua
.split('Version/')[1]) || tv
: tv
);
569 // Mozilla and firefox
570 if(dua
.indexOf('Gecko') >= 0 && !has('khtml') && !has('webkit')){
571 has
.add('mozilla', tv
);
574 //We really need to get away from this. Consider a sane isGecko approach for the future.
575 has
.add('ff', parseFloat(dua
.split('Firefox/')[1] || dua
.split('Minefield/')[1]) || undefined);
579 if(document
.all
&& !has('opera')){
580 var isIE
= parseFloat(dav
.split('MSIE ')[1]) || undefined;
582 //In cases where the page has an HTTP header or META tag with
583 //X-UA-Compatible, then it is in emulation mode.
584 //Make sure isIE reflects the desired version.
585 //document.documentMode of 5 means quirks mode.
586 //Only switch the value if documentMode's major version
587 //is different from isIE's major version.
588 var mode
= document
.documentMode
;
589 if(mode
&& mode
!= 5 && Math
.floor(isIE
) != mode
){
597 has
.add('wii', typeof opera
!= 'undefined' && opera
.wiiremote
);
602 // isFF: Number|undefined
603 // Version as a Number if client is FireFox. undefined otherwise. Corresponds to
604 // major detected FireFox version (1.5, 2, 3, etc.)
607 // isIE: Number|undefined
608 // Version as a Number if client is MSIE(PC). undefined otherwise. Corresponds to
609 // major detected IE version (6, 7, 8, etc.)
612 // isKhtml: Number|undefined
613 // Version as a Number if client is a KHTML browser. undefined otherwise. Corresponds to major
615 isKhtml
: has('khtml'),
617 // isWebKit: Number|undefined
618 // Version as a Number if client is a WebKit-derived browser (Konqueror,
619 // Safari, Chrome, etc.). undefined otherwise.
620 isWebKit
: has('webkit'),
622 // isMozilla: Number|undefined
623 // Version as a Number if client is a Mozilla-based browser (Firefox,
624 // SeaMonkey). undefined otherwise. Corresponds to major detected version.
625 isMozilla
: has('mozilla'),
626 // isMoz: Number|undefined
627 // Version as a Number if client is a Mozilla-based browser (Firefox,
628 // SeaMonkey). undefined otherwise. Corresponds to major detected version.
629 isMoz
: has('mozilla'),
631 // isOpera: Number|undefined
632 // Version as a Number if client is Opera. undefined otherwise. Corresponds to
633 // major detected version.
634 isOpera
: has('opera'),
636 // isSafari: Number|undefined
637 // Version as a Number if client is Safari or iPhone. undefined otherwise.
638 isSafari
: has('safari'),
640 // isChrome: Number|undefined
641 // Version as a Number if client is Chrome browser. undefined otherwise.
642 isChrome
: has('chrome'),
645 // True if the client runs on Mac
649 // True if client is iPhone, iPod, or iPad
652 // isAndroid: Number|undefined
653 // Version as a Number if client is android browser. undefined otherwise.
654 isAndroid
: has('android'),
657 // True if client is Wii
661 // Page is in quirks mode.
662 isQuirks
: has('quirks'),
665 // True if client is Adobe Air
674 * The above code is a derivative work of some part of the dojox.gfx library.
675 * @source http://svn.dojotoolkit.org/src/dojox/trunk/_base/sniff.js
682 * The following is the license notice for the part of JavaScript code of
683 * this page included between the '@libreofficestart' and the '@libreofficeend'
687 /***** ******************************************************************
689 * This file is part of the LibreOffice project.
691 * This Source Code Form is subject to the terms of the Mozilla Public
692 * License, v. 2.0. If a copy of the MPL was not distributed with this
693 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
695 * This file incorporates work covered by the following license notice:
697 * Licensed to the Apache Software Foundation (ASF) under one or more
698 * contributor license agreements. See the NOTICE file distributed
699 * with this work for additional information regarding copyright
700 * ownership. The ASF licenses this file to you under the Apache
701 * License, Version 2.0 (the 'License'); you may not use this file
702 * except in compliance with the License. You may obtain a copy of
703 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
705 ************************************************************************/
710 * The above is the license notice for the part of JavaScript code of
711 * this page included between the '@libreofficestart' and the '@libreofficeend'
720 * Several parts of the following code are the result of the porting,
721 * started on August 2011, of the C++ code included in the source
722 * files placed under the folder '/slideshow/source' and
723 * subfolders. This got later rebased onto the AL2-licensed versions
724 * of those files in early 2013.
725 * @source http://cgit.freedesktop.org/libreoffice/core/tree/slideshow/source
730 window
.onload
= init
;
734 var aOOOElemMetaSlides
= 'ooo:meta_slides';
735 var aOOOElemMetaSlide
= 'ooo:meta_slide';
736 var aOOOElemTextField
= 'ooo:text_field';
737 var aPresentationClipPathId
= 'presentation_clip_path';
740 var aOOOAttrNumberOfSlides
= 'number-of-slides';
741 var aOOOAttrStartSlideNumber
= 'start-slide-number';
742 var aOOOAttrNumberingType
= 'page-numbering-type';
743 var aOOOAttrListItemNumberingType
= 'numbering-type';
745 var aOOOAttrSlide
= 'slide';
746 var aOOOAttrMaster
= 'master';
747 var aOOOAttrHasTransition
= 'has-transition';
748 var aOOOAttrBackgroundVisibility
= 'background-visibility';
749 var aOOOAttrMasterObjectsVisibility
= 'master-objects-visibility';
750 var aOOOAttrPageNumberVisibility
= 'page-number-visibility';
751 var aOOOAttrDateTimeVisibility
= 'date-time-visibility';
752 var aOOOAttrFooterVisibility
= 'footer-visibility';
753 var aOOOAttrHeaderVisibility
= 'header-visibility';
754 var aOOOAttrDateTimeField
= 'date-time-field';
755 var aOOOAttrFooterField
= 'footer-field';
756 var aOOOAttrHeaderField
= 'header-field';
758 var aOOOAttrDateTimeFormat
= 'date-time-format';
760 var aOOOAttrTextAdjust
= 'text-adjust';
762 // element class names
763 var aClipPathGroupClassName
= 'ClipPathGroup';
764 var aPageClassName
= 'Page';
765 var aSlideNumberClassName
= 'Slide_Number';
766 var aDateTimeClassName
= 'Date/Time';
767 var aFooterClassName
= 'Footer';
768 var aHeaderClassName
= 'Header';
770 // Creating a namespace dictionary.
771 var NSS
= new Object();
772 NSS
['svg']='http://www.w3.org/2000/svg';
773 NSS
['rdf']='http://www.w3.org/1999/02/22-rdf-syntax-ns#';
774 NSS
['xlink']='http://www.w3.org/1999/xlink';
775 NSS
['xml']='http://www.w3.org/XML/1998/namespace';
776 NSS
['ooo'] = 'http://xml.openoffice.org/svg/export';
778 // Presentation modes.
782 // Mouse handler actions.
789 var LEFT_KEY
= 37; // cursor left keycode
790 var UP_KEY
= 38; // cursor up keycode
791 var RIGHT_KEY
= 39; // cursor right keycode
792 var DOWN_KEY
= 40; // cursor down keycode
793 var PAGE_UP_KEY
= 33; // page up keycode
794 var PAGE_DOWN_KEY
= 34; // page down keycode
795 var HOME_KEY
= 36; // home keycode
796 var END_KEY
= 35; // end keycode
805 var aVisibilityAttributeValue
= [ 'hidden', 'visible', 'inherit' ];
806 var aVisibilityValue
= { 'hidden' : HIDDEN
, 'visible' : VISIBLE
, 'inherit' : INHERIT
};
809 var ROOT_NODE
= document
.getElementsByTagNameNS( NSS
['svg'], 'svg' )[0];
812 var INDEX_COLUMNS_DEFAULT
= 3;
813 var INDEX_OFFSET
= 0;
816 var Detect
= configureDetectionTools();
818 var theSlideIndexPage
;
819 var currentMode
= SLIDE_MODE
;
820 var processingEffect
= false;
821 var nCurSlide
= undefined;
822 var bTextHasBeenSelected
= false;
823 var sLastSelectedText
= '';
826 // Initialize char and key code dictionaries.
827 var charCodeDictionary
= getDefaultCharCodeDictionary();
828 var keyCodeDictionary
= getDefaultKeyCodeDictionary();
830 // Initialize mouse handler dictionary.
831 var mouseHandlerDictionary
= getDefaultMouseHandlerDictionary();
833 /***************************
834 ** OOP support functions **
835 ***************************/
837 function object( aObject
)
839 var F = function() {};
840 F
.prototype = aObject
;
845 function extend( aSubType
, aSuperType
)
847 if (!aSuperType
|| !aSubType
)
849 alert('extend failed, verify dependencies');
851 var OP
= Object
.prototype;
852 var sp
= aSuperType
.prototype;
853 var rp
= object( sp
);
854 aSubType
.prototype = rp
;
856 rp
.constructor = aSubType
;
857 aSubType
.superclass
= sp
;
859 // assign constructor property
860 if (aSuperType
!= Object
&& sp
.constructor == OP
.constructor)
862 sp
.constructor = aSuperType
;
869 function instantiate( TemplateClass
, BaseType
)
871 if( !TemplateClass
.instanceSet
)
872 TemplateClass
.instanceSet
= new Array();
874 var nSize
= TemplateClass
.instanceSet
.length
;
876 for( var i
= 0; i
< nSize
; ++i
)
878 if( TemplateClass
.instanceSet
[i
].base
=== BaseType
)
879 return TemplateClass
.instanceSet
[i
].instance
;
882 TemplateClass
.instanceSet
[ nSize
] = new Object();
883 TemplateClass
.instanceSet
[ nSize
].base
= BaseType
;
884 TemplateClass
.instanceSet
[ nSize
].instance
= TemplateClass( BaseType
);
886 return TemplateClass
.instanceSet
[ nSize
].instance
;
890 // ------------------------------------------------------------------------------------------ //
891 /**********************************
892 ** Helper functions and classes **
893 **********************************/
895 function Rectangle( aSVGRectElem
)
897 var x
= parseInt( aSVGRectElem
.getAttribute( 'x' ) );
898 var y
= parseInt( aSVGRectElem
.getAttribute( 'y' ) );
899 var width
= parseInt( aSVGRectElem
.getAttribute( 'width' ) );
900 var height
= parseInt( aSVGRectElem
.getAttribute( 'height' ) );
903 this.right
= x
+ width
;
905 this.bottom
= y
+ height
;
908 function log( message
)
910 if( typeof console
== 'object' )
912 console
.log( message
);
914 else if( typeof opera
== 'object' )
916 opera
.postError( message
);
918 else if( typeof java
== 'object' && typeof java
.lang
== 'object' )
920 java
.lang
.System
.out
.println( message
);
924 function warning( bCondition
, sMessage
)
931 function getNSAttribute( sNSPrefix
, aElem
, sAttrName
)
933 if( !aElem
) return null;
934 if( 'getAttributeNS' in aElem
)
936 return aElem
.getAttributeNS( NSS
[sNSPrefix
], sAttrName
);
940 return aElem
.getAttribute( sNSPrefix
+ ':' + sAttrName
);
942 // if( aElem.hasAttributeNS( NSS[sNSPrefix], sAttrName ) )
944 // return aElem.getAttributeNS( NSS[sNSPrefix], sAttrName );
949 function getOOOAttribute( aElem
, sAttrName
)
951 return getNSAttribute( 'ooo', aElem
, sAttrName
);
954 function setNSAttribute( sNSPrefix
, aElem
, sAttrName
, aValue
)
956 if( !aElem
) return false;
957 if( 'setAttributeNS' in aElem
)
959 aElem
.setAttributeNS( NSS
[sNSPrefix
], sAttrName
, aValue
);
964 aElem
.setAttribute(sNSPrefix
+ ':' + sAttrName
, aValue
);
969 function setOOOAttribute( aElem
, sAttrName
, aValue
)
971 return setNSAttribute( 'ooo', aElem
, sAttrName
, aValue
);
974 function checkElemAndSetAttribute( aElem
, sAttrName
, aValue
)
977 aElem
.setAttribute( sAttrName
, aValue
);
980 function getElementsByClassName( aElem
, sClassName
)
983 var aElementSet
= new Array();
984 // not all browsers support the 'getElementsByClassName' method
985 if( 'getElementsByClassName' in aElem
)
987 aElementSet
= aElem
.getElementsByClassName( sClassName
);
991 var aElementSetByClassProperty
= getElementsByProperty( aElem
, 'class' );
992 for( var i
= 0; i
< aElementSetByClassProperty
.length
; ++i
)
994 var sAttrClassName
= aElementSetByClassProperty
[i
].getAttribute( 'class' );
995 if( sAttrClassName
== sClassName
)
997 aElementSet
.push( aElementSetByClassProperty
[i
] );
1004 function getElementByClassName( aElem
, sClassName
/*, sTagName */)
1006 var aElementSet
= getElementsByClassName( aElem
, sClassName
);
1007 if ( aElementSet
.length
== 1 )
1008 return aElementSet
[0];
1013 function getClassAttribute( aElem
)
1016 return aElem
.getAttribute( 'class' );
1020 function createElementGroup( aParentElement
, aElementList
, nFrom
, nCount
, sGroupClass
, sGroupId
)
1022 var nTo
= nFrom
+ nCount
;
1023 if( nCount
< 1 || aElementList
.length
< nTo
)
1025 log( 'createElementGroup: not enough elements available.' );
1028 var firstElement
= aElementList
[nFrom
];
1031 log( 'createElementGroup: element not found.' );
1034 var aGroupElement
= document
.createElementNS( NSS
['svg'], 'g' );
1036 aGroupElement
.setAttribute( 'id', sGroupId
);
1038 aGroupElement
.setAttribute( 'class', sGroupClass
);
1039 aParentElement
.insertBefore( aGroupElement
, firstElement
);
1041 for( ; i
< nTo
; ++i
)
1043 aParentElement
.removeChild( aElementList
[i
] );
1044 aGroupElement
.appendChild( aElementList
[i
] );
1048 function initVisibilityProperty( aElement
)
1050 var nVisibility
= VISIBLE
;
1051 var sVisibility
= aElement
.getAttribute( 'visibility' );
1052 if( sVisibility
) nVisibility
= aVisibilityValue
[ sVisibility
];
1056 function setElementVisibility( aElement
, nCurrentVisibility
, nNewVisibility
)
1058 if( nCurrentVisibility
!= nNewVisibility
)
1060 checkElemAndSetAttribute( aElement
, 'visibility', aVisibilityAttributeValue
[nNewVisibility
] );
1061 return nNewVisibility
;
1063 return nCurrentVisibility
;
1066 function getSafeIndex( nIndex
, nMin
, nMax
)
1070 else if( nIndex
> nMax
)
1076 function isTextFieldElement( aElement
)
1078 var sClassName
= aElement
.getAttribute( 'class' );
1079 return ( sClassName
=== aSlideNumberClassName
) ||
1080 ( sClassName
=== aFooterClassName
) ||
1081 ( sClassName
=== aHeaderClassName
) ||
1082 ( sClassName
=== aDateTimeClassName
);
1085 // ------------------------------------------------------------------------------------------ //
1086 /*********************
1087 ** Debug Utilities **
1088 *********************/
1090 function DebugPrinter()
1092 this.bEnabled
= false;
1096 DebugPrinter
.prototype.on = function()
1098 this.bEnabled
= true;
1101 DebugPrinter
.prototype.off = function()
1103 this.bEnabled
= false;
1106 DebugPrinter
.prototype.isEnabled = function()
1108 return this.bEnabled
;
1111 DebugPrinter
.prototype.print = function( sMessage
, nTime
)
1113 if( this.isEnabled() )
1115 var sInfo
= 'DBG: ' + sMessage
;
1117 sInfo
+= ' (at: ' + String( nTime
/ 1000 ) + 's)';
1123 // - Debug Printers -
1124 var aGenericDebugPrinter
= new DebugPrinter();
1125 aGenericDebugPrinter
.on();
1126 var DBGLOG
= bind2( DebugPrinter
.prototype.print
, aGenericDebugPrinter
);
1128 var NAVDBG
= new DebugPrinter();
1131 var ANIMDBG
= new DebugPrinter();
1134 var aRegisterEventDebugPrinter
= new DebugPrinter();
1135 aRegisterEventDebugPrinter
.off();
1137 var aTimerEventQueueDebugPrinter
= new DebugPrinter();
1138 aTimerEventQueueDebugPrinter
.off();
1140 var aEventMultiplexerDebugPrinter
= new DebugPrinter();
1141 aEventMultiplexerDebugPrinter
.off();
1143 var aNextEffectEventArrayDebugPrinter
= new DebugPrinter();
1144 aNextEffectEventArrayDebugPrinter
.off();
1146 var aActivityQueueDebugPrinter
= new DebugPrinter();
1147 aActivityQueueDebugPrinter
.off();
1149 var aAnimatedElementDebugPrinter
= new DebugPrinter();
1150 aAnimatedElementDebugPrinter
.off();
1154 // ------------------------------------------------------------------------------------------ //
1155 /************************
1156 *** Core Classes ***
1157 ************************/
1159 /** Class MetaDocument
1160 * This class provides a pool of properties related to the whole presentation.
1161 * Moreover it is responsible for:
1162 * - initializing the set of MetaSlide objects that handle the meta information
1164 * - creating a map with key an id and value the svg element containing
1165 * the animations performed on the slide with such an id.
1168 function MetaDocument()
1170 // We look for the svg element that provides the following presentation
1172 // - the number of slides in the presentation;
1173 // - the type of numbering used in the presentation.
1174 // Moreover it wraps svg elements providing meta information on each slide
1175 // and svg elements providing content and properties of each text field.
1176 var aMetaDocElem
= document
.getElementById( aOOOElemMetaSlides
);
1177 assert( aMetaDocElem
, 'MetaDocument: the svg element with id:' + aOOOElemMetaSlides
+ 'is not valid.');
1179 // We initialize general presentation properties:
1180 // - the number of slides in the presentation;
1181 this.nNumberOfSlides
= parseInt( aMetaDocElem
.getAttributeNS( NSS
['ooo'], aOOOAttrNumberOfSlides
) );
1182 assert( typeof this.nNumberOfSlides
== 'number' && this.nNumberOfSlides
> 0,
1183 'MetaDocument: number of slides is zero or undefined.' );
1184 // - the index of the slide to show when the presentation starts;
1185 this.nStartSlideNumber
= parseInt( aMetaDocElem
.getAttributeNS( NSS
['ooo'], aOOOAttrStartSlideNumber
) ) || 0;
1186 // - the numbering type used in the presentation, default type is arabic.
1187 this.sPageNumberingType
= aMetaDocElem
.getAttributeNS( NSS
['ooo'], aOOOAttrNumberingType
) || 'arabic';
1189 // The <defs> element used for wrapping <clipPath>.
1190 this.aClipPathGroup
= getElementByClassName( ROOT_NODE
, aClipPathGroupClassName
);
1191 assert( this.aClipPathGroup
, 'MetaDocument: the clip path group element is not valid.');
1193 // The <clipPath> element used to clip all slides.
1194 this.aPresentationClipPath
= document
.getElementById( aPresentationClipPathId
);
1195 assert( this.aPresentationClipPath
,
1196 'MetaDocument: the presentation clip path element element is not valid.');
1198 // The collections for handling properties of each slide, svg elements
1199 // related to master pages and content and properties of text fields.
1200 this.aMetaSlideSet
= new Array();
1201 this.aMasterPageSet
= new Object();
1202 this.aTextFieldHandlerSet
= new Object();
1203 this.aTextFieldContentProviderSet
= new Array();
1204 this.aSlideNumberProvider
= new SlideNumberProvider( this.nStartSlideNumber
+ 1, this.sPageNumberingType
);
1206 // We create a map with key an id and value the svg element containing
1207 // the animations performed on the slide with such an id.
1208 this.bIsAnimated
= false;
1209 this.aSlideAnimationsMap
= new Object();
1210 this.initSlideAnimationsMap();
1212 // We initialize the set of MetaSlide objects that handle the meta
1213 // information for each slide.
1214 for( var i
= 0; i
< this.nNumberOfSlides
; ++i
)
1216 var sMetaSlideId
= aOOOElemMetaSlide
+ '_' + i
;
1217 this.aMetaSlideSet
.push( new MetaSlide( sMetaSlideId
, this ) );
1219 assert( this.aMetaSlideSet
.length
== this.nNumberOfSlides
,
1220 'MetaDocument: aMetaSlideSet.length != nNumberOfSlides.' );
1223 MetaDocument
.prototype =
1225 /*** public methods ***/
1230 * The MetaSlide object handling the current slide.
1232 getCurrentSlide : function()
1234 return this.aMetaSlideSet
[nCurSlide
];
1239 * @param nSlideIndex
1240 * The index of the slide to show.
1242 setCurrentSlide : function( nSlideIndex
)
1244 if( nSlideIndex
>= 0 && nSlideIndex
< this.nNumberOfSlides
)
1246 if( nCurSlide
!== undefined )
1247 this.aMetaSlideSet
[nCurSlide
].hide();
1248 this.aMetaSlideSet
[nSlideIndex
].show();
1249 nCurSlide
= nSlideIndex
;
1253 log('MetaDocument.setCurrentSlide: slide index out of range: ' + nSlideIndex
);
1257 /*** private methods ***/
1259 initSlideAnimationsMap : function()
1261 var aAnimationsSection
= document
.getElementById( 'presentation-animations' );
1262 if( aAnimationsSection
)
1264 var aAnimationsDefSet
= aAnimationsSection
.getElementsByTagName( 'defs' );
1266 // we have at least one slide with animations ?
1267 this.bIsAnimated
= ( typeof aAnimationsDefSet
.length
=='number' &&
1268 aAnimationsDefSet
.length
> 0 );
1270 for( var i
= 0; i
< aAnimationsDefSet
.length
; ++i
)
1272 var sSlideId
= aAnimationsDefSet
[i
].getAttributeNS( NSS
['ooo'], aOOOAttrSlide
);
1273 var aChildSet
= getElementChildren( aAnimationsDefSet
[i
] );
1274 if( sSlideId
&& ( aChildSet
.length
=== 1 ) )
1276 this.aSlideAnimationsMap
[ sSlideId
] = aChildSet
[0];
1282 }; // end MetaDocument prototype
1285 * This class is responsible for:
1286 * - parsing and initializing slide properties;
1287 * - creating a MasterSlide object that provides direct access to the target
1288 * master slide and its sub-elements;
1289 * - initializing text field content providers;
1290 * - initializing the slide animation handler.
1292 * @param sMetaSlideId
1293 * The string representing the id attribute of the meta-slide element.
1295 * The MetaDocument global object.
1297 function MetaSlide( sMetaSlideId
, aMetaDoc
)
1299 this.theDocument
= document
;
1300 this.id
= sMetaSlideId
;
1301 this.theMetaDoc
= aMetaDoc
;
1303 // We get a reference to the meta-slide element.
1304 this.element
= this.theDocument
.getElementById( this.id
);
1305 assert( this.element
,
1306 'MetaSlide: meta_slide element <' + this.id
+ '> not found.' );
1308 // We get a reference to the slide element.
1309 this.slideId
= this.element
.getAttributeNS( NSS
['ooo'], aOOOAttrSlide
);
1310 this.slideElement
= this.theDocument
.getElementById( this.slideId
);
1311 assert( this.slideElement
,
1312 'MetaSlide: slide element <' + this.slideId
+ '> not found.' );
1313 this.nSlideNumber
= parseInt( this.slideId
.substr(2) );
1315 // Each slide element is wrapped by a <g> element that is responsible for
1316 // the slide element visibility. In fact the visibility attribute has
1317 // to be set on the parent of the slide element and not directly on
1318 // the slide element. The reason is that in index mode each slide
1319 // rendered in a thumbnail view is targeted by a <use> element, however
1320 // when the visibility attribute is set directly on the referred slide
1321 // element its visibility is not overridden by the visibility attribute
1322 // defined by the targeting <use> element. The previous solution was,
1323 // when the user switched to index mode, to set up the visibility attribute
1324 // of all slides rendered in a thumbnail to 'visible'.
1325 // Obviously the slides were not really visible because the grid of
1326 // thumbnails was above them, anyway Firefox performance was really bad.
1327 // The workaround of setting up the visibility attribute on the slide
1328 // parent element let us to make visible a slide in a <use> element
1329 // even if the slide parent element visibility is set to 'hidden'.
1330 this.aVisibilityStatusElement
= this.slideElement
.parentNode
;
1332 // We get a reference to the draw page element, where all shapes specific
1333 // of this slide live.
1334 this.pageElement
= getElementByClassName( this.slideElement
, aPageClassName
);
1335 assert( this.pageElement
,
1336 'MetaSlide: page element <' + this.slideId
+ '> not found.' );
1338 // We initialize the MasterPage object that provides direct access to
1339 // the target master page element.
1340 this.masterPage
= this.initMasterPage();
1342 // We initialize visibility properties of the target master page elements.
1343 this.nAreMasterObjectsVisible
= this.initVisibilityProperty( aOOOAttrMasterObjectsVisibility
, VISIBLE
);
1344 this.nIsBackgroundVisible
= this.initVisibilityProperty( aOOOAttrBackgroundVisibility
, VISIBLE
);
1345 this.nIsPageNumberVisible
= this.initVisibilityProperty( aOOOAttrPageNumberVisibility
, HIDDEN
);
1346 this.nIsDateTimeVisible
= this.initVisibilityProperty( aOOOAttrDateTimeVisibility
, VISIBLE
);
1347 this.nIsFooterVisible
= this.initVisibilityProperty( aOOOAttrFooterVisibility
, VISIBLE
);
1348 this.nIsHeaderVisible
= this.initVisibilityProperty( aOOOAttrHeaderVisibility
, VISIBLE
);
1350 // This property tell us if the date/time field need to be updated
1351 // each time the slide is shown. It is initialized in
1352 // the initDateTimeFieldContentProvider method.
1353 this.bIsDateTimeVariable
= undefined;
1355 // We initialize the objects responsible to provide the content to text field.
1356 this.aTextFieldContentProviderSet
= new Object();
1357 this.aTextFieldContentProviderSet
[aSlideNumberClassName
] = this.initSlideNumberFieldContentProvider();
1358 this.aTextFieldContentProviderSet
[aDateTimeClassName
] = this.initDateTimeFieldContentProvider( aOOOAttrDateTimeField
);
1359 this.aTextFieldContentProviderSet
[aFooterClassName
] = this.initFixedTextFieldContentProvider( aOOOAttrFooterField
);
1360 this.aTextFieldContentProviderSet
[aHeaderClassName
] = this.initFixedTextFieldContentProvider( aOOOAttrHeaderField
);
1362 // We look for slide transition.
1363 this.aTransitionHandler
= null;
1364 this.bHasTransition
= this.initHasTransition() || true;
1365 if( this.bHasTransition
)
1367 this.aTransitionHandler
= new SlideTransition( this.getSlideAnimationsRoot(), this.slideId
);
1368 //if( this.aTransitionHandler.isValid() )
1369 // log( this.aTransitionHandler.info() );
1372 // We initialize the SlideAnimationsHandler object
1373 this.aSlideAnimationsHandler
= new SlideAnimations( aSlideShow
.getContext() );
1374 this.aSlideAnimationsHandler
.importAnimations( this.getSlideAnimationsRoot() );
1375 this.aSlideAnimationsHandler
.parseElements();
1377 // this statement is used only for debugging
1378 if( false && this.aSlideAnimationsHandler
.aRootNode
)
1379 log( this.aSlideAnimationsHandler
.aRootNode
.info( true ) );
1381 // We collect text shapes included in this slide .
1382 this.aTextShapeSet
= this.collectTextShapes();
1384 // We initialize hyperlinks
1385 this.aHyperlinkSet
= this.initHyperlinks();
1389 MetaSlide
.prototype =
1391 /*** public methods ***/
1394 * Set the visibility property of the slide to 'inherit'
1395 * and update the master page view.
1399 this.updateMasterPageView();
1400 this.aVisibilityStatusElement
.setAttribute( 'visibility', 'inherit' );
1404 * Set the visibility property of the slide to 'hidden'.
1408 this.aVisibilityStatusElement
.setAttribute( 'visibility', 'hidden' );
1411 /** updateMasterPageView
1412 * On first call it creates a master page view element and insert it at
1413 * the begin of the slide element. Moreover it updates the text fields
1414 * included in the master page view.
1416 updateMasterPageView : function()
1418 // The master page view element is generated and attached on first time
1419 // the slide is shown.
1420 if( !this.aMasterPageView
)
1422 this.aMasterPageView
= new MasterPageView( this );
1423 this.aMasterPageView
.attachToSlide();
1425 this.aMasterPageView
.update();
1428 /*** private methods ***/
1429 initMasterPage : function()
1431 var sMasterPageId
= this.element
.getAttributeNS( NSS
['ooo'], aOOOAttrMaster
);
1433 // Check that the master page handler object has not already been
1434 // created by an other slide that target the same master page.
1435 if( !this.theMetaDoc
.aMasterPageSet
.hasOwnProperty( sMasterPageId
) )
1437 this.theMetaDoc
.aMasterPageSet
[ sMasterPageId
] = new MasterPage( sMasterPageId
);
1439 // We initialize aTextFieldHandlerSet[ sMasterPageId ] to an empty
1441 this.theMetaDoc
.aTextFieldHandlerSet
[ sMasterPageId
] = new Object();
1443 return this.theMetaDoc
.aMasterPageSet
[ sMasterPageId
];
1446 initHasTransition : function()
1448 var sHasTransition
= this.element
.getAttributeNS( NSS
['ooo'], aOOOAttrHasTransition
);
1449 return ( sHasTransition
=== 'true' );
1452 initVisibilityProperty : function( aVisibilityAttribute
, nDefaultValue
)
1454 var nVisibility
= nDefaultValue
;
1455 var sVisibility
= getOOOAttribute( this.element
, aVisibilityAttribute
);
1457 nVisibility
= aVisibilityValue
[ sVisibility
];
1461 initSlideNumberFieldContentProvider : function()
1463 return this.theMetaDoc
.aSlideNumberProvider
;
1466 initDateTimeFieldContentProvider : function( aOOOAttrDateTimeField
)
1468 var sTextFieldId
= getOOOAttribute( this.element
, aOOOAttrDateTimeField
);
1469 if( !sTextFieldId
) return null;
1471 var nLength
= aOOOElemTextField
.length
+ 1;
1472 var nIndex
= parseInt(sTextFieldId
.substring( nLength
) );
1473 if( typeof nIndex
!= 'number') return null;
1475 if( !this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
] )
1478 var aTextFieldElem
= document
.getElementById( sTextFieldId
);
1479 var sClassName
= getClassAttribute( aTextFieldElem
);
1480 if( sClassName
== 'FixedDateTimeField' )
1482 aTextField
= new FixedTextProvider( aTextFieldElem
);
1483 this.bIsDateTimeVariable
= false;
1485 else if( sClassName
== 'VariableDateTimeField' )
1487 aTextField
= new CurrentDateTimeProvider( aTextFieldElem
);
1488 this.bIsDateTimeVariable
= true;
1494 this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
] = aTextField
;
1496 return this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
];
1499 initFixedTextFieldContentProvider : function( aOOOAttribute
)
1501 var sTextFieldId
= getOOOAttribute( this.element
, aOOOAttribute
);
1502 if( !sTextFieldId
) return null;
1504 var nLength
= aOOOElemTextField
.length
+ 1;
1505 var nIndex
= parseInt( sTextFieldId
.substring( nLength
) );
1506 if( typeof nIndex
!= 'number') return null;
1508 if( !this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
] )
1510 var aTextFieldElem
= document
.getElementById( sTextFieldId
);
1511 this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
]
1512 = new FixedTextProvider( aTextFieldElem
);
1514 return this.theMetaDoc
.aTextFieldContentProviderSet
[ nIndex
];
1517 collectTextShapes : function()
1519 var aTextShapeSet
= new Array();
1520 var aTextShapeIndexElem
= getElementByClassName( document
, 'TextShapeIndex' );
1521 if( aTextShapeIndexElem
)
1523 var aIndexEntryList
= getElementChildren( aTextShapeIndexElem
);
1525 for( i
= 0; i
< aIndexEntryList
.length
; ++i
)
1527 var sSlideId
= getOOOAttribute( aIndexEntryList
[i
], 'slide' );
1528 if( sSlideId
=== this.slideId
)
1530 var sTextShapeIds
= getOOOAttribute( aIndexEntryList
[i
], 'id-list' );
1533 //log( 'slide id: ' + this.slideId + ' text shape id list: ' + sTextShapeIds );
1534 var aTextShapeIdSet
= sTextShapeIds
.split( ' ' );
1536 for( j
= 0; j
< aTextShapeIdSet
.length
; ++j
)
1538 var aTextShapeElem
= document
.getElementById( aTextShapeIdSet
[j
] );
1539 if( aTextShapeElem
)
1541 aTextShapeSet
.push( aTextShapeElem
);
1545 log( 'warning: MetaSlide.collectTextShapes: text shape with id <' + aTextShapeIdSet
[j
] + '> is not valid.' );
1553 return aTextShapeSet
;
1556 initHyperlinks : function()
1558 var aHyperlinkSet
= new Object();
1560 for( i
= 0; i
< this.aTextShapeSet
.length
; ++i
)
1562 if( this.aTextShapeSet
[i
] )
1564 var aHyperlinkIdList
= getElementByClassName( this.aTextShapeSet
[i
], 'HyperlinkIdList' );
1565 if( aHyperlinkIdList
)
1567 var sHyperlinkIds
= aHyperlinkIdList
.textContent
;
1570 var aHyperlinkIdSet
= sHyperlinkIds
.trim().split( ' ' );
1572 for( j
= 0; j
< aHyperlinkIdSet
.length
; ++j
)
1574 var sId
= aHyperlinkIdSet
[j
];
1575 //log( 'initHyperlinks: j=' + j + ' id: <' + sId + '>' );
1576 aHyperlinkSet
[ sId
] = new HyperlinkElement( sId
, this.aSlideAnimationsHandler
.aEventMultiplexer
);
1582 return aHyperlinkSet
;
1585 getSlideAnimationsRoot : function()
1587 return this.theMetaDoc
.aSlideAnimationsMap
[ this.slideId
];
1590 }; // end MetaSlide prototype
1592 /** Class MasterPage
1593 * This class gives direct access to a master page element and to the following
1594 * elements included in the master page:
1595 * - the background element,
1596 * - the background objects group element,
1597 * Moreover for each text field element a Placeholder object is created which
1598 * manages the text field element itself.
1600 * The master page element structure is the following:
1601 * <g class='Master_Slide'>
1602 * <g class='Background'>
1605 * <g class='BackgroundObjects'>
1606 * <g class='Date/Time'>
1607 * date/time placeholder
1609 * <g class='Header'>
1610 * header placeholder
1612 * <g class='Footer'>
1613 * footer placeholder
1615 * <g class='Slide_Number'>
1616 * slide number placeholder
1622 * @param sMasterPageId
1623 * A string representing the value of the id attribute of the master page
1624 * element to be handled.
1626 function MasterPage( sMasterPageId
)
1628 this.id
= sMasterPageId
;
1630 // The master page element to be handled.
1631 this.element
= document
.getElementById( this.id
);
1632 assert( this.element
,
1633 'MasterPage: master page element <' + this.id
+ '> not found.' );
1635 // The master page background element and its id attribute.
1636 this.background
= getElementByClassName( this.element
, 'Background' );
1637 if( this.background
)
1639 this.backgroundId
= this.background
.getAttribute( 'id' );
1640 this.backgroundVisibility
= initVisibilityProperty( this.background
);
1644 this.backgroundId
= '';
1645 log( 'MasterPage: the background element is not valid.' );
1648 // The background objects group element that contains every element presents
1649 // on the master page except the background element.
1650 this.backgroundObjects
= getElementByClassName( this.element
, 'BackgroundObjects' );
1651 if( this.backgroundObjects
)
1653 this.backgroundObjectsId
= this.backgroundObjects
.getAttribute( 'id' );
1654 this.backgroundObjectsVisibility
= initVisibilityProperty( this.backgroundObjects
);
1656 if( this.backgroundObjectsVisibility
!= HIDDEN
)
1658 var aBackgroundObjectList
= getElementChildren( this.backgroundObjects
);
1661 var nSubGroupId
= 1;
1664 this.aBackgroundObjectSubGroupIdList
= new Array();
1666 for( ; i
< aBackgroundObjectList
.length
; ++i
)
1668 sClass
= aBackgroundObjectList
[i
].getAttribute( 'class' );
1669 if( !sClass
|| ( ( sClass
!== aDateTimeClassName
) && ( sClass
!== aFooterClassName
)
1670 && ( sClass
!== aHeaderClassName
) && ( sClass
!== aSlideNumberClassName
) ) )
1675 sId
= this.backgroundObjectsId
+ '.' + nSubGroupId
;
1677 this.aBackgroundObjectSubGroupIdList
.push( sId
);
1683 this.aBackgroundObjectSubGroupIdList
.push( sClass
);
1686 createElementGroup( this.backgroundObjects
, aBackgroundObjectList
, nFrom
, nCount
, 'BackgroundObjectSubgroup', sId
);
1693 createElementGroup( this.backgroundObjects
, aBackgroundObjectList
, nFrom
, nCount
, 'BackgroundObjectSubgroup', sId
);
1699 this.backgroundObjectsId
= '';
1700 log( 'MasterPage: the background objects element is not valid.' );
1703 // We populate the collection of placeholders.
1704 this.aPlaceholderShapeSet
= new Object();
1705 this.initPlaceholderShapes();
1708 MasterPage
.prototype =
1710 /*** private methods ***/
1712 initPlaceholderShapes : function()
1714 this.aPlaceholderShapeSet
[ aSlideNumberClassName
] = new PlaceholderShape( this, aSlideNumberClassName
);
1715 this.aPlaceholderShapeSet
[ aDateTimeClassName
] = new PlaceholderShape( this, aDateTimeClassName
);
1716 this.aPlaceholderShapeSet
[ aFooterClassName
] = new PlaceholderShape( this, aFooterClassName
);
1717 this.aPlaceholderShapeSet
[ aHeaderClassName
] = new PlaceholderShape( this, aHeaderClassName
);
1720 }; // end MasterPage prototype
1722 /** Class PlaceholderShape
1723 * This class provides direct access to a text field element and
1724 * to the embedded placeholder element.
1725 * Moreover it set up the text adjustment and position for the placeholder
1727 * Note: the text field element included in a master page is used only as
1728 * a template element, it is cloned for each specific text content
1729 * (see the TextFieldContentProvider class and its derived classes).
1731 * @param aMasterPage
1732 * The master page object to which the text field to be handled belongs.
1734 * A string representing the value of the class attribute of the text
1735 * field element to be handled.
1737 function PlaceholderShape( aMasterPage
, sClassName
)
1739 this.masterPage
= aMasterPage
;
1740 this.className
= sClassName
;
1742 this.element
= null;
1743 this.textElement
= null;
1747 /* public methods */
1748 PlaceholderShape
.prototype.isValid = function()
1750 return ( this.element
&& this.textElement
);
1753 /* private methods */
1756 * In case a text field element of class type 'className' exists and such
1757 * an element embeds a placeholder element, the text adjustment and position
1758 * of the placeholder element is set up.
1760 PlaceholderShape
.prototype.init = function()
1763 var aTextFieldElement
= getElementByClassName( this.masterPage
.backgroundObjects
, this.className
);
1764 if( aTextFieldElement
)
1766 var aPlaceholderElement
= getElementByClassName( aTextFieldElement
, 'PlaceholderText' );
1767 if( aPlaceholderElement
)
1769 // Each text field element has an invisible rectangle that can be
1770 // regarded as the text field bounding box.
1771 // We exploit such a feature and the exported text adjust attribute
1772 // value in order to set up correctly the position and text
1773 // adjustment for the placeholder element.
1774 var aSVGRectElemSet
= aTextFieldElement
.getElementsByTagName( 'rect' );
1775 // As far as text field element exporting is implemented it should
1776 // be only one <rect> element!
1777 if( aSVGRectElemSet
.length
=== 1)
1779 var aRect
= new Rectangle( aSVGRectElemSet
[0] );
1780 var sTextAdjust
= getOOOAttribute( aTextFieldElement
, aOOOAttrTextAdjust
) || 'left';
1781 var sTextAnchor
, sX
;
1782 if( sTextAdjust
== 'left' )
1784 sTextAnchor
= 'start';
1785 sX
= String( aRect
.left
);
1787 else if( sTextAdjust
== 'right' )
1789 sTextAnchor
= 'end';
1790 sX
= String( aRect
.right
);
1792 else if( sTextAdjust
== 'center' )
1794 sTextAnchor
= 'middle';
1795 var nMiddle
= ( aRect
.left
+ aRect
.right
) / 2;
1796 sX
= String( parseInt( String( nMiddle
) ) );
1799 aPlaceholderElement
.setAttribute( 'text-anchor', sTextAnchor
);
1801 aPlaceholderElement
.setAttribute( 'x', sX
);
1804 this.element
= aTextFieldElement
;
1805 this.textElement
= aPlaceholderElement
;
1807 // We remove all text lines but the first one used as placeholder.
1808 var aTextLineGroupElem
= this.textElement
.parentNode
.parentNode
;
1809 if( aTextLineGroupElem
)
1811 // Just to be sure it is the element we are looking for.
1812 var sFontFamilyAttr
= aTextLineGroupElem
.getAttribute( 'font-family' );
1813 if( sFontFamilyAttr
)
1815 var aChildSet
= getElementChildren( aTextLineGroupElem
);
1816 if( aChildSet
.length
> 1 )
1818 for( ; i
< aChildSet
.length
; ++i
)
1820 aTextLineGroupElem
.removeChild( aChildSet
[i
] );
1828 /** Class MasterPageView
1829 * This class is used to creates a svg element of class MasterPageView and its
1831 * It is also responsible for updating the content of the included text fields.
1833 * MasterPageView element structure:
1835 * <g class='MasterPageView'>
1836 * <use class='Background'> // reference to master page background element
1837 * <g class='BackgroundObjects'>
1838 * <use class='BackgroundObjectSubGroup'> // reference to the group of shapes on the master page that are below text fields
1839 * <g class='Slide_Number'> // a cloned element
1842 * <use class='Date/Time'> // reference to a clone
1843 * <use class='Footer'>
1844 * <use class='Header'>
1845 * <use class='BackgroundObjectSubGroup'> // reference to the group of shapes on the master page that are above text fields
1849 * Sub-elements are present only if they are visible.
1852 * The MetaSlide object managing the slide element that targets
1853 * the master page view element created by an instance of MasterPageView.
1855 function MasterPageView( aMetaSlide
)
1857 this.aMetaSlide
= aMetaSlide
;
1858 this.aSlideElement
= aMetaSlide
.slideElement
;
1859 this.aPageElement
= aMetaSlide
.pageElement
;
1860 this.aMasterPage
= aMetaSlide
.masterPage
;
1861 this.aMPVElement
= this.createElement();
1862 this.bIsAttached
= false;
1865 /*** public methods ***/
1868 * Prepend the master slide view element to the slide element.
1870 MasterPageView
.prototype.attachToSlide = function()
1872 if( !this.bIsAttached
)
1874 var aInsertedElement
= this.aSlideElement
.insertBefore( this.aMPVElement
, this.aPageElement
);
1875 assert( aInsertedElement
=== this.aMPVElement
,
1876 'MasterPageView.attachToSlide: aInsertedElement != this.aMPVElement' );
1878 this.bIsAttached
= true;
1883 * Remove the master slide view element from the slide element.
1885 MasterPageView
.prototype.detachFromSlide = function()
1887 if( this.bIsAttached
)
1889 this.aSlideElement
.removeChild( this.aMPVElement
);
1890 this.bIsAttached
= false;
1895 * Update the content of text fields placed on the master page.
1897 MasterPageView
.prototype.update = function()
1899 if( this.aDateTimeFieldHandler
&& this.aMetaSlide
.bIsDateTimeVariable
)
1900 this.aDateTimeFieldHandler
.update();
1903 /*** private methods ***/
1905 MasterPageView
.prototype.createElement = function()
1907 var theDocument
= document
;
1908 var aMasterPageViewElement
= theDocument
.createElementNS( NSS
['svg'], 'g' );
1909 assert( aMasterPageViewElement
,
1910 'MasterPageView.createElement: failed to create a master page view element.' );
1911 aMasterPageViewElement
.setAttribute( 'class', 'MasterPageView' );
1913 // we place a white rect below any else element
1914 // that is also a workaround for some kind of slide transition
1915 // when the master page is empty
1916 var aWhiteRect
= theDocument
.createElementNS( NSS
['svg'], 'rect' );
1917 aWhiteRect
.setAttribute( 'width', String( WIDTH
) );
1918 aWhiteRect
.setAttribute( 'height', String( HEIGHT
) );
1919 aWhiteRect
.setAttribute( 'fill', '#FFFFFF' );
1920 aMasterPageViewElement
.appendChild( aWhiteRect
);
1922 // init the Background element
1923 if( this.aMetaSlide
.nIsBackgroundVisible
)
1925 this.aBackgroundElement
= theDocument
.createElementNS( NSS
['svg'], 'use' );
1926 this.aBackgroundElement
.setAttribute( 'class', 'Background' );
1927 setNSAttribute( 'xlink', this.aBackgroundElement
,
1928 'href', '#' + this.aMasterPage
.backgroundId
);
1931 aMasterPageViewElement
.appendChild( this.aBackgroundElement
);
1934 // init the BackgroundObjects element
1935 if( this.aMetaSlide
.nAreMasterObjectsVisible
)
1937 this.aBackgroundObjectsElement
= theDocument
.createElementNS( NSS
['svg'], 'g' );
1938 this.aBackgroundObjectsElement
.setAttribute( 'class', 'BackgroundObjects' );
1940 // clone and initialize text field elements
1941 var aBackgroundObjectSubGroupIdList
= this.aMasterPage
.aBackgroundObjectSubGroupIdList
;
1942 this.aBackgroundSubGroupElementSet
= new Array();
1943 var aPlaceholderShapeSet
= this.aMasterPage
.aPlaceholderShapeSet
;
1944 var aTextFieldContentProviderSet
= this.aMetaSlide
.aTextFieldContentProviderSet
;
1945 // where cloned elements are appended
1946 var aDefsElement
= this.aMetaSlide
.element
.parentNode
;
1947 var aTextFieldHandlerSet
= this.aMetaSlide
.theMetaDoc
.aTextFieldHandlerSet
;
1948 var sMasterSlideId
= this.aMasterPage
.id
;
1952 for( ; i
< aBackgroundObjectSubGroupIdList
.length
; ++i
)
1954 sId
= aBackgroundObjectSubGroupIdList
[i
];
1955 if( sId
=== aSlideNumberClassName
)
1957 // Slide Number Field
1958 // The cloned element is appended directly to the field group element
1959 // since there is no slide number field content shared between two slide
1960 // (because the slide number of two slide is always different).
1961 if( aPlaceholderShapeSet
[aSlideNumberClassName
] &&
1962 aPlaceholderShapeSet
[aSlideNumberClassName
].isValid() &&
1963 this.aMetaSlide
.nIsPageNumberVisible
&&
1964 aTextFieldContentProviderSet
[aSlideNumberClassName
] )
1966 this.aSlideNumberFieldHandler
=
1967 new SlideNumberFieldHandler( aPlaceholderShapeSet
[aSlideNumberClassName
],
1968 aTextFieldContentProviderSet
[aSlideNumberClassName
] );
1969 this.aSlideNumberFieldHandler
.update( this.aMetaSlide
.nSlideNumber
);
1970 this.aSlideNumberFieldHandler
.appendTo( this.aBackgroundObjectsElement
);
1973 else if( sId
=== aDateTimeClassName
)
1976 if( this.aMetaSlide
.nIsDateTimeVisible
)
1978 this.aDateTimeFieldHandler
=
1979 this.initTextFieldHandler( aDateTimeClassName
, aPlaceholderShapeSet
,
1980 aTextFieldContentProviderSet
, aDefsElement
,
1981 aTextFieldHandlerSet
, sMasterSlideId
);
1984 else if( sId
=== aFooterClassName
)
1987 if( this.aMetaSlide
.nIsFooterVisible
)
1989 this.aFooterFieldHandler
=
1990 this.initTextFieldHandler( aFooterClassName
, aPlaceholderShapeSet
,
1991 aTextFieldContentProviderSet
, aDefsElement
,
1992 aTextFieldHandlerSet
, sMasterSlideId
);
1995 else if( sId
=== aHeaderClassName
)
1998 if( this.aMetaSlide
.nIsHeaderVisible
)
2000 this.aHeaderFieldHandler
=
2001 this.initTextFieldHandler( aHeaderClassName
, aPlaceholderShapeSet
,
2002 aTextFieldContentProviderSet
, aDefsElement
,
2003 aTextFieldHandlerSet
, sMasterSlideId
);
2008 // init BackgroundObjectSubGroup elements
2009 var aBackgroundSubGroupElement
= theDocument
.createElementNS( NSS
['svg'], 'use' );
2010 aBackgroundSubGroupElement
.setAttribute( 'class', 'BackgroundObjectSubGroup' );
2011 setNSAttribute( 'xlink', aBackgroundSubGroupElement
,
2012 'href', '#' + sId
);
2013 this.aBackgroundSubGroupElementSet
.push( aBackgroundSubGroupElement
);
2015 this.aBackgroundObjectsElement
.appendChild( aBackgroundSubGroupElement
);
2020 aMasterPageViewElement
.appendChild( this.aBackgroundObjectsElement
);
2023 return aMasterPageViewElement
;
2026 MasterPageView
.prototype.initTextFieldHandler
=
2027 function( sClassName
, aPlaceholderShapeSet
, aTextFieldContentProviderSet
,
2028 aDefsElement
, aTextFieldHandlerSet
, sMasterSlideId
)
2030 var aTextFieldHandler
= null;
2031 if( aPlaceholderShapeSet
[sClassName
] &&
2032 aPlaceholderShapeSet
[sClassName
].isValid()
2033 && aTextFieldContentProviderSet
[sClassName
] )
2035 var sTextFieldContentProviderId
= aTextFieldContentProviderSet
[sClassName
].sId
;
2036 // We create only one single TextFieldHandler object (and so one only
2037 // text field clone) per master slide and text content.
2038 if ( !aTextFieldHandlerSet
[ sMasterSlideId
][ sTextFieldContentProviderId
] )
2040 aTextFieldHandlerSet
[ sMasterSlideId
][ sTextFieldContentProviderId
] =
2041 new TextFieldHandler( aPlaceholderShapeSet
[sClassName
],
2042 aTextFieldContentProviderSet
[sClassName
] );
2043 aTextFieldHandler
= aTextFieldHandlerSet
[ sMasterSlideId
][ sTextFieldContentProviderId
];
2044 aTextFieldHandler
.update();
2045 aTextFieldHandler
.appendTo( aDefsElement
);
2049 aTextFieldHandler
= aTextFieldHandlerSet
[ sMasterSlideId
][ sTextFieldContentProviderId
];
2052 // We create a <use> element referring to the cloned text field and
2053 // append it to the field group element.
2054 var aTextFieldElement
= document
.createElementNS( NSS
['svg'], 'use' );
2055 aTextFieldElement
.setAttribute( 'class', sClassName
);
2056 setNSAttribute( 'xlink', aTextFieldElement
,
2057 'href', '#' + aTextFieldHandler
.sId
);
2059 this.aBackgroundObjectsElement
.appendChild( aTextFieldElement
);
2061 return aTextFieldHandler
;
2064 /** Class TextFieldHandler
2065 * This class clone a text field field of a master page and set up
2066 * the content of the cloned element on demand.
2068 * @param aPlaceholderShape
2069 * A PlaceholderShape object that provides the text field to be cloned.
2070 * @param aTextContentProvider
2071 * A TextContentProvider object to which the actual content updating is
2074 function TextFieldHandler( aPlaceholderShape
, aTextContentProvider
)
2076 this.aPlaceHolderShape
= aPlaceholderShape
;
2077 this.aTextContentProvider
= aTextContentProvider
;
2078 assert( this.aTextContentProvider
,
2079 'TextFieldHandler: text content provider not defined.' );
2080 this.sId
= 'tf' + String( TextFieldHandler
.getUniqueId() );
2081 // The cloned text field element to be handled.
2082 this.aTextFieldElement
= null;
2083 // The actual <text> element where the field content has to be placed.
2084 this.aTextPlaceholderElement
= null;
2085 this.cloneElement();
2088 /*** private methods ***/
2090 TextFieldHandler
.CURR_UNIQUE_ID
= 0;
2092 TextFieldHandler
.getUniqueId = function()
2094 ++TextFieldHandler
.CURR_UNIQUE_ID
;
2095 return TextFieldHandler
.CURR_UNIQUE_ID
;
2098 TextFieldHandler
.prototype.cloneElement = function()
2100 assert( this.aPlaceHolderShape
&& this.aPlaceHolderShape
.isValid(),
2101 'TextFieldHandler.cloneElement: placeholder shape is not valid.' );
2102 // The cloned text field element.
2103 this.aTextFieldElement
= this.aPlaceHolderShape
.element
.cloneNode( true /* deep clone */ );
2104 assert( this.aTextFieldElement
,
2105 'TextFieldHandler.cloneElement: aTextFieldElement is not defined' );
2106 this.aTextFieldElement
.setAttribute( 'id', this.sId
);
2107 // Text field placeholder visibility is always set to 'hidden'.
2108 this.aTextFieldElement
.removeAttribute( 'visibility' );
2109 // The actual <text> element where the field content has to be placed.
2110 this.aTextPlaceholderElement
= getElementByClassName( this.aTextFieldElement
, 'PlaceholderText' );
2111 assert( this.aTextPlaceholderElement
,
2112 'TextFieldHandler.cloneElement: aTextPlaceholderElement is not defined' );
2115 /*** public methods ***/
2118 * Append the cloned text field element to a svg element.
2120 * @param aParentNode
2121 * The svg element to which the cloned text field has to be appended.
2123 TextFieldHandler
.prototype.appendTo = function( aParentNode
)
2125 if( !this.aTextFieldElement
)
2127 log( 'TextFieldHandler.appendTo: aTextFieldElement is not defined' );
2132 log( 'TextFieldHandler.appendTo: parent node is not defined' );
2136 aParentNode
.appendChild( this.aTextFieldElement
);
2140 * Modify the content of the cloned text field.
2143 * A string representing the new content of the cloned text field.
2145 TextFieldHandler
.prototype.setTextContent = function( sText
)
2147 if( !this.aTextPlaceholderElement
)
2149 log( 'PlaceholderShape.setTextContent: text element is not valid in placeholder of type '
2150 + this.className
+ ' that belongs to master slide ' + this.masterPage
.id
);
2153 this.aTextPlaceholderElement
.textContent
= sText
;
2157 * Update the content of the handled text field. The new content is provided
2158 * directly from the TextContentProvider data member.
2160 TextFieldHandler
.prototype.update = function()
2162 if( !this.aTextContentProvider
)
2163 log('TextFieldHandler.update: text content provider not defined.');
2165 this.aTextContentProvider
.update( this );
2168 /** SlideNumberFieldHandler
2169 * This class clone the slide number field of a master page and set up
2170 * the content of the cloned element on demand.
2172 * @param aPlaceholderShape
2173 * A PlaceholderShape object that provides the slide number field
2175 * @param aTextContentProvider
2176 * A SlideNumberProvider object to which the actual content updating is
2179 function SlideNumberFieldHandler( aPlaceholderShape
, aTextContentProvider
)
2181 SlideNumberFieldHandler
.superclass
.constructor.call( this, aPlaceholderShape
, aTextContentProvider
);
2183 extend( SlideNumberFieldHandler
, TextFieldHandler
);
2185 /*** public methods ***/
2188 * Update the content of the handled slide number field with the passed number.
2190 * @param nPageNumber
2191 * The number representing the new content of the slide number field.
2193 SlideNumberFieldHandler
.prototype.update = function( nPageNumber
)
2195 // The actual content updating is demanded to the related
2196 // SlideNumberProvider instance that have the needed info on
2197 // the numbering type.
2198 if( !this.aTextContentProvider
)
2199 log('TextFieldHandler.update: text content provider not defined.');
2201 this.aTextContentProvider
.update( this, nPageNumber
);
2204 // ------------------------------------------------------------------------------------------ //
2205 /******************************************************************************
2206 * Text Field Content Provider Class Hierarchy
2208 * The following classes are responsible to format and set the text content
2211 ******************************************************************************/
2213 /** Class TextFieldContentProvider
2214 * This class is the root abstract class of the hierarchy.
2216 * @param aTextFieldContentElement
2217 * The svg element that contains the text content for one or more
2218 * master slide text field.
2220 function TextFieldContentProvider( aTextFieldContentElement
)
2222 // This id is used as key for the theMetaDoc.aTextFieldHandlerSet object.
2223 if( aTextFieldContentElement
)
2224 this.sId
= aTextFieldContentElement
.getAttribute( 'id' );
2227 /** Class FixedTextProvider
2228 * This class handles text field with a fixed text.
2229 * The text content is provided by the 'text' property.
2231 * @param aTextFieldContentElement
2232 * The svg element that contains the text content for one or more
2233 * master slide text field.
2235 function FixedTextProvider( aTextFieldContentElement
)
2237 FixedTextProvider
.superclass
.constructor.call( this, aTextFieldContentElement
);
2238 this.text
= aTextFieldContentElement
.textContent
;
2240 extend( FixedTextProvider
, TextFieldContentProvider
);
2242 /*** public methods ***/
2245 * Set up the content of a fixed text field.
2247 * @param aFixedTextField
2248 * An object that implement a setTextContent( String ) method in order
2249 * to set the content of a given text field.
2251 FixedTextProvider
.prototype.update = function( aFixedTextField
)
2253 aFixedTextField
.setTextContent( this.text
);
2256 /** Class CurrentDateTimeProvider
2257 * Provide the text content to a date/time field by generating the current
2258 * date/time in the format specified by the 'dateTimeFormat' property.
2260 * @param aTextFieldContentElement
2261 * The svg element that contains the date/time format for one or more
2262 * master slide date/time field.
2264 function CurrentDateTimeProvider( aTextFieldContentElement
)
2266 CurrentDateTimeProvider
.superclass
.constructor.call( this, aTextFieldContentElement
);
2267 this.dateTimeFormat
= getOOOAttribute( aTextFieldContentElement
, aOOOAttrDateTimeFormat
);
2269 extend( CurrentDateTimeProvider
, TextFieldContentProvider
);
2271 /*** public methods ***/
2274 * Set up the content of a variable date/time field.
2276 * @param aDateTimeField
2277 * An object that implement a setTextContent( String ) method in order
2278 * to set the content of a given text field.
2280 CurrentDateTimeProvider
.prototype.update = function( aDateTimeField
)
2282 var sText
= this.createDateTimeText( this.dateTimeFormat
);
2283 aDateTimeField
.setTextContent( sText
);
2286 /*** private methods ***/
2288 CurrentDateTimeProvider
.prototype.createDateTimeText = function( sDateTimeFormat
)
2290 // TODO handle date/time format
2291 var aDate
= new Date();
2292 var sDate
= aDate
.toLocaleString();
2296 /** Class SlideNumberProvider
2297 * Provides the text content to the related text field by generating
2298 * the current page number in the given page numbering type.
2300 function SlideNumberProvider( nInitialSlideNumber
, sPageNumberingType
)
2302 SlideNumberProvider
.superclass
.constructor.call( this, null );
2303 this.nInitialSlideNumber
= nInitialSlideNumber
;
2304 this.pageNumberingType
= sPageNumberingType
;
2307 extend( SlideNumberProvider
, TextFieldContentProvider
);
2309 /*** public methods ***/
2311 /** getNumberingType
2314 * The page numbering type.
2316 SlideNumberProvider
.prototype.getNumberingType = function()
2318 return this.pageNumberingType
;
2322 * Set up the content of a slide number field.
2324 * @param aSlideNumberField
2325 * An object that implement a setTextContent( String ) method in order
2326 * to set the content of a given text field.
2327 * @param nSlideNumber
2328 * An integer representing the slide number.
2331 SlideNumberProvider
.prototype.update = function( aSlideNumberField
, nSlideNumber
)
2333 if( nSlideNumber
=== undefined )
2335 if( nCurSlide
=== undefined )
2336 nSlideNumber
= this.nInitialSlideNumber
;
2338 nSlideNumber
= nCurSlide
+ 1;
2340 var sText
= this.createSlideNumberText( nSlideNumber
, this.getNumberingType() );
2341 aSlideNumberField
.setTextContent( sText
);
2344 /*** private methods ***/
2346 SlideNumberProvider
.prototype.createSlideNumberText = function( nSlideNumber
, sNumberingType
)
2348 // TODO handle page numbering type
2349 return String( nSlideNumber
);
2354 //------------------------------------------------------------------------------------------- //
2355 /********************************
2356 ** Slide Index Classes **
2357 ********************************/
2359 /** Class SlideIndexPage **
2360 * This class is responsible for handling the slide index page
2362 function SlideIndexPage()
2364 this.pageElementId
= 'slide_index';
2365 this.pageBgColor
= 'rgb(252,252,252)';
2366 this.pageElement
= this.createPageElement();
2367 assert( this.pageElement
, 'SlideIndexPage: pageElement is not valid' );
2368 this.indexColumns
= INDEX_COLUMNS_DEFAULT
;
2369 this.totalThumbnails
= this.indexColumns
* this.indexColumns
;
2370 this.selectedSlideIndex
= undefined;
2372 // set up layout parameters
2373 this.xSpacingFactor
= 600/28000;
2374 this.ySpacingFactor
= 450/21000;
2375 this.xSpacing
= WIDTH
* this.xSpacingFactor
;
2376 this.ySpacing
= HEIGHT
* this.ySpacingFactor
;
2377 this.halfBorderWidthFactor
= ( 300/28000 ) * ( this.indexColumns / 3 );
2378 this.halfBorderWidth
= WIDTH
* this.halfBorderWidthFactor
;
2379 this.borderWidth
= 2 * this.halfBorderWidth
;
2380 // the following formula is used to compute the slide shrinking factor:
2381 // scaleFactor = ( WIDTH - ( columns + 1 ) * xSpacing ) / ( columns * ( WIDTH + borderWidth ) )
2382 // indeed we can divide everything by WIDTH:
2383 this.scaleFactor
= ( 1 - ( this.indexColumns
+ 1 ) * this.xSpacingFactor
) /
2384 ( this.indexColumns
* ( 1 + 2 * this.halfBorderWidthFactor
) );
2386 // We create a Thumbnail Border and Thumbnail MouseArea rectangle template that will be
2387 // used by every Thumbnail. The Mouse Area rectangle is used in order to trigger the
2388 // mouseover event properly even when the slide background is hidden.
2389 this.thumbnailMouseAreaTemplateId
= 'thumbnail_mouse_area';
2390 this.thumbnailMouseAreaTemplateElement
= null;
2391 this.thumbnailBorderTemplateId
= 'thumbnail_border';
2392 this.thumbnailBorderTemplateElement
= null;
2393 this.createTemplateElements();
2395 // Now we create the grid of thumbnails
2396 this.aThumbnailSet
= new Array( this.totalThumbnails
);
2397 for( var i
= 0; i
< this.totalThumbnails
; ++i
)
2399 this.aThumbnailSet
[i
] = new Thumbnail( this, i
);
2400 this.aThumbnailSet
[i
].updateView();
2403 // this.curThumbnailIndex = this.selectedSlideIndex % this.totalThumbnails;
2404 this.curThumbnailIndex
= 0;
2405 // this.aThumbnailSet[ this.curThumbnailIndex ].select();
2409 /* public methods */
2410 SlideIndexPage
.prototype.getTotalThumbnails = function()
2412 return this.totalThumbnails
;
2415 SlideIndexPage
.prototype.show = function()
2417 this.pageElement
.setAttribute( 'display', 'inherit' );
2420 SlideIndexPage
.prototype.hide = function()
2422 this.pageElement
.setAttribute( 'display', 'none' );
2427 * Change the selected thumbnail from the current one to the thumbnail with index nIndex.
2429 * @param nIndex - the thumbnail index
2431 SlideIndexPage
.prototype.setSelection = function( nIndex
)
2433 nIndex
= getSafeIndex( nIndex
, 0, this.getTotalThumbnails() - 1 );
2434 if( this.curThumbnailIndex
!= nIndex
)
2436 this.aThumbnailSet
[ this.curThumbnailIndex
].unselect();
2437 this.aThumbnailSet
[ nIndex
].select();
2438 this.curThumbnailIndex
= nIndex
;
2440 this.selectedSlideIndex
= this.aThumbnailSet
[ nIndex
].slideIndex
;
2443 SlideIndexPage
.prototype.createPageElement = function()
2445 var aPageElement
= document
.createElementNS( NSS
['svg'], 'g' );
2446 aPageElement
.setAttribute( 'id', this.pageElementId
);
2447 aPageElement
.setAttribute( 'display', 'none' );
2448 aPageElement
.setAttribute( 'visibility', 'visible' );
2450 // the slide index page background
2451 var sPageBgColor
= this.pageBgColor
+ ';';
2452 var aRectElement
= document
.createElementNS( NSS
['svg'], 'rect' );
2453 aRectElement
.setAttribute( 'x', 0 );
2454 aRectElement
.setAttribute( 'y', 0 );
2455 aRectElement
.setAttribute( 'width', WIDTH
);
2456 aRectElement
.setAttribute( 'height', HEIGHT
);
2457 aRectElement
.setAttribute( 'style', 'stroke:none;fill:' + sPageBgColor
);
2459 aPageElement
.appendChild( aRectElement
);
2460 // The index page is appended after all slide elements
2461 // so when it is displayed it covers them all
2462 ROOT_NODE
.appendChild( aPageElement
);
2463 return( document
.getElementById( this.pageElementId
) );
2466 SlideIndexPage
.prototype.createTemplateElements = function()
2468 // We define a Rect element as a template of thumbnail border for all slide-thumbnails.
2469 // The stroke color is defined individually by each thumbnail according to
2470 // its selection status.
2471 var aDefsElement
= document
.createElementNS( NSS
['svg'], 'defs' );
2472 var aRectElement
= document
.createElementNS( NSS
['svg'], 'rect' );
2473 aRectElement
.setAttribute( 'id', this.thumbnailBorderTemplateId
);
2474 aRectElement
.setAttribute( 'x', -this.halfBorderWidth
);
2475 aRectElement
.setAttribute( 'y', -this.halfBorderWidth
);
2476 aRectElement
.setAttribute( 'rx', this.halfBorderWidth
);
2477 aRectElement
.setAttribute( 'ry', this.halfBorderWidth
);
2478 aRectElement
.setAttribute( 'width', WIDTH
+ this.halfBorderWidth
);
2479 aRectElement
.setAttribute( 'height', HEIGHT
+ this.halfBorderWidth
);
2480 aRectElement
.setAttribute( 'stroke-width', this.borderWidth
);
2481 aRectElement
.setAttribute( 'fill', 'none' );
2482 aDefsElement
.appendChild( aRectElement
);
2484 // We define a Rect element as a template of mouse area for triggering the mouseover event.
2485 // A copy is used by each thumbnail element.
2486 aRectElement
= document
.createElementNS( NSS
['svg'], 'rect' );
2487 aRectElement
.setAttribute( 'id', this.thumbnailMouseAreaTemplateId
);
2488 aRectElement
.setAttribute( 'x', 0 );
2489 aRectElement
.setAttribute( 'y', 0 );
2490 aRectElement
.setAttribute( 'width', WIDTH
);
2491 aRectElement
.setAttribute( 'height', HEIGHT
);
2492 aRectElement
.setAttribute( 'fill', this.pageBgColor
);
2493 aDefsElement
.appendChild( aRectElement
);
2495 this.pageElement
.appendChild( aDefsElement
);
2497 this.thumbnailMouseAreaTemplateElement
= document
.getElementById( this.thumbnailMouseAreaTemplateId
);
2498 this.thumbnailBorderTemplateElement
= document
.getElementById( this.thumbnailBorderTemplateId
);
2501 SlideIndexPage
.prototype.decreaseNumberOfColumns = function()
2503 this.setNumberOfColumns( this.indexColumns
- 1 );
2506 SlideIndexPage
.prototype.increaseNumberOfColumns = function()
2508 this.setNumberOfColumns( this.indexColumns
+ 1 );
2511 SlideIndexPage
.prototype.resetNumberOfColumns = function()
2513 this.setNumberOfColumns( INDEX_COLUMNS_DEFAULT
);
2516 /** setNumberOfColumns
2518 * Change the size of the thumbnail grid.
2520 * @param nNumberOfColumns - the new number of columns/rows of the thumbnail grid
2522 SlideIndexPage
.prototype.setNumberOfColumns = function( nNumberOfColumns
)
2524 if( this.indexColumns
== nNumberOfColumns
) return;
2525 if( nNumberOfColumns
< 2 || nNumberOfColumns
> 6 ) return;
2527 var suspendHandle
= ROOT_NODE
.suspendRedraw(500);
2529 var nOldTotalThumbnails
= this.totalThumbnails
;
2530 this.indexColumns
= nNumberOfColumns
;
2531 this.totalThumbnails
= nNumberOfColumns
* nNumberOfColumns
;
2533 this.aThumbnailSet
[this.curThumbnailIndex
].unselect();
2535 // if we decreased the number of used columns we remove the exceeding thumbnail elements
2537 for( i
= this.totalThumbnails
; i
< nOldTotalThumbnails
; ++i
)
2539 this.aThumbnailSet
[i
].removeElement();
2542 // if we increased the number of used columns we create the needed thumbnail objects
2543 for( i
= nOldTotalThumbnails
; i
< this.totalThumbnails
; ++i
)
2545 this.aThumbnailSet
[i
] = new Thumbnail( this, i
);
2548 // we set up layout parameters that depend on the number of columns
2549 this.halfBorderWidthFactor
= ( 300/28000 ) * ( this.indexColumns / 3 );
2550 this.halfBorderWidth
= WIDTH
* this.halfBorderWidthFactor
;
2551 this.borderWidth
= 2 * this.halfBorderWidth
;
2552 // scaleFactor = ( WIDTH - ( columns + 1 ) * xSpacing ) / ( columns * ( WIDTH + borderWidth ) )
2553 this.scaleFactor
= ( 1 - ( this.indexColumns
+ 1 ) * this.xSpacingFactor
) /
2554 ( this.indexColumns
* ( 1 + 2 * this.halfBorderWidthFactor
) );
2556 // update the thumbnail border size
2557 var aRectElement
= this.thumbnailBorderTemplateElement
;
2558 aRectElement
.setAttribute( 'x', -this.halfBorderWidth
);
2559 aRectElement
.setAttribute( 'y', -this.halfBorderWidth
);
2560 aRectElement
.setAttribute( 'rx', this.halfBorderWidth
);
2561 aRectElement
.setAttribute( 'ry', this.halfBorderWidth
);
2562 aRectElement
.setAttribute( 'width', WIDTH
+ this.halfBorderWidth
);
2563 aRectElement
.setAttribute( 'height', HEIGHT
+ this.halfBorderWidth
);
2564 aRectElement
.setAttribute( 'stroke-width', this.borderWidth
);
2566 // now we update the displacement on the index page of each thumbnail (old and new)
2567 for( i
= 0; i
< this.totalThumbnails
; ++i
)
2569 this.aThumbnailSet
[i
].updateView();
2572 this.curThumbnailIndex
= this.selectedSlideIndex
% this.totalThumbnails
;
2573 this.aThumbnailSet
[this.curThumbnailIndex
].select();
2575 // needed for forcing the indexSetPageSlide routine to update the INDEX_OFFSET
2577 indexSetPageSlide( this.selectedSlideIndex
);
2579 ROOT_NODE
.unsuspendRedraw( suspendHandle
);
2580 ROOT_NODE
.forceRedraw();
2584 /** Class Thumbnail **
2585 * This class handles a slide thumbnail.
2587 function Thumbnail( aSlideIndexPage
, nIndex
)
2589 this.container
= aSlideIndexPage
;
2590 this.index
= nIndex
;//= getSafeIndex( nIndex, 0, this.container.getTotalThumbnails() );
2591 this.pageElement
= this.container
.pageElement
;
2592 this.thumbnailId
= 'thumbnail' + this.index
;
2593 this.thumbnailElement
= this.createThumbnailElement();
2594 this.slideElement
= getElementByClassName( this.thumbnailElement
, 'Slide' );
2595 this.borderElement
= getElementByClassName( this.thumbnailElement
, 'Border' );
2596 this.mouseAreaElement
= getElementByClassName( this.thumbnailElement
, 'MouseArea' );
2597 //this.mouseAreaElement.setAttribute( 'onmouseover', 'theSlideIndexPage.aThumbnailSet[' + this.index + '].onMouseOver()' );
2598 //this.mouseAreaElement.onmousedown = mouseHandlerDictionary[INDEX_MODE][MOUSE_DOWN];
2599 this.aTransformSet
= new Array( 3 );
2600 this.visibility
= VISIBLE
;
2601 this.isSelected
= false;
2604 /* static const class member */
2605 Thumbnail
.prototype.sNormalBorderColor
= 'rgb(216,216,216)';
2606 Thumbnail
.prototype.sSelectionBorderColor
= 'rgb(92,92,255)';
2608 /* public methods */
2609 Thumbnail
.prototype.removeElement = function()
2611 if( this.thumbnailElement
)
2612 this.container
.pageElement
.removeChild( this.thumbnailElement
);
2615 Thumbnail
.prototype.show = function()
2617 if( this.visibility
== HIDDEN
)
2619 this.thumbnailElement
.setAttribute( 'display', 'inherit' );
2620 this.visibility
= VISIBLE
;
2624 Thumbnail
.prototype.hide = function()
2626 if( this.visibility
== VISIBLE
)
2628 this.thumbnailElement
.setAttribute( 'display', 'none' );
2629 this.visibility
= HIDDEN
;
2633 Thumbnail
.prototype.select = function()
2635 if( !this.isSelected
)
2637 this.borderElement
.setAttribute( 'stroke', this.sSelectionBorderColor
);
2638 this.isSelected
= true;
2642 Thumbnail
.prototype.unselect = function()
2644 if( this.isSelected
)
2646 this.borderElement
.setAttribute( 'stroke', this.sNormalBorderColor
);
2647 this.isSelected
= false;
2653 * This method updates the displacement of the thumbnail on the slide index page,
2654 * the value of the row, column coordinates of the thumbnail in the grid, and
2655 * the onmouseover property of the thumbnail element.
2658 Thumbnail
.prototype.updateView = function()
2660 this.column
= this.index
% this.container
.indexColumns
;
2661 this.row
= ( this.index
- this.column
) / this.container
.indexColumns
;
2662 this.halfBorderWidth
= this.container
.halfBorderWidth
;
2663 this.borderWidth
= this.container
.borderWidth
;
2664 this.width
= ( WIDTH
+ this.borderWidth
) * this.container
.scaleFactor
;
2665 this.height
= ( HEIGHT
+ this.borderWidth
) * this.container
.scaleFactor
;
2666 this.aTransformSet
[2] = 'translate(' + this.halfBorderWidth
+ ' ' + this.halfBorderWidth
+ ')';
2667 this.aTransformSet
[1] = 'scale(' + this.container
.scaleFactor
+ ')';
2668 var sTransformAttrValue
= this.computeTransform();
2669 this.thumbnailElement
.setAttribute( 'transform', sTransformAttrValue
);
2670 this.mouseAreaElement
.setAttribute( 'onmouseover', 'theSlideIndexPage.aThumbnailSet[' + this.index
+ '].onMouseOver()' );
2675 * This method update the content of the thumbnail view
2677 * @param nIndex - the index of the slide to be shown in the thumbnail
2679 Thumbnail
.prototype.update = function( nIndex
)
2681 if( this.slideIndex
== nIndex
) return;
2683 var aMetaSlide
= theMetaDoc
.aMetaSlideSet
[nIndex
];
2684 aMetaSlide
.updateMasterPageView();
2685 setNSAttribute( 'xlink', this.slideElement
, 'href', '#' + aMetaSlide
.slideId
);
2686 this.slideIndex
= nIndex
;
2689 Thumbnail
.prototype.clear = function( nIndex
)
2691 setNSAttribute( 'xlink', this.slideElement
, 'href', '' );
2694 /* private methods */
2695 Thumbnail
.prototype.createThumbnailElement = function()
2697 var aThumbnailElement
= document
.createElementNS( NSS
['svg'], 'g' );
2698 aThumbnailElement
.setAttribute( 'id', this.thumbnailId
);
2699 aThumbnailElement
.setAttribute( 'display', 'inherit' );
2701 var aSlideElement
= document
.createElementNS( NSS
['svg'], 'use' );
2702 setNSAttribute( 'xlink', aSlideElement
, 'href', '' );
2703 aSlideElement
.setAttribute( 'class', 'Slide' );
2704 aThumbnailElement
.appendChild( aSlideElement
);
2706 var aMouseAreaElement
= document
.createElementNS( NSS
['svg'], 'use' );
2707 setNSAttribute( 'xlink', aMouseAreaElement
, 'href', '#' + this.container
.thumbnailMouseAreaTemplateId
);
2708 aMouseAreaElement
.setAttribute( 'class', 'MouseArea' );
2709 aMouseAreaElement
.setAttribute( 'opacity', 0.0 );
2710 aThumbnailElement
.appendChild( aMouseAreaElement
);
2712 var aBorderElement
= document
.createElementNS( NSS
['svg'], 'use' );
2713 setNSAttribute( 'xlink', aBorderElement
, 'href', '#' + this.container
.thumbnailBorderTemplateId
);
2714 aBorderElement
.setAttribute( 'stroke', this.sNormalBorderColor
);
2715 aBorderElement
.setAttribute( 'class', 'Border' );
2716 aThumbnailElement
.appendChild( aBorderElement
);
2718 this.container
.pageElement
.appendChild( aThumbnailElement
);
2719 return( document
.getElementById( this.thumbnailId
) );
2722 Thumbnail
.prototype.computeTransform = function()
2724 var nXSpacing
= this.container
.xSpacing
;
2725 var nYSpacing
= this.container
.ySpacing
;
2727 var nXOffset
= nXSpacing
+ ( this.width
+ nXSpacing
) * this.column
;
2728 var nYOffset
= nYSpacing
+ ( this.height
+ nYSpacing
) * this.row
;
2730 this.aTransformSet
[0] = 'translate(' + nXOffset
+ ' ' + nYOffset
+ ')';
2732 var sTransform
= this.aTransformSet
.join( ' ' );
2737 Thumbnail
.prototype.onMouseOver = function()
2739 if( ( currentMode
== INDEX_MODE
) && ( this.container
.curThumbnailIndex
!= this.index
) )
2741 this.container
.setSelection( this.index
);
2748 // ------------------------------------------------------------------------------------------ //
2749 /** Initialization function.
2750 * The whole presentation is set-up in this function.
2754 var VIEWBOX
= ROOT_NODE
.getAttribute('viewBox');
2758 WIDTH
= ROOT_NODE
.viewBox
.animVal
.width
;
2759 HEIGHT
= ROOT_NODE
.viewBox
.animVal
.height
;
2762 aSlideShow
= new SlideShow();
2763 theMetaDoc
= new MetaDocument();
2764 aSlideShow
.bIsEnabled
= theMetaDoc
.bIsAnimated
;
2765 theSlideIndexPage
= new SlideIndexPage();
2766 aSlideShow
.displaySlide( theMetaDoc
.nStartSlideNumber
, false );
2768 //=====================================//
2769 // ===== timing test ===== //
2770 //=====================================//
2771 // var aTimingAttributeList = [ '0.001s', '-12:16.123', 'next', 'id23.click', 'id3.end + 5s', 'id4.begin - 12:45' ];
2773 // for( var i = 0; i < aTimingAttributeList.length; ++i)
2775 // var aTiming = new Timing( aTimingAttributeList[i] );
2781 //=====================================//
2782 // == animations parsing test == //
2783 //=====================================//
2785 // var aSlideShowContext = aSlideShow.getContext();
2786 // var aSlideAnimations = new SlideAnimations( aSlideShowContext );
2787 // aSlideAnimations.importAnimations( getSlideAnimationsRoot( 'id7' ) );
2788 // aSlideAnimations.parseElements();
2789 // log( aSlideAnimations.aRootNode.info( true ) );
2793 function presentationEngineStop()
2795 alert( 'We are sorry! An unexpected error occurred.\nThe presentation engine will be stopped' );
2796 document
.onkeydown
= null;
2797 document
.onkeypress
= null;
2798 document
.onclick
= null;
2799 window
.onmousewheel
= null;
2802 function assert( condition
, message
)
2806 presentationEngineStop();
2807 if (typeof console
== 'object')
2809 throw new Error( message
);
2813 function dispatchEffects(dir
)
2815 // TODO to be implemented
2819 var bRet
= aSlideShow
.nextEffect();
2823 switchSlide( 1, false );
2828 switchSlide( dir
, false );
2832 function skipAllEffects()
2834 var bRet
= aSlideShow
.skipAllEffects();
2837 switchSlide( 1, true );
2841 function skipEffects(dir
)
2845 var bRet
= aSlideShow
.skipPlayingOrNextEffect();
2849 switchSlide( 1, true );
2854 switchSlide( dir
, true );
2858 function switchSlide( nOffset
, bSkipTransition
)
2860 var nNextSlide
= nCurSlide
+ nOffset
;
2861 aSlideShow
.displaySlide( nNextSlide
, bSkipTransition
);
2864 /** Function to display the index sheet.
2866 * @param offsetNumber offset number
2868 function displayIndex( offsetNumber
)
2870 var aMetaSlideSet
= theMetaDoc
.aMetaSlideSet
;
2871 offsetNumber
= getSafeIndex( offsetNumber
, 0, aMetaSlideSet
.length
- 1 );
2873 var nTotalThumbnails
= theSlideIndexPage
.getTotalThumbnails();
2874 var nEnd
= Math
.min( offsetNumber
+ nTotalThumbnails
, aMetaSlideSet
.length
);
2876 var aThumbnailSet
= theSlideIndexPage
.aThumbnailSet
;
2878 for( var i
= offsetNumber
; i
< nEnd
; ++i
, ++j
)
2880 aThumbnailSet
[j
].update( i
);
2881 aThumbnailSet
[j
].show();
2883 for( ; j
< nTotalThumbnails
; ++j
)
2885 aThumbnailSet
[j
].hide();
2888 //do we need to save the current offset?
2889 if (INDEX_OFFSET
!= offsetNumber
)
2890 INDEX_OFFSET
= offsetNumber
;
2893 /** Function to toggle between index and slide mode.
2895 function toggleSlideIndex()
2897 //var suspendHandle = ROOT_NODE.suspendRedraw(500);
2899 if( currentMode
== SLIDE_MODE
)
2902 theMetaDoc
.getCurrentSlide().hide();
2904 indexSetPageSlide( nCurSlide
);
2905 theSlideIndexPage
.show();
2906 currentMode
= INDEX_MODE
;
2908 else if( currentMode
== INDEX_MODE
)
2910 theSlideIndexPage
.hide();
2911 var nNewSlide
= theSlideIndexPage
.selectedSlideIndex
;
2913 aSlideShow
.displaySlide( nNewSlide
, true );
2914 currentMode
= SLIDE_MODE
;
2917 //ROOT_NODE.unsuspendRedraw(suspendHandle);
2918 //ROOT_NODE.forceRedraw();
2921 /** Function that exit from the index mode without changing the shown slide
2924 function abandonIndexMode()
2926 theSlideIndexPage
.selectedSlideIndex
= nCurSlide
;
2934 /*********************************************************************************************
2935 *********************************************************************************************
2936 *********************************************************************************************
2938 ***** ANIMATION ENGINE *****
2940 *********************************************************************************************
2941 *********************************************************************************************
2942 *********************************************************************************************/
2948 // ------------------------------------------------------------------------------------------ //
2952 var CURR_UNIQUE_ID
= 0;
2954 function getUniqueId()
2957 return CURR_UNIQUE_ID
;
2960 function mem_fn( sMethodName
)
2962 return function( aObject
)
2964 var aMethod
= aObject
[ sMethodName
];
2966 aMethod
.call( aObject
);
2968 log( 'method sMethodName not found' );
2972 function bind( aObject
, aMethod
)
2976 return aMethod
.call( aObject
, arguments
[0] );
2980 function bind2( aFunction
)
2983 log( 'bind2: passed function is not valid.' );
2985 var aBoundArgList
= arguments
;
2987 var aResultFunction
= null;
2989 switch( aBoundArgList
.length
)
2991 case 1: aResultFunction = function()
2993 return aFunction
.call( arguments
[0], arguments
[1],
2994 arguments
[2], arguments
[3],
2998 case 2: aResultFunction = function()
3000 return aFunction
.call( aBoundArgList
[1], arguments
[0],
3001 arguments
[1], arguments
[2],
3005 case 3: aResultFunction = function()
3007 return aFunction
.call( aBoundArgList
[1], aBoundArgList
[2],
3008 arguments
[0], arguments
[1],
3012 case 4: aResultFunction = function()
3014 return aFunction
.call( aBoundArgList
[1], aBoundArgList
[2],
3015 aBoundArgList
[3], arguments
[0],
3019 case 5: aResultFunction = function()
3021 return aFunction
.call( aBoundArgList
[1], aBoundArgList
[2],
3022 aBoundArgList
[3], aBoundArgList
[4],
3027 log( 'bind2: arity not handled.' );
3030 return aResultFunction
;
3033 //function concat3( s1, s2, s3 )
3035 // log( s1 + s2 + s3 );
3038 //var bound1 = bind2( concat3, 'Qui' );
3039 //bound1( 'Quo', 'Qua' );
3041 //var bound2 = bind2( concat3, 'Qui', 'Quo' );
3044 function getCurrentSystemTime()
3046 return ( new Date() ).getTime();
3047 //return ROOT_NODE.getCurrentTime();
3050 function getSlideAnimationsRoot( sSlideId
)
3052 return theMetaDoc
.aSlideAnimationsMap
[ sSlideId
];
3055 /** This function return an array populated with all children nodes of the
3056 * passed element that are elements
3058 * @param aElement any XML element
3060 * @returns an array that contains all children elements
3062 function getElementChildren( aElement
)
3064 var aChildrenArray
= new Array();
3066 var nSize
= aElement
.childNodes
.length
;
3068 for( var i
= 0; i
< nSize
; ++i
)
3070 if( aElement
.childNodes
[i
].nodeType
== 1 )
3071 aChildrenArray
.push( aElement
.childNodes
[i
] );
3074 return aChildrenArray
;
3077 function removeWhiteSpaces( str
)
3083 var aSplittedString
= str
.split( re
);
3084 return aSplittedString
.join('');
3087 function clamp( nValue
, nMinimum
, nMaximum
)
3089 if( nValue
< nMinimum
)
3093 else if( nValue
> nMaximum
)
3103 function makeMatrixString( a
, b
, c
, d
, e
, f
)
3116 function matrixToString( aSVGMatrix
)
3118 return makeMatrixString( aSVGMatrix
.a
, aSVGMatrix
.b
, aSVGMatrix
.c
,
3119 aSVGMatrix
.d
, aSVGMatrix
.e
, aSVGMatrix
.f
);
3124 // ------------------------------------------------------------------------------------------ //
3125 // Attribute Parsers
3127 function numberParser( sValue
)
3129 if( sValue
=== '.' )
3131 var reFloatNumber
= /^[+-]?[0-9]*[.]?[0-9]*$/;
3133 if( reFloatNumber
.test( sValue
) )
3134 return parseFloat( sValue
);
3139 function booleanParser( sValue
)
3141 sValue
= sValue
.toLowerCase();
3142 if( sValue
=== 'true' )
3144 else if( sValue
=== 'false' )
3150 function colorParser( sValue
)
3153 function hsl( nHue
, nSaturation
, nLuminance
)
3155 return new HSLColor( nHue
, nSaturation
/ 100, nLuminance
/ 100 );
3158 function rgb( nRed
, nGreen
, nBlue
)
3160 return new RGBColor( nRed
/ 255, nGreen
/ 255, nBlue
/ 255 );
3163 function prgb( nRed
, nGreen
, nBlue
)
3165 return new RGBColor( nRed
/ 100, nGreen
/ 100, nBlue
/ 100 );
3168 var sCommaPattern
= ' *[,] *';
3169 var sIntegerPattern
= '[+-]?[0-9]+';
3170 var sHexDigitPattern
= '[0-9A-Fa-f]';
3172 var sHexColorPattern
= '#(' + sHexDigitPattern
+ '{2})('
3173 + sHexDigitPattern
+ '{2})('
3174 + sHexDigitPattern
+ '{2})';
3176 var sRGBIntegerPattern
= 'rgb[(] *' + sIntegerPattern
+ sCommaPattern
3177 + sIntegerPattern
+ sCommaPattern
3178 + sIntegerPattern
+ ' *[)]';
3180 var sRGBPercentPattern
= 'rgb[(] *' + sIntegerPattern
+ '%' + sCommaPattern
3181 + sIntegerPattern
+ '%' + sCommaPattern
3182 + sIntegerPattern
+ '%' + ' *[)]';
3184 var sHSLPercentPattern
= 'hsl[(] *' + sIntegerPattern
+ sCommaPattern
3185 + sIntegerPattern
+ '%' + sCommaPattern
3186 + sIntegerPattern
+ '%' + ' *[)]';
3188 var reHexColor
= new RegExp( sHexColorPattern
);
3189 var reRGBInteger
= new RegExp( sRGBIntegerPattern
);
3190 var reRGBPercent
= new RegExp( sRGBPercentPattern
);
3191 var reHSLPercent
= new RegExp( sHSLPercentPattern
);
3193 if( reHexColor
.test( sValue
) )
3195 var aRGBTriple
= reHexColor
.exec( sValue
);
3197 var nRed
= parseInt( aRGBTriple
[1], 16 ) / 255;
3198 var nGreen
= parseInt( aRGBTriple
[2], 16 ) / 255;
3199 var nBlue
= parseInt( aRGBTriple
[3], 16 ) / 255;
3201 return new RGBColor( nRed
, nGreen
, nBlue
);
3203 else if( reHSLPercent
.test( sValue
) )
3205 sValue
= sValue
.replace( '%', '' ).replace( '%', '' );
3206 return eval( sValue
);
3208 else if( reRGBInteger
.test( sValue
) )
3210 return eval( sValue
);
3212 else if( reRGBPercent
.test( sValue
) )
3214 sValue
= 'p' + sValue
.replace( '%', '' ).replace( '%', '' ).replace( '%', '' );
3215 return eval( sValue
);
3225 /**********************************************************************************************
3226 * RGB and HSL color classes
3227 **********************************************************************************************/
3229 // ------------------------------------------------------------------------------------------ //
3230 function RGBColor( nRed
, nGreen
, nBlue
)
3232 this.eColorSpace
= COLOR_SPACE_RGB
;
3233 // values in the [0,1] range
3235 this.nGreen
= nGreen
;
3240 RGBColor
.prototype.clone = function()
3242 return new RGBColor( this.nRed
, this.nGreen
, this.nBlue
);
3245 RGBColor
.prototype.equal = function( aRGBColor
)
3247 return ( this.nRed
== aRGBColor
.nRed
) &&
3248 ( this.nGreen
== aRGBColor
.nGreen
) &&
3249 ( this.nBlue
== aRGBColor
.nBlue
);
3252 RGBColor
.prototype.add = function( aRGBColor
)
3254 this.nRed
+= aRGBColor
.nRed
;
3255 this.nGreen
+= aRGBColor
.nGreen
;
3256 this.nBlue
+= aRGBColor
.nBlue
;
3260 RGBColor
.prototype.scale = function( aT
)
3268 RGBColor
.clamp = function( aRGBColor
)
3270 var aClampedRGBColor
= new RGBColor( 0, 0, 0 );
3272 aClampedRGBColor
.nRed
= clamp( aRGBColor
.nRed
, 0.0, 1.0 );
3273 aClampedRGBColor
.nGreen
= clamp( aRGBColor
.nGreen
, 0.0, 1.0 );
3274 aClampedRGBColor
.nBlue
= clamp( aRGBColor
.nBlue
, 0.0, 1.0 );
3276 return aClampedRGBColor
;
3279 RGBColor
.prototype.convertToHSL = function()
3281 var nRed
= clamp( this.nRed
, 0.0, 1.0 );
3282 var nGreen
= clamp( this.nGreen
, 0.0, 1.0 );
3283 var nBlue
= clamp( this.nBlue
, 0.0, 1.0 );
3285 var nMax
= Math
.max( nRed
, nGreen
, nBlue
);
3286 var nMin
= Math
.min( nRed
, nGreen
, nBlue
);
3287 var nDelta
= nMax
- nMin
;
3289 var nLuminance
= ( nMax
+ nMin
) / 2.0;
3290 var nSaturation
= 0.0;
3294 nSaturation
= ( nLuminance
> 0.5 ) ?
3295 ( nDelta
/ ( 2.0 - nMax
- nMin
) ) :
3296 ( nDelta
/ ( nMax
+ nMin
) );
3299 nHue
= ( nGreen
- nBlue
) / nDelta
;
3300 else if( nGreen
== nMax
)
3301 nHue
= 2.0 + ( nBlue
- nRed
) / nDelta
;
3302 else if( nBlue
== nMax
)
3303 nHue
= 4.0 + ( nRed
- nGreen
) / nDelta
;
3311 return new HSLColor( nHue
, nSaturation
, nLuminance
);
3315 RGBColor
.prototype.toString = function( bClamped
)
3320 aRGBColor
= RGBColor
.clamp( this );
3327 var nRed
= Math
.round( aRGBColor
.nRed
* 255 );
3328 var nGreen
= Math
.round( aRGBColor
.nGreen
* 255 );
3329 var nBlue
= Math
.round( aRGBColor
.nBlue
* 255 );
3331 return ( 'rgb(' + nRed
+ ',' + nGreen
+ ',' + nBlue
+ ')' );
3334 RGBColor
.interpolate = function( aStartRGB
, aEndRGB
, nT
)
3336 var aResult
= aStartRGB
.clone();
3337 var aTEndRGB
= aEndRGB
.clone();
3338 aResult
.scale( 1.0 - nT
);
3339 aTEndRGB
.scale( nT
);
3340 aResult
.add( aTEndRGB
);
3347 // ------------------------------------------------------------------------------------------ //
3348 function HSLColor( nHue
, nSaturation
, nLuminance
)
3350 this.eColorSpace
= COLOR_SPACE_HSL
;
3351 // Hue is in the [0,360[ range, Saturation and Luminance are in the [0,1] range
3353 this.nSaturation
= nSaturation
;
3354 this.nLuminance
= nLuminance
;
3356 this.normalizeHue();
3360 HSLColor
.prototype.clone = function()
3362 return new HSLColor( this.nHue
, this.nSaturation
, this.nLuminance
);
3365 HSLColor
.prototype.equal = function( aHSLColor
)
3367 return ( this.nHue
== aHSLColor
.nHue
) &&
3368 ( this.nSaturation
+= aHSLColor
.nSaturation
) &&
3369 ( this.nLuminance
+= aHSLColor
.nLuminance
);
3372 HSLColor
.prototype.add = function( aHSLColor
)
3374 this.nHue
+= aHSLColor
.nHue
;
3375 this.nSaturation
+= aHSLColor
.nSaturation
;
3376 this.nLuminance
+= aHSLColor
.nLuminance
;
3377 this.normalizeHue();
3381 HSLColor
.prototype.scale = function( aT
)
3384 this.nSaturation
*= aT
;
3385 this.nLuminance
*= aT
;
3386 this.normalizeHue();
3390 HSLColor
.clamp = function( aHSLColor
)
3392 var aClampedHSLColor
= new HSLColor( 0, 0, 0 );
3394 aClampedHSLColor
.nHue
= aHSLColor
.nHue
% 360;
3395 if( aClampedHSLColor
.nHue
< 0 )
3396 aClampedHSLColor
.nHue
+= 360;
3397 aClampedHSLColor
.nSaturation
= clamp( aHSLColor
.nSaturation
, 0.0, 1.0 );
3398 aClampedHSLColor
.nLuminance
= clamp( aHSLColor
.nLuminance
, 0.0, 1.0 );
3401 HSLColor
.prototype.normalizeHue = function()
3403 this.nHue
= this.nHue
% 360;
3404 if( this.nHue
< 0 ) this.nHue
+= 360;
3407 HSLColor
.prototype.toString = function()
3409 return 'hsl(' + this.nHue
.toFixed( 3 ) + ','
3410 + this.nSaturation
.toFixed( 3 ) + ','
3411 + this.nLuminance
.toFixed( 3 ) + ')';
3414 HSLColor
.prototype.convertToRGB = function()
3417 var nHue
= this.nHue
% 360;
3418 if( nHue
< 0 ) nHue
+= 360;
3419 var nSaturation
= clamp( this.nSaturation
, 0.0, 1.0 );
3420 var nLuminance
= clamp( this.nLuminance
, 0.0, 1.0 );
3423 if( nSaturation
=== 0 )
3425 return new RGBColor( nLuminance
, nLuminance
, nLuminance
);
3428 var nVal1
= ( nLuminance
<= 0.5 ) ?
3429 ( nLuminance
* (1.0 + nSaturation
) ) :
3430 ( nLuminance
+ nSaturation
- nLuminance
* nSaturation
);
3432 var nVal2
= 2.0 * nLuminance
- nVal1
;
3434 var nRed
= HSLColor
.hsl2rgbHelper( nVal2
, nVal1
, nHue
+ 120 );
3435 var nGreen
= HSLColor
.hsl2rgbHelper( nVal2
, nVal1
, nHue
);
3436 var nBlue
= HSLColor
.hsl2rgbHelper( nVal2
, nVal1
, nHue
- 120 );
3438 return new RGBColor( nRed
, nGreen
, nBlue
);
3441 HSLColor
.hsl2rgbHelper = function( nValue1
, nValue2
, nHue
)
3448 return nValue1
+ ( nValue2
- nValue1
) * nHue
/ 60.0;
3449 else if( nHue
< 180.0 )
3451 else if( nHue
< 240.0 )
3452 return ( nValue1
+ ( nValue2
- nValue1
) * ( 240.0 - nHue
) / 60.0 );
3457 HSLColor
.interpolate = function( aFrom
, aTo
, nT
, bCCW
)
3462 if( aFrom
.nHue
<= aTo
.nHue
&& !bCCW
)
3464 // interpolate hue clockwise. That is, hue starts at
3465 // high values and ends at low ones. Therefore, we
3466 // must 'cross' the 360 degrees and start at low
3467 // values again (imagine the hues to lie on the
3468 // circle, where values above 360 degrees are mapped
3469 // back to [0,360)).
3470 nHue
= nS
* (aFrom
.nHue
+ 360.0) + nT
* aTo
.nHue
;
3472 else if( aFrom
.nHue
> aTo
.nHue
&& bCCW
)
3474 // interpolate hue counter-clockwise. That is, hue
3475 // starts at high values and ends at low
3476 // ones. Therefore, we must 'cross' the 360 degrees
3477 // and start at low values again (imagine the hues to
3478 // lie on the circle, where values above 360 degrees
3479 // are mapped back to [0,360)).
3480 nHue
= nS
* aFrom
.nHue
+ nT
* (aTo
.nHue
+ 360.0);
3484 // interpolate hue counter-clockwise. That is, hue
3485 // starts at low values and ends at high ones (imagine
3486 // the hue value as degrees on a circle, with
3487 // increasing values going counter-clockwise)
3488 nHue
= nS
* aFrom
.nHue
+ nT
* aTo
.nHue
;
3491 var nSaturation
= nS
* aFrom
.nSaturation
+ nT
* aTo
.nSaturation
;
3492 var nLuminance
= nS
* aFrom
.nLuminance
+ nT
* aTo
.nLuminance
;
3494 return new HSLColor( nHue
, nSaturation
, nLuminance
);
3499 /**********************************************************************************************
3500 * SVGMatrix extensions
3501 **********************************************************************************************/
3503 SVGIdentityMatrix
= document
.documentElement
.createSVGMatrix();
3505 SVGMatrix
.prototype.setToIdentity = function()
3507 this.a
= this.d
= 1;
3508 this.b
= this.c
= this.d
= this.e
= 0;
3511 SVGMatrix
.prototype.setToRotationAroundPoint = function( nX
, nY
, nAngle
)
3513 // convert to radians
3514 nAngle
= Math
.PI
* nAngle
/ 180;
3515 var nSin
= Math
.sin( nAngle
);
3516 var nCos
= Math
.cos( nAngle
);
3518 this.a
= nCos
; this.c
= -nSin
; this.e
= nX
* (1 - nCos
) + nY
* nSin
;
3519 this.b
= nSin
; this.d
= nCos
; this.f
= nY
* (1 - nCos
) - nX
* nSin
;
3524 /**********************************************************************************************
3525 * SVGPath extensions
3526 **********************************************************************************************/
3528 /** SVGPathElement.prependPath
3529 * Merge the two path together by inserting the passed path before this one.
3532 * An object of type SVGPathElement to be prepended.
3534 SVGPathElement
.prototype.prependPath = function( aPath
)
3536 var sPathData
= aPath
.getAttribute( 'd' );
3537 sPathData
+= ( ' ' + this.getAttribute( 'd' ) );
3538 this.setAttribute( 'd', sPathData
);
3541 /** SVGPathElement.appendPath
3542 * Merge the two path together by inserting the passed path after this one.
3545 * An object of type SVGPathElement to be appended.
3547 SVGPathElement
.prototype.appendPath = function( aPath
)
3549 var sPathData
= this.getAttribute( 'd' );
3550 sPathData
+= ( ' ' + aPath
.getAttribute( 'd' ) );
3551 this.setAttribute( 'd', sPathData
);
3554 /** SVGPathElement.matrixTransform
3555 * Apply the transformation defined by the passed matrix to the referenced
3556 * svg <path> element.
3557 * After the transformation 'this' element is modified in order to reference
3558 * the transformed path.
3561 * An SVGMatrix instance.
3563 SVGPathElement
.prototype.matrixTransform = function( aSVGMatrix
)
3565 var aPathSegList
= this.pathSegList
;
3566 var nLength
= aPathSegList
.numberOfItems
;
3568 for( i
= 0; i
< nLength
; ++i
)
3570 aPathSegList
.getItem( i
).matrixTransform( aSVGMatrix
);
3574 /** SVGPathElement.changeOrientation
3575 * Invert the path orientation by inverting the path command list.
3578 SVGPathElement
.prototype.changeOrientation = function()
3580 var aPathSegList
= this.pathSegList
;
3581 var nLength
= aPathSegList
.numberOfItems
;
3582 if( nLength
== 0 ) return;
3587 var aPathSeg
= aPathSegList
.getItem( 0 );
3588 if( aPathSeg
.pathSegTypeAsLetter
== 'M' )
3590 nCurrentX
= aPathSeg
.x
;
3591 nCurrentY
= aPathSeg
.y
;
3592 aPathSegList
.removeItem( 0 );
3597 for( i
= 0; i
< nLength
; ++i
)
3599 aPathSeg
= aPathSegList
.getItem( i
);
3600 var aPoint
= aPathSeg
.changeOrientation( nCurrentX
, nCurrentY
);
3601 nCurrentX
= aPoint
.x
;
3602 nCurrentY
= aPoint
.y
;
3606 for( i
= nLength
- 2; i
>= 0; --i
)
3608 aPathSeg
= aPathSegList
.removeItem( i
);
3609 aPathSegList
.appendItem( aPathSeg
);
3612 var aMovePathSeg
= this.createSVGPathSegMovetoAbs( nCurrentX
, nCurrentY
);
3613 aPathSegList
.insertItemBefore( aMovePathSeg
, 0 );
3617 /** matrixTransform and changeOrientation
3618 * We implement these methods for each path segment type still present
3619 * after the path normalization (M, L, Q, C).
3621 * Note: Opera doesn't have any SVGPathSeg* class and rises an error.
3622 * We exploit this fact for providing a different implementation.
3625 { // Firefox, Google Chrome, Internet Explorer, Safari.
3627 SVGPathSegMovetoAbs
.prototype.matrixTransform = function( aSVGMatrix
)
3629 SVGPathMatrixTransform( this, aSVGMatrix
);
3632 SVGPathSegLinetoAbs
.prototype.matrixTransform = function( aSVGMatrix
)
3634 SVGPathMatrixTransform( this, aSVGMatrix
);
3637 SVGPathSegCurvetoQuadraticAbs
.prototype.matrixTransform = function( aSVGMatrix
)
3639 SVGPathMatrixTransform( this, aSVGMatrix
);
3641 this.x1
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* this.y1
+ aSVGMatrix
.e
;
3642 this.y1
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* this.y1
+ aSVGMatrix
.f
;
3645 SVGPathSegCurvetoCubicAbs
.prototype.matrixTransform = function( aSVGMatrix
)
3647 SVGPathMatrixTransform( this, aSVGMatrix
);
3649 this.x1
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* this.y1
+ aSVGMatrix
.e
;
3650 this.y1
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* this.y1
+ aSVGMatrix
.f
;
3652 this.x2
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* this.y2
+ aSVGMatrix
.e
;
3653 this.y2
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* this.y2
+ aSVGMatrix
.f
;
3657 SVGPathSegMovetoAbs
.prototype.changeOrientation = function( nCurrentX
, nCurrentY
)
3659 var aPoint
= { x
: this.x
, y
: this.y
};
3665 SVGPathSegLinetoAbs
.prototype.changeOrientation = function( nCurrentX
, nCurrentY
)
3667 var aPoint
= { x
: this.x
, y
: this.y
};
3673 SVGPathSegCurvetoQuadraticAbs
.prototype.changeOrientation = function( nCurrentX
, nCurrentY
)
3675 var aPoint
= { x
: this.x
, y
: this.y
};
3681 SVGPathSegCurvetoCubicAbs
.prototype.changeOrientation = function( nCurrentX
, nCurrentY
)
3683 var aPoint
= { x
: this.x
, y
: this.y
};
3699 if( e
.name
== 'ReferenceError' )
3701 SVGPathSeg
.prototype.matrixTransform = function( aSVGMatrix
)
3704 switch( this.pathSegTypeAsLetter
)
3708 this.x2
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* this.y2
+ aSVGMatrix
.e
;
3709 this.y2
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* this.y2
+ aSVGMatrix
.f
;
3710 // fall through intended
3713 this.x1
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* this.y1
+ aSVGMatrix
.e
;
3714 this.y1
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* this.y1
+ aSVGMatrix
.f
;
3715 // fall through intended
3718 SVGPathMatrixTransform( this, aSVGMatrix
);
3721 log( 'SVGPathSeg.matrixTransform: unexpected path segment type: '
3722 + this.pathSegTypeAsLetter
);
3726 SVGPathSeg
.prototype.changeOrientation = function( nCurrentX
, nCurrentY
)
3728 switch( this.pathSegTypeAsLetter
)
3737 // fall through intended
3741 var aPoint
= { x
: this.x
, y
: this.y
};
3746 log( 'SVGPathSeg.changeOrientation: unexpected path segment type: '
3747 + this.pathSegTypeAsLetter
);
3755 function SVGPathMatrixTransform( aPath
, aSVGMatrix
)
3758 aPath
.x
= aSVGMatrix
.a
* nX
+ aSVGMatrix
.c
* aPath
.y
+ aSVGMatrix
.e
;
3759 aPath
.y
= aSVGMatrix
.b
* nX
+ aSVGMatrix
.d
* aPath
.y
+ aSVGMatrix
.f
;
3763 /**********************************************************************************************
3764 * simple PriorityQueue
3765 **********************************************************************************************/
3767 function PriorityQueue( aCompareFunc
)
3769 this.aSequence
= new Array();
3770 this.aCompareFunc
= aCompareFunc
;
3771 this.bSorted
= true;
3774 PriorityQueue
.prototype.top = function()
3778 this.aSequence
.sort(this.aCompareFunc
)
3779 this.bSorted
= true;
3781 return this.aSequence
[this.aSequence
.length
- 1];
3784 PriorityQueue
.prototype.isEmpty = function()
3786 return ( this.aSequence
.length
=== 0 );
3789 PriorityQueue
.prototype.push = function( aValue
)
3791 this.bSorted
= false;
3792 this.aSequence
.push( aValue
);
3795 PriorityQueue
.prototype.clear = function()
3797 this.bSorted
= true;
3798 this.aSequence
= new Array();
3801 PriorityQueue
.prototype.pop = function()
3805 this.aSequence
.sort(this.aCompareFunc
)
3806 this.bSorted
= true;
3809 return this.aSequence
.pop();
3813 /**********************************************************************************************
3814 * AnimationNode Class Hierarchy
3815 **********************************************************************************************/
3817 // ------------------------------------------------------------------------------------------ //
3820 var ANIMATION_NODE_CUSTOM
= 0;
3821 var ANIMATION_NODE_PAR
= 1;
3822 var ANIMATION_NODE_SEQ
= 2;
3823 var ANIMATION_NODE_ITERATE
= 3;
3824 var ANIMATION_NODE_ANIMATE
= 4;
3825 var ANIMATION_NODE_SET
= 5;
3826 var ANIMATION_NODE_ANIMATEMOTION
= 6;
3827 var ANIMATION_NODE_ANIMATECOLOR
= 7;
3828 var ANIMATION_NODE_ANIMATETRANSFORM
= 8;
3829 var ANIMATION_NODE_TRANSITIONFILTER
= 9;
3830 var ANIMATION_NODE_AUDIO
= 10;
3831 var ANIMATION_NODE_COMMAND
= 11;
3833 aAnimationNodeTypeInMap
= {
3834 'par' : ANIMATION_NODE_PAR
,
3835 'seq' : ANIMATION_NODE_SEQ
,
3836 'iterate' : ANIMATION_NODE_ITERATE
,
3837 'animate' : ANIMATION_NODE_ANIMATE
,
3838 'set' : ANIMATION_NODE_SET
,
3839 'animatemotion' : ANIMATION_NODE_ANIMATEMOTION
,
3840 'animatecolor' : ANIMATION_NODE_ANIMATECOLOR
,
3841 'animatetransform' : ANIMATION_NODE_ANIMATETRANSFORM
,
3842 'transitionfilter' : ANIMATION_NODE_TRANSITIONFILTER
3847 // ------------------------------------------------------------------------------------------ //
3848 function getAnimationElementType( aElement
)
3850 var sName
= aElement
.localName
.toLowerCase();
3851 //log( 'getAnimationElementType: ' + sName );
3853 if( sName
&& aAnimationNodeTypeInMap
[ sName
] )
3854 return aAnimationNodeTypeInMap
[ sName
];
3856 return ANIMATION_NODE_CUSTOM
;
3861 // ------------------------------------------------------------------------------------------ //
3864 var INVALID_NODE
= 0;
3865 var UNRESOLVED_NODE
= 1;
3866 var RESOLVED_NODE
= 2;
3867 var ACTIVE_NODE
= 4;
3868 var FROZEN_NODE
= 8;
3869 var ENDED_NODE
= 16;
3871 function getNodeStateName( eNodeState
)
3873 switch( eNodeState
)
3877 case UNRESOLVED_NODE
:
3878 return 'UNRESOLVED';
3893 // Impress Node Types
3894 IMPRESS_DEFAULT_NODE
= 0;
3895 IMPRESS_ON_CLICK_NODE
= 1;
3896 IMPRESS_WITH_PREVIOUS_NODE
= 2;
3897 IMPRESS_AFTER_PREVIOUS_NODE
= 3;
3898 IMPRESS_MAIN_SEQUENCE_NODE
= 4;
3899 IMPRESS_TIMING_ROOT_NODE
= 5;
3900 IMPRESS_INTERACTIVE_SEQUENCE_NODE
= 6;
3902 aImpressNodeTypeInMap
= {
3903 'on-click' : IMPRESS_ON_CLICK_NODE
,
3904 'with-previous' : IMPRESS_WITH_PREVIOUS_NODE
,
3905 'after-previous' : IMPRESS_AFTER_PREVIOUS_NODE
,
3906 'main-sequence' : IMPRESS_MAIN_SEQUENCE_NODE
,
3907 'timing-root' : IMPRESS_TIMING_ROOT_NODE
,
3908 'interactive-sequence' : IMPRESS_INTERACTIVE_SEQUENCE_NODE
3911 aImpressNodeTypeOutMap
= [ 'default', 'on-click', 'with-previous', 'after-previous',
3912 'main-sequence', 'timing-root', 'interactive-sequence' ];
3916 aPresetClassInMap
= {};
3920 aPresetIdInMap
= {};
3924 var RESTART_MODE_DEFAULT
= 0;
3925 var RESTART_MODE_INHERIT
= 0;
3926 var RESTART_MODE_ALWAYS
= 1;
3927 var RESTART_MODE_WHEN_NOT_ACTIVE
= 2;
3928 var RESTART_MODE_NEVER
= 3;
3930 aRestartModeInMap
= {
3931 'inherit' : RESTART_MODE_DEFAULT
,
3932 'always' : RESTART_MODE_ALWAYS
,
3933 'whenNotActive' : RESTART_MODE_WHEN_NOT_ACTIVE
,
3934 'never' : RESTART_MODE_NEVER
3937 aRestartModeOutMap
= [ 'inherit','always', 'whenNotActive', 'never' ];
3941 var FILL_MODE_DEFAULT
= 0;
3942 var FILL_MODE_INHERIT
= 0;
3943 var FILL_MODE_REMOVE
= 1;
3944 var FILL_MODE_FREEZE
= 2;
3945 var FILL_MODE_HOLD
= 3;
3946 var FILL_MODE_TRANSITION
= 4;
3947 var FILL_MODE_AUTO
= 5;
3950 'inherit' : FILL_MODE_DEFAULT
,
3951 'remove' : FILL_MODE_REMOVE
,
3952 'freeze' : FILL_MODE_FREEZE
,
3953 'hold' : FILL_MODE_HOLD
,
3954 'transition' : FILL_MODE_TRANSITION
,
3955 'auto' : FILL_MODE_AUTO
3958 aFillModeOutMap
= [ 'inherit', 'remove', 'freeze', 'hold', 'transition', 'auto' ];
3962 var ADDITIVE_MODE_BASE
= 0;
3963 var ADDITIVE_MODE_SUM
= 1;
3964 var ADDITIVE_MODE_REPLACE
= 2;
3965 var ADDITIVE_MODE_MULTIPLY
= 3;
3966 var ADDITIVE_MODE_NONE
= 4;
3968 aAddittiveModeInMap
= {
3969 'base' : ADDITIVE_MODE_BASE
,
3970 'sum' : ADDITIVE_MODE_SUM
,
3971 'replace' : ADDITIVE_MODE_REPLACE
,
3972 'multiply' : ADDITIVE_MODE_MULTIPLY
,
3973 'none' : ADDITIVE_MODE_NONE
3976 aAddittiveModeOutMap
= [ 'base', 'sum', 'replace', 'multiply', 'none' ];
3980 var ACCUMULATE_MODE_NONE
= 0;
3981 var ACCUMULATE_MODE_SUM
= 1;
3983 aAccumulateModeOutMap
= [ 'none', 'sum' ];
3985 // Calculation Modes
3986 var CALC_MODE_DISCRETE
= 0;
3987 var CALC_MODE_LINEAR
= 1;
3988 var CALC_MODE_PACED
= 2;
3989 var CALC_MODE_SPLINE
= 3;
3992 'discrete' : CALC_MODE_DISCRETE
,
3993 'linear' : CALC_MODE_LINEAR
,
3994 'paced' : CALC_MODE_PACED
,
3995 'spline' : CALC_MODE_SPLINE
3998 aCalcModeOutMap
= [ 'discrete', 'linear', 'paced', 'spline' ];
4002 var COLOR_SPACE_RGB
= 0;
4003 var COLOR_SPACE_HSL
= 1;
4005 aColorSpaceInMap
= { 'rgb': COLOR_SPACE_RGB
, 'hsl': COLOR_SPACE_HSL
};
4007 aColorSpaceOutMap
= [ 'rgb', 'hsl' ];
4012 var COUNTERCLOCKWISE
= 1;
4014 aClockDirectionInMap
= { 'clockwise': CLOCKWISE
, 'counter-clockwise': COUNTERCLOCKWISE
};
4016 aClockDirectionOutMap
= [ 'clockwise', 'counter-clockwise' ];
4019 // Attribute Value Types
4020 UNKNOWN_PROPERTY
= 0;
4021 NUMBER_PROPERTY
= 1;
4024 STRING_PROPERTY
= 4;
4027 aValueTypeOutMap
= [ 'unknown', 'number', 'enum', 'color', 'string', 'boolean' ];
4033 'height': { 'type': NUMBER_PROPERTY
,
4036 'getmod': 'makeScaler( 1/nHeight )',
4037 'setmod': 'makeScaler( nHeight)' },
4039 'opacity': { 'type': NUMBER_PROPERTY
,
4040 'get': 'getOpacity',
4041 'set': 'setOpacity' },
4043 'width': { 'type': NUMBER_PROPERTY
,
4046 'getmod': 'makeScaler( 1/nWidth )',
4047 'setmod': 'makeScaler( nWidth)' },
4049 'x': { 'type': NUMBER_PROPERTY
,
4052 'getmod': 'makeScaler( 1/nWidth )',
4053 'setmod': 'makeScaler( nWidth)' },
4055 'y': { 'type': NUMBER_PROPERTY
,
4058 'getmod': 'makeScaler( 1/nHeight )',
4059 'setmod': 'makeScaler( nHeight)' },
4061 'fill': { 'type': ENUM_PROPERTY
,
4062 'get': 'getFillStyle',
4063 'set': 'setFillStyle' },
4065 'stroke': { 'type': ENUM_PROPERTY
,
4066 'get': 'getStrokeStyle',
4067 'set': 'setStrokeStyle' },
4069 'visibility': { 'type': ENUM_PROPERTY
,
4070 'get': 'getVisibility',
4071 'set': 'setVisibility' },
4073 'fill-color': { 'type': COLOR_PROPERTY
,
4074 'get': 'getFillColor',
4075 'set': 'setFillColor' },
4077 'stroke-color': { 'type': COLOR_PROPERTY
,
4078 'get': 'getStrokeColor',
4079 'set': 'setStrokeColor' },
4081 'color': { 'type': COLOR_PROPERTY
,
4082 'get': 'getFontColor',
4083 'set': 'setFontColor' }
4088 // Transition Classes
4089 TRANSITION_INVALID
= 0; // Invalid type
4090 TRANSITION_CLIP_POLYPOLYGON
= 1; // Transition expressed by parametric clip polygon
4091 TRANSITION_SPECIAL
= 2; // Transition expressed by hand-crafted function
4093 aTransitionClassOutMap
= ['invalid', 'clip polypolygon', 'special'];
4097 BARWIPE_TRANSITION
= 1;
4098 BOXWIPE_TRANSITION
= 2;
4099 FOURBOXWIPE_TRANSITION
= 3;
4100 ELLIPSEWIPE_TRANSITION
= 4; // 17
4101 CLOCKWIPE_TRANSITION
= 5; // 22
4102 PINWHEELWIPE_TRANSITION
= 6; // 23
4103 PUSHWIPE_TRANSITION
= 7; // 35
4104 SLIDEWIPE_TRANSITION
= 8; // 36
4105 FADE_TRANSITION
= 9; // 37
4106 CHECKERBOARDWIPE_TRANSITION
= 10; // 39
4108 aTransitionTypeInMap
= {
4109 'barWipe' : BARWIPE_TRANSITION
,
4110 'boxWipe' : BOXWIPE_TRANSITION
,
4111 'fourBoxWipe' : FOURBOXWIPE_TRANSITION
,
4112 'ellipseWipe' : ELLIPSEWIPE_TRANSITION
,
4113 'clockWipe' : CLOCKWIPE_TRANSITION
,
4114 'pinWheelWipe' : PINWHEELWIPE_TRANSITION
,
4115 'pushWipe' : PUSHWIPE_TRANSITION
,
4116 'slideWipe' : SLIDEWIPE_TRANSITION
,
4117 'fade' : FADE_TRANSITION
,
4118 'checkerBoardWipe' : CHECKERBOARDWIPE_TRANSITION
4121 aTransitionTypeOutMap
= [ '', 'barWipe', 'boxWipe', 'fourBoxWipe', 'ellipseWipe',
4122 'clockWipe', 'pinWheelWipe', 'pushWipe', 'slideWipe',
4123 'fade', 'checkerBoardWipe' ];
4126 // Transition Subtypes
4127 DEFAULT_TRANS_SUBTYPE
= 0;
4128 LEFTTORIGHT_TRANS_SUBTYPE
= 1;
4129 TOPTOBOTTOM_TRANS_SUBTYPE
= 2;
4130 CORNERSIN_TRANS_SUBTYPE
= 3; // 11
4131 CORNERSOUT_TRANS_SUBTYPE
= 4;
4132 VERTICAL_TRANS_SUBTYPE
= 5;
4133 HORIZONTAL_TRANS_SUBTYPE
= 6; // 14
4134 DOWN_TRANS_SUBTYPE
= 7 // 19
4135 CIRCLE_TRANS_SUBTYPE
= 8; // 27
4136 CLOCKWISETWELVE_TRANS_SUBTYPE
= 9; // 33
4137 CLOCKWISETHREE_TRANS_SUBTYPE
= 10;
4138 CLOCKWISESIX_TRANS_SUBTYPE
= 11;
4139 CLOCKWISENINE_TRANS_SUBTYPE
= 12;
4140 TWOBLADEVERTICAL_TRANS_SUBTYPE
= 13;
4141 TWOBLADEHORIZONTAL_TRANS_SUBTYPE
= 14;
4142 FOURBLADE_TRANS_SUBTYPE
= 15; // 39
4143 FROMLEFT_TRANS_SUBTYPE
= 16; // 97
4144 FROMTOP_TRANS_SUBTYPE
= 17;
4145 FROMRIGHT_TRANS_SUBTYPE
= 18;
4146 FROMBOTTOM_TRANS_SUBTYPE
= 19;
4147 CROSSFADE_TRANS_SUBTYPE
= 20;
4148 FADETOCOLOR_TRANS_SUBTYPE
= 21;
4149 FADEFROMCOLOR_TRANS_SUBTYPE
= 22;
4150 FADEOVERCOLOR_TRANS_SUBTYPE
= 23;
4151 THREEBLADE_TRANS_SUBTYPE
= 24;
4152 EIGHTBLADE_TRANS_SUBTYPE
= 25;
4153 ONEBLADE_TRANS_SUBTYPE
= 26; // 107
4154 ACROSS_TRANS_SUBTYPE
= 27;
4156 aTransitionSubtypeInMap
= {
4157 'leftToRight' : LEFTTORIGHT_TRANS_SUBTYPE
,
4158 'topToBottom' : TOPTOBOTTOM_TRANS_SUBTYPE
,
4159 'cornersIn' : CORNERSIN_TRANS_SUBTYPE
,
4160 'cornersOut' : CORNERSOUT_TRANS_SUBTYPE
,
4161 'vertical' : VERTICAL_TRANS_SUBTYPE
,
4162 'horizontal' : HORIZONTAL_TRANS_SUBTYPE
,
4163 'down' : DOWN_TRANS_SUBTYPE
,
4164 'circle' : CIRCLE_TRANS_SUBTYPE
,
4165 'clockwiseTwelve' : CLOCKWISETWELVE_TRANS_SUBTYPE
,
4166 'clockwiseThree' : CLOCKWISETHREE_TRANS_SUBTYPE
,
4167 'clockwiseSix' : CLOCKWISESIX_TRANS_SUBTYPE
,
4168 'clockwiseNine' : CLOCKWISENINE_TRANS_SUBTYPE
,
4169 'twoBladeVertical' : TWOBLADEVERTICAL_TRANS_SUBTYPE
,
4170 'twoBladeHorizontal': TWOBLADEHORIZONTAL_TRANS_SUBTYPE
,
4171 'fourBlade' : FOURBLADE_TRANS_SUBTYPE
,
4172 'fromLeft' : FROMLEFT_TRANS_SUBTYPE
,
4173 'fromTop' : FROMTOP_TRANS_SUBTYPE
,
4174 'fromRight' : FROMRIGHT_TRANS_SUBTYPE
,
4175 'fromBottom' : FROMBOTTOM_TRANS_SUBTYPE
,
4176 'crossfade' : CROSSFADE_TRANS_SUBTYPE
,
4177 'fadeToColor' : FADETOCOLOR_TRANS_SUBTYPE
,
4178 'fadeFromColor' : FADEFROMCOLOR_TRANS_SUBTYPE
,
4179 'fadeOverColor' : FADEOVERCOLOR_TRANS_SUBTYPE
,
4180 'threeBlade' : THREEBLADE_TRANS_SUBTYPE
,
4181 'eightBlade' : EIGHTBLADE_TRANS_SUBTYPE
,
4182 'oneBlade' : ONEBLADE_TRANS_SUBTYPE
,
4183 'across' : ACROSS_TRANS_SUBTYPE
4186 aTransitionSubtypeOutMap
= [ 'default', 'leftToRight', 'topToBottom', 'cornersIn',
4187 'cornersOut', 'vertical', 'horizontal', 'down', 'circle',
4188 'clockwiseTwelve', 'clockwiseThree', 'clockwiseSix',
4189 'clockwiseNine', 'twoBladeVertical', 'twoBladeHorizontal',
4190 'fourBlade', 'fromLeft', 'fromTop', 'fromRight',
4191 'fromBottom', 'crossfade', 'fadeToColor', 'fadeFromColor',
4192 'fadeOverColor', 'threeBlade', 'eightBlade', 'oneBlade',
4197 TRANSITION_MODE_IN
= 1;
4198 TRANSITION_MODE_OUT
= 0;
4200 aTransitionModeInMap
= { 'out': TRANSITION_MODE_OUT
, 'in': TRANSITION_MODE_IN
};
4201 aTransitionModeOutMap
= [ 'out', 'in' ];
4204 // Transition Reverse Methods
4206 // Ignore direction attribute altogether.
4207 // (If it has no sensible meaning for this transition.)
4208 REVERSEMETHOD_IGNORE
= 0;
4209 // Revert by changing the direction of the parameter sweep.
4210 // (From 1->0 instead of 0->1)
4211 REVERSEMETHOD_INVERT_SWEEP
= 1;
4212 // Revert by subtracting the generated polygon from the target bound rect.
4213 REVERSEMETHOD_SUBTRACT_POLYGON
= 2;
4214 // Combination of REVERSEMETHOD_INVERT_SWEEP and REVERSEMETHOD_SUBTRACT_POLYGON.
4215 REVERSEMETHOD_SUBTRACT_AND_INVERT
= 3;
4216 // Reverse by rotating polygon 180 degrees.
4217 REVERSEMETHOD_ROTATE_180
= 4;
4218 // Reverse by flipping polygon at the y axis.
4219 REVERSEMETHOD_FLIP_X
= 5;
4220 // Reverse by flipping polygon at the x axis.
4221 REVERSEMETHOD_FLIP_Y
= 6;
4223 aReverseMethodOutMap
= ['ignore', 'invert sweep', 'subtract polygon',
4224 'subtract and invert', 'rotate 180', 'flip x', 'flip y'];
4226 // ------------------------------------------------------------------------------------------ //
4227 // Transition filter info table
4229 var aTransitionInfoTable
= {};
4231 // type: fake transition
4232 aTransitionInfoTable
[0] = {};
4234 aTransitionInfoTable
[0][0] =
4236 'class' : TRANSITION_INVALID
,
4237 'rotationAngle' : 0.0,
4240 'reverseMethod' : REVERSEMETHOD_IGNORE
,
4241 'outInvertsSweep' : false,
4242 'scaleIsotropically' : false
4246 aTransitionInfoTable
[BARWIPE_TRANSITION
] = {};
4247 aTransitionInfoTable
[BARWIPE_TRANSITION
][LEFTTORIGHT_TRANS_SUBTYPE
] =
4249 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4250 'rotationAngle' : 0.0,
4253 'reverseMethod' : REVERSEMETHOD_FLIP_X
,
4254 'outInvertsSweep' : false,
4255 'scaleIsotropically' : false
4257 aTransitionInfoTable
[BARWIPE_TRANSITION
][TOPTOBOTTOM_TRANS_SUBTYPE
] =
4259 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4260 'rotationAngle' : 90.0,
4263 'reverseMethod' : REVERSEMETHOD_FLIP_Y
,
4264 'outInvertsSweep' : false,
4265 'scaleIsotropically' : false
4268 aTransitionInfoTable
[FOURBOXWIPE_TRANSITION
] = {};
4269 aTransitionInfoTable
[FOURBOXWIPE_TRANSITION
][CORNERSIN_TRANS_SUBTYPE
] =
4270 aTransitionInfoTable
[FOURBOXWIPE_TRANSITION
][CORNERSOUT_TRANS_SUBTYPE
] =
4272 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4273 'rotationAngle' : 0.0,
4276 'reverseMethod' : REVERSEMETHOD_SUBTRACT_AND_INVERT
,
4277 'outInvertsSweep' : true,
4278 'scaleIsotropically' : false
4281 aTransitionInfoTable
[ELLIPSEWIPE_TRANSITION
] = {};
4282 aTransitionInfoTable
[ELLIPSEWIPE_TRANSITION
][CIRCLE_TRANS_SUBTYPE
] =
4284 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4285 'rotationAngle' : 0.0,
4288 'reverseMethod' : REVERSEMETHOD_SUBTRACT_AND_INVERT
,
4289 'outInvertsSweep' : true,
4290 'scaleIsotropically' : true
4292 aTransitionInfoTable
[ELLIPSEWIPE_TRANSITION
][HORIZONTAL_TRANS_SUBTYPE
] =
4294 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4295 'rotationAngle' : 0.0,
4298 'reverseMethod' : REVERSEMETHOD_SUBTRACT_AND_INVERT
,
4299 'outInvertsSweep' : true,
4300 'scaleIsotropically' : false
4302 aTransitionInfoTable
[ELLIPSEWIPE_TRANSITION
][VERTICAL_TRANS_SUBTYPE
] =
4304 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4305 'rotationAngle' : 90.0,
4308 'reverseMethod' : REVERSEMETHOD_SUBTRACT_AND_INVERT
,
4309 'outInvertsSweep' : true,
4310 'scaleIsotropically' : false
4313 aTransitionInfoTable
[CLOCKWIPE_TRANSITION
] = {};
4314 aTransitionInfoTable
[CLOCKWIPE_TRANSITION
][CLOCKWISETWELVE_TRANS_SUBTYPE
] =
4316 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4317 'rotationAngle' : 0.0,
4320 'reverseMethod' : REVERSEMETHOD_FLIP_X
,
4321 'outInvertsSweep' : true,
4322 'scaleIsotropically' : false
4324 aTransitionInfoTable
[CLOCKWIPE_TRANSITION
][CLOCKWISETHREE_TRANS_SUBTYPE
] =
4326 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4327 'rotationAngle' : 90.0,
4330 'reverseMethod' : REVERSEMETHOD_FLIP_Y
,
4331 'outInvertsSweep' : true,
4332 'scaleIsotropically' : false
4334 aTransitionInfoTable
[CLOCKWIPE_TRANSITION
][CLOCKWISESIX_TRANS_SUBTYPE
] =
4336 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4337 'rotationAngle' : 180.0,
4340 'reverseMethod' : REVERSEMETHOD_FLIP_X
,
4341 'outInvertsSweep' : true,
4342 'scaleIsotropically' : false
4344 aTransitionInfoTable
[CLOCKWIPE_TRANSITION
][CLOCKWISENINE_TRANS_SUBTYPE
] =
4346 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4347 'rotationAngle' : 270.0,
4350 'reverseMethod' : REVERSEMETHOD_FLIP_Y
,
4351 'outInvertsSweep' : true,
4352 'scaleIsotropically' : false
4355 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
] = {};
4356 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][ONEBLADE_TRANS_SUBTYPE
] =
4357 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][TWOBLADEVERTICAL_TRANS_SUBTYPE
] =
4358 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][THREEBLADE_TRANS_SUBTYPE
] =
4359 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][FOURBLADE_TRANS_SUBTYPE
] =
4360 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][EIGHTBLADE_TRANS_SUBTYPE
] =
4362 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4363 'rotationAngle' : 0.0,
4366 'reverseMethod' : REVERSEMETHOD_FLIP_X
,
4367 'outInvertsSweep' : true,
4368 'scaleIsotropically' : true
4370 aTransitionInfoTable
[PINWHEELWIPE_TRANSITION
][TWOBLADEHORIZONTAL_TRANS_SUBTYPE
] =
4372 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4373 'rotationAngle' : -90.0,
4376 'reverseMethod' : REVERSEMETHOD_FLIP_Y
,
4377 'outInvertsSweep' : true,
4378 'scaleIsotropically' : true
4381 aTransitionInfoTable
[PUSHWIPE_TRANSITION
] = {};
4382 aTransitionInfoTable
[PUSHWIPE_TRANSITION
][FROMLEFT_TRANS_SUBTYPE
] =
4383 aTransitionInfoTable
[PUSHWIPE_TRANSITION
][FROMTOP_TRANS_SUBTYPE
] =
4384 aTransitionInfoTable
[PUSHWIPE_TRANSITION
][FROMRIGHT_TRANS_SUBTYPE
] =
4385 aTransitionInfoTable
[PUSHWIPE_TRANSITION
][FROMBOTTOM_TRANS_SUBTYPE
] =
4387 'class' : TRANSITION_SPECIAL
,
4388 'rotationAngle' : 0.0,
4391 'reverseMethod' : REVERSEMETHOD_IGNORE
,
4392 'outInvertsSweep' : true,
4393 'scaleIsotropically' : false
4396 aTransitionInfoTable
[SLIDEWIPE_TRANSITION
] = {};
4397 aTransitionInfoTable
[SLIDEWIPE_TRANSITION
][FROMLEFT_TRANS_SUBTYPE
] =
4398 aTransitionInfoTable
[SLIDEWIPE_TRANSITION
][FROMTOP_TRANS_SUBTYPE
] =
4399 aTransitionInfoTable
[SLIDEWIPE_TRANSITION
][FROMRIGHT_TRANS_SUBTYPE
] =
4400 aTransitionInfoTable
[SLIDEWIPE_TRANSITION
][FROMBOTTOM_TRANS_SUBTYPE
] =
4402 'class' : TRANSITION_SPECIAL
,
4403 'rotationAngle' : 0.0,
4406 'reverseMethod' : REVERSEMETHOD_IGNORE
,
4407 'outInvertsSweep' : true,
4408 'scaleIsotropically' : false
4411 aTransitionInfoTable
[FADE_TRANSITION
] = {};
4412 aTransitionInfoTable
[FADE_TRANSITION
][CROSSFADE_TRANS_SUBTYPE
] =
4413 aTransitionInfoTable
[FADE_TRANSITION
][FADETOCOLOR_TRANS_SUBTYPE
] =
4414 aTransitionInfoTable
[FADE_TRANSITION
][FADEFROMCOLOR_TRANS_SUBTYPE
] =
4415 aTransitionInfoTable
[FADE_TRANSITION
][FADEOVERCOLOR_TRANS_SUBTYPE
] =
4417 'class' : TRANSITION_SPECIAL
,
4418 'rotationAngle' : 0.0,
4421 'reverseMethod' : REVERSEMETHOD_IGNORE
,
4422 'outInvertsSweep' : true,
4423 'scaleIsotropically' : false
4426 aTransitionInfoTable
[CHECKERBOARDWIPE_TRANSITION
] = {};
4427 aTransitionInfoTable
[CHECKERBOARDWIPE_TRANSITION
][DOWN_TRANS_SUBTYPE
] =
4429 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4430 'rotationAngle' : 90.0,
4433 'reverseMethod' : REVERSEMETHOD_FLIP_Y
,
4434 'outInvertsSweep' : true,
4435 'scaleIsotropically' : false
4437 aTransitionInfoTable
[CHECKERBOARDWIPE_TRANSITION
][ACROSS_TRANS_SUBTYPE
] =
4439 'class' : TRANSITION_CLIP_POLYPOLYGON
,
4440 'rotationAngle' : 0.0,
4443 'reverseMethod' : REVERSEMETHOD_FLIP_X
,
4444 'outInvertsSweep' : true,
4445 'scaleIsotropically' : false
4448 // ------------------------------------------------------------------------------------------ //
4449 // Transition tables
4451 function createStateTransitionTable()
4456 aSTT
[RESTART_MODE_NEVER
] = {};
4457 aSTT
[RESTART_MODE_WHEN_NOT_ACTIVE
] = {};
4458 aSTT
[RESTART_MODE_ALWAYS
] = {};
4460 // transition table for restart=NEVER, fill=REMOVE
4462 aSTT
[RESTART_MODE_NEVER
][FILL_MODE_REMOVE
] = {};
4463 aTable
[INVALID_NODE
] = INVALID_NODE
;
4464 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4465 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4466 aTable
[ACTIVE_NODE
] = ENDED_NODE
;
4467 aTable
[FROZEN_NODE
] = INVALID_NODE
; // this state is unreachable here
4468 aTable
[ENDED_NODE
] = ENDED_NODE
; // this state is a sink here (cannot restart)
4470 // transition table for restart=NEVER, fill=FREEZE
4472 aSTT
[RESTART_MODE_NEVER
][FILL_MODE_FREEZE
] =
4473 aSTT
[RESTART_MODE_NEVER
][FILL_MODE_HOLD
] =
4474 aSTT
[RESTART_MODE_NEVER
][FILL_MODE_TRANSITION
] = {};
4475 aTable
[INVALID_NODE
] = INVALID_NODE
;
4476 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4477 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4478 aTable
[ACTIVE_NODE
] = FROZEN_NODE
| ENDED_NODE
;
4479 aTable
[FROZEN_NODE
] = ENDED_NODE
;
4480 aTable
[ENDED_NODE
] = ENDED_NODE
; // this state is a sink here (cannot restart)
4482 // transition table for restart=WHEN_NOT_ACTIVE, fill=REMOVE
4484 aSTT
[RESTART_MODE_WHEN_NOT_ACTIVE
][FILL_MODE_REMOVE
] = {};
4485 aTable
[INVALID_NODE
] = INVALID_NODE
;
4486 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4487 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4488 aTable
[ACTIVE_NODE
] = ENDED_NODE
;
4489 aTable
[FROZEN_NODE
] = INVALID_NODE
; // this state is unreachable here
4490 aTable
[ENDED_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4492 // transition table for restart=WHEN_NOT_ACTIVE, fill=FREEZE
4494 aSTT
[RESTART_MODE_WHEN_NOT_ACTIVE
][FILL_MODE_FREEZE
] =
4495 aSTT
[RESTART_MODE_WHEN_NOT_ACTIVE
][FILL_MODE_HOLD
] =
4496 aSTT
[RESTART_MODE_WHEN_NOT_ACTIVE
][FILL_MODE_TRANSITION
] = {};
4497 aTable
[INVALID_NODE
] = INVALID_NODE
;
4498 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4499 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4500 aTable
[ACTIVE_NODE
] = FROZEN_NODE
| ENDED_NODE
;
4501 aTable
[FROZEN_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4502 aTable
[ENDED_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4504 // transition table for restart=ALWAYS, fill=REMOVE
4506 aSTT
[RESTART_MODE_ALWAYS
][FILL_MODE_REMOVE
] = {};
4507 aTable
[INVALID_NODE
] = INVALID_NODE
;
4508 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4509 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4510 aTable
[ACTIVE_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4511 aTable
[FROZEN_NODE
] = INVALID_NODE
; // this state is unreachable here
4512 aTable
[ENDED_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4514 // transition table for restart=ALWAYS, fill=FREEZE
4516 aSTT
[RESTART_MODE_ALWAYS
][FILL_MODE_FREEZE
] =
4517 aSTT
[RESTART_MODE_ALWAYS
][FILL_MODE_HOLD
] =
4518 aSTT
[RESTART_MODE_ALWAYS
][FILL_MODE_TRANSITION
] = {};
4519 aTable
[INVALID_NODE
] = INVALID_NODE
;
4520 aTable
[UNRESOLVED_NODE
] = RESOLVED_NODE
| ENDED_NODE
;
4521 aTable
[RESOLVED_NODE
] = ACTIVE_NODE
| ENDED_NODE
;
4522 aTable
[ACTIVE_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| FROZEN_NODE
| ENDED_NODE
;
4523 aTable
[FROZEN_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4524 aTable
[ENDED_NODE
] = RESOLVED_NODE
| ACTIVE_NODE
| ENDED_NODE
; // restart is possible
4530 var aStateTransitionTable
= createStateTransitionTable();
4533 // ------------------------------------------------------------------------------------------ //
4534 function getTransitionTable( eRestartMode
, eFillMode
)
4536 // If restart mode has not been resolved we use 'never'.
4537 // Note: RESTART_MODE_DEFAULT == RESTART_MODE_INHERIT.
4538 if( eRestartMode
== RESTART_MODE_DEFAULT
)
4540 log( 'getTransitionTable: unexpected restart mode: ' + eRestartMode
4541 + '. Used NEVER instead.');
4542 eRestartMode
= RESTART_MODE_NEVER
;
4545 // If fill mode has not been resolved we use 'remove'.
4546 // Note: FILL_MODE_DEFAULT == FILL_MODE_INHERIT
4547 if( eFillMode
== FILL_MODE_DEFAULT
||
4548 eFillMode
== FILL_MODE_AUTO
)
4550 eFillMode
= FILL_MODE_REMOVE
;
4553 return aStateTransitionTable
[eRestartMode
][eFillMode
];
4558 // ------------------------------------------------------------------------------------------ //
4561 var EVENT_TRIGGER_UNKNOWN
= 0;
4562 var EVENT_TRIGGER_ON_SLIDE_BEGIN
= 1;
4563 var EVENT_TRIGGER_ON_SLIDE_END
= 2;
4564 var EVENT_TRIGGER_BEGIN_EVENT
= 3;
4565 var EVENT_TRIGGER_END_EVENT
= 4;
4566 var EVENT_TRIGGER_ON_CLICK
= 5;
4567 var EVENT_TRIGGER_ON_DBL_CLICK
= 6;
4568 var EVENT_TRIGGER_ON_MOUSE_ENTER
= 7;
4569 var EVENT_TRIGGER_ON_MOUSE_LEAVE
= 8;
4570 var EVENT_TRIGGER_ON_NEXT_EFFECT
= 9;
4571 var EVENT_TRIGGER_ON_PREV_EFFECT
= 10;
4572 var EVENT_TRIGGER_REPEAT
= 11;
4574 aEventTriggerOutMap
= [ 'unknown', 'slideBegin', 'slideEnd', 'begin', 'end', 'click',
4575 'doubleClick', 'mouseEnter', 'mouseLeave', 'next', 'previous', 'repeat' ];
4578 function getEventTriggerType( sEventTrigger
)
4580 if( sEventTrigger
== 'begin' )
4581 return EVENT_TRIGGER_BEGIN_EVENT
;
4582 else if( sEventTrigger
== 'end' )
4583 return EVENT_TRIGGER_END_EVENT
;
4584 else if( sEventTrigger
== 'next' )
4585 return EVENT_TRIGGER_ON_NEXT_EFFECT
;
4586 else if( sEventTrigger
== 'prev' )
4587 return EVENT_TRIGGER_ON_PREV_EFFECT
;
4588 else if( sEventTrigger
== 'click' )
4589 return EVENT_TRIGGER_ON_CLICK
;
4591 return EVENT_TRIGGER_UNKNOWN
;
4596 // ------------------------------------------------------------------------------------------ //
4599 var UNKNOWN_TIMING
= 0;
4600 var OFFSET_TIMING
= 1;
4601 var WALLCLOCK_TIMING
= 2;
4602 var INDEFINITE_TIMING
= 3;
4603 var EVENT_TIMING
= 4;
4604 var SYNCBASE_TIMING
= 5;
4605 var MEDIA_TIMING
= 6;
4607 aTimingTypeOutMap
= [ 'unknown', 'offset', 'wallclock', 'indefinite', 'event', 'syncbase', 'media' ];
4611 var CHARCODE_PLUS
= '+'.charCodeAt(0);
4612 var CHARCODE_MINUS
= '-'.charCodeAt(0);
4613 var CHARCODE_0
= '0'.charCodeAt(0);
4614 var CHARCODE_9
= '9'.charCodeAt(0);
4618 function Timing( aAnimationNode
, sTimingAttribute
)
4620 this.aAnimationNode
= aAnimationNode
; // the node, the timing attribute belongs to
4621 this.sTimingDescription
= removeWhiteSpaces( sTimingAttribute
);
4622 this.eTimingType
= UNKNOWN_TIMING
;
4623 this.nOffset
= 0.0; // in seconds
4624 this.sEventBaseElementId
= ''; // the element id for event based timing
4625 this.eEventType
= EVENT_TRIGGER_UNKNOWN
; // the event type
4628 Timing
.prototype.getAnimationNode = function()
4630 return this.aAnimationNode
;
4633 Timing
.prototype.getType = function()
4635 return this.eTimingType
;
4638 Timing
.prototype.getOffset = function()
4640 return this.nOffset
;
4643 Timing
.prototype.getEventBaseElementId = function()
4645 return this.sEventBaseElementId
;
4648 Timing
.prototype.getEventType = function()
4650 return this.eEventType
;
4653 Timing
.prototype.parse = function()
4655 if( !this.sTimingDescription
)
4657 this.eTimingType
= OFFSET_TIMING
;
4661 if( this.sTimingDescription
== 'indefinite' )
4662 this.eTimingType
= INDEFINITE_TIMING
;
4665 var nFirstCharCode
= this.sTimingDescription
.charCodeAt(0);
4666 var bPositiveOffset
= !( nFirstCharCode
== CHARCODE_MINUS
);
4667 if ( ( nFirstCharCode
== CHARCODE_PLUS
) ||
4668 ( nFirstCharCode
== CHARCODE_MINUS
) ||
4669 ( ( nFirstCharCode
>= CHARCODE_0
) && ( nFirstCharCode
<= CHARCODE_9
) ) )
4672 = ( ( nFirstCharCode
== CHARCODE_PLUS
) || ( nFirstCharCode
== CHARCODE_MINUS
) )
4673 ? this.sTimingDescription
.substr( 1 )
4674 : this.sTimingDescription
;
4676 var TimeInSec
= Timing
.parseClockValue( sClockValue
);
4677 if( TimeInSec
!= undefined )
4679 this.eTimingType
= OFFSET_TIMING
;
4680 this.nOffset
= bPositiveOffset
? TimeInSec
: -TimeInSec
;
4685 var aTimingSplit
= new Array();
4686 bPositiveOffset
= true;
4687 if( this.sTimingDescription
.indexOf( '+' ) != -1 )
4689 aTimingSplit
= this.sTimingDescription
.split( '+' );
4691 else if( this.sTimingDescription
.indexOf( '-' ) != -1 )
4693 aTimingSplit
= this.sTimingDescription
.split( '-' );
4694 bPositiveOffset
= false;
4698 aTimingSplit
[0] = this.sTimingDescription
;
4699 aTimingSplit
[1] = '';
4702 if( aTimingSplit
[0].indexOf( '.' ) != -1 )
4704 var aEventSplit
= aTimingSplit
[0].split( '.' );
4705 this.sEventBaseElementId
= aEventSplit
[0];
4706 this.eEventType
= getEventTriggerType( aEventSplit
[1] );
4710 this.eEventType
= getEventTriggerType( aTimingSplit
[0] );
4713 if( this.eEventType
== EVENT_TRIGGER_UNKNOWN
)
4716 if( ( this.eEventType
== EVENT_TRIGGER_BEGIN_EVENT
) ||
4717 ( this.eEventType
== EVENT_TRIGGER_END_EVENT
) )
4719 this.eTimingType
= SYNCBASE_TIMING
;
4723 this.eTimingType
= EVENT_TIMING
;
4726 if( aTimingSplit
[1] )
4728 sClockValue
= aTimingSplit
[1];
4729 TimeInSec
= Timing
.parseClockValue( sClockValue
);
4730 if( TimeInSec
!= undefined )
4732 this.nOffset
= ( bPositiveOffset
) ? TimeInSec
: -TimeInSec
;
4736 this.eTimingType
= UNKNOWN_TIMING
;
4745 Timing
.parseClockValue = function( sClockValue
)
4750 var nTimeInSec
= undefined;
4752 var reFullClockValue
= /^([0-9]+):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?$/;
4753 var rePartialClockValue
= /^([0-5][0-9]):([0-5][0-9])(.[0-9]+)?$/;
4754 var reTimeCountValue
= /^([0-9]+)(.[0-9]+)?(h|min|s|ms)?$/;
4756 if( reFullClockValue
.test( sClockValue
) )
4758 var aClockTimeParts
= reFullClockValue
.exec( sClockValue
);
4760 var nHours
= parseInt( aClockTimeParts
[1] );
4761 var nMinutes
= parseInt( aClockTimeParts
[2] );
4762 var nSeconds
= parseInt( aClockTimeParts
[3] );
4763 if( aClockTimeParts
[4] )
4764 nSeconds
+= parseFloat( aClockTimeParts
[4] );
4766 nTimeInSec
= ( ( nHours
* 60 ) + nMinutes
) * 60 + nSeconds
;
4769 else if( rePartialClockValue
.test( sClockValue
) )
4771 aClockTimeParts
= rePartialClockValue
.exec( sClockValue
);
4773 nMinutes
= parseInt( aClockTimeParts
[1] );
4774 nSeconds
= parseInt( aClockTimeParts
[2] );
4775 if( aClockTimeParts
[3] )
4776 nSeconds
+= parseFloat( aClockTimeParts
[3] );
4778 nTimeInSec
= nMinutes
* 60 + nSeconds
;
4780 else if( reTimeCountValue
.test( sClockValue
) )
4782 aClockTimeParts
= reTimeCountValue
.exec( sClockValue
);
4784 var nTimeCount
= parseInt( aClockTimeParts
[1] );
4785 if( aClockTimeParts
[2] )
4786 nTimeCount
+= parseFloat( aClockTimeParts
[2] );
4788 if( aClockTimeParts
[3] )
4790 if( aClockTimeParts
[3] == 'h' )
4792 nTimeInSec
= nTimeCount
* 3600;
4794 else if( aClockTimeParts
[3] == 'min' )
4796 nTimeInSec
= nTimeCount
* 60;
4798 else if( aClockTimeParts
[3] == 's' )
4800 nTimeInSec
= nTimeCount
;
4802 else if( aClockTimeParts
[3] == 'ms' )
4804 nTimeInSec
= nTimeCount
/ 1000;
4809 nTimeInSec
= nTimeCount
;
4815 nTimeInSec
= parseFloat( nTimeInSec
.toFixed( 3 ) );
4819 Timing
.prototype.info = function( bVerbose
)
4826 sInfo
= 'description: ' + this.sTimingDescription
+ ', ';
4828 sInfo
+= ', type: ' + aTimingTypeOutMap
[ this.getType() ];
4829 sInfo
+= ', offset: ' + this.getOffset();
4830 sInfo
+= ', event base element id: ' + this.getEventBaseElementId();
4831 sInfo
+= ', timing event type: ' + aEventTriggerOutMap
[ this.getEventType() ];
4835 switch( this.getType() )
4837 case INDEFINITE_TIMING
:
4838 sInfo
+= 'indefinite';
4841 sInfo
+= this.getOffset();
4844 case SYNCBASE_TIMING
:
4845 if( this.getEventBaseElementId() )
4846 sInfo
+= this.getEventBaseElementId() + '.';
4847 sInfo
+= aEventTriggerOutMap
[ this.getEventType() ];
4848 if( this.getOffset() )
4850 if( this.getOffset() > 0 )
4852 sInfo
+= this.getOffset();
4862 // ------------------------------------------------------------------------------------------ //
4863 function Duration( sDurationAttribute
)
4865 this.bIndefinite
= false;
4866 this.bMedia
= false;
4867 this.nValue
= undefined;
4868 this.bDefined
= false;
4870 if( !sDurationAttribute
)
4873 if( sDurationAttribute
== 'indefinite' )
4874 this.bIndefinite
= true;
4875 else if( sDurationAttribute
== 'media' )
4879 this.nValue
= Timing
.parseClockValue( sDurationAttribute
);
4880 if( this.nValue
<= 0.0 )
4881 this.nValue
= 0.001; // duration must be always greater than 0
4883 this.bDefined
= true;
4887 Duration
.prototype.isSet = function()
4889 return this.bDefined
;
4892 Duration
.prototype.isIndefinite = function()
4894 return this.bIndefinite
;
4897 Duration
.prototype.isMedia = function()
4902 Duration
.prototype.isValue = function()
4904 return this.nValue
!= undefined;
4907 Duration
.prototype.getValue= function()
4912 Duration
.prototype.info= function()
4916 if( this.isIndefinite() )
4917 sInfo
= 'indefinite';
4918 else if( this.isMedia() )
4920 else if( this.getValue() )
4921 sInfo
= this.getValue();
4928 // ------------------------------------------------------------------------------------------ //
4929 function AnimationNode()
4933 AnimationNode
.prototype.init = function() {};
4934 AnimationNode
.prototype.resolve = function() {};
4935 AnimationNode
.prototype.activate = function() {};
4936 AnimationNode
.prototype.deactivate = function() {};
4937 AnimationNode
.prototype.end = function() {};
4938 AnimationNode
.prototype.getState = function() {};
4939 AnimationNode
.prototype.registerDeactivatingListener = function() {};
4940 AnimationNode
.prototype.notifyDeactivating = function() {};
4944 // ------------------------------------------------------------------------------------------ //
4945 function NodeContext( aSlideShowContext
)
4947 this.aContext
= aSlideShowContext
;
4948 this.aAnimationNodeMap
= null;
4949 this.aAnimatedElementMap
= null;
4950 this.aSourceEventElementMap
= null;
4951 this.nStartDelay
= 0.0;
4952 this.bFirstRun
= undefined;
4953 this.aSlideHeight
= HEIGHT
;
4954 this.aSlideWidth
= WIDTH
;
4958 NodeContext
.prototype.makeSourceEventElement = function( sId
, aEventBaseElem
)
4960 if( !aEventBaseElem
)
4962 log( 'NodeContext.makeSourceEventElement: event base element is not valid' );
4966 if( !this.aContext
.aEventMultiplexer
)
4968 log( 'NodeContext.makeSourceEventElement: event multiplexer not initialized' );
4972 if( !this.aSourceEventElementMap
[ sId
] )
4974 this.aSourceEventElementMap
[ sId
] = new SourceEventElement( sId
, aEventBaseElem
, this.aContext
.aEventMultiplexer
);
4976 return this.aSourceEventElementMap
[ sId
];
4981 // ------------------------------------------------------------------------------------------ //
4982 function StateTransition( aBaseNode
)
4984 this.aNode
= aBaseNode
;
4985 this.eToState
= INVALID_NODE
;
4988 StateTransition
.prototype.enter = function( eNodeState
, bForce
)
4990 if( !bForce
) bForce
= false;
4992 if( this.eToState
!= INVALID_NODE
)
4994 log( 'StateTransition.enter: commit() before enter()ing again!' );
4997 if( !bForce
&& !this.aNode
.isTransition( this.aNode
.getState(), eNodeState
) )
5000 // recursion detection:
5001 if( ( this.aNode
.nCurrentStateTransition
& eNodeState
) != 0 )
5002 return false; // already in wanted transition
5005 this.aNode
.nCurrentStateTransition
|= eNodeState
;
5006 this.eToState
= eNodeState
;
5010 StateTransition
.prototype.commit = function()
5012 if( this.eToState
!= INVALID_NODE
)
5014 this.aNode
.eCurrentState
= this.eToState
;
5019 StateTransition
.prototype.clear = function()
5021 if( this.eToState
!= INVALID_NODE
)
5023 this.aNode
.nCurrentStateTransition
&= ~this.eToState
;
5024 this.eToState
= INVALID_NODE
;
5030 // ------------------------------------------------------------------------------------------ //
5031 function BaseNode( aAnimElem
, aParentNode
, aNodeContext
)
5033 this.nId
= getUniqueId();
5034 this.sClassName
= 'BaseNode';
5037 log( 'BaseNode(id:' + this.nId
+ ') constructor: aAnimElem is not valid' );
5040 log( 'BaseNode(id:' + this.nId
+ ') constructor: aNodeContext is not valid' );
5042 if( !aNodeContext
.aContext
)
5043 log( 'BaseNode(id:' + this.nId
+ ') constructor: aNodeContext.aContext is not valid' );
5046 this.bIsContainer
= false;
5047 this.aElement
= aAnimElem
;
5048 this.aParentNode
= aParentNode
;
5049 this.aNodeContext
= aNodeContext
;
5050 this.aContext
= aNodeContext
.aContext
;
5051 this.nStartDelay
= aNodeContext
.nStartDelay
;
5052 this.eCurrentState
= UNRESOLVED_NODE
;
5053 this.nCurrentStateTransition
= 0;
5054 this.aDeactivatingListenerArray
= new Array();
5055 this.aActivationEvent
= null;
5056 this.aDeactivationEvent
= null;
5059 this.aDuration
= null;
5061 this.bMainSequenceRootNode
= false;
5062 this.bInteractiveSequenceRootNode
= false;
5063 this.eFillMode
= FILL_MODE_FREEZE
;
5064 this.eRestartMode
= RESTART_MODE_NEVER
;
5065 this.nReapeatCount
= undefined;
5066 this.nAccelerate
= 0.0;
5067 this.nDecelerate
= 0.0;
5068 this.bAutoReverse
= false;
5071 extend( BaseNode
, AnimationNode
);
5074 BaseNode
.prototype.getId = function()
5079 BaseNode
.prototype.parseElement = function()
5081 var aAnimElem
= this.aElement
;
5084 var sIdAttr
= aAnimElem
.getAttributeNS( NSS
['xml'], 'id' );
5085 // we append the animation node to the Animation Node Map so that it can be retrieved
5086 // by the registerEvent routine for resolving begin values of type 'id.begin', 'id.end'
5088 this.aNodeContext
.aAnimationNodeMap
[ sIdAttr
] = this;
5092 var sBeginAttr
= aAnimElem
.getAttribute( 'begin' );
5093 this.aBegin
= new Timing( this, sBeginAttr
);
5094 this.aBegin
.parse();
5098 var sEndAttr
= aAnimElem
.getAttribute( 'end' );
5101 this.aEnd
= new Timing( this, sEndAttr
);
5106 this.aDuration
= null;
5107 var sDurAttr
= aAnimElem
.getAttribute( 'dur' );
5108 this.aDuration
= new Duration( sDurAttr
);
5109 if( !this.aDuration
.isSet() )
5111 if( this.isContainer() )
5112 this.aDuration
= null;
5114 this.aDuration
= new Duration( 'indefinite' );
5118 var sFillAttr
= aAnimElem
.getAttribute( 'fill' );
5119 if( sFillAttr
&& aFillModeInMap
[ sFillAttr
])
5120 this.eFillMode
= aFillModeInMap
[ sFillAttr
];
5122 this.eFillMode
= FILL_MODE_DEFAULT
;
5124 // restart attribute
5125 var sRestartAttr
= aAnimElem
.getAttribute( 'restart' );
5126 if( sRestartAttr
&& aRestartModeInMap
[ sRestartAttr
] )
5127 this.eRestartMode
= aRestartModeInMap
[ sRestartAttr
];
5129 this.eRestartMode
= RESTART_MODE_DEFAULT
;
5131 // repeatCount attribute
5132 var sRepeatCount
= aAnimElem
.getAttribute( 'repeatCount' );
5134 this.nReapeatCount
= 1;
5136 this.nReapeatCount
= parseFloat( sRepeatCount
);
5137 if( ( this.nReapeatCount
== NaN
) && ( sRepeatCount
!= 'indefinite' ) )
5138 this.nReapeatCount
= 1;
5140 // accelerate attribute
5141 this.nAccelerate
= 0.0;
5142 var sAccelerateAttr
= aAnimElem
.getAttribute( 'accelerate' );
5143 if( sAccelerateAttr
)
5144 this.nAccelerate
= parseFloat( sAccelerateAttr
);
5145 if( this.nAccelerate
== NaN
)
5146 this.nAccelerate
= 0.0;
5148 // decelerate attribute
5149 this.nDecelerate
= 0.0;
5150 var sDecelerateAttr
= aAnimElem
.getAttribute( 'decelerate' );
5151 if( sDecelerateAttr
)
5152 this.nDecelerate
= parseFloat( sDecelerateAttr
);
5153 if( this.nDecelerate
== NaN
)
5154 this.nDecelerate
= 0.0;
5156 // autoReverse attribute
5157 this.bAutoreverse
= false;
5158 var sAutoReverseAttr
= aAnimElem
.getAttribute( 'autoReverse' );
5159 if( sAutoReverseAttr
== 'true' )
5160 this.bAutoreverse
= true;
5163 // resolve fill value
5164 if( this.eFillMode
== FILL_MODE_DEFAULT
)
5165 if( this.getParentNode() )
5166 this.eFillMode
= this.getParentNode().getFillMode();
5168 this.eFillMode
= FILL_MODE_AUTO
;
5170 if( this.eFillMode
== FILL_MODE_AUTO
) // see SMIL recommendation document
5172 this.eFillMode
= ( this.aEnd
||
5173 ( this.nReapeatCount
!= 1) ||
5179 // resolve restart value
5180 if( this.eRestartMode
== RESTART_MODE_DEFAULT
)
5181 if( this.getParentNode() )
5182 this.eRestartMode
= this.getParentNode().getRestartMode();
5184 // SMIL recommendation document says to set it to 'always'
5185 this.eRestartMode
= RESTART_MODE_ALWAYS
;
5187 // resolve accelerate and decelerate attributes
5188 // from the SMIL recommendation document: if the individual values of the accelerate
5189 // and decelerate attributes are between 0 and 1 and the sum is greater than 1,
5190 // then both the accelerate and decelerate attributes will be ignored and the timed
5191 // element will behave as if neither attribute was specified.
5192 if( ( this.nAccelerate
+ this.nDecelerate
) > 1.0 )
5194 this.nAccelerate
= 0.0;
5195 this.nDecelerate
= 0.0;
5198 this.aStateTransTable
= getTransitionTable( this.getRestartMode(), this.getFillMode() );
5203 BaseNode
.prototype.getParentNode = function()
5205 return this.aParentNode
;
5208 BaseNode
.prototype.init = function()
5210 this.DBG( this.callInfo( 'init' ) );
5211 if( ! this.checkValidNode() )
5213 if( this.aActivationEvent
)
5214 this.aActivationEvent
.dispose();
5215 if( this.aDeactivationEvent
)
5216 this.aDeactivationEvent
.dispose();
5218 this.eCurrentState
= UNRESOLVED_NODE
;
5220 return this.init_st();
5223 BaseNode
.prototype.resolve = function()
5225 if( ! this.checkValidNode() )
5228 this.DBG( this.callInfo( 'resolve' ) );
5230 if( this.eCurrentState
== RESOLVED_NODE
)
5231 log( 'BaseNode.resolve: already in RESOLVED state' );
5233 var aStateTrans
= new StateTransition( this );
5235 if( aStateTrans
.enter( RESOLVED_NODE
) &&
5236 this.isTransition( RESOLVED_NODE
, ACTIVE_NODE
) &&
5239 aStateTrans
.commit();
5241 if( this.aActivationEvent
)
5243 this.aActivationEvent
.charge();
5247 this.aActivationEvent
= makeDelay( bind( this, this.activate
), this.getBegin().getOffset() + this.nStartDelay
);
5249 registerEvent( this.getId(), this.getBegin(), this.aActivationEvent
, this.aNodeContext
);
5257 BaseNode
.prototype.activate = function()
5259 if( ! this.checkValidNode() )
5262 if( this.eCurrentState
== ACTIVE_NODE
)
5263 log( 'BaseNode.activate: already in ACTIVE state' );
5265 this.DBG( this.callInfo( 'activate' ), getCurrentSystemTime() );
5267 var aStateTrans
= new StateTransition( this );
5269 if( aStateTrans
.enter( ACTIVE_NODE
) )
5272 aStateTrans
.commit();
5273 if( !this.aContext
.aEventMultiplexer
)
5274 log( 'BaseNode.activate: this.aContext.aEventMultiplexer is not valid' );
5275 this.aContext
.aEventMultiplexer
.notifyEvent( EVENT_TRIGGER_BEGIN_EVENT
, this.getId() );
5281 BaseNode
.prototype.deactivate = function()
5283 if( this.inStateOrTransition( ENDED_NODE
| FROZEN_NODE
) || !this.checkValidNode() )
5286 if( this.isTransition( this.eCurrentState
, FROZEN_NODE
) )
5288 this.DBG( this.callInfo( 'deactivate' ), getCurrentSystemTime() );
5290 var aStateTrans
= new StateTransition( this );
5291 if( aStateTrans
.enter( FROZEN_NODE
, true /* FORCE */ ) )
5293 this.deactivate_st( FROZEN_NODE
);
5294 aStateTrans
.commit();
5296 this.notifyEndListeners();
5298 if( this.aActivationEvent
)
5299 this.aActivationEvent
.dispose();
5300 if( this.aDeactivationEvent
)
5301 this.aDeactivationEvent
.dispose();
5308 // state has changed either to FROZEN or ENDED
5311 BaseNode
.prototype.end = function()
5313 var bIsFrozenOrInTransitionToFrozen
= this.inStateOrTransition( FROZEN_NODE
);
5314 if( this.inStateOrTransition( ENDED_NODE
) || !this.checkValidNode() )
5317 if( !(this.isTransition( this.eCurrentState
, ENDED_NODE
) ) )
5318 log( 'BaseNode.end: end state not reachable in transition table' );
5320 this.DBG( this.callInfo( 'end' ), getCurrentSystemTime() );
5322 var aStateTrans
= new StateTransition( this );
5323 if( aStateTrans
.enter( ENDED_NODE
, true /* FORCE */ ) )
5325 this.deactivate_st( ENDED_NODE
);
5326 aStateTrans
.commit();
5328 // if is FROZEN or is to be FROZEN, then
5329 // will/already notified deactivating listeners
5330 if( !bIsFrozenOrInTransitionToFrozen
)
5331 this.notifyEndListeners();
5333 if( this.aActivationEvent
)
5334 this.aActivationEvent
.dispose();
5335 if( this.aDeactivationEvent
)
5336 this.aDeactivationEvent
.dispose();
5340 BaseNode
.prototype.dispose = function()
5342 if( this.aActivationEvent
)
5343 this.aActivationEvent
.dispose();
5344 if( this.aDeactivationEvent
)
5345 this.aDeactivationEvent
.dispose();
5346 this.aDeactivatingListenerArray
= new Array();
5349 BaseNode
.prototype.getState = function()
5351 return this.eCurrentState
;
5354 BaseNode
.prototype.registerDeactivatingListener = function( aNotifiee
)
5356 if (! this.checkValidNode())
5361 log( 'BaseNode.registerDeactivatingListener(): invalid notifee' );
5364 this.aDeactivatingListenerArray
.push( aNotifiee
);
5369 BaseNode
.prototype.notifyDeactivating = function( aNotifier
)
5371 assert( ( aNotifier
.getState() == FROZEN_NODE
) || ( aNotifier
.getState() == ENDED_NODE
),
5372 'BaseNode.notifyDeactivating: Notifier node is neither in FROZEN nor in ENDED state' );
5375 BaseNode
.prototype.isMainSequenceRootNode = function()
5377 return this.bMainSequenceRootNode
;
5380 BaseNode
.prototype.isInteractiveSequenceRootNode = function()
5382 return this.bInteractiveSequenceRootNode
;
5385 BaseNode
.prototype.makeDeactivationEvent = function( nDelay
)
5387 if( this.aDeactivationEvent
)
5389 this.aDeactivationEvent
.charge();
5393 if( typeof( nDelay
) == typeof(0) )
5394 this.aDeactivationEvent
= makeDelay( bind( this, this.deactivate
), nDelay
);
5396 this.aDeactivationEvent
= null;
5398 return this.aDeactivationEvent
;
5401 BaseNode
.prototype.scheduleDeactivationEvent = function( aEvent
)
5403 this.DBG( this.callInfo( 'scheduleDeactivationEvent' ) );
5407 if( this.getDuration() && this.getDuration().isValue() )
5408 aEvent
= this.makeDeactivationEvent( this.getDuration().getValue() );
5412 this.aContext
.aTimerEventQueue
.addEvent( aEvent
);
5416 BaseNode
.prototype.checkValidNode = function()
5418 return ( this.eCurrentState
!= INVALID_NODE
);
5421 BaseNode
.prototype.init_st = function()
5426 BaseNode
.prototype.resolve_st = function()
5431 BaseNode
.prototype.activate_st = function()
5433 this.scheduleDeactivationEvent();
5436 BaseNode
.prototype.deactivate_st = function( aNodeState
)
5441 BaseNode
.prototype.notifyEndListeners = function()
5443 var nDeactivatingListenerCount
= this.aDeactivatingListenerArray
.length
;
5445 for( var i
= 0; i
< nDeactivatingListenerCount
; ++i
)
5447 this.aDeactivatingListenerArray
[i
].notifyDeactivating( this );
5450 this.aContext
.aEventMultiplexer
.notifyEvent( EVENT_TRIGGER_END_EVENT
, this.getId() );
5451 if( this.getParentNode() && this.getParentNode().isMainSequenceRootNode() )
5452 this.aContext
.aEventMultiplexer
.notifyNextEffectEndEvent();
5455 BaseNode
.prototype.getContext = function()
5457 return this.aContext
;
5460 BaseNode
.prototype.isTransition = function( eFromState
, eToState
)
5462 return ( ( this.aStateTransTable
[ eFromState
] & eToState
) != 0 );
5465 BaseNode
.prototype.inStateOrTransition = function( nMask
)
5467 return ( ( ( this.eCurrentState
& nMask
) != 0 ) || ( ( this.nCurrentStateTransition
& nMask
) != 0 ) );
5470 BaseNode
.prototype.isContainer = function()
5472 return this.bIsContainer
;
5475 BaseNode
.prototype.getBegin = function()
5480 BaseNode
.prototype.getDuration = function()
5482 return this.aDuration
;
5485 BaseNode
.prototype.getEnd = function()
5490 BaseNode
.prototype.getFillMode = function()
5492 return this.eFillMode
;
5495 BaseNode
.prototype.getRestartMode = function()
5497 return this.eRestartMode
;
5500 BaseNode
.prototype.getRepeatCount = function()
5502 return this.nReapeatCount
;
5505 BaseNode
.prototype.getAccelerateValue = function()
5507 return this.nAccelerate
;
5510 BaseNode
.prototype.getDecelerateValue = function()
5512 return this.nDecelerate
;
5515 BaseNode
.prototype.isAutoReverseEnabled = function()
5517 return this.bAutoreverse
;
5520 BaseNode
.prototype.info = function( bVerbose
)
5522 var sInfo
= 'class name: ' + this.sClassName
;
5523 sInfo
+= '; element name: ' + this.aElement
.localName
;
5524 sInfo
+= '; id: ' + this.getId();
5525 sInfo
+= '; state: ' + getNodeStateName( this.getState() );
5530 sInfo
+= '; is container: ' + this.isContainer();
5533 if( this.getBegin() )
5534 sInfo
+= '; begin: ' + this.getBegin().info();
5537 if( this.getDuration() )
5538 sInfo
+= '; dur: ' + this.getDuration().info();
5542 sInfo
+= '; end: ' + this.getEnd().info();
5545 if( this.getFillMode() )
5546 sInfo
+= '; fill: ' + aFillModeOutMap
[ this.getFillMode() ];
5549 if( this.getRestartMode() )
5550 sInfo
+= '; restart: ' + aRestartModeOutMap
[ this.getRestartMode() ];
5553 if( this.getRepeatCount() && ( this.getRepeatCount() != 1.0 ) )
5554 sInfo
+= '; repeatCount: ' + this.getRepeatCount();
5557 if( this.getAccelerateValue() )
5558 sInfo
+= '; accelerate: ' + this.getAccelerateValue();
5561 if( this.getDecelerateValue() )
5562 sInfo
+= '; decelerate: ' + this.getDecelerateValue();
5565 if( this.isAutoReverseEnabled() )
5566 sInfo
+= '; autoReverse: true';
5573 BaseNode
.prototype.callInfo = function( sMethodName
)
5575 var sInfo
= this.sClassName
+
5576 '( ' + this.getId() +
5577 ', ' + getNodeStateName( this.getState() ) +
5578 ' ).' + sMethodName
;
5582 BaseNode
.prototype.DBG = function( sMessage
, nTime
)
5584 ANIMDBG
.print( sMessage
, nTime
);
5589 // ------------------------------------------------------------------------------------------ //
5590 function AnimationBaseNode( aAnimElem
, aParentNode
, aNodeContext
)
5592 AnimationBaseNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
5594 this.sClassName
= 'AnimationBaseNode';
5595 this.bIsContainer
= false;
5596 this.aTargetElement
= null;
5597 this.bIsTargetTextElement
= false
5598 this.aAnimatedElement
= null;
5599 this.aActivity
= null;
5601 this.nMinFrameCount
= undefined;
5602 this.eAdditiveMode
= undefined;
5605 extend( AnimationBaseNode
, BaseNode
);
5608 AnimationBaseNode
.prototype.parseElement = function()
5610 var bRet
= AnimationBaseNode
.superclass
.parseElement
.call( this );
5612 var aAnimElem
= this.aElement
;
5614 // targetElement attribute
5615 this.aTargetElement
= null;
5616 var sTargetElementAttr
= aAnimElem
.getAttribute( 'targetElement' );
5617 if( sTargetElementAttr
)
5618 this.aTargetElement
= document
.getElementById( sTargetElementAttr
);
5620 if( !this.aTargetElement
)
5622 this.eCurrentState
= INVALID_NODE
;
5623 log( 'AnimationBaseNode.parseElement: target element not found: ' + sTargetElementAttr
);
5626 // sub-item attribute for text animated element
5627 var sSubItemAttr
= aAnimElem
.getAttribute( 'sub-item' );
5628 this.bIsTargetTextElement
= ( sSubItemAttr
&& ( sSubItemAttr
=== 'text' ) );
5630 // additive attribute
5631 var sAdditiveAttr
= aAnimElem
.getAttribute( 'additive' );
5632 if( sAdditiveAttr
&& aAddittiveModeInMap
[sAdditiveAttr
] )
5633 this.eAdditiveMode
= aAddittiveModeInMap
[sAdditiveAttr
];
5635 this.eAdditiveMode
= ADDITIVE_MODE_REPLACE
;
5637 // set up min frame count value;
5638 this.nMinFrameCount
= ( this.getDuration().isValue() )
5639 ? ( this.getDuration().getValue() * MINIMUM_FRAMES_PER_SECONDS
)
5640 : MINIMUM_FRAMES_PER_SECONDS
;
5641 if( this.nMinFrameCount
< 1.0 )
5642 this.nMinFrameCount
= 1;
5643 else if( this.nMinFrameCount
> MINIMUM_FRAMES_PER_SECONDS
)
5644 this.nMinFrameCount
= MINIMUM_FRAMES_PER_SECONDS
;
5647 if( this.aTargetElement
)
5649 // set up target element initial visibility
5650 if( aAnimElem
.getAttribute( 'attributeName' ) === 'visibility' )
5652 if( aAnimElem
.getAttribute( 'to' ) === 'visible' )
5653 this.aTargetElement
.setAttribute( 'visibility', 'hidden' );
5656 // create animated element
5657 if( !this.aNodeContext
.aAnimatedElementMap
[ sTargetElementAttr
] )
5659 if( this.bIsTargetTextElement
)
5661 this.aNodeContext
.aAnimatedElementMap
[ sTargetElementAttr
]
5662 = new AnimatedTextElement( this.aTargetElement
);
5666 this.aNodeContext
.aAnimatedElementMap
[ sTargetElementAttr
]
5667 = new AnimatedElement( this.aTargetElement
);
5670 this.aAnimatedElement
= this.aNodeContext
.aAnimatedElementMap
[ sTargetElementAttr
];
5672 // set additive mode
5673 this.aAnimatedElement
.setAdditiveMode( this.eAdditiveMode
);
5680 AnimationBaseNode
.prototype.init_st = function()
5682 if( this.aActivity
)
5683 this.aActivity
.activate( makeEvent( bind( this, this.deactivate
) ) );
5685 this.aActivity
= this.createActivity();
5689 AnimationBaseNode
.prototype.resolve_st = function()
5694 AnimationBaseNode
.prototype.activate_st = function()
5696 if( this.aActivity
)
5698 this.saveStateOfAnimatedElement();
5699 this.aActivity
.setTargets( this.getAnimatedElement() );
5700 if( this.getContext().bIsSkipping
)
5702 this.aActivity
.end();
5706 this.getContext().aActivityQueue
.addActivity( this.aActivity
);
5711 AnimationBaseNode
.superclass
.scheduleDeactivationEvent
.call( this );
5714 // TODO: only for testing! to be removed!
5715 //AnimationBaseNode.superclass.scheduleDeactivationEvent.call( this );
5718 AnimationBaseNode
.prototype.deactivate_st = function( eDestState
)
5720 if( eDestState
== FROZEN_NODE
)
5722 if( this.aActivity
)
5723 this.aActivity
.end();
5725 if( eDestState
== ENDED_NODE
)
5727 if( this.aActivity
)
5728 this.aActivity
.dispose();
5729 if( ( this.getFillMode() == FILL_MODE_REMOVE
) && this.getAnimatedElement() )
5730 this.removeEffect();
5734 AnimationBaseNode
.prototype.createActivity = function()
5736 log( 'AnimationBaseNode.createActivity: abstract method called' );
5739 AnimationBaseNode
.prototype.fillActivityParams = function()
5743 var nDuration
= 0.001;
5744 if( this.getDuration().isValue() )
5746 nDuration
= this.getDuration().getValue();
5750 log( 'AnimationBaseNode.fillActivityParams: duration is not a number' );
5753 // create and set up activity params
5754 var aActivityParamSet
= new ActivityParamSet();
5756 aActivityParamSet
.aEndEvent
= makeEvent( bind( this, this.deactivate
) );
5757 aActivityParamSet
.aTimerEventQueue
= this.aContext
.aTimerEventQueue
;
5758 aActivityParamSet
.aActivityQueue
= this.aContext
.aActivityQueue
;
5759 aActivityParamSet
.nMinDuration
= nDuration
;
5760 aActivityParamSet
.nMinNumberOfFrames
= this.getMinFrameCount();
5761 aActivityParamSet
.bAutoReverse
= this.isAutoReverseEnabled();
5762 aActivityParamSet
.nRepeatCount
= this.getRepeatCount();
5763 aActivityParamSet
.nAccelerationFraction
= this.getAccelerateValue();
5764 aActivityParamSet
.nDecelerationFraction
= this.getDecelerateValue();
5765 aActivityParamSet
.nSlideWidth
= this.aNodeContext
.aSlideWidth
;
5766 aActivityParamSet
.nSlideHeight
= this.aNodeContext
.aSlideHeight
;
5768 return aActivityParamSet
;
5771 AnimationBaseNode
.prototype.hasPendingAnimation = function()
5776 AnimationBaseNode
.prototype.saveStateOfAnimatedElement = function()
5778 this.getAnimatedElement().saveState( this.getId() );
5781 AnimationBaseNode
.prototype.removeEffect = function()
5783 this.getAnimatedElement().restoreState( this.getId() );
5786 AnimationBaseNode
.prototype.getTargetElement = function()
5788 return this.aTargetElement
;
5791 AnimationBaseNode
.prototype.getAnimatedElement = function()
5793 return this.aAnimatedElement
;
5796 AnimationBaseNode
.prototype.dispose= function()
5798 if( this.aActivity
)
5799 this.aActivity
.dispose();
5801 AnimationBaseNode
.superclass
.dispose
.call( this );
5804 AnimationBaseNode
.prototype.getMinFrameCount = function()
5806 return this.nMinFrameCount
;
5809 AnimationBaseNode
.prototype.getAdditiveMode = function()
5811 return this.eAdditiveMode
;
5814 AnimationBaseNode
.prototype.info = function( bVerbose
)
5816 var sInfo
= AnimationBaseNode
.superclass
.info
.call( this, bVerbose
);
5821 if( this.getMinFrameCount() )
5822 sInfo
+= '; min frame count: ' + this.getMinFrameCount();
5825 sInfo
+= '; additive: ' + aAddittiveModeOutMap
[ this.getAdditiveMode() ];
5828 if( this.getTargetElement() )
5830 var sElemId
= this.getTargetElement().getAttribute( 'id' );
5831 sInfo
+= '; targetElement: ' + sElemId
;
5839 // ------------------------------------------------------------------------------------------ //
5840 function AnimationBaseNode2( aAnimElem
, aParentNode
, aNodeContext
)
5842 AnimationBaseNode2
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
5844 this.sAttributeName
= '';
5845 this.aToValue
= null;
5848 extend( AnimationBaseNode2
, AnimationBaseNode
);
5851 AnimationBaseNode2
.prototype.parseElement = function()
5853 var bRet
= AnimationBaseNode2
.superclass
.parseElement
.call( this );
5855 var aAnimElem
= this.aElement
;
5857 // attributeName attribute
5858 this.sAttributeName
= aAnimElem
.getAttribute( 'attributeName' );
5859 if( !this.sAttributeName
)
5861 this.eCurrentState
= INVALID_NODE
;
5862 log( 'AnimationBaseNode2.parseElement: target attribute name not found: ' + this.sAttributeName
);
5866 this.aToValue
= aAnimElem
.getAttribute( 'to' );
5871 AnimationBaseNode2
.prototype.getAttributeName = function()
5873 return this.sAttributeName
;
5876 AnimationBaseNode2
.prototype.getToValue = function()
5878 return this.aToValue
;
5881 AnimationBaseNode2
.prototype.info = function( bVerbose
)
5883 var sInfo
= AnimationBaseNode2
.superclass
.info
.call( this, bVerbose
);
5888 if( this.getAttributeName() )
5889 sInfo
+= '; attributeName: ' + this.getAttributeName();
5892 if( this.getToValue() )
5893 sInfo
+= '; to: ' + this.getToValue();
5901 // ------------------------------------------------------------------------------------------ //
5902 function AnimationBaseNode3( aAnimElem
, aParentNode
, aNodeContext
)
5904 AnimationBaseNode3
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
5906 this.eAccumulate
= undefined;
5907 this.eCalcMode
= undefined;
5908 this.aFromValue
= null;
5909 this.aByValue
= null;
5910 this.aKeyTimes
= null;
5911 this.aValues
= null;
5913 extend( AnimationBaseNode3
, AnimationBaseNode2
);
5916 AnimationBaseNode3
.prototype.parseElement = function()
5918 var bRet
= AnimationBaseNode3
.superclass
.parseElement
.call( this );
5920 var aAnimElem
= this.aElement
;
5922 // accumulate attribute
5923 this.eAccumulate
= ACCUMULATE_MODE_NONE
;
5924 var sAccumulateAttr
= aAnimElem
.getAttribute( 'accumulate' );
5925 if( sAccumulateAttr
== 'sum' )
5926 this.eAccumulate
= ACCUMULATE_MODE_SUM
;
5928 // calcMode attribute
5929 this.eCalcMode
= CALC_MODE_LINEAR
;
5930 var sCalcModeAttr
= aAnimElem
.getAttribute( 'calcMode' );
5931 if( sCalcModeAttr
&& aCalcModeInMap
[ sCalcModeAttr
] )
5932 this.eCalcMode
= aCalcModeInMap
[ sCalcModeAttr
];
5935 this.aFromValue
= aAnimElem
.getAttribute( 'from' );
5938 this.aByValue
= aAnimElem
.getAttribute( 'by' );
5940 // keyTimes attribute
5941 this.aKeyTimes
= new Array();
5942 var sKeyTimesAttr
= aAnimElem
.getAttribute( 'keyTimes' );
5943 sKeyTimesAttr
= removeWhiteSpaces( sKeyTimesAttr
);
5946 var aKeyTimes
= sKeyTimesAttr
.split( ';' );
5947 for( var i
= 0; i
< aKeyTimes
.length
; ++i
)
5948 this.aKeyTimes
.push( parseFloat( aKeyTimes
[i
] ) );
5952 var sValuesAttr
= aAnimElem
.getAttribute( 'values' );
5955 this.aValues
= sValuesAttr
.split( ';' );
5959 this.aValues
= new Array();
5965 AnimationBaseNode3
.prototype.getAccumulate = function()
5967 return this.eAccumulate
;
5970 AnimationBaseNode3
.prototype.getCalcMode = function()
5972 return this.eCalcMode
;
5975 AnimationBaseNode3
.prototype.getFromValue = function()
5977 return this.aFromValue
;
5980 AnimationBaseNode3
.prototype.getByValue = function()
5982 return this.aByValue
;
5985 AnimationBaseNode3
.prototype.getKeyTimes = function()
5987 return this.aKeyTimes
;
5990 AnimationBaseNode3
.prototype.getValues = function()
5992 return this.aValues
;
5995 AnimationBaseNode3
.prototype.info = function( bVerbose
)
5997 var sInfo
= AnimationBaseNode3
.superclass
.info
.call( this, bVerbose
);
6002 if( this.getAccumulate() )
6003 sInfo
+= '; accumulate: ' + aAccumulateModeOutMap
[ this.getAccumulate() ];
6006 sInfo
+= '; calcMode: ' + aCalcModeOutMap
[ this.getCalcMode() ];
6009 if( this.getFromValue() )
6010 sInfo
+= '; from: ' + this.getFromValue();
6013 if( this.getByValue() )
6014 sInfo
+= '; by: ' + this.getByValue();
6017 if( this.getKeyTimes().length
)
6018 sInfo
+= '; keyTimes: ' + this.getKeyTimes().join( ',' );
6021 if( this.getKeyTimes().length
)
6022 sInfo
+= '; values: ' + this.getValues().join( ',' );
6030 // ------------------------------------------------------------------------------------------ //
6031 function BaseContainerNode( aAnimElem
, aParentNode
, aNodeContext
)
6033 BaseContainerNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6035 this.sClassName
= 'BaseContainerNode';
6036 this.bIsContainer
= true;
6037 this.aChildrenArray
= new Array();
6038 this.nFinishedChildren
= 0;
6039 this.bDurationIndefinite
= false;
6040 this.nLeftIterations
= 1;
6042 this.eImpressNodeType
= undefined;
6043 this.ePresetClass
= undefined;
6044 this.ePresetId
= undefined;
6046 extend( BaseContainerNode
, BaseNode
);
6049 BaseContainerNode
.prototype.parseElement= function()
6051 var bRet
= BaseContainerNode
.superclass
.parseElement
.call( this );
6053 var aAnimElem
= this.aElement
;
6055 // node-type attribute
6056 this.eImpressNodeType
= IMPRESS_DEFAULT_NODE
;
6057 var sNodeTypeAttr
= aAnimElem
.getAttribute( 'node-type' );
6058 if( sNodeTypeAttr
&& aImpressNodeTypeInMap
[ sNodeTypeAttr
] )
6059 this.eImpressNodeType
= aImpressNodeTypeInMap
[ sNodeTypeAttr
];
6060 this.bMainSequenceRootNode
= ( this.eImpressNodeType
== IMPRESS_MAIN_SEQUENCE_NODE
);
6061 this.bInteractiveSequenceRootNode
= ( this.eImpressNodeType
== IMPRESS_INTERACTIVE_SEQUENCE_NODE
);
6063 // preset-class attribute
6064 this.ePresetClass
= undefined;
6065 var sPresetClassAttr
= aAnimElem
.getAttribute( 'preset-class' );
6066 if( sPresetClassAttr
&& aPresetClassInMap
[ sPresetClassAttr
] )
6067 this.ePresetClass
= aPresetClassInMap
[ sPresetClassAttr
];
6069 // preset-id attribute
6070 this.ePresetId
= undefined;
6071 var sPresetIdAttr
= aAnimElem
.getAttribute( 'preset-id' );
6072 if( sPresetIdAttr
&& aPresetIdInMap
[ sPresetIdAttr
] )
6073 this.ePresetId
= aPresetIdInMap
[ sPresetIdAttr
];
6076 // parse children elements
6077 var nChildrenCount
= this.aChildrenArray
.length
;
6078 for( var i
= 0; i
< nChildrenCount
; ++i
)
6080 this.aChildrenArray
[i
].parseElement();
6085 this.bDurationIndefinite
6086 = ( !this.getDuration() || this.getDuration().isIndefinite() ) &&
6087 ( !this.getEnd() || ( this.getEnd().getType() != OFFSET_TIMING
) );
6092 BaseContainerNode
.prototype.appendChildNode = function( aAnimationNode
)
6094 if( ! this.checkValidNode() )
6097 if( aAnimationNode
.registerDeactivatingListener( this ) )
6098 this.aChildrenArray
.push( aAnimationNode
);
6101 BaseContainerNode
.prototype.init_st = function()
6103 this.nLeftIterations
= this.getRepeatCount();
6105 return this.init_children();
6108 BaseContainerNode
.prototype.init_children = function()
6110 this.nFinishedChildren
= 0;
6111 var nChildrenCount
= this.aChildrenArray
.length
;
6112 var nInitChildren
= 0;
6113 for( var i
= 0; i
< nChildrenCount
; ++i
)
6115 if( this.aChildrenArray
[i
].init() )
6120 return ( nChildrenCount
== nInitChildren
);
6124 BaseContainerNode
.prototype.deactivate_st = function( eDestState
)
6126 this.nLeftIterations
= 0;
6127 if( eDestState
== FROZEN_NODE
)
6129 // deactivate all children that are not FROZEN or ENDED:
6130 this.forEachChildNode( mem_fn( 'deactivate' ), ~( FROZEN_NODE
| ENDED_NODE
) );
6134 // end all children that are not ENDED:
6135 this.forEachChildNode( mem_fn( 'end' ), ~ENDED_NODE
);
6136 if( this.getFillMode() == FILL_MODE_REMOVE
)
6137 this.removeEffect();
6141 BaseContainerNode
.prototype.hasPendingAnimation = function()
6143 var nChildrenCount
= this.aChildrenArray
.length
;
6144 for( var i
= 0; i
< nChildrenCount
; ++i
)
6146 if( this.aChildrenArray
[i
].hasPendingAnimation() )
6152 BaseContainerNode
.prototype.activate_st = function()
6154 log( 'BaseContainerNode.activate_st: abstract method called' );
6157 BaseContainerNode
.prototype.notifyDeactivating = function( aAnimationNode
)
6159 log( 'BaseContainerNode.notifyDeactivating: abstract method called' );
6162 BaseContainerNode
.prototype.isDurationIndefinite = function()
6164 return this.bDurationIndefinite
;
6167 BaseContainerNode
.prototype.isChildNode = function( aAnimationNode
)
6169 var nChildrenCount
= this.aChildrenArray
.length
;
6170 for( var i
= 0; i
< nChildrenCount
; ++i
)
6172 if( this.aChildrenArray
[i
].getId() == aAnimationNode
.getId() )
6178 BaseContainerNode
.prototype.notifyDeactivatedChild = function( aChildNode
)
6180 assert( ( aChildNode
.getState() == FROZEN_NODE
) || ( aChildNode
.getState() == ENDED_NODE
),
6181 'BaseContainerNode.notifyDeactivatedChild: passed child node is neither in FROZEN nor in ENDED state' );
6183 assert( this.getState() != INVALID_NODE
,
6184 'BaseContainerNode.notifyDeactivatedChild: this node is invalid' );
6186 if( !this.isChildNode( aChildNode
) )
6188 log( 'BaseContainerNode.notifyDeactivatedChild: unknown child notifier!' );
6192 var nChildrenCount
= this.aChildrenArray
.length
;
6194 assert( ( this.nFinishedChildren
< nChildrenCount
),
6195 'BaseContainerNode.notifyDeactivatedChild: assert(this.nFinishedChildren < nChildrenCount) failed' );
6197 ++this.nFinishedChildren
;
6198 var bFinished
= ( this.nFinishedChildren
>= nChildrenCount
);
6200 if( bFinished
&& this.isDurationIndefinite() )
6202 if( this.nLeftIterations
>= 1.0 )
6204 this.nLeftIterations
-= 1.0;
6206 if( this.nLeftIterations
>= 1.0 )
6209 var aRepetitionEvent
= makeDelay( bind( this, this.repeat
), 0.0 );
6210 this.aContext
.aTimerEventQueue
.addEvent( aRepetitionEvent
);
6221 BaseContainerNode
.prototype.repeat = function()
6223 // end all children that are not ENDED:
6224 this.forEachChildNode( mem_fn( 'end' ), ~ENDED_NODE
);
6225 this.removeEffect();
6226 var bInitialized
= this.init_children();
6229 return bInitialized
;
6232 BaseContainerNode
.prototype.removeEffect = function()
6234 var nChildrenCount
= this.aChildrenArray
.length
;
6235 if( nChildrenCount
== 0 )
6237 // We remove effect in reverse order.
6238 for( var i
= nChildrenCount
- 1; i
>= 0; --i
)
6240 if( ( this.aChildrenArray
[i
].getState() & ( FROZEN_NODE
| ENDED_NODE
) ) == 0 )
6242 log( 'BaseContainerNode.removeEffect: child(id:'
6243 + this.aChildrenArray
[i
].getId() + ') is neither frozen nor ended;'
6245 + aTransitionModeOutMap
[ this.aChildrenArray
[i
].getState() ] );
6248 this.aChildrenArray
[i
].removeEffect();
6252 BaseContainerNode
.prototype.saveStateOfAnimatedElement = function()
6254 var nChildrenCount
= this.aChildrenArray
.length
;
6255 for( var i
= 0; i
< nChildrenCount
; ++i
)
6257 this.aChildrenArray
[i
].saveStateOfAnimatedElement();
6261 BaseContainerNode
.prototype.forEachChildNode = function( aFunction
, eNodeStateMask
)
6263 if( !eNodeStateMask
)
6264 eNodeStateMask
= -1;
6266 var nChildrenCount
= this.aChildrenArray
.length
;
6267 for( var i
= 0; i
< nChildrenCount
; ++i
)
6269 if( ( eNodeStateMask
!= -1 ) && ( ( this.aChildrenArray
[i
].getState() & eNodeStateMask
) == 0 ) )
6271 aFunction( this.aChildrenArray
[i
] );
6275 BaseContainerNode
.prototype.dispose = function()
6277 var nChildrenCount
= this.aChildrenArray
.length
;
6278 for( var i
= 0; i
< nChildrenCount
; ++i
)
6280 this.aChildrenArray
[i
].dispose();
6283 BaseContainerNode
.superclass
.dispose
.call( this );
6286 BaseContainerNode
.prototype.getImpressNodeType = function()
6288 return this.eImpressNodeType
;
6291 BaseContainerNode
.prototype.info = function( bVerbose
)
6293 var sInfo
= BaseContainerNode
.superclass
.info
.call( this, bVerbose
);
6297 // impress node type
6298 if( this.getImpressNodeType() )
6299 sInfo
+= '; node-type: ' + aImpressNodeTypeOutMap
[ this.getImpressNodeType() ];
6302 var nChildrenCount
= this.aChildrenArray
.length
;
6303 for( var i
= 0; i
< nChildrenCount
; ++i
)
6306 sInfo
+= this.aChildrenArray
[i
].info( bVerbose
);
6312 // ------------------------------------------------------------------------------------------ //
6313 function ParallelTimeContainer( aAnimElem
, aParentNode
, aNodeContext
)
6315 ParallelTimeContainer
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6317 this.sClassName
= 'ParallelTimeContainer';
6319 extend( ParallelTimeContainer
, BaseContainerNode
);
6322 ParallelTimeContainer
.prototype.activate_st = function()
6324 var nChildrenCount
= this.aChildrenArray
.length
;
6325 var nResolvedChildren
= 0;
6326 for( var i
= 0; i
< nChildrenCount
; ++i
)
6328 if( this.aChildrenArray
[i
].resolve() )
6330 ++nResolvedChildren
;
6334 if( nChildrenCount
!= nResolvedChildren
)
6336 log( 'ParallelTimeContainer.activate_st: resolving all children failed' );
6341 if( this.isDurationIndefinite() && ( nChildrenCount
== 0 ) )
6343 this.scheduleDeactivationEvent( this.makeDeactivationEvent( 0.0 ) );
6347 this.scheduleDeactivationEvent();
6351 ParallelTimeContainer
.prototype.notifyDeactivating = function( aAnimationNode
)
6353 this.notifyDeactivatedChild( aAnimationNode
);
6358 // ------------------------------------------------------------------------------------------ //
6359 function SequentialTimeContainer( aAnimElem
, aParentNode
, aNodeContext
)
6361 SequentialTimeContainer
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6363 this.sClassName
= 'SequentialTimeContainer';
6364 this.bIsRewinding
= false;
6365 this.aCurrentSkipEvent
= null;
6366 this.aRewindCurrentEffectEvent
= null;
6367 this.aRewindLastEffectEvent
= null;
6369 extend( SequentialTimeContainer
, BaseContainerNode
);
6372 SequentialTimeContainer
.prototype.activate_st = function()
6374 var nChildrenCount
= this.aChildrenArray
.length
;
6375 for( ; this.nFinishedChildren
< nChildrenCount
; ++this.nFinishedChildren
)
6377 if( this.resolveChild( this.aChildrenArray
[ this.nFinishedChildren
] ) )
6380 log( 'SequentialTimeContainer.activate_st: resolving child failed!' );
6383 if( this.isDurationIndefinite() && ( ( nChildrenCount
== 0 ) || ( this.nFinishedChildren
>= nChildrenCount
) ) )
6386 this.scheduleDeactivationEvent( this.makeDeactivationEvent( 0.0 ) );
6390 this.scheduleDeactivationEvent();
6394 SequentialTimeContainer
.prototype.notifyDeactivating = function( aNotifier
)
6396 // If we are rewinding we have not to resolve the next child.
6397 if( this.bIsRewinding
)
6400 if( this.notifyDeactivatedChild( aNotifier
) )
6403 assert( this.nFinishedChildren
< this.aChildrenArray
.length
,
6404 'SequentialTimeContainer.notifyDeactivating: assertion (this.nFinishedChildren < this.aChildrenArray.length) failed' );
6406 var aNextChild
= this.aChildrenArray
[ this.nFinishedChildren
];
6408 assert( aNextChild
.getState() == UNRESOLVED_NODE
,
6409 'SequentialTimeContainer.notifyDeactivating: assertion (aNextChild.getState == UNRESOLVED_NODE) failed' );
6411 if( !this.resolveChild( aNextChild
) )
6413 // could not resolve child - since we risk to
6414 // stall the chain of events here, play it safe
6415 // and deactivate this node (only if we have
6416 // indefinite duration - otherwise, we'll get a
6417 // deactivation event, anyways).
6423 * Skip the current playing shape effect.
6424 * Requires: the current node is the main sequence root node.
6427 * An animation node representing the root node of the shape effect being
6430 SequentialTimeContainer
.prototype.skipEffect = function( aChildNode
)
6432 if( this.isChildNode( aChildNode
) )
6434 // First off we end all queued activities.
6435 this.getContext().aActivityQueue
.endAll();
6436 // We signal that we are going to skip all subsequent animations by
6437 // setting the bIsSkipping flag to 'true', then all queued events are
6438 // fired immediately. In such a way the correct order of the various
6439 // events that belong to the animation time-line is preserved.
6440 this.getContext().bIsSkipping
= true;
6441 this.getContext().aTimerEventQueue
.forceEmpty();
6442 this.getContext().bIsSkipping
= false;
6443 var aEvent
= makeEvent( bind2( aChildNode
.deactivate
, aChildNode
) );
6444 this.getContext().aTimerEventQueue
.addEvent( aEvent
);
6448 log( 'SequentialTimeContainer.skipEffect: unknown child: '
6449 + aChildNode
.getId() );
6453 /** rewindCurrentEffect
6454 * Rewind a playing shape effect.
6455 * Requires: the current node is the main sequence root node.
6458 * An animation node representing the root node of the shape effect being
6461 SequentialTimeContainer
.prototype.rewindCurrentEffect = function( aChildNode
)
6463 if( this.isChildNode( aChildNode
) )
6465 assert( !this.bIsRewinding
,
6466 'SequentialTimeContainer.rewindCurrentEffect: is already rewinding.' );
6468 // We signal we are rewinding so the notifyDeactivating method returns
6469 // immediately without increment the finished children counter and
6470 // resolve the next child.
6471 this.bIsRewinding
= true;
6472 // First off we end all queued activities.
6473 this.getContext().aActivityQueue
.endAll();
6474 // We signal that we are going to skip all subsequent animations by
6475 // setting the bIsSkipping flag to 'true', then all queued events are
6476 // fired immediately. In such a way the correct order of the various
6477 // events that belong to the animation time-line is preserved.
6478 this.getContext().bIsSkipping
= true;
6479 this.getContext().aTimerEventQueue
.forceEmpty();
6480 this.getContext().bIsSkipping
= false;
6481 // We end all new activities appended to the activity queue by
6482 // the fired events.
6483 this.getContext().aActivityQueue
.endAll();
6485 // Now we perform a final 'end' and restore the animated shape to
6486 // the state it was before the current effect was applied.
6488 aChildNode
.removeEffect();
6489 // Finally we place the child node to the 'unresolved' state and
6490 // resolve it again.
6492 this.resolveChild( aChildNode
);
6493 this.notifyRewindedEvent( aChildNode
);
6494 this.bIsRewinding
= false;
6498 log( 'SequentialTimeContainer.rewindCurrentEffect: unknown child: '
6499 + aChildNode
.getId() );
6503 /** rewindLastEffect
6504 * Rewind the last ended effect.
6505 * Requires: the current node is the main sequence root node.
6508 * An animation node representing the root node of the next shape effect
6511 SequentialTimeContainer
.prototype.rewindLastEffect = function( aChildNode
)
6513 if( this.isChildNode( aChildNode
) )
6515 assert( !this.bIsRewinding
,
6516 'SequentialTimeContainer.rewindLastEffect: is already rewinding.' );
6518 // We signal we are rewinding so the notifyDeactivating method returns
6519 // immediately without increment the finished children counter and
6520 // resolve the next child.
6521 this.bIsRewinding
= true;
6522 // We end the current effect.
6523 this.getContext().aTimerEventQueue
.forceEmpty();
6524 this.getContext().aActivityQueue
.clear();
6526 // Invoking the end method on the current child node that has not yet
6527 // been activated should not lead to any change on the animated shape.
6528 // However for safety we used to call the removeEffect method but
6529 // lately we noticed that when interactive animation sequences are
6530 // involved into the shape effect invoking such a method causes
6532 //aChildNode.removeEffect();
6534 // As we rewind the previous effect we need to decrease the finished
6535 // children counter.
6536 --this.nFinishedChildren
;
6537 var aPreviousChildNode
= this.aChildrenArray
[ this.nFinishedChildren
];
6538 // No need to invoke the end method for the previous child as it is
6539 // already in the ENDED state.
6541 aPreviousChildNode
.removeEffect();
6542 // We place the child node to the 'unresolved' state.
6543 aPreviousChildNode
.init();
6544 // We need to re-initialize the old current child too, because it is
6545 // in ENDED state now, On the contrary it cannot be resolved again later.
6547 this.resolveChild( aPreviousChildNode
);
6548 this.notifyRewindedEvent( aChildNode
);
6549 this.bIsRewinding
= false;
6553 log( 'SequentialTimeContainer.rewindLastEffect: unknown child: '
6554 + aChildNode
.getId() );
6559 * Resolve the passed child.
6560 * In case this node is a main sequence root node events for skipping and
6561 * rewinding the effect related to the passed child node are created and
6565 * An animation node representing the root node of the next shape effect
6568 * It returns true if the passed child has been resolved successfully,
6571 SequentialTimeContainer
.prototype.resolveChild = function( aChildNode
)
6573 var bResolved
= aChildNode
.resolve();
6575 if( bResolved
&& ( this.isMainSequenceRootNode() || this.isInteractiveSequenceRootNode() ) )
6577 if( this.aCurrentSkipEvent
)
6578 this.aCurrentSkipEvent
.dispose();
6579 this.aCurrentSkipEvent
= makeEvent( bind2( SequentialTimeContainer
.prototype.skipEffect
, this, aChildNode
) );
6581 if( this.aRewindCurrentEffectEvent
)
6582 this.aRewindCurrentEffectEvent
.dispose();
6583 this.aRewindCurrentEffectEvent
= makeEvent( bind2( SequentialTimeContainer
.prototype.rewindCurrentEffect
, this, aChildNode
) );
6585 if( this.aRewindLastEffectEvent
)
6586 this.aRewindLastEffectEvent
.dispose();
6587 this.aRewindLastEffectEvent
= makeEvent( bind2( SequentialTimeContainer
.prototype.rewindLastEffect
, this, aChildNode
) );
6589 if( this.isMainSequenceRootNode() )
6591 this.aContext
.aEventMultiplexer
.registerSkipEffectEvent( this.aCurrentSkipEvent
);
6592 this.aContext
.aEventMultiplexer
.registerRewindCurrentEffectEvent( this.aRewindCurrentEffectEvent
);
6593 this.aContext
.aEventMultiplexer
.registerRewindLastEffectEvent( this.aRewindLastEffectEvent
);
6595 else if( this.isInteractiveSequenceRootNode() )
6597 this.aContext
.aEventMultiplexer
.registerSkipInteractiveEffectEvent( aChildNode
.getId(), this.aCurrentSkipEvent
);
6598 this.aContext
.aEventMultiplexer
.registerRewindRunningInteractiveEffectEvent( aChildNode
.getId(), this.aRewindCurrentEffectEvent
);
6599 this.aContext
.aEventMultiplexer
.registerRewindEndedInteractiveEffectEvent( aChildNode
.getId(), this.aRewindLastEffectEvent
);
6605 SequentialTimeContainer
.prototype.notifyRewindedEvent = function( aChildNode
)
6607 if( this.isInteractiveSequenceRootNode() )
6609 this.aContext
.aEventMultiplexer
.notifyRewindedEffectEvent( aChildNode
.getId() );
6611 var sId
= aChildNode
.getBegin().getEventBaseElementId();
6614 this.aContext
.aEventMultiplexer
.notifyRewindedEffectEvent( sId
);
6619 SequentialTimeContainer
.prototype.dispose = function()
6621 if( this.aCurrentSkipEvent
)
6622 this.aCurrentSkipEvent
.dispose();
6624 SequentialTimeContainer
.superclass
.dispose
.call( this );
6629 // ------------------------------------------------------------------------------------------ //
6630 function PropertyAnimationNode( aAnimElem
, aParentNode
, aNodeContext
)
6632 PropertyAnimationNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6634 this.sClassName
= 'PropertyAnimationNode';
6636 extend( PropertyAnimationNode
, AnimationBaseNode3
);
6639 PropertyAnimationNode
.prototype.createActivity = function()
6643 var aActivityParamSet = this.fillActivityParams();
6644 var aAnimation = createPropertyAnimation( 'opacity',
6645 this.getAnimatedElement(),
6646 this.aNodeContext.aSlideWidth,
6647 this.aNodeContext.aSlideHeight );
6649 return new SimpleActivity( aActivityParamSet, aAnimation, FORWARD );
6654 if( true && this.getAttributeName() === 'x' )
6656 var sAttributeName = 'x';
6658 this.aDuration = new Duration( '2s' );
6659 this.sAttributeName = sAttributeName;
6660 this.aKeyTimes = [ 0.0, 0.25, 0.50, 0.75, 1.0 ];
6661 //this.aKeyTimes = [ 0.0, 1.0 ];
6662 var aM = 5000 / this.aNodeContext.aSlideWidth;
6663 this.aValues = [ 'x', 'x - ' + aM, 'x', 'x + ' + aM, 'x' ];
6664 //this.aValues = [ '0', 'width' ];
6666 //this.aFromValue = '';
6667 //this.aToValue = '0 + ' + aTranslationValue;
6668 //this.aByValue = aTranslationValue;
6669 //this.nRepeatCount = 3;
6671 var aActivityParamSet = this.fillActivityParams();
6673 var aAnimation = createPropertyAnimation( this.getAttributeName(),
6674 this.getAnimatedElement(),
6675 this.aNodeContext.aSlideWidth,
6676 this.aNodeContext.aSlideHeight );
6678 var aInterpolator = null;
6679 return createActivity( aActivityParamSet, this, aAnimation, aInterpolator );
6682 if( true && this.getAttributeName() === 'y' )
6684 var sAttributeName = 'height';
6685 this.aDuration = new Duration( '2s' );
6686 this.sAttributeName = sAttributeName;
6687 this.aKeyTimes = [ 0.0, 0.25, 0.50, 0.75, 1.0 ];
6688 //this.aKeyTimes = [ 0.0, 1.0 ];
6689 var aM = 5000 / this.aNodeContext.aSlideHeight;
6690 this.aValues = new Array();
6691 //this.aValues = [ 'y', 'y', 'y - ' + aM, 'y - ' + aM, 'y' ];
6692 this.aValues = [ 'height', '0', 'height', '2*height', 'height' ];
6693 //this.aValues = [ '0', 'height' ];
6695 //this.aFromValue = '2 * height';
6696 //this.aToValue = 'width';
6697 //this.aByValue = 'width';//aTranslationValue;
6700 var aActivityParamSet = this.fillActivityParams();
6702 var aAnimation = createPropertyAnimation( this.getAttributeName(),
6703 this.getAnimatedElement(),
6704 this.aNodeContext.aSlideWidth,
6705 this.aNodeContext.aSlideHeight );
6707 var aInterpolator = null;
6708 return createActivity( aActivityParamSet, this, aAnimation, aInterpolator );
6714 var aActivityParamSet
= this.fillActivityParams();
6716 var aAnimation
= createPropertyAnimation( this.getAttributeName(),
6717 this.getAnimatedElement(),
6718 this.aNodeContext
.aSlideWidth
,
6719 this.aNodeContext
.aSlideHeight
);
6721 var aInterpolator
= null; // createActivity will compute it;
6722 return createActivity( aActivityParamSet
, this, aAnimation
, aInterpolator
);
6728 // ------------------------------------------------------------------------------------------ //
6729 function AnimationSetNode( aAnimElem
, aParentNode
, aNodeContext
)
6731 AnimationSetNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6733 this.sClassName
= 'AnimationSetNode';
6735 extend( AnimationSetNode
, AnimationBaseNode2
);
6738 AnimationSetNode
.prototype.createActivity = function()
6740 var aAnimation
= createPropertyAnimation( this.getAttributeName(),
6741 this.getAnimatedElement(),
6742 this.aNodeContext
.aSlideWidth
,
6743 this.aNodeContext
.aSlideHeight
);
6745 var aActivityParamSet
= this.fillActivityParams();
6747 return new SetActivity( aActivityParamSet
, aAnimation
, this.getToValue() );
6752 // ------------------------------------------------------------------------------------------ //
6753 function AnimationColorNode( aAnimElem
, aParentNode
, aNodeContext
)
6755 AnimationColorNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6757 this.sClassName
= 'AnimationColorNode';
6759 this.eColorInterpolation
= undefined;
6760 this.eColorInterpolationDirection
= undefined;
6762 extend( AnimationColorNode
, AnimationBaseNode3
);
6765 AnimationColorNode
.prototype.parseElement = function()
6767 var bRet
= AnimationColorNode
.superclass
.parseElement
.call( this );
6769 var aAnimElem
= this.aElement
;
6771 // color-interpolation attribute
6772 this.eColorInterpolation
= COLOR_SPACE_RGB
;
6773 var sColorInterpolationAttr
= aAnimElem
.getAttribute( 'color-interpolation' );
6774 if( sColorInterpolationAttr
&& aColorSpaceInMap
[ sColorInterpolationAttr
] )
6775 this.eColorInterpolation
= aColorSpaceInMap
[ sColorInterpolationAttr
];
6777 // color-interpolation-direction attribute
6778 this.eColorInterpolationDirection
= CLOCKWISE
;
6779 var sColorInterpolationDirectionAttr
= aAnimElem
.getAttribute( 'color-interpolation-direction' );
6780 if( sColorInterpolationDirectionAttr
&& aClockDirectionInMap
[ sColorInterpolationDirectionAttr
] )
6781 this.eColorInterpolationDirection
= aClockDirectionInMap
[ sColorInterpolationDirectionAttr
];
6786 AnimationColorNode
.prototype.createActivity = function()
6789 var aActivityParamSet = this.fillActivityParams();
6791 var aAnimation = createPropertyAnimation( 'opacity',
6792 this.getAnimatedElement(),
6793 this.aNodeContext.aSlideWidth,
6794 this.aNodeContext.aSlideHeight );
6796 return new SimpleActivity( aActivityParamSet, aAnimation, FORWARD );
6800 if( false && this.getAttributeName() === 'fill-color' )
6802 var sAttributeName = 'stroke-color';
6804 this.aDuration = new Duration( '2s' );
6805 this.nAccelerate = 0.0;
6806 this.nDecelerate = 0.0;
6807 this.eColorInterpolation = COLOR_SPACE_RGB;
6808 this.eColorInterpolationDirection = COUNTERCLOCKWISE;
6810 this.sAttributeName = sAttributeName;
6812 this.aFromValue = 'rgb( 0%, 0%, 0% )';
6813 this.aToValue = 'rgb( 0%, 0%, 100% )';
6814 //this.aByValue = 'hsl( 0, -12%, -25% )';
6818 var aActivityParamSet = this.fillActivityParams();
6820 var aAnimation = createPropertyAnimation( this.getAttributeName(),
6821 this.getAnimatedElement(),
6822 this.aNodeContext.aSlideWidth,
6823 this.aNodeContext.aSlideHeight );
6824 var aColorAnimation;
6826 if( this.getColorInterpolation() === COLOR_SPACE_HSL )
6828 ANIMDBG.print( 'AnimationColorNode.createActivity: color space hsl' );
6829 aColorAnimation = new HSLAnimationWrapper( aAnimation );
6830 var aInterpolatorMaker = aInterpolatorHandler.getInterpolator( this.getCalcMode(),
6833 aInterpolator = aInterpolatorMaker( this.getColorInterpolationDirection() );
6837 ANIMDBG.print( 'AnimationColorNode.createActivity: color space rgb' );
6838 aColorAnimation = aAnimation;
6839 aInterpolator = aInterpolatorHandler.getInterpolator( this.getCalcMode(),
6844 return createActivity( aActivityParamSet, this, aColorAnimation, aInterpolator );
6849 var aActivityParamSet
= this.fillActivityParams();
6851 var aAnimation
= createPropertyAnimation( this.getAttributeName(),
6852 this.getAnimatedElement(),
6853 this.aNodeContext
.aSlideWidth
,
6854 this.aNodeContext
.aSlideHeight
);
6856 var aColorAnimation
;
6858 if( this.getColorInterpolation() === COLOR_SPACE_HSL
)
6860 ANIMDBG
.print( 'AnimationColorNode.createActivity: color space hsl' );
6861 aColorAnimation
= new HSLAnimationWrapper( aAnimation
);
6862 var aInterpolatorMaker
= aInterpolatorHandler
.getInterpolator( this.getCalcMode(),
6865 aInterpolator
= aInterpolatorMaker( this.getColorInterpolationDirection() );
6869 ANIMDBG
.print( 'AnimationColorNode.createActivity: color space rgb' );
6870 aColorAnimation
= aAnimation
;
6871 aInterpolator
= aInterpolatorHandler
.getInterpolator( this.getCalcMode(),
6876 return createActivity( aActivityParamSet
, this, aColorAnimation
, aInterpolator
);
6881 AnimationColorNode
.prototype.getColorInterpolation = function()
6883 return this.eColorInterpolation
;
6886 AnimationColorNode
.prototype.getColorInterpolationDirection = function()
6888 return this.eColorInterpolationDirection
;
6891 AnimationColorNode
.prototype.info = function( bVerbose
)
6893 var sInfo
= AnimationColorNode
.superclass
.info
.call( this, bVerbose
);
6897 // color interpolation
6898 sInfo
+= '; color-interpolation: ' + aColorSpaceOutMap
[ this.getColorInterpolation() ];
6900 // color interpolation direction
6901 sInfo
+= '; color-interpolation-direction: ' + aClockDirectionOutMap
[ this.getColorInterpolationDirection() ];
6908 // ------------------------------------------------------------------------------------------ //
6909 function AnimationTransitionFilterNode( aAnimElem
, aParentNode
, aNodeContext
)
6911 AnimationTransitionFilterNode
.superclass
.constructor.call( this, aAnimElem
, aParentNode
, aNodeContext
);
6913 this.sClassName
= 'AnimationTransitionFilterNode';
6915 this.eTransitionType
= undefined;
6916 this.eTransitionSubType
= undefined;
6917 this.bReverseDirection
= undefined;
6918 this.eTransitionMode
= undefined;
6920 extend( AnimationTransitionFilterNode
, AnimationBaseNode
);
6923 AnimationTransitionFilterNode
.prototype.createActivity = function()
6925 var aActivityParamSet
= this.fillActivityParams();
6927 return createShapeTransition( aActivityParamSet
,
6928 this.getAnimatedElement(),
6929 this.aNodeContext
.aSlideWidth
,
6930 this.aNodeContext
.aSlideHeight
,
6934 AnimationTransitionFilterNode
.prototype.parseElement = function()
6936 var bRet
= AnimationTransitionFilterNode
.superclass
.parseElement
.call( this );
6938 var aAnimElem
= this.aElement
;
6941 this.eTransitionType
= undefined;
6942 var sTypeAttr
= aAnimElem
.getAttribute( 'type' );
6943 if( sTypeAttr
&& aTransitionTypeInMap
[ sTypeAttr
] )
6945 this.eTransitionType
= aTransitionTypeInMap
[ sTypeAttr
];
6949 this.eCurrentState
= INVALID_NODE
;
6950 log( 'AnimationTransitionFilterNode.parseElement: transition type not valid: ' + sTypeAttr
);
6953 // subtype attribute
6954 this.eTransitionSubType
= undefined;
6955 var sSubTypeAttr
= aAnimElem
.getAttribute( 'subtype' );
6956 if( sSubTypeAttr
&& aTransitionSubtypeInMap
[ sSubTypeAttr
] )
6958 this.eTransitionSubType
= aTransitionSubtypeInMap
[ sSubTypeAttr
];
6962 this.eCurrentState
= INVALID_NODE
;
6963 log( 'AnimationTransitionFilterNode.parseElement: transition subtype not valid: ' + sSubTypeAttr
);
6966 // direction attribute
6967 this.bReverseDirection
= false;
6968 var sDirectionAttr
= aAnimElem
.getAttribute( 'direction' );
6969 if( sDirectionAttr
== 'reverse' )
6970 this.bReverseDirection
= true;
6973 this.eTransitionMode
= TRANSITION_MODE_IN
;
6974 var sModeAttr
= aAnimElem
.getAttribute( 'mode' );
6975 if( sModeAttr
=== 'out' )
6976 this.eTransitionMode
= TRANSITION_MODE_OUT
;
6981 AnimationTransitionFilterNode
.prototype.getTransitionType = function()
6983 return this.eTransitionType
;
6986 AnimationTransitionFilterNode
.prototype.getTransitionSubType = function()
6988 return this.eTransitionSubType
;
6991 AnimationTransitionFilterNode
.prototype.getTransitionMode = function()
6993 return this.eTransitionMode
;
6996 AnimationTransitionFilterNode
.prototype.getReverseDirection = function()
6998 return this.bReverseDirection
;
7001 AnimationTransitionFilterNode
.prototype.info = function( bVerbose
)
7003 var sInfo
= AnimationTransitionFilterNode
.superclass
.info
.call( this, bVerbose
);
7008 sInfo
+= '; type: ' + aTransitionTypeOutMap
[ String( this.getTransitionType() ) ];
7010 // transition subtype
7011 sInfo
+= '; subtype: ' + aTransitionSubtypeOutMap
[ this.getTransitionSubType() ];
7013 // transition direction
7014 if( this.getReverseDirection() )
7015 sInfo
+= '; direction: reverse';
7023 /**********************************************************************************************
7024 * Animation Node Factory
7025 **********************************************************************************************/
7027 // ------------------------------------------------------------------------------------------ //
7028 function createAnimationTree( aRootElement
, aNodeContext
)
7030 return createAnimationNode( aRootElement
, null, aNodeContext
);
7035 // ------------------------------------------------------------------------------------------ //
7036 function createAnimationNode( aElement
, aParentNode
, aNodeContext
)
7038 assert( aElement
, 'createAnimationNode: invalid animation element' );
7040 var eAnimationNodeType
= getAnimationElementType( aElement
);
7042 var aCreatedNode
= null;
7043 var aCreatedContainer
= null;
7045 switch( eAnimationNodeType
)
7047 case ANIMATION_NODE_PAR
:
7048 aCreatedNode
= aCreatedContainer
=
7049 new ParallelTimeContainer( aElement
, aParentNode
, aNodeContext
);
7051 case ANIMATION_NODE_ITERATE
:
7052 // map iterate container to ParallelTimeContainer.
7053 // the iterating functionality is to be found
7054 // below, (see method implCreateIteratedNodes)
7055 aCreatedNode
= aCreatedContainer
=
7056 new ParallelTimeContainer( aElement
, aParentNode
, aNodeContext
);
7058 case ANIMATION_NODE_SEQ
:
7059 aCreatedNode
= aCreatedContainer
=
7060 new SequentialTimeContainer( aElement
, aParentNode
, aNodeContext
);
7062 case ANIMATION_NODE_ANIMATE
:
7063 aCreatedNode
= new PropertyAnimationNode( aElement
, aParentNode
, aNodeContext
);
7065 case ANIMATION_NODE_SET
:
7066 aCreatedNode
= new AnimationSetNode( aElement
, aParentNode
, aNodeContext
);
7068 case ANIMATION_NODE_ANIMATEMOTION
:
7069 //aCreatedNode = new AnimationPathMotionNode( aElement, aParentNode, aNodeContext );
7071 log( 'createAnimationNode: ANIMATEMOTION not implemented' );
7073 case ANIMATION_NODE_ANIMATECOLOR
:
7074 aCreatedNode
= new AnimationColorNode( aElement
, aParentNode
, aNodeContext
);
7076 case ANIMATION_NODE_ANIMATETRANSFORM
:
7077 //aCreatedNode = new AnimationTransformNode( aElement, aParentNode, aNodeContext );
7079 log( 'createAnimationNode: ANIMATETRANSFORM not implemented' );
7081 case ANIMATION_NODE_TRANSITIONFILTER
:
7082 aCreatedNode
= new AnimationTransitionFilterNode( aElement
, aParentNode
, aNodeContext
);
7085 log( 'createAnimationNode: invalid Animation Node Type: ' + eAnimationNodeType
);
7089 if( aCreatedContainer
)
7091 if( eAnimationNodeType
== ANIMATION_NODE_ITERATE
)
7093 createIteratedNodes( aElement
, aCreatedContainer
, aNodeContext
);
7097 var aChildrenArray
= getElementChildren( aElement
);
7098 for( var i
= 0; i
< aChildrenArray
.length
; ++i
)
7100 if( !createChildNode( aChildrenArray
[i
], aCreatedContainer
, aNodeContext
) )
7108 return aCreatedNode
;
7113 // ------------------------------------------------------------------------------------------ //
7114 function createChildNode( aElement
, aParentNode
, aNodeContext
)
7116 var aChildNode
= createAnimationNode( aElement
, aParentNode
, aNodeContext
);
7120 log( 'createChildNode: child node creation failed' );
7125 aParentNode
.appendChildNode( aChildNode
);
7132 // ------------------------------------------------------------------------------------------ //
7133 function createIteratedNodes( aElement
, aContainerNode
, aNodeContext
)
7140 /**********************************************************************************************
7142 **********************************************************************************************/
7145 // ------------------------------------------------------------------------------------------ //
7146 function makeScaler( nScale
)
7148 if( ( typeof( nScale
) !== typeof( 0 ) ) || !isFinite( nScale
) )
7150 log( 'makeScaler: not valid param passed: ' + nScale
);
7154 return function( nValue
)
7156 return ( nScale
* nValue
);
7162 // ------------------------------------------------------------------------------------------ //
7163 function createPropertyAnimation( sAttrName
, aAnimatedElement
, nWidth
, nHeight
)
7165 if( !aAttributeMap
[ sAttrName
] )
7167 log( 'createPropertyAnimation: attribute is unknown' );
7172 var aFunctorSet
= aAttributeMap
[ sAttrName
];
7174 var sGetValueMethod
= aFunctorSet
.get;
7175 var sSetValueMethod
= aFunctorSet
.set;
7177 if( !sGetValueMethod
|| !sSetValueMethod
)
7179 log( 'createPropertyAnimation: attribute is not handled' );
7183 var aGetModifier
= eval( aFunctorSet
.getmod
);
7184 var aSetModifier
= eval( aFunctorSet
.setmod
);
7187 return new GenericAnimation( bind( aAnimatedElement
, aAnimatedElement
[ sGetValueMethod
] ),
7188 bind( aAnimatedElement
, aAnimatedElement
[ sSetValueMethod
] ),
7195 // ------------------------------------------------------------------------------------------ //
7196 /** createShapeTransition
7198 * @param aActivityParamSet
7199 * The set of property for the activity to be created.
7200 * @param aAnimatedElement
7201 * The element to be animated.
7202 * @param nSlideWidth
7203 * The width of a slide.
7204 * @param nSlideHeight
7205 * The height of a slide.
7206 * @param aAnimatedTransitionFilterNode
7207 * An instance of the AnimationFilterNode that invoked this function.
7208 * @return {SimpleActivity}
7209 * A simple activity handling a shape transition.
7211 function createShapeTransition( aActivityParamSet
, aAnimatedElement
,
7212 nSlideWidth
, nSlideHeight
,
7213 aAnimatedTransitionFilterNode
)
7215 if( !aAnimatedTransitionFilterNode
)
7217 log( 'createShapeTransition: the animated transition filter node is not valid.' );
7220 var eTransitionType
= aAnimatedTransitionFilterNode
.getTransitionType();
7221 var eTransitionSubType
= aAnimatedTransitionFilterNode
.getTransitionSubType();
7222 var bDirectionForward
= ! aAnimatedTransitionFilterNode
.getReverseDirection();
7223 var bModeIn
= ( aAnimatedTransitionFilterNode
.getTransitionMode() == FORWARD
);
7225 var aTransitionInfo
= aTransitionInfoTable
[eTransitionType
][eTransitionSubType
];
7226 var eTransitionClass
= aTransitionInfo
['class'];
7228 switch( eTransitionClass
)
7231 case TRANSITION_INVALID
:
7232 log( 'createShapeTransition: transition class: TRANSITION_INVALID' );
7235 case TRANSITION_CLIP_POLYPOLYGON
:
7236 var aParametricPolyPolygon
7237 = createClipPolyPolygon( eTransitionType
, eTransitionSubType
);
7238 var aClippingAnimation
7239 = new ClippingAnimation( aParametricPolyPolygon
, aTransitionInfo
,
7240 bDirectionForward
, bModeIn
);
7241 return new SimpleActivity( aActivityParamSet
, aClippingAnimation
, true );
7243 case TRANSITION_SPECIAL
:
7244 switch( eTransitionType
)
7246 // no special transition filter provided
7247 // we map everything to crossfade
7250 = createPropertyAnimation( 'opacity',
7254 return new SimpleActivity( aActivityParamSet
, aAnimation
, bModeIn
);
7262 // ------------------------------------------------------------------------------------------ //
7263 /** Class ClippingAnimation
7264 * This class performs a shape transition where the effect is achieved by
7265 * clipping the shape to be animated with a parametric path.
7267 * @param aParametricPolyPolygon
7268 * An object handling a <path> element that depends on a parameter.
7269 * @param aTransitionInfo
7270 * The set of parameters defining the shape transition to be performed.
7271 * @param bDirectionForward
7272 * The direction the shape transition has to be performed.
7274 * If true the element to be animated becomes more visible as the transition
7275 * progress else it becomes less visible.
7277 function ClippingAnimation( aParametricPolyPolygon
, aTransitionInfo
,
7278 bDirectionForward
, bModeIn
)
7280 this.aClippingFunctor
= new ClippingFunctor( aParametricPolyPolygon
,
7282 bDirectionForward
, bModeIn
);
7283 this.bAnimationStarted
= false;
7287 * This method notifies to the element involved in the transition that
7288 * the animation is starting and creates the <clipPath> element used for
7291 * @param aAnimatableElement
7292 * The element to be animated.
7294 ClippingAnimation
.prototype.start = function( aAnimatableElement
)
7296 assert( aAnimatableElement
,
7297 'ClippingAnimation.start: animatable element is not valid' );
7298 this.aAnimatableElement
= aAnimatableElement
;
7299 this.aAnimatableElement
.initClipPath();
7300 this.aAnimatableElement
.notifyAnimationStart();
7302 if( !this.bAnimationStarted
)
7303 this.bAnimationStarted
= true;
7308 * The transition clean up is performed here.
7310 ClippingAnimation
.prototype.end = function()
7312 if( this.bAnimationStarted
)
7314 this.aAnimatableElement
.cleanClipPath();
7315 this.bAnimationStarted
= false;
7316 this.aAnimatableElement
.notifyAnimationEnd();
7321 * This method set the position of the element to be animated according to
7322 * the passed time value.
7325 * The time parameter.
7327 ClippingAnimation
.prototype.perform = function( nValue
)
7329 var nWidth
= this.aAnimatableElement
.aClippingBBox
.width
;
7330 var nHeight
= this.aAnimatableElement
.aClippingBBox
.height
;
7331 var aPolyPolygonElement
= this.aClippingFunctor
.perform( nValue
, nWidth
, nHeight
);
7332 this.aAnimatableElement
.setClipPath( aPolyPolygonElement
);
7335 ClippingAnimation
.prototype.getUnderlyingValue = function()
7342 // ------------------------------------------------------------------------------------------ //
7343 function GenericAnimation( aGetValueFunc
, aSetValueFunc
, aGetModifier
, aSetModifier
)
7345 assert( aGetValueFunc
&& aSetValueFunc
,
7346 'GenericAnimation constructor: get value functor and/or set value functor are not valid' );
7348 this.aGetValueFunc
= aGetValueFunc
;
7349 this.aSetValueFunc
= aSetValueFunc
;
7350 this.aGetModifier
= aGetModifier
;
7351 this.aSetModifier
= aSetModifier
;
7352 this.aAnimatableElement
= null;
7353 this.bAnimationStarted
= false;
7357 GenericAnimation
.prototype.start = function( aAnimatableElement
)
7359 assert( aAnimatableElement
, 'GenericAnimation.start: animatable element is not valid' );
7361 this.aAnimatableElement
= aAnimatableElement
;
7362 this.aAnimatableElement
.notifyAnimationStart();
7364 if( !this.bAnimationStarted
)
7365 this.bAnimationStarted
= true;
7368 GenericAnimation
.prototype.end = function()
7370 if( this.bAnimationStarted
)
7372 this.bAnimationStarted
= false;
7373 this.aAnimatableElement
.notifyAnimationEnd();
7377 GenericAnimation
.prototype.perform = function( aValue
)
7379 if( this.aSetModifier
)
7380 aValue
= this.aSetModifier( aValue
);
7382 this.aSetValueFunc( aValue
);
7385 GenericAnimation
.prototype.getUnderlyingValue = function()
7387 var aValue
= this.aGetValueFunc();
7388 if( this.aGetModifier
)
7389 aValue
= this.aGetModifier( aValue
);
7395 // ------------------------------------------------------------------------------------------ //
7396 function HSLAnimationWrapper( aColorAnimation
)
7398 assert( aColorAnimation
,
7399 'HSLAnimationWrapper constructor: invalid color animation delegate' );
7401 this.aAnimation
= aColorAnimation
;
7405 HSLAnimationWrapper
.prototype.start = function( aAnimatableElement
)
7407 this.aAnimation
.start( aAnimatableElement
);
7410 HSLAnimationWrapper
.prototype.end = function()
7412 this.aAnimation
.end();
7414 HSLAnimationWrapper
.prototype.perform = function( aHSLValue
)
7416 this.aAnimation
.perform( aHSLValue
.convertToRGB() );
7419 HSLAnimationWrapper
.prototype.getUnderlyingValue = function()
7421 return this.aAnimation
.getUnderlyingValue().convertToHSL();
7426 // ------------------------------------------------------------------------------------------ //
7427 /** Class SlideChangeBase
7428 * The base abstract class of classes performing slide transitions.
7430 * @param aLeavingSlide
7431 * An object of type AnimatedSlide handling the leaving slide.
7432 * @param aEnteringSlide
7433 * An object of type AnimatedSlide handling the entering slide.
7435 function SlideChangeBase(aLeavingSlide
, aEnteringSlide
)
7437 this.aLeavingSlide
= aLeavingSlide
;
7438 this.aEnteringSlide
= aEnteringSlide
;
7439 this.bIsFinished
= false;
7443 * The transition initialization is performed here.
7445 SlideChangeBase
.prototype.start = function()
7447 if( this.bIsFinished
)
7452 * The transition clean up is performed here.
7454 SlideChangeBase
.prototype.end = function()
7456 if( this.bIsFinished
)
7459 this.aLeavingSlide
.hide();
7460 this.aEnteringSlide
.reset();
7461 this.aLeavingSlide
.reset();
7463 this.bIsFinished
= true;
7467 * This method is responsible for performing the slide transition.
7470 * The time parameter.
7472 * If the transition is performed returns tue else returns false.
7474 SlideChangeBase
.prototype.perform = function( nValue
)
7476 if( this.bIsFinished
) return false;
7478 if( this.aLeavingSlide
)
7479 this.performOut( nValue
);
7481 if( this.aEnteringSlide
)
7482 this.performIn( nValue
);
7487 SlideChangeBase
.prototype.getUnderlyingValue = function()
7492 SlideChangeBase
.prototype.performIn = function( nValue
)
7494 log( 'SlideChangeBase.performIn: abstract method called' );
7497 SlideChangeBase
.prototype.performOut = function( nValue
)
7499 log( 'SlideChangeBase.performOut: abstract method called' );
7504 // ------------------------------------------------------------------------------------------ //
7505 /** Class FadingSlideChange
7506 * This class performs a slide transition by fading out the leaving slide and
7507 * fading in the entering slide.
7509 * @param aLeavingSlide
7510 * An object of type AnimatedSlide handling the leaving slide.
7511 * @param aEnteringSlide
7512 * An object of type AnimatedSlide handling the entering slide.
7514 function FadingSlideChange( aLeavingSlide
, aEnteringSlide
)
7516 FadingSlideChange
.superclass
.constructor.call( this, aLeavingSlide
, aEnteringSlide
);
7517 this.bFirstRun
= true;
7519 extend( FadingSlideChange
, SlideChangeBase
);
7522 * This method notifies to the slides involved in the transition the attributes
7523 * appended to the slide elements for performing the animation.
7524 * Moreover it sets the entering slide in the initial state and makes the slide
7527 FadingSlideChange
.prototype.start = function()
7529 FadingSlideChange
.superclass
.start
.call( this );
7530 this.aEnteringSlide
.notifyUsedAttribute( 'opacity' );
7531 this.aLeavingSlide
.notifyUsedAttribute( 'opacity' );
7532 this.aEnteringSlide
.setOpacity( 0.0 );
7533 this.aEnteringSlide
.show();
7537 * This method set the opacity of the entering slide according to the passed
7541 * The time parameter.
7543 FadingSlideChange
.prototype.performIn = function( nT
)
7545 this.aEnteringSlide
.setOpacity( nT
);
7549 * This method set the opacity of the leaving slide according to the passed
7553 * The time parameter.
7555 FadingSlideChange
.prototype.performOut = function( nT
)
7558 this.aLeavingSlide
.setOpacity( 1 - nT
);
7563 // ------------------------------------------------------------------------------------------ //
7564 /** Class FadingOverColorSlideChange
7565 * This class performs a slide transition by fading out the leaving slide to
7566 * a given color and fading in the entering slide from the same color.
7568 * @param aLeavingSlide
7569 * An object of type AnimatedSlide handling the leaving slide.
7570 * @param aEnteringSlide
7571 * An object of type AnimatedSlide handling the entering slide.
7573 * A string representing the color the leaving slide fades out to and
7574 * the entering slide fade in from.
7576 function FadingOverColorSlideChange( aLeavingSlide
, aEnteringSlide
, sFadeColor
)
7578 FadingSlideChange
.superclass
.constructor.call( this, aLeavingSlide
, aEnteringSlide
);
7579 this.sFadeColor
= sFadeColor
;
7580 if( !this.sFadeColor
)
7582 log( 'FadingOverColorSlideChange: sFadeColor not valid.' );
7583 this.sFadeColor
= '#000000';
7585 this.aColorPlaneElement
= this.createColorPlaneElement();
7587 extend( FadingOverColorSlideChange
, SlideChangeBase
);
7590 * This method notifies to the slides involved in the transition the attributes
7591 * appended to the slide elements for performing the animation.
7592 * Moreover it inserts the color plane element below the leaving slide.
7593 * Finally it sets the entering slide in the initial state and makes
7594 * the slide visible.
7596 FadingOverColorSlideChange
.prototype.start = function()
7598 FadingOverColorSlideChange
.superclass
.start
.call( this );
7599 this.aEnteringSlide
.notifyUsedAttribute( 'opacity' );
7600 this.aLeavingSlide
.notifyUsedAttribute( 'opacity' );
7601 this.aLeavingSlide
.insertBefore( this.aColorPlaneElement
);
7602 this.aEnteringSlide
.setOpacity( 0.0 );
7603 this.aEnteringSlide
.show();
7607 * This method removes the color plane element.
7609 FadingOverColorSlideChange
.prototype.end = function()
7611 FadingOverColorSlideChange
.superclass
.end
.call( this );
7612 this.aLeavingSlide
.removeElement( this.aColorPlaneElement
);
7616 * This method set the opacity of the entering slide according to the passed
7620 * The time parameter.
7622 FadingOverColorSlideChange
.prototype.performIn = function( nT
)
7624 this.aEnteringSlide
.setOpacity( (nT
> 0.55) ? 2.0*(nT
-0.55) : 0.0 );
7628 * This method set the opacity of the leaving slide according to the passed
7632 * The time parameter.
7634 FadingOverColorSlideChange
.prototype.performOut = function( nT
)
7636 this.aLeavingSlide
.setOpacity( (nT
> 0.45) ? 0.0 : 2.0*(0.45-nT
) );
7639 FadingOverColorSlideChange
.prototype.createColorPlaneElement = function()
7641 var aColorPlaneElement
= document
.createElementNS( NSS
['svg'], 'rect' );
7642 aColorPlaneElement
.setAttribute( 'width', String( this.aLeavingSlide
.getWidth() ) );
7643 aColorPlaneElement
.setAttribute( 'height', String( this.aLeavingSlide
.getHeight() ) );
7644 aColorPlaneElement
.setAttribute( 'fill', this.sFadeColor
);
7645 return aColorPlaneElement
;
7650 // ------------------------------------------------------------------------------------------ //
7651 /** Class MovingSlideChange
7652 * This class performs a slide transition that involves translating the leaving
7653 * slide and/or the entering one in a given direction.
7655 * @param aLeavingSlide
7656 * An object of type AnimatedSlide handling the leaving slide.
7657 * @param aEnteringSlide
7658 * An object of type AnimatedSlide handling the entering slide.
7659 * @param aLeavingDirection
7660 * A 2D vector object {x, y}.
7661 * @param aEnteringDirection
7662 * A 2D vector object {x, y}.
7664 function MovingSlideChange( aLeavingSlide
, aEnteringSlide
,
7665 aLeavingDirection
, aEnteringDirection
)
7667 MovingSlideChange
.superclass
.constructor.call( this, aLeavingSlide
, aEnteringSlide
);
7668 this.aLeavingDirection
= aLeavingDirection
;
7669 this.aEnteringDirection
= aEnteringDirection
;
7671 extend( MovingSlideChange
, SlideChangeBase
);
7674 * This method notifies to the slides involved in the transition the attributes
7675 * appended to the slide elements for performing the animation.
7676 * Moreover it sets the entering slide in the initial state and makes the slide
7679 MovingSlideChange
.prototype.start = function()
7681 MovingSlideChange
.superclass
.start
.call( this );
7682 this.aEnteringSlide
.notifyUsedAttribute( 'transform' );
7683 this.aLeavingSlide
.notifyUsedAttribute( 'transform' );
7684 // Before setting the 'visibility' attribute of the entering slide to 'visible'
7685 // we translate it to the initial position so that it is not really visible
7686 // because it is clipped out.
7687 this.performIn( 0 );
7688 this.aEnteringSlide
.show();
7692 * This method set the position of the entering slide according to the passed
7696 * The time parameter.
7698 MovingSlideChange
.prototype.performIn = function( nT
)
7701 var dx
= nS
* this.aEnteringDirection
.x
* this.aEnteringSlide
.getWidth();
7702 var dy
= nS
* this.aEnteringDirection
.y
* this.aEnteringSlide
.getHeight();
7703 this.aEnteringSlide
.translate( dx
, dy
);
7707 * This method set the position of the leaving slide according to the passed
7711 * The time parameter.
7713 MovingSlideChange
.prototype.performOut = function( nT
)
7715 var dx
= nT
* this.aLeavingDirection
.x
* this.aLeavingSlide
.getWidth();
7716 var dy
= nT
* this.aLeavingDirection
.y
* this.aLeavingSlide
.getHeight();
7717 this.aLeavingSlide
.translate( dx
, dy
);
7722 // ------------------------------------------------------------------------------------------ //
7723 /** Class ClippedSlideChange
7724 * This class performs a slide transition where the entering slide wipes
7725 * the leaving one out. The wipe effect is achieved by clipping the entering
7726 * slide with a parametric path.
7728 * @param aLeavingSlide
7729 * An object of type AnimatedSlide handling the leaving slide.
7730 * @param aEnteringSlide
7731 * An object of type AnimatedSlide handling the entering slide.
7732 * @param aParametricPolyPolygon
7733 * An object handling a <path> element that depends on a parameter.
7734 * @param aTransitionInfo
7735 * The set of parameters defining the slide transition to be performed.
7736 * @param bIsDirectionForward
7737 * The direction the slide transition has to be performed.
7739 function ClippedSlideChange( aLeavingSlide
, aEnteringSlide
, aParametricPolyPolygon
,
7740 aTransitionInfo
, bIsDirectionForward
)
7742 ClippedSlideChange
.superclass
.constructor.call( this, aLeavingSlide
, aEnteringSlide
);
7744 var bIsModeIn
= true;
7745 this.aClippingFunctor
= new ClippingFunctor( aParametricPolyPolygon
, aTransitionInfo
,
7746 bIsDirectionForward
, bIsModeIn
);
7748 extend( ClippedSlideChange
, SlideChangeBase
);
7751 * This method notifies to the slides involved in the transition the attributes
7752 * appended to the slide elements for performing the animation.
7753 * Moreover it sets the entering slide in the initial state and makes the slide
7756 ClippedSlideChange
.prototype.start = function()
7758 ClippedSlideChange
.superclass
.start
.call( this );
7759 this.aEnteringSlide
.notifyUsedAttribute( 'clip-path' );;
7760 this.performIn( 0 );
7761 this.aEnteringSlide
.show();
7765 * This method set the position of the entering slide according to the passed
7769 * The time parameter.
7771 ClippedSlideChange
.prototype.performIn = function( nT
)
7773 var nWidth
= this.aEnteringSlide
.getWidth();
7774 var nHeight
= this.aEnteringSlide
.getHeight();
7775 var aPolyPolygonElement
= this.aClippingFunctor
.perform( nT
, nWidth
, nHeight
);
7776 this.aEnteringSlide
.setClipPath( aPolyPolygonElement
);
7779 ClippedSlideChange
.prototype.performOut = function( nT
)
7786 // ------------------------------------------------------------------------------------------ //
7787 /** Class ClippingFunctor
7788 * This class is responsible for computing the <path> used for clipping
7789 * the entering slide in a polypolygon clipping slide transition or the
7790 * animated shape in a transition filter effect.
7792 * @param aParametricPolyPolygon
7793 * An object that handle a <path> element defined in the [0,1]x[0,1]
7794 * unit square and that depends on a parameter.
7795 * @param aTransitionInfo
7796 * The set of parameters defining the slide transition to be performed.
7797 * @param bIsDirectionForward
7798 * The direction the slide transition has to be performed.
7800 * The direction the filter effect has to be performed
7802 function ClippingFunctor( aParametricPolyPolygon
, aTransitionInfo
,
7803 bIsDirectionForward
, bIsModeIn
)
7805 this.aParametricPolyPolygon
= aParametricPolyPolygon
;
7806 this.aStaticTransformation
= null;
7807 this.bForwardParameterSweep
= true;
7808 this.bSubtractPolygon
= false;
7809 this.bScaleIsotropically
= aTransitionInfo
.scaleIsotropically
;
7812 assert( this.aParametricPolyPolygon
,
7813 'ClippingFunctor: parametric polygon is not valid' );
7815 if( aTransitionInfo
.rotationAngle
!= 0.0 ||
7816 aTransitionInfo
.scaleX
!= 1.0 || aTransitionInfo
.scaleY
!= 1.0 )
7818 // note: operations must be defined in reverse order.
7819 this.aStaticTransformation
= SVGIdentityMatrix
.translate( 0.5, 0.5 );
7820 if( aTransitionInfo
.scaleX
!= 1.0 || aTransitionInfo
.scaleY
!= 1.0 )
7821 this.aStaticTransformation
7822 = this.aStaticTransformation
.scaleNonUniform( aTransitionInfo
.scaleX
,
7823 aTransitionInfo
.scaleY
);
7824 if( aTransitionInfo
.rotationAngle
!= 0.0 )
7825 this.aStaticTransformation
7826 = this.aStaticTransformation
.rotate( aTransitionInfo
.rotationAngle
);
7827 this.aStaticTransformation
= this.aStaticTransformation
.translate( -0.5, -0.5 );
7831 this.aStaticTransformation
= document
.documentElement
.createSVGMatrix();
7834 if( !bIsDirectionForward
)
7837 switch( aTransitionInfo
.reverseMethod
)
7840 log( 'ClippingFunctor: unexpected reverse method.' )
7842 case REVERSEMETHOD_IGNORE
:
7844 case REVERSEMETHOD_INVERT_SWEEP
:
7845 this.bForwardParameterSweep
= !this.bForwardParameterSweep
;
7847 case REVERSEMETHOD_SUBTRACT_POLYGON
:
7848 this.bSubtractPolygon
= !this.bSubtractPolygon
;
7850 case REVERSEMETHOD_SUBTRACT_AND_INVERT
:
7851 this.bForwardParameterSweep
= !this.bForwardParameterSweep
;
7852 this.bSubtractPolygon
= !this.bSubtractPolygon
;
7854 case REVERSEMETHOD_ROTATE_180
:
7855 aMatrix
= document
.documentElement
.createSVGMatrix();
7856 aMatrix
.setToRotationAroundPoint( 0.5, 0.5, 180 );
7857 this.aStaticTransformation
= aMatrix
.multiply( this.aStaticTransformation
);
7859 case REVERSEMETHOD_FLIP_X
:
7860 aMatrix
= document
.documentElement
.createSVGMatrix();
7863 aMatrix
.a
= -1; aMatrix
.e
= 1.0;
7864 this.aStaticTransformation
= aMatrix
.multiply( this.aStaticTransformation
);
7867 case REVERSEMETHOD_FLIP_Y
:
7868 aMatrix
= document
.documentElement
.createSVGMatrix();
7871 aMatrix
.d
= -1; aMatrix
.f
= 1.0;
7872 this.aStaticTransformation
= aMatrix
.multiply( this.aStaticTransformation
);
7880 if( aTransitionInfo
.outInvertsSweep
)
7882 this.bForwardParameterSweep
= !this.bForwardParameterSweep
;
7886 this.bSubtractPolygon
= !this.bSubtractPolygon
;
7891 // This path is used when the direction is the reverse one and
7892 // the reverse method type is the subtraction type.
7893 ClippingFunctor
.aBoundingPath
= document
.createElementNS( NSS
['svg'], 'path' );
7894 ClippingFunctor
.aBoundingPath
.setAttribute( 'd', 'M -1 -1 L 2 -1 L 2 2 L -1 2 L -1 -1' );
7899 * A parameter in [0,1] representing normalized time.
7901 * The width of the bounding box of the slide/shape to be clipped.
7903 * The height of the bounding box of the slide/shape to be clipped.
7904 * @return {SVGPathElement}
7905 * A svg <path> element representing the path to be used for the clipping
7908 ClippingFunctor
.prototype.perform = function( nT
, nWidth
, nHeight
)
7910 var aClipPoly
= this.aParametricPolyPolygon
.perform( this.bForwardParameterSweep
? nT
: (1 - nT
) );
7912 // Note: even if the reverse method involves flipping we don't need to
7913 // change the clip-poly orientation because we utilize the 'nonzero'
7915 // See: http://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
7917 if( this.bSubtractPolygon
)
7919 aClipPoly
.changeOrientation();
7920 aClipPoly
.prependPath( ClippingFunctor
.aBoundingPath
);
7924 if( this.bScaleIsotropically
)
7926 var nScaleFactor
= Math
.max( nWidth
, nHeight
);
7927 // translate( scale( aStaticTransformation() ) )
7928 // note: operations must be defined in reverse order.
7929 aMatrix
= SVGIdentityMatrix
.translate( -( nScaleFactor
- nWidth
) / 2.0,
7930 -( nScaleFactor
- nHeight
) / 2.0 );
7931 aMatrix
= aMatrix
.scale( nScaleFactor
);
7932 aMatrix
= aMatrix
.multiply( this.aStaticTransformation
);
7936 aMatrix
= SVGIdentityMatrix
.scaleNonUniform( nWidth
, nHeight
);
7937 aMatrix
= aMatrix
.multiply( this.aStaticTransformation
);
7940 aClipPoly
.matrixTransform( aMatrix
);
7947 // ------------------------------------------------------------------------------------------ //
7948 /** createClipPolyPolygon
7951 * An enumerator representing the transition type.
7953 * An enumerator representing the transition subtype.
7955 * An object that handles a parametric <path> element.
7957 function createClipPolyPolygon( nType
, nSubtype
)
7962 log( 'createClipPolyPolygon: unknown transition type: ' + nType
);
7964 case BARWIPE_TRANSITION
:
7965 return new BarWipePath( 1 );
7966 case FOURBOXWIPE_TRANSITION
:
7967 return new FourBoxWipePath( nSubtype
=== CORNERSOUT_TRANS_SUBTYPE
);
7968 case ELLIPSEWIPE_TRANSITION
:
7969 return new EllipseWipePath( nSubtype
);
7970 case PINWHEELWIPE_TRANSITION
:
7974 case ONEBLADE_TRANS_SUBTYPE
:
7977 case DEFAULT_TRANS_SUBTYPE
:
7978 case TWOBLADEVERTICAL_TRANS_SUBTYPE
:
7981 case TWOBLADEHORIZONTAL_TRANS_SUBTYPE
:
7984 case THREEBLADE_TRANS_SUBTYPE
:
7987 case FOURBLADE_TRANS_SUBTYPE
:
7990 case EIGHTBLADE_TRANS_SUBTYPE
:
7994 log( 'createClipPolyPolygon: unknown subtype: ' + nSubtype
);
7997 return new PinWheelWipePath( nBlades
);
7998 case CHECKERBOARDWIPE_TRANSITION
:
7999 return new CheckerBoardWipePath( 10 );
8005 // ------------------------------------------------------------------------------------------ //
8006 function createUnitSquarePath()
8008 var aPath
= document
.createElementNS( NSS
['svg'], 'path' );
8009 var sD
= 'M 0 0 L 1 0 L 1 1 L 0 1 L 0 0';
8010 aPath
.setAttribute( 'd', sD
);
8014 function pruneScaleValue( nVal
)
8017 return (nVal
< -0.00001 ? nVal
: -0.00001);
8019 return (nVal
> 0.00001 ? nVal
: 0.00001);
8022 // ------------------------------------------------------------------------------------------ //
8023 /** Class BarWipePath
8024 * This class handles a <path> element that defines a unit square and
8025 * transforms it accordingly to a parameter in the [0,1] range for performing
8026 * a left to right barWipe transition.
8029 * The number of bars to be generated.
8031 function BarWipePath( nBars
/* nBars > 1: blinds effect */ )
8034 if( this.nBars
=== undefined || this.nBars
< 1 )
8036 this.aBasePath
= createUnitSquarePath();
8042 * A parameter in [0,1] representing the width of the generated bars.
8043 * @return {SVGPathElement}
8044 * A svg <path> element representing a multi-bars.
8046 BarWipePath
.prototype.perform = function( nT
)
8049 var aMatrix
= SVGIdentityMatrix
.scaleNonUniform( pruneScaleValue( nT
/ this.nBars
), 1.0 );
8051 var aPolyPath
= this.aBasePath
.cloneNode( true );
8052 aPolyPath
.matrixTransform( aMatrix
);
8054 if( this.nBars
> 1 )
8059 for( i
= this.nBars
- 1; i
> 0; --i
)
8061 aTransform
= SVGIdentityMatrix
.translate( i
/ this.nBars
, 0.0 );
8062 aTransform
= aTransform
.multiply( aMatrix
);
8063 aPath
= this.aBasePath
.cloneNode( true );
8064 aPath
.matrixTransform( aTransform
);
8065 aPolyPath
.appendPath( aPath
);
8073 // ------------------------------------------------------------------------------------------ //
8074 /** Class FourBoxWipePath
8075 * This class handles a path made up by four squares and is utilized for
8076 * performing fourBoxWipe transitions.
8078 * @param bCornersOut
8079 * If true the transition subtype is cornersOut else is cornersIn.
8081 function FourBoxWipePath( bCornersOut
)
8083 this.bCornersOut
= bCornersOut
;
8084 this.aBasePath
= createUnitSquarePath();
8087 FourBoxWipePath
.prototype.perform = function( nT
)
8090 var d
= pruneScaleValue( nT
/ 2.0 );
8092 if( this.bCornersOut
)
8094 aMatrix
= SVGIdentityMatrix
.translate( -0.25, -0.25 ).scale( d
).translate( -0.5, -0.5 );
8098 aMatrix
= SVGIdentityMatrix
.translate( -0.5, -0.5 ).scale( d
);
8102 var aTransform
= aMatrix
;
8104 var aSquare
= this.aBasePath
.cloneNode( true );
8105 aSquare
.matrixTransform( aTransform
);
8106 var aPolyPath
= aSquare
;
8107 // bottom left, flip on x-axis:
8108 aMatrix
= SVGIdentityMatrix
.flipY();
8109 aTransform
= aMatrix
.multiply( aTransform
);
8110 aSquare
= this.aBasePath
.cloneNode( true );
8111 aSquare
.matrixTransform( aTransform
);
8112 aSquare
.changeOrientation();
8113 aPolyPath
.appendPath( aSquare
);
8114 // bottom right, flip on y-axis:
8115 aMatrix
= SVGIdentityMatrix
.flipX();
8116 aTransform
= aMatrix
.multiply( aTransform
);
8117 aSquare
= this.aBasePath
.cloneNode( true );
8118 aSquare
.matrixTransform( aTransform
);
8119 aPolyPath
.appendPath( aSquare
);
8120 // top right, flip on x-axis:
8121 aMatrix
= SVGIdentityMatrix
.flipY();
8122 aTransform
= aMatrix
.multiply( aTransform
);
8123 aSquare
= this.aBasePath
.cloneNode( true );
8124 aSquare
.matrixTransform( aTransform
);
8125 aSquare
.changeOrientation();
8126 aPolyPath
.appendPath( aSquare
);
8128 // Remind: operations are applied in inverse order
8129 aMatrix
= SVGIdentityMatrix
.translate( 0.5, 0.5 );
8130 // We enlarge a bit the clip path so we avoid that in reverse direction
8131 // some thin line of the border stroke is visible.
8132 aMatrix
= aMatrix
.scale( 1.1 );
8133 aPolyPath
.matrixTransform( aMatrix
);
8140 // ------------------------------------------------------------------------------------------ //
8141 /** Class EllipseWipePath
8142 * This class handles a parametric ellipse represented by a path made up of
8143 * cubic Bezier curve segments that helps in performing the ellipseWipe
8147 * The transition subtype.
8149 function EllipseWipePath( eSubtype
)
8151 this.eSubtype
= eSubtype
;
8153 // precomputed circle( 0.5, 0.5, SQRT2 / 2 )
8154 var sPathData
= 'M 0.5 -0.207107 ' +
8155 'C 0.687536 -0.207107 0.867392 -0.132608 1 0 ' +
8156 'C 1.13261 0.132608 1.20711 0.312464 1.20711 0.5 ' +
8157 'C 1.20711 0.687536 1.13261 0.867392 1 1 ' +
8158 'C 0.867392 1.13261 0.687536 1.20711 0.5 1.20711 ' +
8159 'C 0.312464 1.20711 0.132608 1.13261 0 1 ' +
8160 'C -0.132608 0.867392 -0.207107 0.687536 -0.207107 0.5 ' +
8161 'C -0.207107 0.312464 -0.132608 0.132608 0 0 ' +
8162 'C 0.132608 -0.132608 0.312464 -0.207107 0.5 -0.207107';
8164 this.aBasePath
= document
.createElementNS( NSS
['svg'], 'path' );
8165 this.aBasePath
.setAttribute( 'd', sPathData
);
8168 EllipseWipePath
.prototype.perform = function( nT
)
8171 var aTransform
= SVGIdentityMatrix
.translate( 0.5, 0.5 ).scale( nT
).translate( -0.5, -0.5 );
8172 var aEllipse
= this.aBasePath
.cloneNode( true );
8173 aEllipse
.matrixTransform( aTransform
);
8180 // ------------------------------------------------------------------------------------------ //
8181 /** Class PinWheelWipePath
8182 * This class handles a parametric poly-path that is used for performing
8183 * a spinWheelWipe transition.
8186 * Number of blades generated by the transition.
8188 function PinWheelWipePath( nBlades
)
8190 this.nBlades
= nBlades
;
8191 if( !this.nBlades
|| this.nBlades
< 1 )
8195 PinWheelWipePath
.calcCenteredClock = function( nT
, nE
)
8199 var aTransform
= SVGIdentityMatrix
.rotate( nT
* 360 );
8201 var aPoint
= document
.documentElement
.createSVGPoint();
8202 aPoint
.y
= -nMAX_EDGE
;
8203 aPoint
= aPoint
.matrixTransform( aTransform
);
8205 var sPathData
= 'M ' + aPoint
.x
+ ' ' + aPoint
.y
+ ' ';
8208 sPathData
+= 'L ' + '-' + nE
+ ' -' + nE
+ ' ';
8211 sPathData
+= 'L ' + '-' + nE
+ ' ' + nE
+ ' ';
8214 sPathData
+= 'L ' + nE
+ ' ' + nE
+ ' ';
8217 sPathData
+= 'L ' + nE
+ ' -' + nE
+ ' ';
8220 sPathData
+= 'L 0 -' + nE
+ ' ';
8221 sPathData
+= 'L 0 0 ';
8223 sPathData
+= 'L ' + aPoint
.x
+ ' ' + aPoint
.y
;
8225 var aPath
= document
.createElementNS( NSS
['svg'], 'path' );
8226 aPath
.setAttribute( 'd', sPathData
);
8230 PinWheelWipePath
.prototype.perform = function( nT
)
8232 var aBasePath
= PinWheelWipePath
.calcCenteredClock( nT
/ this.nBlades
,
8233 2.0 /* max edge when rotating */ );
8235 var aPolyPath
= aBasePath
.cloneNode( true );
8239 for( i
= this.nBlades
- 1; i
> 0; --i
)
8241 aRotation
= SVGIdentityMatrix
.rotate( (i
* 360) / this.nBlades
);
8242 aPath
= aBasePath
.cloneNode( true );
8243 aPath
.matrixTransform( aRotation
);
8244 aPolyPath
.appendPath( aPath
);
8247 var aTransform
= SVGIdentityMatrix
.translate( 0.5, 0.5 ).scale( 0.5 );
8248 aPolyPath
.matrixTransform( aTransform
);
8254 // ------------------------------------------------------------------------------------------ //
8255 /** Class CheckerBoardWipePath
8257 * @param unitsPerEdge
8258 * The number of cells (per line and column) in the checker board.
8260 function CheckerBoardWipePath( unitsPerEdge
)
8262 this.unitsPerEdge
= unitsPerEdge
;
8263 if( this.unitsPerEdge
=== undefined || this.unitsPerEdge
< 1 )
8264 this.unitsPerEdge
= 10;
8265 this.aBasePath
= createUnitSquarePath();
8271 * A parameter in [0,1] representing the width of the generated bars.
8272 * @return {SVGPathElement}
8273 * A svg <path> element representing a multi-bars.
8275 CheckerBoardWipePath
.prototype.perform = function( nT
)
8277 var d
= pruneScaleValue(1.0 / this.unitsPerEdge
);
8278 var aMatrix
= SVGIdentityMatrix
.scaleNonUniform(pruneScaleValue( d
*2.0*nT
),
8279 pruneScaleValue( d
) );
8281 var aPolyPath
= null;
8285 for ( i
= this.unitsPerEdge
; i
--; )
8287 aTransform
= SVGIdentityMatrix
;
8289 if ((i
% 2) == 1) // odd line
8290 aTransform
= aTransform
.translate( -d
, 0.0 );
8292 aTransform
= aTransform
.multiply( aMatrix
);
8294 for ( j
= (this.unitsPerEdge
/ 2) + 1; j
--;)
8296 aPath
= this.aBasePath
.cloneNode( true );
8297 aPath
.matrixTransform( aTransform
);
8298 if (aPolyPath
== null) aPolyPath
= aPath
;
8299 else aPolyPath
.appendPath( aPath
);
8300 aTransform
= SVGIdentityMatrix
.translate( d
*2.0, 0.0 ).multiply( aTransform
);
8303 aMatrix
= SVGIdentityMatrix
.translate( 0.0, d
).multiply( aMatrix
); // next line
8310 // ------------------------------------------------------------------------------------------ //
8311 /** Class AnimatedSlide
8312 * This class handle a slide element during a slide transition.
8315 * The MetaSlide object related to the slide element to be handled.
8317 function AnimatedSlide( aMetaSlide
)
8321 log( 'AnimatedSlide constructor: meta slide is not valid' );
8324 this.aMetaSlide
= aMetaSlide
;
8325 this.aSlideElement
= this.aMetaSlide
.slideElement
;
8326 this.sSlideId
= this.aMetaSlide
.slideId
;
8328 this.aUsedAttributeSet
= new Array();
8330 this.aClipPathElement
= null;
8331 this.aClipPathContent
= null;
8332 this.bIsClipped
= false;
8336 * Set the visibility property of the slide to 'inherit'
8337 * and update the master page view.
8339 AnimatedSlide
.prototype.show = function()
8341 this.aMetaSlide
.show();
8345 * Set the visibility property of the slide to 'hidden'.
8347 AnimatedSlide
.prototype.hide = function()
8349 this.aMetaSlide
.hide();
8352 /** notifyUsedAttribute
8353 * Populate the set of attribute used for the transition.
8356 * A string representing an attribute name.
8358 AnimatedSlide
.prototype.notifyUsedAttribute = function( sName
)
8360 if( sName
== 'clip-path' )
8362 this.initClipPath();
8363 this.bIsClipped
= true;
8367 this.aUsedAttributeSet
.push( sName
);
8372 * Remove from the handled slide element any attribute that was appended for
8373 * performing the transition.
8375 AnimatedSlide
.prototype.reset = function()
8377 if( this.bIsClipped
)
8379 this.cleanClipPath();
8380 this.bIsClipped
= false;
8384 for( i
= 0; i
< this.aUsedAttributeSet
.length
; ++i
)
8386 var sAttrName
= this.aUsedAttributeSet
[i
];
8387 this.aSlideElement
.removeAttribute( sAttrName
);
8389 this.aUsedAttributeSet
= new Array();
8393 * Create a new clip path element and append it to the clip path def group.
8394 * Moreover the created <clipPath> element is referenced by the handled slide
8397 AnimatedSlide
.prototype.initClipPath = function()
8399 // We create the clip path element.
8400 this.aClipPathElement
= document
.createElementNS( NSS
['svg'], 'clipPath' );
8402 var sId
= 'clip-path-' + this.sSlideId
;
8403 this.aClipPathElement
.setAttribute( 'id', sId
);
8404 this.aClipPathElement
.setAttribute( 'clipPathUnits', 'userSpaceOnUse' );
8406 // We create and append a placeholder content.
8407 this.aClipPathContent
= document
.createElementNS( NSS
['svg'], 'path' );
8408 var sPathData
= 'M 0 0 h ' + WIDTH
+ ' v ' + HEIGHT
+ ' h -' + WIDTH
+ ' z';
8409 this.aClipPathContent
.setAttribute( 'd', sPathData
);
8410 this.aClipPathElement
.appendChild( this.aClipPathContent
);
8412 // We insert it into the svg document.
8413 var aClipPathGroup
= theMetaDoc
.aClipPathGroup
;
8414 aClipPathGroup
.appendChild( this.aClipPathElement
);
8416 // Finally we set the reference to the created clip path.
8417 // We set it on the parent element because a slide element already
8418 // owns a clip path attribute.
8419 var sRef
= 'url(#' + sId
+ ')';
8420 this.aSlideElement
.parentNode
.setAttribute( 'clip-path', sRef
);
8424 * Removes the related <clipPath> element from the <defs> group,
8425 * and remove the 'clip-path' attribute from the slide element.
8428 AnimatedSlide
.prototype.cleanClipPath = function()
8430 this.aSlideElement
.parentNode
.removeAttribute( 'clip-path' );
8432 if( this.aClipPathElement
)
8434 var aClipPathGroup
= theMetaDoc
.aClipPathGroup
;
8435 aClipPathGroup
.removeChild( this.aClipPathElement
);
8436 this.aClipPathElement
= null;
8437 this.aClipPathContent
= null;
8442 * Insert an svg element before the handled slide element.
8447 AnimatedSlide
.prototype.insertBefore = function( aElement
)
8451 this.aSlideElement
.parentNode
.insertBefore( aElement
, this.aSlideElement
);
8456 * Insert an svg element after the handled slide element.
8461 AnimatedSlide
.prototype.appendElement = function( aElement
)
8465 this.aSlideElement
.parentNode
.appendChild( aElement
);
8470 * Remove an svg element.
8475 AnimatedSlide
.prototype.removeElement = function( aElement
)
8479 this.aSlideElement
.parentNode
.removeChild( aElement
);
8488 AnimatedSlide
.prototype.getWidth = function()
8498 AnimatedSlide
.prototype.getHeight = function()
8506 * A number in the [0,1] range representing the slide opacity.
8508 AnimatedSlide
.prototype.setOpacity = function( nValue
)
8510 this.aSlideElement
.setAttribute( 'opacity', nValue
);
8514 * Translate the handled slide.
8517 * A number representing the translation that occurs in the x direction.
8519 * A number representing the translation that occurs in the y direction.
8521 AnimatedSlide
.prototype.translate = function( nDx
, nDy
)
8523 var sTransformAttr
= 'translate(' + nDx
+ ',' + nDy
+ ')';
8524 this.aSlideElement
.setAttribute( 'transform', sTransformAttr
);
8528 * Replace the current content of the <clipPath> element with the one
8529 * passed through the parameter.
8531 * @param aClipPathContent
8532 * A <g> element representing a <path> element used for clipping.
8534 AnimatedSlide
.prototype.setClipPath = function( aClipPathContent
)
8536 // Earlier we used to replace the current <path> element with the passed one,
8537 // anyway that does not work in IE9, so we replace the 'd' attribute, only.
8538 if( this.aClipPathContent
)
8540 // this.aClipPathElement.replaceChild( aClipPathContent, this.aClipPathContent );
8541 // this.aClipPathContent = aClipPathContent;
8542 var sPathData
= aClipPathContent
.getAttribute( 'd' );
8543 this.aClipPathContent
.setAttribute( 'd', sPathData
);
8548 // ------------------------------------------------------------------------------------------ //
8549 function AnimatedElement( aElement
)
8553 log( 'AnimatedElement constructor: element is not valid' );
8556 this.aSlideShowContext
= null;
8558 this.aBaseElement
= aElement
.cloneNode( true );
8559 this.aActiveElement
= aElement
;
8560 this.sElementId
= this.aActiveElement
.getAttribute( 'id' );
8562 this.aBaseBBox
= this.aActiveElement
.getBBox();
8563 this.nBaseCenterX
= this.aBaseBBox
.x
+ this.aBaseBBox
.width
/ 2;
8564 this.nBaseCenterY
= this.aBaseBBox
.y
+ this.aBaseBBox
.height
/ 2;
8567 this.aClipPathElement
= null;
8568 this.aClipPathContent
= null;
8570 this.aPreviousElement
= null;
8571 this.aStateSet
= new Object();
8573 this.eAdditiveMode
= ADDITIVE_MODE_REPLACE
;
8574 this.bIsUpdated
= true;
8576 this.aTMatrix
= document
.documentElement
.createSVGMatrix();
8577 this.aCTM
= document
.documentElement
.createSVGMatrix();
8578 this.aICTM
= document
.documentElement
.createSVGMatrix();
8583 AnimatedElement
.prototype.initElement = function()
8585 this.nCenterX
= this.nBaseCenterX
;
8586 this.nCenterY
= this.nBaseCenterY
;
8587 this.nScaleFactorX
= 1.0;
8588 this.nScaleFactorY
= 1.0;
8591 // add a transform attribute of type matrix
8592 this.aActiveElement
.setAttribute( 'transform', makeMatrixString( 1, 0, 0, 1, 0, 0 ) );
8596 * Create a new clip path element and append it to the clip path def group.
8597 * Moreover the created <clipPath> element is referenced by the handled
8601 AnimatedElement
.prototype.initClipPath = function()
8603 // We create the clip path element.
8604 this.aClipPathElement
= document
.createElementNS( NSS
['svg'], 'clipPath' );
8606 var sId
= 'clip-path-' + this.sElementId
;
8607 this.aClipPathElement
.setAttribute( 'id', sId
);
8608 this.aClipPathElement
.setAttribute( 'clipPathUnits', 'userSpaceOnUse' );
8610 // We create and append a placeholder content.
8611 this.aClipPathContent
= document
.createElementNS( NSS
['svg'], 'path' );
8612 this.aClippingBBox
= this.getBBoxWithStroke();
8613 var nWidth
= this.aClippingBBox
.width
;
8614 var nHeight
= this.aClippingBBox
.height
;
8615 var sPathData
= 'M ' + this.aClippingBBox
.x
+ ' ' + this.aClippingBBox
.y
+
8616 ' h ' + nWidth
+ ' v ' + nHeight
+ ' h -' + nWidth
+ ' z';
8617 this.aClipPathContent
.setAttribute( 'd', sPathData
);
8618 this.aClipPathElement
.appendChild( this.aClipPathContent
);
8620 // We insert it into the svg document.
8621 var aClipPathGroup
= theMetaDoc
.aClipPathGroup
;
8622 aClipPathGroup
.appendChild( this.aClipPathElement
);
8624 // Finally we set the reference to the created clip path.
8625 var sRef
= 'url(#' + sId
+ ')';
8626 this.aActiveElement
.setAttribute( 'clip-path', sRef
);
8630 * Removes the related <clipPath> element from the <defs> group,
8631 * and remove the 'clip-path' attribute from the animated element.
8634 AnimatedElement
.prototype.cleanClipPath = function()
8636 this.aActiveElement
.removeAttribute( 'clip-path' );
8638 if( this.aClipPathElement
)
8640 var aClipPathGroup
= theMetaDoc
.aClipPathGroup
;
8641 aClipPathGroup
.removeChild( this.aClipPathElement
);
8642 this.aClipPathElement
= null;
8643 this.aClipPathContent
= null;
8647 AnimatedElement
.prototype.getId = function()
8649 return this.aActiveElement
.getAttribute( 'id' );
8652 AnimatedElement
.prototype.getAdditiveMode = function()
8654 return this.eAdditiveMode
;
8657 AnimatedElement
.prototype.setAdditiveMode = function( eAdditiveMode
)
8659 this.eAdditiveMode
= eAdditiveMode
;
8662 AnimatedElement
.prototype.setToElement = function( aElement
)
8666 log( 'AnimatedElement(' + this.getId() + ').setToElement: element is not valid' );
8670 var aClone
= aElement
.cloneNode( true );
8671 this.aPreviousElement
= this.aActiveElement
.parentNode
.replaceChild( aClone
, this.aActiveElement
);
8672 this.aActiveElement
= aClone
;
8677 AnimatedElement
.prototype.notifySlideStart = function( aSlideShowContext
)
8679 if( !aSlideShowContext
)
8681 log( 'AnimatedElement.notifySlideStart: slideshow context is not valid' );
8683 this.aSlideShowContext
= aSlideShowContext
;
8685 var aClone
= this.aBaseElement
.cloneNode( true );
8686 this.aActiveElement
.parentNode
.replaceChild( aClone
, this.aActiveElement
);
8687 this.aActiveElement
= aClone
;
8690 this.DBG( '.notifySlideStart invoked' );
8693 AnimatedElement
.prototype.notifySlideEnd = function()
8698 AnimatedElement
.prototype.notifyAnimationStart = function()
8703 AnimatedElement
.prototype.notifyAnimationEnd = function()
8708 AnimatedElement
.prototype.notifyNextEffectStart = function( nEffectIndex
)
8714 * Save the state of the managed animated element and append it to aStateSet
8715 * using the passed animation node id as key.
8717 * @param nAnimationNodeId
8718 * A non negative integer representing the unique id of an animation node.
8720 AnimatedElement
.prototype.saveState = function( nAnimationNodeId
)
8722 ANIMDBG
.print( 'AnimatedElement(' + this.getId() + ').saveState(' + nAnimationNodeId
+')' );
8723 if( !this.aStateSet
[ nAnimationNodeId
] )
8725 this.aStateSet
[ nAnimationNodeId
] = new Object();
8727 var aState
= this.aStateSet
[ nAnimationNodeId
];
8728 aState
.aElement
= this.aActiveElement
.cloneNode( true );
8729 aState
.nCenterX
= this.nCenterX
;
8730 aState
.nCenterY
= this.nCenterY
;
8731 aState
.nScaleFactorX
= this.nScaleFactorX
;
8732 aState
.nScaleFactorY
= this.nScaleFactorY
;
8737 * Restore the state of the managed animated element to the state with key
8738 * the passed animation node id.
8740 * @param nAnimationNodeId
8741 * A non negative integer representing the unique id of an animation node.
8744 * True if the restoring operation is successful, false otherwise.
8746 AnimatedElement
.prototype.restoreState = function( nAnimationNodeId
)
8748 if( !this.aStateSet
[ nAnimationNodeId
] )
8750 log( 'AnimatedElement(' + this.getId() + ').restoreState: state '
8751 +nAnimationNodeId
+ ' is not valid' );
8755 ANIMDBG
.print( 'AnimatedElement(' + this.getId() + ').restoreState(' + nAnimationNodeId
+')' );
8756 var aState
= this.aStateSet
[ nAnimationNodeId
];
8757 var bRet
= this.setToElement( aState
.aElement
);
8760 this.nCenterX
= aState
.nCenterX
;
8761 this.nCenterY
= aState
.nCenterY
;
8762 this.nScaleFactorX
= aState
.nScaleFactorX
;
8763 this.nScaleFactorY
= aState
.nScaleFactorY
;
8768 AnimatedElement
.prototype.getBaseBBox = function()
8770 return this.aBaseBBox
;
8773 AnimatedElement
.prototype.getBaseCenterX = function()
8775 return this.nBaseCenterX
;
8778 AnimatedElement
.prototype.getBaseCenterY = function()
8780 return this.nBaseCenterY
;
8783 AnimatedElement
.prototype.getBBox = function()
8785 return this.aActiveElement
.parentNode
.getBBox();
8788 AnimatedElement
.prototype.getBBoxWithStroke = function()
8790 var aBBox
= this.aActiveElement
.parentNode
.getBBox();
8792 var aChildrenSet
= this.aActiveElement
.childNodes
;
8794 var sStroke
, sStrokeWidth
;
8795 var nStrokeWidth
= 0;
8797 for( i
= 0; i
< aChildrenSet
.length
; ++i
)
8799 if( ! aChildrenSet
[i
].getAttribute
)
8802 sStroke
= aChildrenSet
[i
].getAttribute( 'stroke' );
8803 if( sStroke
&& sStroke
!= 'none' )
8805 sStrokeWidth
= aChildrenSet
[i
].getAttribute( 'stroke-width' );
8806 var nSW
= parseFloat( sStrokeWidth
);
8807 if( nSW
> nStrokeWidth
)
8812 if( nStrokeWidth
== 0 )
8814 sStrokeWidth
= ROOT_NODE
.getAttribute( 'stroke-width' );
8815 nStrokeWidth
= parseFloat( sStrokeWidth
);
8817 if( nStrokeWidth
!= 0 )
8819 // It is hard to clip properly the stroke so we try to enlarge
8820 // the resulting bounding box even more.
8821 nStrokeWidth
*= 1.1;
8822 var nHalfStrokeWidth
= nStrokeWidth
/ 2;
8823 var nDoubleStrokeWidth
= nStrokeWidth
* 2;
8825 // Note: IE10 don't let modify the values of a element BBox.
8826 var aEBBox
= document
.documentElement
.createSVGRect();
8827 aEBBox
.x
= aBBox
.x
- nHalfStrokeWidth
;
8828 aEBBox
.y
= aBBox
.y
- nHalfStrokeWidth
;
8829 aEBBox
.width
= aBBox
.width
+ nDoubleStrokeWidth
;
8830 aEBBox
.height
= aBBox
.height
+ nDoubleStrokeWidth
;
8837 * Replace the current content of the <clipPath> element with the one
8838 * passed through the parameter.
8840 * @param aClipPathContent
8841 * A <g> element representing a <path> element used for clipping.
8843 AnimatedElement
.prototype.setClipPath = function( aClipPathContent
)
8845 if( this.aClipPathContent
)
8847 // We need to translate the clip path to the top left corner of
8848 // the element bounding box.
8849 var aTranslation
= SVGIdentityMatrix
.translate( this.aClippingBBox
.x
,
8850 this.aClippingBBox
.y
);
8851 aClipPathContent
.matrixTransform( aTranslation
);
8852 var sPathData
= aClipPathContent
.getAttribute( 'd' );
8853 this.aClipPathContent
.setAttribute( 'd', sPathData
);
8858 AnimatedElement
.prototype.getX = function()
8860 return this.nCenterX
;
8863 AnimatedElement
.prototype.getY = function()
8865 return this.nCenterY
;
8868 AnimatedElement
.prototype.getWidth = function()
8870 return this.nScaleFactorX
* this.getBaseBBox().width
;
8873 AnimatedElement
.prototype.getHeight = function()
8875 return this.nScaleFactorY
* this.getBaseBBox().height
;
8878 AnimatedElement
.prototype.setCTM = function()
8881 this.aICTM
.e
= this.getBaseCenterX();
8882 this.aICTM
.f
= this.getBaseCenterY();
8884 this.aCTM
.e
= -this.aICTM
.e
;
8885 this.aCTM
.f
= -this.aICTM
.f
;
8888 AnimatedElement
.prototype.updateTransformAttribute = function()
8890 this.aTransformAttrList
= this.aActiveElement
.transform
.baseVal
;
8891 this.aTransformAttr
= this.aTransformAttrList
.getItem( 0 );
8892 this.aTransformAttr
.setMatrix( this.aTMatrix
);
8895 AnimatedElement
.prototype.setX = function( nXNewPos
)
8897 this.aTransformAttrList
= this.aActiveElement
.transform
.baseVal
;
8898 this.aTransformAttr
= this.aTransformAttrList
.getItem( 0 );
8899 this.aTransformAttr
.matrix
.e
+= ( nXNewPos
- this.getX() );
8900 this.nCenterX
= nXNewPos
;
8903 AnimatedElement
.prototype.setY = function( nYNewPos
)
8905 this.aTransformAttrList
= this.aActiveElement
.transform
.baseVal
;
8906 this.aTransformAttr
= this.aTransformAttrList
.getItem( 0 );
8907 this.aTransformAttr
.matrix
.f
+= ( nYNewPos
- this.getY() );
8908 this.nCenterY
= nYNewPos
;
8911 AnimatedElement
.prototype.setWidth = function( nNewWidth
)
8913 var nBaseWidth
= this.getBaseBBox().width
;
8914 if( nBaseWidth
<= 0 )
8917 this.nScaleFactorX
= nNewWidth
/ nBaseWidth
;
8921 AnimatedElement
.prototype.setHeight = function( nNewHeight
)
8923 var nBaseHeight
= this.getBaseBBox().height
;
8924 if( nBaseHeight
<= 0 )
8927 this.nScaleFactorY
= nNewHeight
/ nBaseHeight
;
8931 AnimatedElement
.prototype.implScale = function( )
8933 this.aTMatrix
= document
.documentElement
.createSVGMatrix();
8934 this.aTMatrix
.a
= this.nScaleFactorX
;
8935 this.aTMatrix
.d
= this.nScaleFactorY
;
8936 this.aTMatrix
= this.aICTM
.multiply( this.aTMatrix
.multiply( this.aCTM
) );
8938 var nDeltaX
= this.getX() - this.getBaseCenterX();
8939 var nDeltaY
= this.getY() - this.getBaseCenterY();
8940 this.aTMatrix
= this.aTMatrix
.translate( nDeltaX
, nDeltaY
);
8941 this.updateTransformAttribute();
8944 AnimatedElement
.prototype.setWidth2 = function( nNewWidth
)
8947 log( 'AnimatedElement(' + this.getId() + ').setWidth: negative width!' );
8948 if( nNewWidth
< 0.001 )
8953 var nCurWidth
= this.getWidth();
8954 if( nCurWidth
<= 0 )
8957 var nScaleFactor
= nNewWidth
/ nCurWidth
;
8958 if( nScaleFactor
< 1e-5 )
8959 nScaleFactor
= 1e-5;
8960 this.aTMatrix
= document
.documentElement
.createSVGMatrix();
8961 this.aTMatrix
.a
= nScaleFactor
;
8962 this.aTMatrix
= this.aICTM
.multiply( this.aTMatrix
.multiply( this.aCTM
) );
8963 this.updateTransformAttribute();
8966 AnimatedElement
.prototype.setHeight2 = function( nNewHeight
)
8968 ANIMDBG
.print( 'AnimatedElement.setHeight: nNewHeight = ' + nNewHeight
);
8969 if( nNewHeight
< 0 )
8970 log( 'AnimatedElement(' + this.getId() + ').setWidth: negative height!' );
8971 if( nNewHeight
< 0.001 )
8976 var nCurHeight
= this.getHeight();
8977 ANIMDBG
.print( 'AnimatedElement.setHeight: nCurHeight = ' + nCurHeight
);
8978 if( nCurHeight
<= 0 )
8981 var nScaleFactor
= nNewHeight
/ nCurHeight
;
8982 ANIMDBG
.print( 'AnimatedElement.setHeight: nScaleFactor = ' + nScaleFactor
);
8983 if( nScaleFactor
< 1e-5 )
8984 nScaleFactor
= 1e-5;
8985 this.aTMatrix
= document
.documentElement
.createSVGMatrix();
8986 this.aTMatrix
.d
= nScaleFactor
;
8987 this.aTMatrix
= this.aICTM
.multiply( this.aTMatrix
.multiply( this.aCTM
) );
8988 this.updateTransformAttribute();
8991 AnimatedElement
.prototype.getOpacity = function()
8993 return this.aActiveElement
.getAttribute( 'opacity' );
8996 AnimatedElement
.prototype.setOpacity = function( nValue
)
8998 this.aActiveElement
.setAttribute( 'opacity', nValue
);
9001 AnimatedElement
.prototype.getVisibility = function()
9004 var sVisibilityValue
= this.aActiveElement
.getAttribute( 'visibility' );
9005 if( !sVisibilityValue
|| ( sVisibilityValue
=== 'inherit' ) )
9006 return 'visible'; // TODO: look for parent visibility!
9008 return sVisibilityValue
;
9011 AnimatedElement
.prototype.setVisibility = function( sValue
)
9013 if( sValue
== 'visible' )
9015 this.aActiveElement
.setAttribute( 'visibility', sValue
);
9018 AnimatedElement
.prototype.getStrokeStyle = function()
9020 // TODO: getStrokeStyle: implement it
9024 AnimatedElement
.prototype.setStrokeStyle = function( sValue
)
9026 ANIMDBG
.print( 'AnimatedElement.setStrokeStyle(' + sValue
+ ')' );
9029 AnimatedElement
.prototype.getFillStyle = function()
9031 // TODO: getFillStyle: implement it
9035 AnimatedElement
.prototype.setFillStyle = function( sValue
)
9037 ANIMDBG
.print( 'AnimatedElement.setFillStyle(' + sValue
+ ')' );
9040 AnimatedElement
.prototype.getFillColor = function()
9042 var aChildSet
= getElementChildren( this.aActiveElement
);
9043 var sFillColorValue
= '';
9044 for( var i
= 0; i
< aChildSet
.length
; ++i
)
9046 sFillColorValue
= aChildSet
[i
].getAttribute( 'fill' );
9047 if( sFillColorValue
&& ( sFillColorValue
!== 'none' ) )
9051 return colorParser( sFillColorValue
);
9054 AnimatedElement
.prototype.setFillColor = function( aRGBValue
)
9056 assert( aRGBValue
instanceof RGBColor
,
9057 'AnimatedElement.setFillColor: value argument is not an instance of RGBColor' );
9059 var sValue
= aRGBValue
.toString( true /* clamped values */ );
9060 var aChildSet
= getElementChildren( this.aActiveElement
);
9062 var sFillColorValue
= '';
9063 for( var i
= 0; i
< aChildSet
.length
; ++i
)
9065 sFillColorValue
= aChildSet
[i
].getAttribute( 'fill' );
9066 if( sFillColorValue
&& ( sFillColorValue
!== 'none' ) )
9068 aChildSet
[i
].setAttribute( 'fill', sValue
);
9073 AnimatedElement
.prototype.getStrokeColor = function()
9075 var aChildSet
= getElementChildren( this.aActiveElement
);
9076 var sStrokeColorValue
= '';
9077 for( var i
= 0; i
< aChildSet
.length
; ++i
)
9079 sStrokeColorValue
= aChildSet
[i
].getAttribute( 'stroke' );
9080 if( sStrokeColorValue
&& ( sStrokeColorValue
!== 'none' ) )
9084 return colorParser( sStrokeColorValue
);
9087 AnimatedElement
.prototype.setStrokeColor = function( aRGBValue
)
9089 assert( aRGBValue
instanceof RGBColor
,
9090 'AnimatedElement.setFillColor: value argument is not an instance of RGBColor' );
9092 var sValue
= aRGBValue
.toString( true /* clamped values */ );
9093 var aChildSet
= getElementChildren( this.aActiveElement
);
9095 var sStrokeColorValue
= '';
9096 for( var i
= 0; i
< aChildSet
.length
; ++i
)
9098 sStrokeColorValue
= aChildSet
[i
].getAttribute( 'stroke' );
9099 if( sStrokeColorValue
&& ( sStrokeColorValue
!== 'none' ) )
9101 aChildSet
[i
].setAttribute( 'stroke', sValue
);
9106 AnimatedElement
.prototype.getFontColor = function()
9108 // TODO: getFontColor implement it
9109 return new RGBColor( 0, 0, 0 );
9112 AnimatedElement
.prototype.setFontColor = function( sValue
)
9114 ANIMDBG
.print( 'AnimatedElement.setFontColor(' + sValue
+ ')' );
9117 AnimatedElement
.prototype.DBG = function( sMessage
, nTime
)
9119 aAnimatedElementDebugPrinter
.print( 'AnimatedElement(' + this.getId() + ')' + sMessage
, nTime
);
9122 // ------------------------------------------------------------------------------------------ //
9123 function AnimatedTextElement( aElement
, aEventMultiplexer
)
9125 var theDocument
= document
;
9127 var sTextType
= aElement
.getAttribute( 'class' );
9128 var bIsListItem
= ( sTextType
=== 'ListItem' );
9129 if( ( sTextType
!== 'TextParagraph' ) && !bIsListItem
)
9131 log( 'AnimatedTextElement: passed element is not a paragraph.' );
9134 var aTextShapeElement
= aElement
.parentNode
;
9135 sTextType
= aTextShapeElement
.getAttribute( 'class' );
9136 if( sTextType
!== 'TextShape' )
9138 log( 'AnimatedTextElement: element parent is not a text shape.' );
9141 var aTextShapeGroup
= aTextShapeElement
.parentNode
;
9142 // We search for the helper group element used for inserting
9143 // the element copy to be animated; if it doesn't exist we create it.
9144 var aAnimatedElementGroup
= getElementByClassName( aTextShapeGroup
, 'AnimatedElements' );
9145 if( !aAnimatedElementGroup
)
9147 aAnimatedElementGroup
= theDocument
.createElementNS( NSS
['svg'], 'g' );
9148 aAnimatedElementGroup
.setAttribute( 'class', 'AnimatedElements' );
9149 aTextShapeGroup
.appendChild( aAnimatedElementGroup
);
9152 // Create element used on animating
9153 var aAnimatableElement
= theDocument
.createElementNS( NSS
['svg'], 'g' );
9154 var aTextElement
= theDocument
.createElementNS( NSS
['svg'], 'text' );
9155 // Clone paragraph element <tspan>
9156 var aParagraphElement
= aElement
.cloneNode( true );
9158 // We create a group element for wrapping bullets, bitmaps
9159 // and text decoration
9160 this.aGraphicGroupElement
= theDocument
.createElementNS( NSS
['svg'], 'g' );
9161 this.aGraphicGroupElement
.setAttribute( 'class', 'GraphicGroup' );
9163 // In case we are dealing with a list item that utilizes a bullet char
9164 // we need to clone the related bullet char too.
9165 var aBulletCharClone
= null;
9166 var aBulletCharElem
= null;
9167 var bIsBulletCharStyle
=
9168 ( aElement
.getAttributeNS( NSS
['ooo'], aOOOAttrListItemNumberingType
) === 'bullet-style' );
9169 if( bIsBulletCharStyle
)
9171 var aBulletCharGroupElem
= getElementByClassName( aTextShapeGroup
, 'BulletChars' );
9172 if( aBulletCharGroupElem
)
9174 var aBulletPlaceholderElem
= getElementByClassName( aElement
.firstElementChild
, 'BulletPlaceholder' );
9175 if( aBulletPlaceholderElem
)
9177 var sId
= aBulletPlaceholderElem
.getAttribute( 'id' );
9178 sId
= 'bullet-char(' + sId
+ ')';
9179 aBulletCharElem
= theDocument
.getElementById( sId
);
9180 if( aBulletCharElem
)
9182 aBulletCharClone
= aBulletCharElem
.cloneNode( true );
9186 log( 'AnimatedTextElement: ' + sId
+ ' not found.' );
9191 log( 'AnimatedTextElement: no bullet placeholder found' );
9196 log( 'AnimatedTextElement: no bullet char group found' );
9200 // In case there are embedded bitmaps we need to clone them
9201 var aBitmapElemSet
= new Array();
9202 var aBitmapCloneSet
= new Array();
9203 var aBitmapPlaceholderSet
= getElementsByClassName( aElement
, 'BitmapPlaceholder' );
9204 if( aBitmapPlaceholderSet
)
9207 for( i
= 0; i
< aBitmapPlaceholderSet
.length
; ++i
)
9209 sId
= aBitmapPlaceholderSet
[i
].getAttribute( 'id' );
9210 var sBitmapChecksum
= sId
.substring( 'bitmap-placeholder'.length
+ 1, sId
.length
- 1 );
9211 sId
= 'embedded-bitmap(' + sBitmapChecksum
+ ')';
9212 aBitmapElemSet
[i
] = theDocument
.getElementById( sId
);
9213 if( aBitmapElemSet
[i
] )
9215 aBitmapCloneSet
[i
] = aBitmapElemSet
[i
].cloneNode( true );
9219 log( 'AnimatedTextElement: ' + sId
+ ' not found.' );
9225 // Change clone element id.
9226 this.sParagraphId
= sId
= aParagraphElement
.getAttribute( 'id' );
9227 aParagraphElement
.removeAttribute( 'id' );
9228 aAnimatableElement
.setAttribute( 'id', sId
+'.a' );
9229 if( aBulletCharClone
)
9230 aBulletCharClone
.removeAttribute( 'id' );
9231 for( i
= 0; i
< aBitmapCloneSet
.length
; ++i
)
9233 if( aBitmapCloneSet
[i
] )
9234 aBitmapCloneSet
[i
].removeAttribute( 'id' );
9237 // Set up visibility
9238 var sVisibilityAttr
= aElement
.getAttribute( 'visibility' );
9239 if( !sVisibilityAttr
)
9240 sVisibilityAttr
= 'inherit';
9241 aAnimatableElement
.setAttribute( 'visibility', sVisibilityAttr
);
9242 aParagraphElement
.setAttribute( 'visibility', 'inherit' );
9243 this.aGraphicGroupElement
.setAttribute( 'visibility', 'inherit' );
9244 if( aBulletCharElem
)
9245 aBulletCharElem
.setAttribute( 'visibility', 'hidden' );
9246 for( i
= 0; i
< aBitmapCloneSet
.length
; ++i
)
9248 if( aBitmapElemSet
[i
] )
9249 aBitmapElemSet
[i
].setAttribute( 'visibility', 'hidden' );
9252 // Append each element to its parent.
9253 // <g class='AnimatedElements'>
9256 // <tspan class='TextParagraph'> ... </tspan>
9258 // <g class='GraphicGroup'>
9259 // [<g class='BulletChar'>...</g>]
9260 // [<g class='EmbeddedBitmap'>...</g>]
9263 // [<g class='EmbeddedBitmap'>...</g>]
9268 aTextElement
.appendChild( aParagraphElement
);
9269 aAnimatableElement
.appendChild( aTextElement
);
9271 if( aBulletCharClone
)
9272 this.aGraphicGroupElement
.appendChild( aBulletCharClone
);
9273 for( i
= 0; i
< aBitmapCloneSet
.length
; ++i
)
9275 if( aBitmapCloneSet
[i
] )
9276 this.aGraphicGroupElement
.appendChild( aBitmapCloneSet
[i
] );
9278 aAnimatableElement
.appendChild( this.aGraphicGroupElement
);
9279 aAnimatedElementGroup
.appendChild( aAnimatableElement
);
9281 this.aParentTextElement
= aElement
.parentNode
;
9282 this.aParagraphElement
= aElement
;
9283 this.aAnimatedElementGroup
= aAnimatedElementGroup
;
9284 this.nRunningAnimations
= 0;
9286 // we collect all hyperlink ids
9287 this.aHyperlinkIdSet
= new Array();
9288 var aHyperlinkElementSet
= getElementsByClassName( this.aParagraphElement
, 'UrlField' );
9291 for( ; i
< aHyperlinkElementSet
.length
; ++i
)
9293 sHyperlinkId
= aHyperlinkElementSet
[i
].getAttribute( 'id' );
9295 this.aHyperlinkIdSet
.push( sHyperlinkId
);
9297 log( 'error: AnimatedTextElement constructor: hyperlink element has no id' );
9301 AnimatedTextElement
.superclass
.constructor.call( this, aAnimatableElement
, aEventMultiplexer
);
9304 extend( AnimatedTextElement
, AnimatedElement
);
9307 AnimatedTextElement
.prototype.setToElement = function( aElement
)
9309 var bRet
= AnimatedTextElement
.superclass
.setToElement
.call( this, aElement
);
9312 this.aGraphicGroupElement
= getElementByClassName( this.aActiveElement
, 'GraphicGroup' );
9314 return ( bRet
&& this.aGraphicGroupElement
);
9317 AnimatedTextElement
.prototype.notifySlideStart = function( aSlideShowContext
)
9319 DBGLOG( 'AnimatedTextElement.notifySlideStart' );
9320 AnimatedTextElement
.superclass
.notifySlideStart
.call( this, aSlideShowContext
);
9321 this.aGraphicGroupElement
= getElementByClassName( this.aActiveElement
, 'GraphicGroup' );
9322 this.restoreBaseTextParagraph();
9325 AnimatedTextElement
.prototype.notifySlideEnd = function()
9327 DBGLOG( 'AnimatedTextElement.notifySlideEnd' );
9328 this.aGraphicGroupElement
.setAttribute( 'visibility', 'inherit' );
9331 AnimatedTextElement
.prototype.restoreBaseTextParagraph = function()
9333 var aActiveParagraphElement
= this.aActiveElement
.firstElementChild
.firstElementChild
;
9334 if( aActiveParagraphElement
)
9336 var sVisibilityAttr
= this.aActiveElement
.getAttribute( 'visibility' );
9337 if( !sVisibilityAttr
|| ( sVisibilityAttr
=== 'visible' ) )
9338 sVisibilityAttr
= 'inherit';
9339 if( sVisibilityAttr
=== 'inherit' )
9340 this.aGraphicGroupElement
.setAttribute( 'visibility', 'visible' );
9342 this.aGraphicGroupElement
.setAttribute( 'visibility', 'hidden' );
9344 var aParagraphClone
= aActiveParagraphElement
.cloneNode( true );
9345 aParagraphClone
.setAttribute( 'id', this.sParagraphId
);
9346 aParagraphClone
.setAttribute( 'visibility', sVisibilityAttr
);
9347 this.aParentTextElement
.replaceChild( aParagraphClone
, this.aParagraphElement
);
9348 this.aParagraphElement
= aParagraphClone
;
9351 var aEventMultiplexer
= this.aSlideShowContext
.aEventMultiplexer
;
9352 var aHyperlinkIdSet
= this.aHyperlinkIdSet
;
9353 var aHyperlinkElementSet
= getElementsByClassName( this.aParagraphElement
, 'UrlField' );
9355 for( ; i
< aHyperlinkIdSet
.length
; ++i
)
9357 aEventMultiplexer
.notifyElementChangedEvent( aHyperlinkIdSet
[i
], aHyperlinkElementSet
[i
] );
9360 this.aActiveElement
.setAttribute( 'visibility', 'hidden' );
9363 AnimatedTextElement
.prototype.notifyAnimationStart = function()
9365 DBGLOG( 'AnimatedTextElement.notifyAnimationStart' );
9366 if( this.nRunningAnimations
=== 0 )
9368 var sVisibilityAttr
= this.aParagraphElement
.getAttribute( 'visibility' );
9369 if( !sVisibilityAttr
)
9370 sVisibilityAttr
= 'inherit';
9371 this.aActiveElement
.setAttribute( 'visibility', sVisibilityAttr
);
9372 this.aGraphicGroupElement
.setAttribute( 'visibility', 'inherit' );
9373 this.aParagraphElement
.setAttribute( 'visibility', 'hidden' );
9375 ++this.nRunningAnimations
;
9378 AnimatedTextElement
.prototype.notifyAnimationEnd = function()
9380 DBGLOG( 'AnimatedTextElement.notifyAnimationEnd' );
9381 --this.nRunningAnimations
;
9382 if( this.nRunningAnimations
=== 0 )
9384 this.restoreBaseTextParagraph();
9388 AnimatedTextElement
.prototype.saveState = function( nAnimationNodeId
)
9390 if( this.nRunningAnimations
=== 0 )
9392 var sVisibilityAttr
= this.aParagraphElement
.getAttribute( 'visibility' );
9393 this.aActiveElement
.setAttribute( 'visibility', sVisibilityAttr
);
9394 this.aGraphicGroupElement
.setAttribute( 'visibility', 'inherit' );
9396 AnimatedTextElement
.superclass
.saveState
.call( this, nAnimationNodeId
);
9399 AnimatedTextElement
.prototype.restoreState = function( nAnimationNodeId
)
9401 var bRet
= AnimatedTextElement
.superclass
.restoreState
.call( this, nAnimationNodeId
);
9403 this.restoreBaseTextParagraph();
9409 // ------------------------------------------------------------------------------------------ //
9410 /** Class SlideTransition
9411 * This class is responsible for initializing the properties of a slide
9412 * transition and create the object that actually will perform the transition.
9414 * @param aAnimationsRootElement
9415 * The <defs> element wrapping all animations for the related slide.
9417 * A string representing a slide id.
9419 function SlideTransition( aAnimationsRootElement
, aSlideId
)
9421 this.sSlideId
= aSlideId
;
9422 this.bIsValid
= false;
9423 this.eTransitionType
= undefined;
9424 this.eTransitionSubType
= undefined;
9425 this.bReverseDirection
= false;
9426 this.eTransitionMode
= TRANSITION_MODE_IN
;
9427 this.sFadeColor
= null;
9428 this.aDuration
= null;
9429 this.nMinFrameCount
= undefined;
9431 if( aAnimationsRootElement
)
9433 if( aAnimationsRootElement
.firstElementChild
&&
9434 ( aAnimationsRootElement
.firstElementChild
.getAttribute( 'begin' ) === (this.sSlideId
+ '.begin') ) )
9436 var aTransitionFilterElement
= aAnimationsRootElement
.firstElementChild
.firstElementChild
;
9437 if( aTransitionFilterElement
&& ( aTransitionFilterElement
.localName
=== 'transitionFilter' ) )
9439 this.aElement
= aTransitionFilterElement
;
9440 this.parseElement();
9442 aAnimationsRootElement
.removeChild( aAnimationsRootElement
.firstElementChild
);
9447 SlideTransition
.prototype.createSlideTransition = function( aLeavingSlide
, aEnteringSlide
)
9449 if( !this.isValid() )
9451 if( this.eTransitionType
== 0 )
9454 if( !aEnteringSlide
)
9456 log( 'SlideTransition.createSlideTransition: invalid entering slide.' );
9460 var aTransitionInfo
= aTransitionInfoTable
[this.eTransitionType
][this.eTransitionSubType
];
9461 var eTransitionClass
= aTransitionInfo
['class'];
9463 switch( eTransitionClass
)
9466 case TRANSITION_INVALID
:
9467 log( 'SlideTransition.createSlideTransition: transition class: TRANSITION_INVALID' );
9470 case TRANSITION_CLIP_POLYPOLYGON
:
9471 var aParametricPolyPolygon
9472 = createClipPolyPolygon( this.eTransitionType
, this.eTransitionSubType
);
9473 return new ClippedSlideChange( aLeavingSlide
, aEnteringSlide
, aParametricPolyPolygon
,
9474 aTransitionInfo
, this.isDirectionForward() );
9476 case TRANSITION_SPECIAL
:
9477 switch( this.eTransitionType
)
9480 log( 'SlideTransition.createSlideTransition: ' +
9481 'transition class: TRANSITION_SPECIAL, ' +
9482 'unknown transition type: ' + this.eTransitionType
);
9485 case PUSHWIPE_TRANSITION
:
9487 var bCombined
= false;
9488 var aDirection
= null;
9489 switch( this.eTransitionSubType
)
9492 log( 'SlideTransition.createSlideTransition: ' +
9493 'transition type: PUSHWIPE_TRANSITION, ' +
9494 'unknown transition subtype: ' + this.eTransitionSubType
);
9496 case FROMTOP_TRANS_SUBTYPE
:
9497 aDirection
= { x
: 0.0, y
: 1.0 };
9499 case FROMBOTTOM_TRANS_SUBTYPE
:
9500 aDirection
= { x
: 0.0, y
: -1.0 };
9502 case FROMLEFT_TRANS_SUBTYPE
:
9503 aDirection
= { x
: 1.0, y
: 0.0 };
9505 case FROMRIGHT_TRANS_SUBTYPE
:
9506 aDirection
= { x
: -1.0, y
: 0.0 };
9512 return new MovingSlideChange( aLeavingSlide
, aEnteringSlide
, aDirection
, aDirection
);
9515 case SLIDEWIPE_TRANSITION
:
9517 var aInDirection
= null;
9518 switch( this.eTransitionSubType
)
9521 log( 'SlideTransition.createSlideTransition: ' +
9522 'transition type: SLIDEWIPE_TRANSITION, ' +
9523 'unknown transition subtype: ' + this.eTransitionSubType
);
9525 case FROMTOP_TRANS_SUBTYPE
:
9526 aInDirection
= { x
: 0.0, y
: 1.0 };
9528 case FROMBOTTOM_TRANS_SUBTYPE
:
9529 aInDirection
= { x
: 0.0, y
: -1.0 };
9531 case FROMLEFT_TRANS_SUBTYPE
:
9532 aInDirection
= { x
: 1.0, y
: 0.0 };
9534 case FROMRIGHT_TRANS_SUBTYPE
:
9535 aInDirection
= { x
: -1.0, y
: 0.0 };
9538 var aNoDirection
= { x
: 0.0, y
: 0.0 };
9539 if( !this.bReverseDirection
)
9541 return new MovingSlideChange( aLeavingSlide
, aEnteringSlide
, aNoDirection
, aInDirection
);
9545 return new MovingSlideChange( aLeavingSlide
, aEnteringSlide
, aInDirection
, aNoDirection
);
9549 case FADE_TRANSITION
:
9550 switch( this.eTransitionSubType
)
9553 log( 'SlideTransition.createSlideTransition: ' +
9554 'transition type: FADE_TRANSITION, ' +
9555 'unknown transition subtype: ' + this.eTransitionSubType
);
9557 case CROSSFADE_TRANS_SUBTYPE
:
9558 return new FadingSlideChange( aLeavingSlide
, aEnteringSlide
);
9559 case FADEOVERCOLOR_TRANS_SUBTYPE
:
9560 return new FadingOverColorSlideChange( aLeavingSlide
, aEnteringSlide
, this.getFadeColor() );
9566 SlideTransition
.prototype.parseElement = function()
9568 var aAnimElem
= this.aElement
;
9571 this.eTransitionType
= undefined;
9572 var sTypeAttr
= aAnimElem
.getAttribute( 'type' );
9573 if( sTypeAttr
&& aTransitionTypeInMap
[ sTypeAttr
] )
9575 this.eTransitionType
= aTransitionTypeInMap
[ sTypeAttr
];
9579 log( 'SlideTransition.parseElement: transition type not valid: ' + sTypeAttr
);
9582 // subtype attribute
9583 this.eTransitionSubType
= undefined;
9584 var sSubTypeAttr
= aAnimElem
.getAttribute( 'subtype' );
9585 if( sSubTypeAttr
&& aTransitionSubtypeInMap
[ sSubTypeAttr
] )
9587 this.eTransitionSubType
= aTransitionSubtypeInMap
[ sSubTypeAttr
];
9588 this.bIsValid
= true;
9592 log( 'SlideTransition.parseElement: transition subtype not valid: ' + sSubTypeAttr
);
9595 // direction attribute
9596 this.bReverseDirection
= false;
9597 var sDirectionAttr
= aAnimElem
.getAttribute( 'direction' );
9598 if( sDirectionAttr
== 'reverse' )
9599 this.bReverseDirection
= true;
9602 this.sFadeColor
= null;
9603 if( this.eTransitionType
== FADE_TRANSITION
&&
9604 ( this.eTransitionSubType
== FADEFROMCOLOR_TRANS_SUBTYPE
||
9605 this.eTransitionSubType
== FADEOVERCOLOR_TRANS_SUBTYPE
||
9606 this.eTransitionSubType
== FADETOCOLOR_TRANS_SUBTYPE
) )
9608 var sColorAttr
= aAnimElem
.getAttribute( 'fadeColor' );
9610 this.sFadeColor
= sColorAttr
;
9612 this.sFadeColor
='#000000';
9617 this.aDuration
= null;
9618 var sDurAttr
= aAnimElem
.getAttribute( 'dur' );
9619 this.aDuration
= new Duration( sDurAttr
);
9620 if( !this.aDuration
.isSet() )
9622 this.aDuration
= new Duration( null ); // duration == 0.0
9625 // set up min frame count value;
9626 this.nMinFrameCount
= ( this.getDuration().isValue() )
9627 ? ( this.getDuration().getValue() * MINIMUM_FRAMES_PER_SECONDS
)
9628 : MINIMUM_FRAMES_PER_SECONDS
;
9629 if( this.nMinFrameCount
< 1.0 )
9630 this.nMinFrameCount
= 1;
9631 else if( this.nMinFrameCount
> MINIMUM_FRAMES_PER_SECONDS
)
9632 this.nMinFrameCount
= MINIMUM_FRAMES_PER_SECONDS
;
9636 SlideTransition
.prototype.isValid = function()
9638 return this.bIsValid
;
9641 SlideTransition
.prototype.getTransitionType = function()
9643 return this.eTransitionType
;
9646 SlideTransition
.prototype.getTransitionSubType = function()
9648 return this.eTransitionSubType
;
9651 SlideTransition
.prototype.getTransitionMode = function()
9653 return this.eTransitionMode
;
9656 SlideTransition
.prototype.getFadeColor = function()
9658 return this.sFadeColor
;
9661 SlideTransition
.prototype.isDirectionForward = function()
9663 return !this.bReverseDirection
;
9666 SlideTransition
.prototype.getDuration = function()
9668 return this.aDuration
;
9671 SlideTransition
.prototype.getMinFrameCount = function()
9673 return this.nMinFrameCount
;
9676 SlideTransition
.prototype.info = function()
9679 var sInfo
='slide transition <' + this.sSlideId
+ '>: ';
9681 sInfo
+= '; type: ' + aTransitionTypeOutMap
[ String( this.getTransitionType() ) ];
9683 // transition subtype
9684 sInfo
+= '; subtype: ' + aTransitionSubtypeOutMap
[ this.getTransitionSubType() ];
9686 // transition direction
9687 if( !this.isDirectionForward() )
9688 sInfo
+= '; direction: reverse';
9691 sInfo
+= '; mode: ' + aTransitionModeOutMap
[ this.getTransitionMode() ];
9694 if( this.getDuration() )
9695 sInfo
+= '; duration: ' + this.getDuration().info();
9702 // ------------------------------------------------------------------------------------------ //
9705 function SlideAnimations( aSlideShowContext
)
9707 this.aContext
= new NodeContext( aSlideShowContext
);
9708 this.aAnimationNodeMap
= new Object();
9709 this.aAnimatedElementMap
= new Object();
9710 this.aSourceEventElementMap
= new Object();
9711 this.aNextEffectEventArray
= new NextEffectEventArray();
9712 this.aInteractiveAnimationSequenceMap
= new Object();
9713 this.aEventMultiplexer
= new EventMultiplexer( aSlideShowContext
.aTimerEventQueue
);
9714 this.aRootNode
= null;
9715 this.bElementsParsed
= false;
9717 this.aContext
.aAnimationNodeMap
= this.aAnimationNodeMap
;
9718 this.aContext
.aAnimatedElementMap
= this.aAnimatedElementMap
;
9719 this.aContext
.aSourceEventElementMap
= this.aSourceEventElementMap
;
9721 // We set up a low priority for the invocation of document.handleClick
9722 // in order to make clicks on shapes, that start interactive animation
9723 // sequence (on click), have an higher priority.
9724 this.aEventMultiplexer
.registerMouseClickHandler( document
, 100 );
9728 SlideAnimations
.prototype.importAnimations = function( aAnimationRootElement
)
9730 if( !aAnimationRootElement
)
9733 this.aRootNode
= createAnimationTree( aAnimationRootElement
, this.aContext
);
9735 return ( this.aRootNode
? true : false );
9738 SlideAnimations
.prototype.parseElements = function()
9740 if( !this.aRootNode
)
9744 if( !this.aRootNode
.parseElement() )
9747 this.bElementsParsed
= true;
9750 SlideAnimations
.prototype.elementsParsed = function()
9752 return this.bElementsParsed
;
9755 SlideAnimations
.prototype.isFirstRun = function()
9757 return this.aContext
.bFirstRun
;
9760 SlideAnimations
.prototype.isAnimated = function()
9762 if( !this.bElementsParsed
)
9765 return this.aRootNode
.hasPendingAnimation();
9768 SlideAnimations
.prototype.start = function()
9770 if( !this.bElementsParsed
)
9773 this.chargeSourceEvents();
9774 this.chargeInterAnimEvents();
9776 aSlideShow
.setSlideEvents( this.aNextEffectEventArray
,
9777 this.aInteractiveAnimationSequenceMap
,
9778 this.aEventMultiplexer
);
9780 if( this.aContext
.bFirstRun
== undefined )
9781 this.aContext
.bFirstRun
= true;
9782 else if( this.aContext
.bFirstRun
)
9783 this.aContext
.bFirstRun
= false;
9786 if( !this.aRootNode
.init() )
9789 // resolve root node
9790 if( !this.aRootNode
.resolve() )
9796 SlideAnimations
.prototype.end = function( bLeftEffectsSkipped
)
9798 if( !this.bElementsParsed
)
9799 return; // no animations there
9802 this.aRootNode
.deactivate();
9803 this.aRootNode
.end();
9805 if( bLeftEffectsSkipped
&& this.isFirstRun() )
9807 // in case this is the first run and left events have been skipped
9808 // some next effect events for the slide could not be collected
9809 // so the next time we should behave as it was the first run again
9810 this.aContext
.bFirstRun
= undefined;
9812 else if( this.isFirstRun() )
9814 this.aContext
.bFirstRun
= false;
9819 SlideAnimations
.prototype.dispose = function()
9821 if( this.aRootNode
)
9823 this.aRootNode
.dispose();
9827 SlideAnimations
.prototype.clearNextEffectEvents = function()
9829 ANIMDBG
.print( 'SlideAnimations.clearNextEffectEvents: current slide: ' + nCurSlide
);
9830 this.aNextEffectEventArray
.clear();
9831 this.aContext
.bFirstRun
= undefined;
9834 SlideAnimations
.prototype.chargeSourceEvents = function()
9836 for( var id
in this.aSourceEventElementMap
)
9838 this.aSourceEventElementMap
[id
].charge();
9842 SlideAnimations
.prototype.chargeInterAnimEvents = function()
9844 for( var id
in this.aInteractiveAnimationSequenceMap
)
9846 this.aInteractiveAnimationSequenceMap
[id
].chargeEvents();
9850 /**********************************************************************************************
9851 * Event classes and helper functions
9852 **********************************************************************************************/
9854 // ------------------------------------------------------------------------------------------ //
9857 this.nId
= Event
.getUniqueId();
9861 Event
.CURR_UNIQUE_ID
= 0;
9863 Event
.getUniqueId = function()
9865 ++Event
.CURR_UNIQUE_ID
;
9866 return Event
.CURR_UNIQUE_ID
;
9869 Event
.prototype.getId = function()
9875 // ------------------------------------------------------------------------------------------ //
9876 function DelayEvent( aFunctor
, nTimeout
)
9878 DelayEvent
.superclass
.constructor.call( this );
9880 this.aFunctor
= aFunctor
;
9881 this.nTimeout
= nTimeout
;
9882 this.bWasFired
= false;
9884 extend( DelayEvent
, Event
);
9887 DelayEvent
.prototype.fire = function()
9889 assert( this.isCharged(), 'DelayEvent.fire: assertion isCharged failed' );
9891 this.bWasFired
= true;
9896 DelayEvent
.prototype.isCharged = function()
9898 return !this.bWasFired
;
9901 DelayEvent
.prototype.getActivationTime = function( nCurrentTime
)
9903 return ( this.nTimeout
+ nCurrentTime
);
9906 DelayEvent
.prototype.dispose = function()
9908 // don't clear unconditionally, because it may currently be executed:
9909 if( this.isCharged() )
9910 this.bWasFired
= true;
9913 DelayEvent
.prototype.charge = function()
9915 if( !this.isCharged() )
9916 this.bWasFired
= false;
9920 // ------------------------------------------------------------------------------------------ //
9921 function makeEvent( aFunctor
)
9923 return new DelayEvent( aFunctor
, 0.0 );
9928 // ------------------------------------------------------------------------------------------ //
9929 function makeDelay( aFunctor
, nTimeout
)
9931 return new DelayEvent( aFunctor
, nTimeout
);
9936 // ------------------------------------------------------------------------------------------ //
9937 function registerEvent( nNodeId
, aTiming
, aEvent
, aNodeContext
)
9939 var aSlideShowContext
= aNodeContext
.aContext
;
9940 var eTimingType
= aTiming
.getType();
9942 registerEvent
.DBG( aTiming
);
9944 if( eTimingType
== OFFSET_TIMING
)
9946 aSlideShowContext
.aTimerEventQueue
.addEvent( aEvent
);
9948 else if ( aNodeContext
.bFirstRun
)
9950 var aEventMultiplexer
= aSlideShowContext
.aEventMultiplexer
;
9951 if( !aEventMultiplexer
)
9953 log( 'registerEvent: event multiplexer not initialized' );
9956 var aNextEffectEventArray
= aSlideShowContext
.aNextEffectEventArray
;
9957 if( !aNextEffectEventArray
)
9959 log( 'registerEvent: next effect event array not initialized' );
9962 var aInteractiveAnimationSequenceMap
=
9963 aSlideShowContext
.aInteractiveAnimationSequenceMap
;
9964 if( !aInteractiveAnimationSequenceMap
)
9966 log( 'registerEvent: interactive animation sequence map not initialized' );
9970 switch( eTimingType
)
9973 var eEventType
= aTiming
.getEventType();
9974 var sEventBaseElemId
= aTiming
.getEventBaseElementId();
9975 if( sEventBaseElemId
)
9977 var aEventBaseElem
= document
.getElementById( sEventBaseElemId
);
9978 if( !aEventBaseElem
)
9980 log( 'generateEvent: EVENT_TIMING: event base element not found: ' + sEventBaseElemId
);
9983 var aSourceEventElement
= aNodeContext
.makeSourceEventElement( sEventBaseElemId
, aEventBaseElem
);
9985 if( !aInteractiveAnimationSequenceMap
[ nNodeId
] )
9987 var aInteractiveAnimationSequence
= new InteractiveAnimationSequence( nNodeId
);
9988 aInteractiveAnimationSequenceMap
[ nNodeId
] = aInteractiveAnimationSequence
;
9991 var bEventRegistered
= false;
9992 switch( eEventType
)
9994 case EVENT_TRIGGER_ON_CLICK
:
9995 aEventMultiplexer
.registerEvent( eEventType
, aSourceEventElement
.getId(), aEvent
);
9996 aEventMultiplexer
.registerRewindedEffectHandler( aSourceEventElement
.getId(),
9997 bind2( aSourceEventElement
.charge
, aSourceEventElement
) );
9998 bEventRegistered
= true;
10001 log( 'generateEvent: not handled event type: ' + eEventType
);
10003 if( bEventRegistered
)
10005 var aStartEvent
= aInteractiveAnimationSequenceMap
[ nNodeId
].getStartEvent();
10006 var aEndEvent
= aInteractiveAnimationSequenceMap
[ nNodeId
].getEndEvent();
10007 aEventMultiplexer
.registerEvent( eEventType
, aSourceEventElement
.getId(), aStartEvent
);
10008 aEventMultiplexer
.registerEvent( EVENT_TRIGGER_END_EVENT
, nNodeId
, aEndEvent
);
10009 aEventMultiplexer
.registerRewindedEffectHandler(
10011 bind2( InteractiveAnimationSequence
.prototype.chargeEvents
,
10012 aInteractiveAnimationSequenceMap
[ nNodeId
] )
10016 else // no base event element present
10018 switch( eEventType
)
10020 case EVENT_TRIGGER_ON_NEXT_EFFECT
:
10021 aNextEffectEventArray
.appendEvent( aEvent
);
10024 log( 'generateEvent: not handled event type: ' + eEventType
);
10028 case SYNCBASE_TIMING
:
10029 eEventType
= aTiming
.getEventType();
10030 sEventBaseElemId
= aTiming
.getEventBaseElementId();
10031 if( sEventBaseElemId
)
10033 var aAnimationNode
= aNodeContext
.aAnimationNodeMap
[ sEventBaseElemId
];
10034 if( !aAnimationNode
)
10036 log( 'generateEvent: SYNCBASE_TIMING: event base element not found: ' + sEventBaseElemId
);
10039 aEventMultiplexer
.registerEvent( eEventType
, aAnimationNode
.getId(), aEvent
);
10043 log( 'generateEvent: SYNCBASE_TIMING: event base element not specified' );
10047 log( 'generateEvent: not handled timing type: ' + eTimingType
);
10052 registerEvent
.DEBUG
= aRegisterEventDebugPrinter
.isEnabled();
10054 registerEvent
.DBG = function( aTiming
, nTime
)
10056 if( registerEvent
.DEBUG
)
10058 aRegisterEventDebugPrinter
.print( 'registerEvent( timing: ' + aTiming
.info() + ' )', nTime
);
10064 // ------------------------------------------------------------------------------------------ //
10065 function SourceEventElement( sId
, aElement
, aEventMultiplexer
)
10068 this.aElement
= aElement
;
10069 this.aEventMultiplexer
= aEventMultiplexer
;
10071 this.aEventMultiplexer
.registerMouseClickHandler( this, 1000 );
10073 this.bClickHandled
= false;
10074 this.bIsPointerOver
= false;
10075 this.aElement
.addEventListener( 'mouseover', bind2( SourceEventElement
.prototype.onMouseEnter
, this), false );
10076 this.aElement
.addEventListener( 'mouseout', bind2( SourceEventElement
.prototype.onMouseLeave
, this), false );
10079 SourceEventElement
.prototype.getId = function()
10084 SourceEventElement
.prototype.onMouseEnter = function()
10086 this.bIsPointerOver
= true;
10087 this.setPointerCursor();
10090 SourceEventElement
.prototype.onMouseLeave = function()
10092 this.bIsPointerOver
= false;
10093 this.setDefaultCursor();
10096 SourceEventElement
.prototype.charge = function()
10098 this.bClickHandled
= false;
10099 this.setPointerCursor();
10102 SourceEventElement
.prototype.handleClick = function( aMouseEvent
)
10104 if( !this.bIsPointerOver
) return false;
10106 if( this.bClickHandled
)
10109 this.aEventMultiplexer
.notifyEvent( EVENT_TRIGGER_ON_CLICK
, this.getId() );
10110 aSlideShow
.update();
10111 this.bClickHandled
= true;
10112 this.setDefaultCursor();
10116 SourceEventElement
.prototype.setPointerCursor = function()
10118 if( this.bClickHandled
)
10121 this.aElement
.setAttribute( 'style', 'cursor: pointer' );
10124 SourceEventElement
.prototype.setDefaultCursor = function()
10126 this.aElement
.setAttribute( 'style', 'cursor: default' );
10129 // ------------------------------------------------------------------------------------------ //
10131 function HyperlinkElement( sId
, aEventMultiplexer
)
10133 var aElement
= document
.getElementById( sId
);
10136 log( 'error: HyperlinkElement: no element with id: <' + sId
+ '> found' );
10139 if( !aEventMultiplexer
)
10141 log( 'AnimatedElement constructor: event multiplexer is not valid' );
10145 this.aElement
= aElement
;
10146 this.aEventMultiplexer
= aEventMultiplexer
;
10147 this.nTargetSlideIndex
= undefined;
10149 this.sURL
= getNSAttribute( 'xlink', this.aElement
, 'href' );
10152 if( this.sURL
[0] === '#' )
10154 if( this.sURL
.substr(1, 5) === 'Slide' )
10156 var sSlideIndex
= this.sURL
.split( ' ' )[1];
10157 this.nTargetSlideIndex
= parseInt( sSlideIndex
) - 1;
10161 this.aEventMultiplexer
.registerElementChangedHandler( this.sId
, bind2( HyperlinkElement
.prototype.onElementChanged
, this) );
10162 this.aEventMultiplexer
.registerMouseClickHandler( this, 1100 );
10164 this.bIsPointerOver
= false;
10165 this.mouseEnterHandler
= bind2( HyperlinkElement
.prototype.onMouseEnter
, this);
10166 this.mouseLeaveHandler
= bind2( HyperlinkElement
.prototype.onMouseLeave
, this);
10167 this.aElement
.addEventListener( 'mouseover', this.mouseEnterHandler
, false );
10168 this.aElement
.addEventListener( 'mouseout', this.mouseLeaveHandler
, false );
10172 log( 'warning: HyperlinkElement(' + this.sId
+ '): url is empty' );
10176 HyperlinkElement
.prototype.onElementChanged = function( aElement
)
10178 //var aElement = document.getElementById( this.sId );
10181 log( 'error: HyperlinkElement: passed element is not valid' );
10187 this.aElement
.removeEventListener( 'mouseover', this.mouseEnterHandler
, false );
10188 this.aElement
.removeEventListener( 'mouseout', this.mouseLeaveHandler
, false );
10189 this.aElement
= aElement
;
10190 this.aElement
.addEventListener( 'mouseover', this.mouseEnterHandler
, false );
10191 this.aElement
.addEventListener( 'mouseout', this.mouseLeaveHandler
, false );
10195 HyperlinkElement
.prototype.onMouseEnter = function()
10197 this.bIsPointerOver
= true;
10198 this.setPointerCursor();
10201 HyperlinkElement
.prototype.onMouseLeave = function()
10203 this.bIsPointerOver
= false;
10204 this.setDefaultCursor();
10207 HyperlinkElement
.prototype.handleClick = function( aMouseEvent
)
10209 if( !this.bIsPointerOver
) return false;
10211 //log( 'hyperlink: ' + this.sURL );
10213 if( this.nTargetSlideIndex
!== undefined )
10215 aSlideShow
.displaySlide( this.nTargetSlideIndex
, true );
10219 var aWindowObject
= document
.defaultView
;
10220 if( aWindowObject
)
10222 aWindowObject
.open( this.sURL
, this.sId
);
10226 log( 'error: HyperlinkElement.handleClick: invalid window object.' );
10234 HyperlinkElement
.prototype.setPointerCursor = function()
10236 if( this.bClickHandled
)
10239 this.aElement
.setAttribute( 'style', 'cursor: pointer' );
10242 HyperlinkElement
.prototype.setDefaultCursor = function()
10244 this.aElement
.setAttribute( 'style', 'cursor: default' );
10248 // ------------------------------------------------------------------------------------------ //
10249 function InteractiveAnimationSequence( nId
)
10252 this.bIsRunning
= false;
10253 this.aStartEvent
= null;
10254 this.aEndEvent
= null;
10257 InteractiveAnimationSequence
.prototype.getId = function()
10262 InteractiveAnimationSequence
.prototype.getStartEvent = function()
10264 if( !this.aStartEvent
)
10267 makeEvent( bind2( InteractiveAnimationSequence
.prototype.start
, this ) );
10269 return this.aStartEvent
;
10272 InteractiveAnimationSequence
.prototype.getEndEvent = function()
10274 if( !this.aEndEvent
)
10277 makeEvent( bind2( InteractiveAnimationSequence
.prototype.end
, this ) );
10279 return this.aEndEvent
;
10282 InteractiveAnimationSequence
.prototype.chargeEvents = function()
10284 if( this.aStartEvent
) this.aStartEvent
.charge();
10285 if( this.aEndEvent
) this.aEndEvent
.charge();
10288 InteractiveAnimationSequence
.prototype.isRunning = function()
10290 return this.bIsRunning
;
10293 InteractiveAnimationSequence
.prototype.start = function()
10295 aSlideShow
.notifyInteractiveAnimationSequenceStart( this.getId() );
10296 this.bIsRunning
= true;
10299 InteractiveAnimationSequence
.prototype.end = function()
10301 aSlideShow
.notifyInteractiveAnimationSequenceEnd( this.getId() );
10302 this.bIsRunning
= false;
10305 // ------------------------------------------------------------------------------------------ //
10306 /** class PriorityEntry
10307 * It provides an entry type for priority queues.
10308 * Higher is the value of nPriority higher is the priority of the created entry.
10311 * The object to be prioritized.
10313 * An integral number representing the object priority.
10316 function PriorityEntry( aValue
, nPriority
)
10318 this.aValue
= aValue
;
10319 this.nPriority
= nPriority
;
10322 /** EventEntry.compare
10323 * Compare priority of two entries.
10326 * An instance of type PriorityEntry.
10328 * An instance of type PriorityEntry.
10329 * @return {Boolean}
10330 * True if the first entry has higher priority of the second entry,
10333 PriorityEntry
.compare = function( aLhsEntry
, aRhsEntry
)
10335 return ( aLhsEntry
.nPriority
< aRhsEntry
.nPriority
);
10339 // ------------------------------------------------------------------------------------------ //
10340 function EventMultiplexer( aTimerEventQueue
)
10342 this.nId
= EventMultiplexer
.getUniqueId();
10343 this.aTimerEventQueue
= aTimerEventQueue
;
10344 this.aEventMap
= new Object();
10345 this.aSkipEffectEndHandlerSet
= new Array();
10346 this.aMouseClickHandlerSet
= new PriorityQueue( PriorityEntry
.compare
);
10347 this.aSkipEffectEvent
= null;
10348 this.aRewindCurrentEffectEvent
= null;
10349 this.aRewindLastEffectEvent
= null;
10350 this.aSkipInteractiveEffectEventSet
= new Object();
10351 this.aRewindRunningInteractiveEffectEventSet
= new Object();
10352 this.aRewindEndedInteractiveEffectEventSet
= new Object();
10353 this.aRewindedEffectHandlerSet
= new Object();
10354 this.aElementChangedHandlerSet
= new Object();
10357 EventMultiplexer
.CURR_UNIQUE_ID
= 0;
10359 EventMultiplexer
.getUniqueId = function()
10361 ++EventMultiplexer
.CURR_UNIQUE_ID
;
10362 return EventMultiplexer
.CURR_UNIQUE_ID
;
10365 EventMultiplexer
.prototype.getId = function()
10370 EventMultiplexer
.prototype.hasRegisteredMouseClickHandlers = function()
10372 return !this.aMouseClickHandlerSet
.isEmpty();
10375 EventMultiplexer
.prototype.registerMouseClickHandler = function( aHandler
, nPriority
)
10377 var aHandlerEntry
= new PriorityEntry( aHandler
, nPriority
);
10378 this.aMouseClickHandlerSet
.push( aHandlerEntry
);
10381 EventMultiplexer
.prototype.notifyMouseClick = function( aMouseEvent
)
10383 var aMouseClickHandlerSet
= this.aMouseClickHandlerSet
.clone();
10384 while( !aMouseClickHandlerSet
.isEmpty() )
10386 var aHandlerEntry
= aMouseClickHandlerSet
.top();
10387 aMouseClickHandlerSet
.pop();
10388 if( aHandlerEntry
.aValue
.handleClick( aMouseEvent
) )
10393 EventMultiplexer
.prototype.registerEvent = function( eEventType
, aNotifierId
, aEvent
)
10395 this.DBG( 'registerEvent', eEventType
, aNotifierId
);
10396 if( !this.aEventMap
[ eEventType
] )
10398 this.aEventMap
[ eEventType
] = new Object();
10400 if( !this.aEventMap
[ eEventType
][ aNotifierId
] )
10402 this.aEventMap
[ eEventType
][ aNotifierId
] = new Array();
10404 this.aEventMap
[ eEventType
][ aNotifierId
].push( aEvent
);
10408 EventMultiplexer
.prototype.notifyEvent = function( eEventType
, aNotifierId
)
10410 this.DBG( 'notifyEvent', eEventType
, aNotifierId
);
10411 if( this.aEventMap
[ eEventType
] )
10413 if( this.aEventMap
[ eEventType
][ aNotifierId
] )
10415 var aEventArray
= this.aEventMap
[ eEventType
][ aNotifierId
];
10416 var nSize
= aEventArray
.length
;
10417 for( var i
= 0; i
< nSize
; ++i
)
10419 this.aTimerEventQueue
.addEvent( aEventArray
[i
] );
10425 EventMultiplexer
.prototype.registerNextEffectEndHandler = function( aHandler
)
10427 this.aSkipEffectEndHandlerSet
.push( aHandler
);
10430 EventMultiplexer
.prototype.notifyNextEffectEndEvent = function()
10432 var nSize
= this.aSkipEffectEndHandlerSet
.length
;
10433 for( var i
= 0; i
< nSize
; ++i
)
10435 (this.aSkipEffectEndHandlerSet
[i
])();
10437 this.aSkipEffectEndHandlerSet
= new Array();
10440 EventMultiplexer
.prototype.registerSkipEffectEvent = function( aEvent
)
10442 this.aSkipEffectEvent
= aEvent
;
10445 EventMultiplexer
.prototype.notifySkipEffectEvent = function()
10447 if( this.aSkipEffectEvent
)
10449 this.aTimerEventQueue
.addEvent( this.aSkipEffectEvent
);
10450 this.aSkipEffectEvent
= null;
10454 EventMultiplexer
.prototype.registerRewindCurrentEffectEvent = function( aEvent
)
10456 this.aRewindCurrentEffectEvent
= aEvent
;
10459 EventMultiplexer
.prototype.notifyRewindCurrentEffectEvent = function()
10461 if( this.aRewindCurrentEffectEvent
)
10463 this.aTimerEventQueue
.addEvent( this.aRewindCurrentEffectEvent
);
10464 this.aRewindCurrentEffectEvent
= null;
10468 EventMultiplexer
.prototype.registerRewindLastEffectEvent = function( aEvent
)
10470 this.aRewindLastEffectEvent
= aEvent
;
10473 EventMultiplexer
.prototype.notifyRewindLastEffectEvent = function()
10475 if( this.aRewindLastEffectEvent
)
10477 this.aTimerEventQueue
.addEvent( this.aRewindLastEffectEvent
);
10478 this.aRewindLastEffectEvent
= null;
10482 EventMultiplexer
.prototype.registerSkipInteractiveEffectEvent = function( nNotifierId
, aEvent
)
10484 this.aSkipInteractiveEffectEventSet
[ nNotifierId
] = aEvent
;
10487 EventMultiplexer
.prototype.notifySkipInteractiveEffectEvent = function( nNotifierId
)
10489 if( this.aSkipInteractiveEffectEventSet
[ nNotifierId
] )
10491 this.aTimerEventQueue
.addEvent( this.aSkipInteractiveEffectEventSet
[ nNotifierId
] );
10495 EventMultiplexer
.prototype.registerRewindRunningInteractiveEffectEvent = function( nNotifierId
, aEvent
)
10497 this.aRewindRunningInteractiveEffectEventSet
[ nNotifierId
] = aEvent
;
10500 EventMultiplexer
.prototype.notifyRewindRunningInteractiveEffectEvent = function( nNotifierId
)
10502 if( this.aRewindRunningInteractiveEffectEventSet
[ nNotifierId
] )
10504 this.aTimerEventQueue
.addEvent( this.aRewindRunningInteractiveEffectEventSet
[ nNotifierId
] );
10508 EventMultiplexer
.prototype.registerRewindEndedInteractiveEffectEvent = function( nNotifierId
, aEvent
)
10510 this.aRewindEndedInteractiveEffectEventSet
[ nNotifierId
] = aEvent
;
10513 EventMultiplexer
.prototype.notifyRewindEndedInteractiveEffectEvent = function( nNotifierId
)
10515 if( this.aRewindEndedInteractiveEffectEventSet
[ nNotifierId
] )
10517 this.aTimerEventQueue
.addEvent( this.aRewindEndedInteractiveEffectEventSet
[ nNotifierId
] );
10521 EventMultiplexer
.prototype.registerRewindedEffectHandler = function( aNotifierId
, aHandler
)
10523 this.aRewindedEffectHandlerSet
[ aNotifierId
] = aHandler
;
10526 EventMultiplexer
.prototype.notifyRewindedEffectEvent = function( aNotifierId
)
10528 if( this.aRewindedEffectHandlerSet
[ aNotifierId
] )
10530 (this.aRewindedEffectHandlerSet
[ aNotifierId
])();
10534 EventMultiplexer
.prototype.registerElementChangedHandler = function( aNotifierId
, aHandler
)
10536 this.aElementChangedHandlerSet
[ aNotifierId
] = aHandler
;
10539 EventMultiplexer
.prototype.notifyElementChangedEvent = function( aNotifierId
, aElement
)
10541 if( this.aElementChangedHandlerSet
[ aNotifierId
] )
10543 (this.aElementChangedHandlerSet
[ aNotifierId
])( aElement
);
10547 EventMultiplexer
.DEBUG
= aEventMultiplexerDebugPrinter
.isEnabled();
10549 EventMultiplexer
.prototype.DBG = function( sMethodName
, eEventType
, aNotifierId
, nTime
)
10551 if( EventMultiplexer
.DEBUG
)
10553 var sInfo
= 'EventMultiplexer.' + sMethodName
;
10554 sInfo
+= '( type: ' + aEventTriggerOutMap
[ eEventType
];
10555 sInfo
+= ', notifier: ' + aNotifierId
+ ' )';
10556 aEventMultiplexerDebugPrinter
.print( sInfo
, nTime
);
10562 /**********************************************************************************************
10563 * Interpolator Handler and KeyStopLerp
10564 **********************************************************************************************/
10566 var aInterpolatorHandler
= new Object();
10568 aInterpolatorHandler
.getInterpolator = function( eCalcMode
, eValueType
, eValueSubtype
)
10570 var bHasSubtype
= ( typeof( eValueSubtype
) === typeof( 0 ) );
10572 if( !bHasSubtype
&& aInterpolatorHandler
.aLerpFunctorMap
[ eCalcMode
][ eValueType
] )
10574 return aInterpolatorHandler
.aLerpFunctorMap
[ eCalcMode
][ eValueType
];
10576 else if( bHasSubtype
&& aInterpolatorHandler
.aLerpFunctorMap
[ eCalcMode
][ eValueType
][ eValueSubtype
] )
10578 return aInterpolatorHandler
.aLerpFunctorMap
[ eCalcMode
][ eValueType
][ eValueSubtype
];
10582 log( 'aInterpolatorHandler.getInterpolator: not found any valid interpolator for calc mode '
10583 + aCalcModeOutMap
[eCalcMode
] + 'and value type ' + aValueTypeOutMap
[eValueType
] );
10588 aInterpolatorHandler
.aLerpFunctorMap
= new Array();
10589 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_DISCRETE
] = new Array();
10590 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_LINEAR
] = new Array();
10593 // interpolators for linear calculation
10595 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_LINEAR
][ NUMBER_PROPERTY
] =
10596 function ( nFrom
, nTo
, nT
)
10598 return ( ( 1.0 - nT
)* nFrom
+ nT
* nTo
);
10601 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_LINEAR
][ COLOR_PROPERTY
] = new Array();
10603 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_LINEAR
][ COLOR_PROPERTY
][ COLOR_SPACE_RGB
] =
10604 function ( nFrom
, nTo
, nT
)
10606 return RGBColor
.interpolate( nFrom
, nTo
, nT
);
10609 // For HSLColor we do not return the interpolator but a function
10610 // that generate the interpolator. The AnimationColorNode is 'aware' of that.
10611 aInterpolatorHandler
.aLerpFunctorMap
[ CALC_MODE_LINEAR
][ COLOR_PROPERTY
][ COLOR_SPACE_HSL
] =
10614 return function ( nFrom
, nTo
, nT
)
10616 return HSLColor
.interpolate( nFrom
, nTo
, nT
, bCCW
);
10622 // ------------------------------------------------------------------------------------------ //
10623 function KeyStopLerp( aValueList
)
10625 KeyStopLerp
.validateInput( aValueList
);
10627 this.aKeyStopList
= new Array();
10628 this.nLastIndex
= 0;
10629 this.nKeyStopDistance
= aValueList
[1] - aValueList
[0];
10630 if( this.nKeyStopDistance
<= 0 )
10631 this.nKeyStopDistance
= 0.001;
10633 for( var i
= 0; i
< aValueList
.length
; ++i
)
10634 this.aKeyStopList
.push( aValueList
[i
] );
10636 this.nUpperBoundIndex
= this.aKeyStopList
.length
- 2;
10640 KeyStopLerp
.validateInput = function( aValueList
)
10642 var nSize
= aValueList
.length
;
10643 assert( nSize
> 1, 'KeyStopLerp.validateInput: key stop vector must have two entries or more' );
10645 for( var i
= 1; i
< nSize
; ++i
)
10647 if( aValueList
[i
-1] > aValueList
[i
] )
10648 log( 'KeyStopLerp.validateInput: time vector is not sorted in ascending order!' );
10652 KeyStopLerp
.prototype.reset = function()
10654 KeyStopLerp
.validateInput( this.aKeyStopList
);
10655 this.nLastIndex
= 0;
10656 this.nKeyStopDistance
= this.aKeyStopList
[1] - this.aKeyStopList
[0];
10657 if( this.nKeyStopDistance
<= 0 )
10658 this.nKeyStopDistance
= 0.001;
10662 KeyStopLerp
.prototype.lerp = function( nAlpha
)
10664 if( nAlpha
> this.aKeyStopList
[ this.nLastIndex
+ 1 ] )
10668 var nIndex
= this.nLastIndex
+ 1;
10669 this.nLastIndex
= clamp( nIndex
, 0, this.nUpperBoundIndex
);
10670 this.nKeyStopDistance
= this.aKeyStopList
[ this.nLastIndex
+ 1 ] - this.aKeyStopList
[ this.nLastIndex
];
10672 while( ( this.nKeyStopDistance
<= 0 ) && ( this.nLastIndex
< this.nUpperBoundIndex
) );
10675 var nRawLerp
= ( nAlpha
- this.aKeyStopList
[ this.nLastIndex
] ) / this.nKeyStopDistance
;
10677 nRawLerp
= clamp( nRawLerp
, 0.0, 1.0 );
10679 var aResult
= new Object();
10680 aResult
.nIndex
= this.nLastIndex
;
10681 aResult
.nLerp
= nRawLerp
;
10686 KeyStopLerp
.prototype.lerp_ported = function( nAlpha
)
10688 if( ( this.aKeyStopList
[ this.nLastIndex
] < nAlpha
) ||
10689 ( this.aKeyStopList
[ this.nLastIndex
+ 1 ] >= nAlpha
) )
10692 for( ; i
< this.aKeyStopList
.length
; ++i
)
10694 if( this.aKeyStopList
[i
] >= nAlpha
)
10697 if( this.aKeyStopList
[i
] > nAlpha
)
10699 var nIndex
= i
- 1;
10700 this.nLastIndex
= clamp( nIndex
, 0, this.aKeyStopList
.length
- 2 );
10703 var nRawLerp
= ( nAlpha
- this.aKeyStopList
[ this.nLastIndex
] ) /
10704 ( this.aKeyStopList
[ this.nLastIndex
+1 ] - this.aKeyStopList
[ this.nLastIndex
] );
10706 nRawLerp
= clamp( nRawLerp
, 0.0, 1.0 );
10708 var aResult
= new Object();
10709 aResult
.nIndex
= this.nLastIndex
;
10710 aResult
.nLerp
= nRawLerp
;
10717 /**********************************************************************************************
10719 **********************************************************************************************/
10721 var aOperatorSetMap
= new Array();
10723 // number operators
10724 aOperatorSetMap
[ NUMBER_PROPERTY
] = new Object();
10726 aOperatorSetMap
[ NUMBER_PROPERTY
].equal = function( a
, b
)
10728 return ( a
=== b
);
10731 aOperatorSetMap
[ NUMBER_PROPERTY
].add = function( a
, b
)
10736 aOperatorSetMap
[ NUMBER_PROPERTY
].scale = function( k
, v
)
10742 aOperatorSetMap
[ COLOR_PROPERTY
] = new Object();
10744 aOperatorSetMap
[ COLOR_PROPERTY
].equal = function( a
, b
)
10746 return a
.equal( b
);
10749 aOperatorSetMap
[ COLOR_PROPERTY
].add = function( a
, b
)
10756 aOperatorSetMap
[ COLOR_PROPERTY
].scale = function( k
, v
)
10765 /**********************************************************************************************
10766 * Activity Class Hierarchy
10767 **********************************************************************************************/
10769 // ------------------------------------------------------------------------------------------ //
10770 function ActivityParamSet()
10772 this.aEndEvent
= null;
10773 this.aTimerEventQueue
= null;
10774 this.aActivityQueue
= null;
10775 this.nMinDuration
= undefined;
10776 this.nMinNumberOfFrames
= MINIMUM_FRAMES_PER_SECONDS
;
10777 this.bAutoReverse
= false;
10778 this.nRepeatCount
= 1.0;
10779 this.nAccelerationFraction
= 0.0;
10780 this.nDecelerationFraction
= 0.0;
10781 this.nSlideWidth
= undefined;
10782 this.nSlideHeight
= undefined;
10783 this.aDiscreteTimes
= new Array();
10786 // ------------------------------------------------------------------------------------------ //
10787 function AnimationActivity()
10789 this.nId
= AnimationActivity
.getUniqueId();
10793 AnimationActivity
.CURR_UNIQUE_ID
= 0;
10795 AnimationActivity
.getUniqueId = function()
10797 ++AnimationActivity
.CURR_UNIQUE_ID
;
10798 return AnimationActivity
.CURR_UNIQUE_ID
;
10801 AnimationActivity
.prototype.getId = function()
10808 // ------------------------------------------------------------------------------------------ //
10809 function SetActivity( aCommonParamSet
, aAnimation
, aToAttr
)
10811 SetActivity
.superclass
.constructor.call( this );
10813 this.aAnimation
= aAnimation
;
10814 this.aTargetElement
= null;
10815 this.aEndEvent
= aCommonParamSet
.aEndEvent
;
10816 this.aTimerEventQueue
= aCommonParamSet
.aTimerEventQueue
;
10817 this.aToAttr
= aToAttr
;
10818 this.bIsActive
= true;
10820 extend( SetActivity
, AnimationActivity
);
10823 SetActivity
.prototype.activate = function( aEndEvent
)
10825 this.aEndEvent
= aEndEvent
;
10826 this.bIsActive
= true;
10829 SetActivity
.prototype.dispose = function()
10831 this.bIsActive
= false;
10832 if( this.aEndEvent
&& this.aEndEvent
.isCharged() )
10833 this.aEndEvent
.dispose();
10836 SetActivity
.prototype.calcTimeLag = function()
10841 SetActivity
.prototype.perform = function()
10843 if( !this.isActive() )
10846 // we're going inactive immediately:
10847 this.bIsActive
= false;
10849 if( this.aAnimation
&& this.aTargetElement
)
10851 this.aAnimation
.start( this.aTargetElement
);
10852 this.aAnimation
.perform( this.aToAttr
);
10853 this.aAnimation
.end();
10856 if( this.aEndEvent
)
10857 this.aTimerEventQueue
.addEvent( this.aEndEvent
);
10861 SetActivity
.prototype.isActive = function()
10863 return this.bIsActive
;
10866 SetActivity
.prototype.dequeued = function()
10871 SetActivity
.prototype.end = function()
10876 SetActivity
.prototype.setTargets = function( aTargetElement
)
10878 assert( aTargetElement
, 'SetActivity.setTargets: target element is not valid' );
10879 this.aTargetElement
= aTargetElement
;
10884 // ------------------------------------------------------------------------------------------ //
10885 function ActivityBase( aCommonParamSet
)
10887 ActivityBase
.superclass
.constructor.call( this );
10889 this.aTargetElement
= null;
10890 this.aEndEvent
= aCommonParamSet
.aEndEvent
;
10891 this.aTimerEventQueue
= aCommonParamSet
.aTimerEventQueue
;
10892 this.nRepeats
= aCommonParamSet
.nRepeatCount
;
10893 this.nAccelerationFraction
= aCommonParamSet
.nAccelerationFraction
;
10894 this.nDecelerationFraction
= aCommonParamSet
.nDecelerationFraction
;
10895 this.bAutoReverse
= aCommonParamSet
.bAutoReverse
;
10897 this.bFirstPerformCall
= true;
10898 this.bIsActive
= true;
10901 extend( ActivityBase
, AnimationActivity
);
10904 ActivityBase
.prototype.activate = function( aEndEvent
)
10906 this.aEndEvent
= aEndEvent
;
10907 this.bFirstPerformCall
= true;
10908 this.bIsActive
= true;
10911 ActivityBase
.prototype.dispose = function()
10914 this.bIsActive
= false;
10917 if( this.aEndEvent
)
10918 this.aEndEvent
.dispose();
10920 this.aEndEvent
= null;
10923 ActivityBase
.prototype.perform = function()
10926 if( !this.isActive() )
10927 return false; // no, early exit.
10929 assert( !this.bFirstPerformCall
, 'ActivityBase.perform: assertion (!this.FirstPerformCall) failed' );
10934 ActivityBase
.prototype.calcTimeLag = function()
10936 // TODO(Q1): implement different init process!
10937 if( this.isActive() && this.bFirstPerformCall
)
10939 this.bFirstPerformCall
= false;
10941 // notify derived classes that we're
10943 this.startAnimation();
10948 ActivityBase
.prototype.isActive = function()
10950 return this.bIsActive
;
10953 ActivityBase
.prototype.isDisposed = function()
10955 return ( !this.bIsActive
&& !this.aEndEvent
);
10958 ActivityBase
.prototype.dequeued = function()
10960 if( !this.isActive() )
10961 this.endAnimation();
10964 ActivityBase
.prototype.setTargets = function( aTargetElement
)
10966 assert( aTargetElement
, 'ActivityBase.setTargets: target element is not valid' );
10968 this.aTargetElement
= aTargetElement
;
10971 ActivityBase
.prototype.startAnimation = function()
10973 throw ( 'ActivityBase.startAnimation: abstract method invoked' );
10976 ActivityBase
.prototype.endAnimation = function()
10978 throw ( 'ActivityBase.endAnimation: abstract method invoked' );
10981 ActivityBase
.prototype.endActivity = function()
10983 // this is a regular activity end
10984 this.bIsActive
= false;
10986 // Activity is ending, queue event, then
10987 if( this.aEndEvent
)
10988 this.aTimerEventQueue
.addEvent( this.aEndEvent
);
10990 this.aEndEvent
= null;
10994 ActivityBase
.prototype.calcAcceleratedTime = function( nT
)
10996 // Handle acceleration/deceleration
10999 // clamp nT to permissible [0,1] range
11000 nT
= clamp( nT
, 0.0, 1.0 );
11002 // take acceleration/deceleration into account. if the sum
11003 // of nAccelerationFraction and nDecelerationFraction
11004 // exceeds 1.0, ignore both (that's according to SMIL spec)
11005 if( ( this.nAccelerationFraction
> 0.0 || this.nDecelerationFraction
> 0.0 ) &&
11006 ( this.nAccelerationFraction
+ this.nDecelerationFraction
<= 1.0 ) )
11008 var nC
= 1.0 - 0.5*this.nAccelerationFraction
- 0.5*this.nDecelerationFraction
;
11010 // this variable accumulates the new time value
11013 if( nT
< this.nAccelerationFraction
)
11015 nTPrime
+= 0.5 * nT
* nT
/ this.nAccelerationFraction
; // partial first interval
11019 nTPrime
+= 0.5 * this.nAccelerationFraction
; // full first interval
11021 if( nT
<= ( 1.0 - this.nDecelerationFraction
) )
11023 nTPrime
+= nT
- this.nAccelerationFraction
; // partial second interval
11027 nTPrime
+= 1.0 - this.nAccelerationFraction
- this.nDecelerationFraction
; // full second interval
11029 var nTRelative
= nT
- 1.0 + this.nDecelerationFraction
;
11031 nTPrime
+= nTRelative
- 0.5*nTRelative
*nTRelative
/ this.nDecelerationFraction
;
11035 // normalize, and assign to work variable
11042 ActivityBase
.prototype.getEventQueue = function()
11044 return this.aTimerEventQueue
;
11047 ActivityBase
.prototype.getTargetElement = function()
11049 return this.aTargetElement
;
11052 ActivityBase
.prototype.isRepeatCountValid = function()
11054 if( this.nRepeats
)
11060 ActivityBase
.prototype.getRepeatCount = function()
11062 return this.nRepeats
;
11065 ActivityBase
.prototype.isAutoReverse = function()
11067 return this.bAutoReverse
;
11070 ActivityBase
.prototype.end = function()
11072 if( !this.isActive() || this.isDisposed() )
11075 // assure animation is started:
11076 if( this.bFirstPerformCall
)
11078 this.bFirstPerformCall
= false;
11079 // notify derived classes that we're starting now
11080 this.startAnimation();
11084 this.endAnimation();
11085 this.endActivity();
11088 ActivityBase
.prototype.performEnd = function()
11090 throw ( 'ActivityBase.performEnd: abstract method invoked' );
11095 // ------------------------------------------------------------------------------------------ //
11096 function SimpleContinuousActivityBase( aCommonParamSet
)
11098 SimpleContinuousActivityBase
.superclass
.constructor.call( this, aCommonParamSet
);
11100 // Time elapsed since activity started
11101 this.aTimer
= new ElapsedTime( aCommonParamSet
.aActivityQueue
.getTimer() );
11102 // Simple duration of activity
11103 this.nMinSimpleDuration
= aCommonParamSet
.nMinDuration
;
11104 // Minimal number of frames to show
11105 this.nMinNumberOfFrames
= aCommonParamSet
.nMinNumberOfFrames
;
11106 // Actual number of frames shown until now.
11107 this.nCurrPerformCalls
= 0;
11110 extend( SimpleContinuousActivityBase
, ActivityBase
);
11113 SimpleContinuousActivityBase
.prototype.startAnimation = function()
11115 // init timer. We measure animation time only when we're
11116 // actually started.
11117 this.aTimer
.reset();
11120 SimpleContinuousActivityBase
.prototype.calcTimeLag = function()
11122 SimpleContinuousActivityBase
.superclass
.calcTimeLag
.call( this );
11124 if( !this.isActive() )
11127 // retrieve locally elapsed time
11128 var nCurrElapsedTime
= this.aTimer
.getElapsedTime();
11130 // go to great length to ensure a proper animation
11131 // run. Since we don't know how often we will be called
11132 // here, try to spread the animator calls uniquely over
11133 // the [0,1] parameter range. Be aware of the fact that
11134 // perform will be called at least mnMinNumberOfTurns
11137 // fraction of time elapsed
11138 var nFractionElapsedTime
= nCurrElapsedTime
/ this.nMinSimpleDuration
;
11140 // fraction of minimum calls performed
11141 var nFractionRequiredCalls
= this.nCurrPerformCalls
/ this.nMinNumberOfFrames
;
11143 // okay, so now, the decision is easy:
11145 // If the fraction of time elapsed is smaller than the
11146 // number of calls required to be performed, then we calc
11147 // the position on the animation range according to
11148 // elapsed time. That is, we're so to say ahead of time.
11150 // In contrary, if the fraction of time elapsed is larger,
11151 // then we're lagging, and we thus calc the position on
11152 // the animation time line according to the fraction of
11153 // calls performed. Thus, the animation is forced to slow
11154 // down, and take the required minimal number of steps,
11155 // sufficiently equally distributed across the animation
11158 if( nFractionElapsedTime
< nFractionRequiredCalls
)
11164 // lag global time, so all other animations lag, too:
11165 return ( ( nFractionElapsedTime
- nFractionRequiredCalls
) * this.nMinSimpleDuration
);
11169 SimpleContinuousActivityBase
.prototype.perform = function()
11171 // call base class, for start() calls and end handling
11172 if( !SimpleContinuousActivityBase
.superclass
.perform
.call( this ) )
11173 return false; // done, we're ended
11175 // get relative animation position
11176 var nCurrElapsedTime
= this.aTimer
.getElapsedTime();
11177 var nT
= nCurrElapsedTime
/ this.nMinSimpleDuration
;
11180 // one of the stop criteria reached?
11182 // will be set to true below, if one of the termination criteria matched.
11183 var bActivityEnding
= false;
11185 if( this.isRepeatCountValid() )
11187 // Finite duration case
11189 // When we've autoreverse on, the repeat count doubles
11190 var nRepeatCount
= this.getRepeatCount();
11191 var nEffectiveRepeat
= this.isAutoReverse() ? 2.0 * nRepeatCount
: nRepeatCount
;
11193 // time (or frame count) elapsed?
11194 if( nEffectiveRepeat
<= nT
)
11196 // Ok done for now. Will not exit right here,
11197 // to give animation the chance to render the last
11199 bActivityEnding
= true;
11201 // clamp animation to max permissible value
11202 nT
= nEffectiveRepeat
;
11207 // need to do auto-reverse?
11210 var nRelativeSimpleTime
;
11211 // TODO(Q3): Refactor this mess
11212 if( this.isAutoReverse() )
11214 // divert active duration into repeat and
11215 // fractional part.
11216 nRepeats
= Math
.floor( nT
);
11217 var nFractionalActiveDuration
= nT
- nRepeats
;
11219 // for auto-reverse, map ranges [1,2), [3,4), ...
11220 // to ranges [0,1), [1,2), etc.
11223 // we're in an odd range, reverse sweep
11224 nRelativeSimpleTime
= 1.0 - nFractionalActiveDuration
;
11228 // we're in an even range, pass on as is
11229 nRelativeSimpleTime
= nFractionalActiveDuration
;
11232 // effective repeat count for autoreverse is half of
11233 // the input time's value (each run of an autoreverse
11234 // cycle is half of a repeat)
11239 // determine repeat
11241 // calc simple time and number of repeats from nT
11242 // Now, that's easy, since the fractional part of
11243 // nT gives the relative simple time, and the
11244 // integer part the number of full repeats:
11245 nRepeats
= Math
.floor( nT
);
11246 nRelativeSimpleTime
= nT
- nRepeats
;
11248 // clamp repeats to max permissible value (maRepeats.getValue() - 1.0)
11249 if( this.isRepeatCountValid() && ( nRepeats
>= this.getRepeatCount() ) )
11251 // Note that this code here only gets
11252 // triggered if this.nRepeats is an
11253 // _integer_. Otherwise, nRepeats will never
11254 // reach nor exceed
11255 // maRepeats.getValue(). Thus, the code below
11256 // does not need to handle cases of fractional
11257 // repeats, and can always assume that a full
11258 // animation run has ended (with
11259 // nRelativeSimpleTime = 1.0 for
11260 // non-autoreversed activities).
11262 // with modf, nRelativeSimpleTime will never
11263 // become 1.0, since nRepeats is incremented and
11264 // nRelativeSimpleTime set to 0.0 then.
11266 // For the animation to reach its final value,
11267 // nRepeats must although become this.nRepeats - 1.0,
11268 // and nRelativeSimpleTime = 1.0.
11269 nRelativeSimpleTime
= 1.0;
11275 // actually perform something
11277 this.simplePerform( nRelativeSimpleTime
, nRepeats
);
11279 // delayed endActivity() call from end condition check
11280 // below. Issued after the simplePerform() call above, to
11281 // give animations the chance to correctly reach the
11282 // animation end value, without spurious bail-outs because
11283 // of isActive() returning false.
11284 if( bActivityEnding
)
11285 this.endActivity();
11287 // one more frame successfully performed
11288 ++this.nCurrPerformCalls
;
11290 return this.isActive();
11293 SimpleContinuousActivityBase
.prototype.simplePerform = function( nSimpleTime
, nRepeatCount
)
11295 throw ( 'SimpleContinuousActivityBase.simplePerform: abstract method invoked' );
11300 // ------------------------------------------------------------------------------------------ //
11301 function ContinuousKeyTimeActivityBase( aCommonParamSet
)
11303 var nSize
= aCommonParamSet
.aDiscreteTimes
.length
;
11305 'ContinuousKeyTimeActivityBase constructor: assertion (aDiscreteTimes.length > 1) failed' );
11307 assert( aCommonParamSet
.aDiscreteTimes
[0] == 0.0,
11308 'ContinuousKeyTimeActivityBase constructor: assertion (aDiscreteTimes.front() == 0.0) failed' );
11310 assert( aCommonParamSet
.aDiscreteTimes
[ nSize
- 1 ] <= 1.0,
11311 'ContinuousKeyTimeActivityBase constructor: assertion (aDiscreteTimes.back() <= 1.0) failed' );
11313 ContinuousKeyTimeActivityBase
.superclass
.constructor.call( this, aCommonParamSet
);
11315 this.aLerper
= new KeyStopLerp( aCommonParamSet
.aDiscreteTimes
);
11317 extend( ContinuousKeyTimeActivityBase
, SimpleContinuousActivityBase
);
11320 ContinuousKeyTimeActivityBase
.prototype.activate = function( aEndElement
)
11322 ContinuousKeyTimeActivityBase
.superclass
.activate
.call( this, aEndElement
);
11324 this.aLerper
.reset();
11327 ContinuousKeyTimeActivityBase
.prototype.performHook = function( nIndex
, nFractionalIndex
, nRepeatCount
)
11329 throw ( 'ContinuousKeyTimeActivityBase.performHook: abstract method invoked' );
11332 ContinuousKeyTimeActivityBase
.prototype.simplePerform = function( nSimpleTime
, nRepeatCount
)
11334 var nAlpha
= this.calcAcceleratedTime( nSimpleTime
);
11336 var aLerpResult
= this.aLerper
.lerp( nAlpha
);
11338 this.performHook( aLerpResult
.nIndex
, aLerpResult
.nLerp
, nRepeatCount
);
11343 // ------------------------------------------------------------------------------------------ //
11344 function ContinuousActivityBase( aCommonParamSet
)
11346 ContinuousActivityBase
.superclass
.constructor.call( this, aCommonParamSet
);
11349 extend( ContinuousActivityBase
, SimpleContinuousActivityBase
);
11352 ContinuousActivityBase
.prototype.performHook = function( nModifiedTime
, nRepeatCount
)
11354 throw ( 'ContinuousActivityBase.performHook: abstract method invoked' );
11357 ContinuousActivityBase
.prototype.simplePerform = function( nSimpleTime
, nRepeatCount
)
11359 this.performHook( this.calcAcceleratedTime( nSimpleTime
), nRepeatCount
);
11364 // ------------------------------------------------------------------------------------------ //
11365 function SimpleActivity( aCommonParamSet
, aNumberAnimation
, eDirection
)
11367 assert( ( eDirection
== BACKWARD
) || ( eDirection
== FORWARD
),
11368 'SimpleActivity constructor: animation direction is not valid' );
11370 assert( aNumberAnimation
, 'SimpleActivity constructor: animation object is not valid' );
11372 SimpleActivity
.superclass
.constructor.call( this, aCommonParamSet
);
11374 this.aAnimation
= aNumberAnimation
;
11375 this.nDirection
= ( eDirection
== FORWARD
) ? 1.0 : 0.0;
11377 extend( SimpleActivity
, ContinuousActivityBase
);
11380 SimpleActivity
.prototype.startAnimation = function()
11382 if( this.isDisposed() || !this.aAnimation
)
11385 ANIMDBG
.print( 'SimpleActivity.startAnimation invoked' );
11386 SimpleActivity
.superclass
.startAnimation
.call( this );
11389 this.aAnimation
.start( this.getTargetElement() );
11392 SimpleActivity
.prototype.endAnimation = function()
11394 if( this.aAnimation
)
11395 this.aAnimation
.end();
11399 SimpleActivity
.prototype.performHook = function( nModifiedTime
, nRepeatCount
)
11401 // nRepeatCount is not used
11403 if( this.isDisposed() || !this.aAnimation
)
11406 var nT
= 1.0 - this.nDirection
+ nModifiedTime
* ( 2.0*this.nDirection
- 1.0 );
11407 //ANIMDBG.print( 'SimpleActivity.performHook: nT = ' + nT );
11408 this.aAnimation
.perform( nT
);
11411 SimpleActivity
.prototype.performEnd = function()
11413 if( this.aAnimation
)
11414 this.aAnimation
.perform( this.nDirection
);
11419 // ------------------------------------------------------------------------------------------ //
11420 // FromToByActivity< BaseType > template class
11423 function FromToByActivityTemplate( BaseType
) // template parameter
11426 function FromToByActivity( aFromValue
, aToValue
, aByValue
,
11427 aActivityParamSet
, aAnimation
,
11428 aInterpolator
, aOperatorSet
, bAccumulate
)
11430 assert( aAnimation
, 'FromToByActivity constructor: invalid animation object' );
11431 assert( ( aToValue
!= undefined ) || ( aByValue
!= undefined ),
11432 'FromToByActivity constructor: one of aToValue or aByValue must be valid' );
11434 FromToByActivity
.superclass
.constructor.call( this, aActivityParamSet
);
11436 this.aFrom
= aFromValue
;
11437 this.aTo
= aToValue
;
11438 this.aBy
= aByValue
;
11439 this.aStartValue
= null;
11440 this.aEndValue
= null;
11441 this.aPreviousValue
= null;
11442 this.aStartInterpolationValue
= null;
11443 this.aAnimation
= aAnimation
;
11444 this.aInterpolator
= aInterpolator
;
11445 this.equal
= aOperatorSet
.equal
;
11446 this.add
= aOperatorSet
.add
;
11447 this.scale
= aOperatorSet
.scale
;
11448 this.bDynamicStartValue
= false;
11449 this.nIteration
= 0;
11450 this.bCumulative
= bAccumulate
;
11452 //this.initAnimatedElement();
11455 extend( FromToByActivity
, BaseType
);
11457 FromToByActivity
.prototype.initAnimatedElement = function()
11459 if( this.aAnimation
&& this.aFrom
)
11460 this.aAnimation
.perform( this.aFrom
);
11463 FromToByActivity
.prototype.startAnimation = function()
11465 if( this.isDisposed() || !this.aAnimation
)
11467 log( 'FromToByActivity.startAnimation: activity disposed or not valid animation' );
11471 FromToByActivity
.superclass
.startAnimation
.call( this );
11473 this.aAnimation
.start( this.getTargetElement() );
11476 var aAnimationStartValue
= this.aAnimation
.getUnderlyingValue();
11478 // first of all, determine general type of
11479 // animation, by inspecting which of the FromToBy values
11480 // are actually valid.
11481 // See http://www.w3.org/TR/smil20/animation.html#AnimationNS-FromToBy
11482 // for a definition
11485 // From-to or From-by animation. According to
11486 // SMIL spec, the To value takes precedence
11487 // over the By value, if both are specified
11490 // From-To animation
11491 this.aStartValue
= this.aFrom
;
11492 this.aEndValue
= this.aTo
;
11494 else if( this.aBy
)
11496 // From-By animation
11497 this.aStartValue
= this.aFrom
;
11499 // this.aEndValue = this.aStartValue + this.aBy;
11500 this.aEndValue
= this.add( this.aStartValue
, this.aBy
);
11505 this.aStartValue
= aAnimationStartValue
;
11506 this.aStartInterpolationValue
= this.aStartValue
;
11508 // By or To animation. According to SMIL spec,
11509 // the To value takes precedence over the By
11510 // value, if both are specified
11515 // According to the SMIL spec
11516 // (http://www.w3.org/TR/smil20/animation.html#animationNS-ToAnimation),
11517 // the to animation interpolates between
11518 // the _running_ underlying value and the to value (as the end value)
11519 this.bDynamicStartValue
= true;
11520 this.aPreviousValue
= this.aStartValue
;
11521 this.aEndValue
= this.aTo
;
11523 else if( this.aBy
)
11526 this.aStartValue
= aAnimationStartValue
;
11528 // this.aEndValue = this.aStartValue + this.aBy;
11529 this.aEndValue
= this.add( this.aStartValue
, this.aBy
);
11533 ANIMDBG
.print( 'FromToByActivity.startAnimation: aStartValue = ' + this.aStartValue
+ ', aEndValue = ' + this.aEndValue
);
11536 FromToByActivity
.prototype.endAnimation = function()
11538 if( this.aAnimation
)
11539 this.aAnimation
.end();
11542 // performHook override for ContinuousActivityBase
11543 FromToByActivity
.prototype.performHook = function( nModifiedTime
, nRepeatCount
)
11545 if( this.isDisposed() || !this.aAnimation
)
11547 log( 'FromToByActivity.performHook: activity disposed or not valid animation' );
11552 // According to SMIL 3.0 spec 'to' animation if no other (lower priority)
11553 // animations are active or frozen then a simple interpolation is performed.
11554 // That is, the start interpolation value is constant while the animation
11555 // is running, and is equal to the underlying value retrieved when
11556 // the animation start.
11557 // However if another animation is manipulating the underlying value,
11558 // the 'to' animation will initially add to the effect of the lower priority
11559 // animation, and increasingly dominate it as it nears the end of the
11560 // simple duration, eventually overriding it completely.
11561 // That is, each time the underlying value is changed between two
11562 // computations of the animation function the new underlying value is used
11563 // as start value for the interpolation.
11565 // http://www.w3.org/TR/SMIL3/smil-animation.html#animationNS-ToAnimation
11566 // (Figure 6 - Effect of Additive to animation example)
11567 // Moreover when a 'to' animation is repeated, at each new iteration
11568 // the start interpolation value is reset to the underlying value
11569 // of the animated property when the animation started,
11570 // as it is shown in the example provided by the SMIL 3.0 spec.
11571 // This is exactly as Firefox performs SVG 'to' animations.
11572 if( this.bDynamicStartValue
)
11574 if( this.nIteration
!= nRepeatCount
)
11576 this.nIteration
= nRepeatCount
;
11577 this.aStartInterpolationValue
= this.aStartValue
;
11581 var aActualValue
= this.aAnimation
.getUnderlyingValue();
11582 if( !this.equal( aActualValue
, this.aPreviousValue
) )
11583 this.aStartInterpolationValue
= aActualValue
;
11587 var aValue
= this.aInterpolator( this.aStartInterpolationValue
,
11588 this.aEndValue
, nModifiedTime
);
11590 // According to the SMIL spec:
11591 // Because 'to' animation is defined in terms of absolute values of
11592 // the target attribute, cumulative animation is not defined.
11593 if( this.bCumulative
&& !this.bDynamicStartValue
)
11595 // aValue = this.aEndValue * nRepeatCount + aValue;
11596 aValue
= this.add( this.scale( nRepeatCount
, this.aEndValue
), aValue
);
11599 this.aAnimation
.perform( aValue
);
11601 if( this.bDynamicStartValue
)
11603 this.aPreviousValue
= this.aAnimation
.getUnderlyingValue();
11608 FromToByActivity
.prototype.performEnd = function()
11610 if( this.aAnimation
)
11612 if( this.isAutoReverse() )
11613 this.aAnimation
.perform( this.aStartValue
);
11615 this.aAnimation
.perform( this.aEndValue
);
11619 FromToByActivity
.prototype.dispose = function()
11621 FromToByActivity
.superclass
.dispose
.call( this );
11625 return FromToByActivity
;
11629 // FromToByActivity< ContinuousActivityBase > instantiation
11630 var LinearFromToByActivity
= instantiate( FromToByActivityTemplate
, ContinuousActivityBase
);
11634 // ------------------------------------------------------------------------------------------ //
11635 // ValueListActivity< BaseType > template class
11638 function ValueListActivityTemplate( BaseType
) // template parameter
11641 function ValueListActivity( aValueList
, aActivityParamSet
,
11642 aAnimation
, aInterpolator
,
11643 aOperatorSet
, bAccumulate
)
11645 assert( aAnimation
, 'ValueListActivity constructor: invalid animation object' );
11646 assert( aValueList
.length
!= 0, 'ValueListActivity: value list is empty' );
11648 ValueListActivity
.superclass
.constructor.call( this, aActivityParamSet
);
11650 this.aValueList
= aValueList
;
11651 this.aAnimation
= aAnimation
;
11652 this.aInterpolator
= aInterpolator
;
11653 this.add
= aOperatorSet
.add
;
11654 this.scale
= aOperatorSet
.scale
;
11655 this.bCumulative
= bAccumulate
;
11656 this.aLastValue
= this.aValueList
[ this.aValueList
.length
- 1 ];
11658 //this.initAnimatedElement();
11660 extend( ValueListActivity
, BaseType
);
11662 ValueListActivity
.prototype.activate = function( aEndEvent
)
11664 ValueListActivity
.superclass
.activate
.call( this, aEndEvent
);
11665 for( var i
= 0; i
< this.aValueList
.length
; ++i
)
11667 ANIMDBG
.print( 'createValueListActivity: value[' + i
+ '] = ' + this.aValueList
[i
] );
11670 //this.initAnimatedElement();
11673 ValueListActivity
.prototype.initAnimatedElement = function()
11675 if( this.aAnimation
)
11676 this.aAnimation
.perform( this.aValueList
[0] );
11679 ValueListActivity
.prototype.startAnimation = function()
11681 if( this.isDisposed() || !this.aAnimation
)
11683 log( 'ValueListActivity.startAnimation: activity disposed or not valid animation' );
11687 ValueListActivity
.superclass
.startAnimation
.call( this );
11689 this.aAnimation
.start( this.getTargetElement() );
11692 ValueListActivity
.prototype.endAnimation = function()
11694 if( this.aAnimation
)
11695 this.aAnimation
.end();
11698 // performHook override for ContinuousKeyTimeActivityBase base
11699 ValueListActivity
.prototype.performHook = function( nIndex
, nFractionalIndex
, nRepeatCount
)
11701 if( this.isDisposed() || !this.aAnimation
)
11703 log( 'ValueListActivity.performHook: activity disposed or not valid animation' );
11707 assert( ( nIndex
+ 1 ) < this.aValueList
.length
,
11708 'ValueListActivity.performHook: assertion (nIndex + 1 < this.aValueList.length) failed' );
11710 // interpolate between nIndex and nIndex+1 values
11712 var aValue
= this.aInterpolator( this.aValueList
[ nIndex
],
11713 this.aValueList
[ nIndex
+1 ],
11714 nFractionalIndex
);
11716 if( this.bCumulative
)
11718 aValue
= this.add( aValue
, this.scale( nRepeatCount
, this.aLastValue
) );
11719 //aValue = aValue + nRepeatCount * this.aLastValue;
11721 this.aAnimation
.perform( aValue
);
11724 ValueListActivity
.prototype.performEnd = function()
11726 if( this.aAnimation
)
11728 this.aAnimation
.perform( this.aLastValue
);
11732 ValueListActivity
.prototype.dispose = function()
11734 ValueListActivity
.superclass
.dispose
.call( this );
11738 return ValueListActivity
;
11742 // ValueListActivity< ContinuousKeyTimeActivityBase > instantiation
11743 var LinearValueListActivity
= instantiate( ValueListActivityTemplate
, ContinuousKeyTimeActivityBase
);
11747 /**********************************************************************************************
11749 **********************************************************************************************/
11751 // ------------------------------------------------------------------------------------------ //
11752 function createActivity( aActivityParamSet
, aAnimationNode
, aAnimation
, aInterpolator
)
11754 var eCalcMode
= aAnimationNode
.getCalcMode();
11756 var sAttributeName
= aAnimationNode
.getAttributeName();
11757 var aAttributeProp
= aAttributeMap
[ sAttributeName
];
11759 var eValueType
= aAttributeProp
[ 'type' ];
11760 var eValueSubtype
= aAttributeProp
[ 'subtype' ];
11762 // do we need to get an interpolator ?
11763 if( ! aInterpolator
)
11765 aInterpolator
= aInterpolatorHandler
.getInterpolator( eCalcMode
,
11770 // is it cumulative ?
11771 var bAccumulate
= ( aAnimationNode
.getAccumulate() === ACCUMULATE_MODE_SUM
)
11772 && !( eValueType
=== BOOL_PROPERTY
||
11773 eValueType
=== STRING_PROPERTY
||
11774 eValueType
=== ENUM_PROPERTY
);
11778 aActivityParamSet
.aDiscreteTimes
= aAnimationNode
.getKeyTimes();
11780 // do we have a value list ?
11781 var aValueSet
= aAnimationNode
.getValues();
11782 var nValueSetSize
= aValueSet
.length
;
11784 if( nValueSetSize
!= 0 )
11786 // Value list activity
11788 if( aActivityParamSet
.aDiscreteTimes
.length
== 0 )
11790 for( var i
= 0; i
< nValueSetSize
; ++i
)
11791 aActivityParamSet
.aDiscreteTimes
[i
].push( i
/ nValueSetSize
);
11794 switch( eCalcMode
)
11796 case CALC_MODE_DISCRETE
:
11797 log( 'createActivity: discrete calculation case not yet implemented' );
11801 log( 'createActivity: unexpected calculation mode: ' + eCalcMode
);
11802 // FALLTHROUGH intended
11803 case CALC_MODE_PACED
:
11804 // FALLTHROUGH intended
11805 case CALC_MODE_SPLINE
:
11806 // FALLTHROUGH intended
11807 case CALC_MODE_LINEAR
:
11808 return createValueListActivity( aActivityParamSet
,
11812 LinearValueListActivity
,
11819 // FromToBy activity
11820 switch( eCalcMode
)
11822 case CALC_MODE_DISCRETE
:
11823 log( 'createActivity: discrete calculation case not yet implemented' );
11827 log( 'createActivity: unexpected calculation mode: ' + eCalcMode
);
11828 // FALLTHROUGH intended
11829 case CALC_MODE_PACED
:
11830 // FALLTHROUGH intended
11831 case CALC_MODE_SPLINE
:
11832 // FALLTHROUGH intended
11833 case CALC_MODE_LINEAR
:
11834 return createFromToByActivity( aActivityParamSet
,
11838 LinearFromToByActivity
,
11847 // ------------------------------------------------------------------------------------------ //
11848 function createValueListActivity( aActivityParamSet
, aAnimationNode
, aAnimation
,
11849 aInterpolator
, ClassTemplateInstance
, bAccumulate
, eValueType
)
11851 var aAnimatedElement
= aAnimationNode
.getAnimatedElement();
11852 var aOperatorSet
= aOperatorSetMap
[ eValueType
];
11853 assert( aOperatorSet
, 'createFromToByActivity: no operator set found' );
11855 var aValueSet
= aAnimationNode
.getValues();
11857 var aValueList
= new Array();
11859 extractAttributeValues( eValueType
,
11862 aAnimatedElement
.getBaseBBox(),
11863 aActivityParamSet
.nSlideWidth
,
11864 aActivityParamSet
.nSlideHeight
);
11866 for( var i
= 0; i
< aValueList
.length
; ++i
)
11868 ANIMDBG
.print( 'createValueListActivity: value[' + i
+ '] = ' + aValueList
[i
] );
11871 return new ClassTemplateInstance( aValueList
, aActivityParamSet
, aAnimation
,
11872 aInterpolator
, aOperatorSet
, bAccumulate
);
11877 // ------------------------------------------------------------------------------------------ //
11878 function createFromToByActivity( aActivityParamSet
, aAnimationNode
, aAnimation
,
11879 aInterpolator
, ClassTemplateInstance
, bAccumulate
, eValueType
)
11882 var aAnimatedElement
= aAnimationNode
.getAnimatedElement();
11883 var aOperatorSet
= aOperatorSetMap
[ eValueType
];
11884 assert( aOperatorSet
, 'createFromToByActivity: no operator set found' );
11886 var aValueSet
= new Array();
11887 aValueSet
[0] = aAnimationNode
.getFromValue();
11888 aValueSet
[1] = aAnimationNode
.getToValue();
11889 aValueSet
[2] = aAnimationNode
.getByValue();
11891 ANIMDBG
.print( 'createFromToByActivity: value type: ' + aValueTypeOutMap
[eValueType
] +
11892 ', aFrom = ' + aValueSet
[0] +
11893 ', aTo = ' + aValueSet
[1] +
11894 ', aBy = ' + aValueSet
[2] );
11896 var aValueList
= new Array();
11898 extractAttributeValues( eValueType
,
11901 aAnimatedElement
.getBaseBBox(),
11902 aActivityParamSet
.nSlideWidth
,
11903 aActivityParamSet
.nSlideHeight
);
11905 ANIMDBG
.print( 'createFromToByActivity: ' +
11906 ', aFrom = ' + aValueList
[0] +
11907 ', aTo = ' + aValueList
[1] +
11908 ', aBy = ' + aValueList
[2] );
11910 return new ClassTemplateInstance( aValueList
[0], aValueList
[1], aValueList
[2],
11911 aActivityParamSet
, aAnimation
,
11912 aInterpolator
, aOperatorSet
, bAccumulate
);
11916 // ------------------------------------------------------------------------------------------ //
11917 function extractAttributeValues( eValueType
, aValueList
, aValueSet
, aBBox
, nSlideWidth
, nSlideHeight
)
11920 switch( eValueType
)
11922 case NUMBER_PROPERTY
:
11923 evalValuesAttribute( aValueList
, aValueSet
, aBBox
, nSlideWidth
, nSlideHeight
);
11925 case BOOL_PROPERTY
:
11926 for( i
= 0; i
< aValueSet
.length
; ++i
)
11928 var aValue
= booleanParser( aValueSet
[i
] );
11929 aValueList
.push( aValue
);
11932 case STRING_PROPERTY
:
11933 for( i
= 0; i
< aValueSet
.length
; ++i
)
11935 aValueList
.push( aValueSet
[i
] );
11938 case ENUM_PROPERTY
:
11939 for( i
= 0; i
< aValueSet
.length
; ++i
)
11941 aValueList
.push( aValueSet
[i
] );
11944 case COLOR_PROPERTY
:
11945 for( i
= 0; i
< aValueSet
.length
; ++i
)
11947 aValue
= colorParser( aValueSet
[i
] );
11948 aValueList
.push( aValue
);
11952 log( 'createValueListActivity: unexpected value type: ' + eValueType
);
11957 // ------------------------------------------------------------------------------------------ //
11958 function evalValuesAttribute( aValueList
, aValueSet
, aBBox
, nSlideWidth
, nSlideHeight
)
11960 var width
= aBBox
.width
/ nSlideWidth
;
11961 var height
= aBBox
.height
/ nSlideHeight
;
11962 var x
= ( aBBox
.x
+ aBBox
.width
/ 2 ) / nSlideWidth
;
11963 var y
= ( aBBox
.y
+ aBBox
.height
/ 2 ) / nSlideHeight
;
11965 for( var i
= 0; i
< aValueSet
.length
; ++i
)
11967 var aValue
= eval( aValueSet
[i
] );
11968 aValueList
.push( aValue
);
11974 /**********************************************************************************************
11975 * SlideShow, SlideShowContext and FrameSynchronization
11976 **********************************************************************************************/
11978 // ------------------------------------------------------------------------------------------ //
11980 // direction of animation, important: not change the values!
11984 var MAXIMUM_FRAME_COUNT
= 60;
11985 var MINIMUM_TIMEOUT
= 1.0 / MAXIMUM_FRAME_COUNT
;
11986 var MAXIMUM_TIMEOUT
= 4.0;
11987 var MINIMUM_FRAMES_PER_SECONDS
= 10;
11988 var PREFERRED_FRAMES_PER_SECONDS
= 50;
11989 var PREFERRED_FRAME_RATE
= 1.0 / PREFERRED_FRAMES_PER_SECONDS
;
11992 function Effect( nId
)
11994 this.nId
= ( typeof( nId
) === typeof( 1 ) ) ? nId
: -1;
11995 this.eState
= Effect
.NOT_STARTED
;
11998 Effect
.NOT_STARTED
= 0;
11999 Effect
.PLAYING
= 1;
12002 Effect
.prototype.getId = function()
12007 Effect
.prototype.isMainEffect = function()
12009 return ( this.nId
=== -1 );
12012 Effect
.prototype.isPlaying = function()
12014 return ( this.eState
=== Effect
.PLAYING
);
12017 Effect
.prototype.isEnded = function()
12019 return ( this.eState
=== Effect
.ENDED
);
12022 Effect
.prototype.start = function()
12024 assert( this.eState
=== Effect
.NOT_STARTED
, 'Effect.start: wrong state.' );
12025 this.eState
= Effect
.PLAYING
;
12028 Effect
.prototype.end = function()
12030 assert( this.eState
=== Effect
.PLAYING
, 'Effect.end: wrong state.' );
12031 this.eState
= Effect
.ENDED
;
12034 // ------------------------------------------------------------------------------------------ //
12036 function SlideShow()
12038 this.aTimer
= new ElapsedTime();
12039 this.aFrameSynchronization
= new FrameSynchronization( PREFERRED_FRAME_RATE
);
12040 this.aTimerEventQueue
= new TimerEventQueue( this.aTimer
);
12041 this.aActivityQueue
= new ActivityQueue( this.aTimer
);
12042 this.aNextEffectEventArray
= null;
12043 this.aInteractiveAnimationSequenceMap
= null;
12044 this.aEventMultiplexer
= null;
12046 this.aContext
= new SlideShowContext( this.aTimerEventQueue
,
12047 this.aEventMultiplexer
,
12048 this.aNextEffectEventArray
,
12049 this.aInteractiveAnimationSequenceMap
,
12050 this.aActivityQueue
);
12051 this.bIsIdle
= true;
12052 this.bIsEnabled
= true;
12053 this.bNoSlideTransition
= false;
12055 this.nCurrentEffect
= 0;
12056 this.bIsNextEffectRunning
= false;
12057 this.bIsRewinding
= false;
12058 this.bIsSkipping
= false;
12059 this.bIsSkippingAll
= false;
12060 this.nTotalInteractivePlayingEffects
= 0;
12061 this.aStartedEffectList
= new Array();
12062 this.aStartedEffectIndexMap
= new Object();
12063 this.aStartedEffectIndexMap
[ -1 ] = undefined;
12066 SlideShow
.prototype.setSlideEvents = function( aNextEffectEventArray
,
12067 aInteractiveAnimationSequenceMap
,
12068 aEventMultiplexer
)
12070 if( !aNextEffectEventArray
)
12071 log( 'SlideShow.setSlideEvents: aNextEffectEventArray is not valid' );
12073 if( !aInteractiveAnimationSequenceMap
)
12074 log( 'SlideShow.setSlideEvents:aInteractiveAnimationSequenceMap is not valid' );
12076 if( !aEventMultiplexer
)
12077 log( 'SlideShow.setSlideEvents: aEventMultiplexer is not valid' );
12079 this.aContext
.aNextEffectEventArray
= aNextEffectEventArray
;
12080 this.aNextEffectEventArray
= aNextEffectEventArray
;
12081 this.aContext
.aInteractiveAnimationSequenceMap
= aInteractiveAnimationSequenceMap
;
12082 this.aInteractiveAnimationSequenceMap
= aInteractiveAnimationSequenceMap
;
12083 this.aContext
.aEventMultiplexer
= aEventMultiplexer
;
12084 this.aEventMultiplexer
= aEventMultiplexer
;
12085 this.nCurrentEffect
= 0;
12088 SlideShow
.prototype.createSlideTransition = function( aSlideTransitionHandler
, aLeavingSlide
, aEnteringSlide
, aTransitionEndEvent
)
12090 if( !aEnteringSlide
)
12092 log( 'SlideShow.createSlideTransition: entering slide element is not valid.' );
12096 if( this.bNoSlideTransition
) return null;
12098 var aAnimatedLeavingSlide
= null;
12099 if( aLeavingSlide
)
12100 aAnimatedLeavingSlide
= new AnimatedSlide( aLeavingSlide
);
12101 var aAnimatedEnteringSlide
= new AnimatedSlide( aEnteringSlide
);
12103 var aSlideTransition
= aSlideTransitionHandler
.createSlideTransition( aAnimatedLeavingSlide
, aAnimatedEnteringSlide
);
12104 if( !aSlideTransition
) return null;
12106 // compute duration
12107 var nDuration
= 0.001;
12108 if( aSlideTransitionHandler
.getDuration().isValue() )
12110 nDuration
= aSlideTransitionHandler
.getDuration().getValue();
12114 log( 'SlideShow.createSlideTransition: duration is not a number' );
12117 var aCommonParameterSet
= new ActivityParamSet();
12118 aCommonParameterSet
.aEndEvent
= aTransitionEndEvent
;
12119 aCommonParameterSet
.aTimerEventQueue
= this.aTimerEventQueue
;
12120 aCommonParameterSet
.aActivityQueue
= this.aActivityQueue
;
12121 aCommonParameterSet
.nMinDuration
= nDuration
;
12122 aCommonParameterSet
.nMinNumberOfFrames
= aSlideTransitionHandler
.getMinFrameCount();
12123 aCommonParameterSet
.nSlideWidth
= WIDTH
;
12124 aCommonParameterSet
.nSlideHeight
= HEIGHT
;
12126 return new SimpleActivity( aCommonParameterSet
, aSlideTransition
, FORWARD
);
12130 SlideShow
.prototype.isEnabled = function()
12132 return this.bIsEnabled
;
12135 SlideShow
.prototype.isRunning = function()
12137 return !this.bIsIdle
;
12140 SlideShow
.prototype.isMainEffectPlaying = function()
12142 return this.bIsNextEffectRunning
;
12145 SlideShow
.prototype.isInteractiveEffectPlaying = function()
12147 return ( this.nTotalInteractivePlayingEffects
> 0 );
12150 SlideShow
.prototype.isAnyEffectPlaying = function()
12152 return ( this.isMainEffectPlaying() || this.isInteractiveEffectPlaying() );
12155 SlideShow
.prototype.hasAnyEffectStarted = function()
12157 return ( this.aStartedEffectList
.length
> 0 );
12160 SlideShow
.prototype.notifyNextEffectStart = function()
12162 assert( !this.bIsNextEffectRunning
,
12163 'SlideShow.notifyNextEffectStart: an effect is already started.' );
12164 this.bIsNextEffectRunning
= true;
12165 this.aEventMultiplexer
.registerNextEffectEndHandler( bind2( SlideShow
.prototype.notifyNextEffectEnd
, this ) );
12166 var aEffect
= new Effect();
12168 this.aStartedEffectIndexMap
[ -1 ] = this.aStartedEffectList
.length
;
12169 this.aStartedEffectList
.push( aEffect
);
12172 var aAnimatedElementMap
= theMetaDoc
.aMetaSlideSet
[nCurSlide
].aSlideAnimationsHandler
.aAnimatedElementMap
;
12173 for( var sId
in aAnimatedElementMap
)
12174 aAnimatedElementMap
[ sId
].notifyNextEffectStart( this.nCurrentEffect
);
12177 SlideShow
.prototype.notifyNextEffectEnd = function()
12179 assert( this.bIsNextEffectRunning
,
12180 'SlideShow.notifyNextEffectEnd: effect already ended.' );
12181 this.bIsNextEffectRunning
= false;
12183 this.aStartedEffectList
[ this.aStartedEffectIndexMap
[ -1 ] ].end();
12186 SlideShow
.prototype.notifySlideStart = function( nNewSlideIndex
, nOldSlideIndex
)
12188 this.nCurrentEffect
= 0;
12189 this.bIsRewinding
= false;
12190 this.bIsSkipping
= false;
12191 this.bIsSkippingAll
= false;
12192 this.nTotalInteractivePlayingEffects
= 0;
12193 this.aStartedEffectList
= new Array();
12194 this.aStartedEffectIndexMap
= new Object();
12195 this.aStartedEffectIndexMap
[ -1 ] = undefined;
12197 var aAnimatedElementMap
;
12199 if( nOldSlideIndex
!== undefined )
12201 aAnimatedElementMap
= theMetaDoc
.aMetaSlideSet
[nOldSlideIndex
].aSlideAnimationsHandler
.aAnimatedElementMap
;
12202 for( sId
in aAnimatedElementMap
)
12203 aAnimatedElementMap
[ sId
].notifySlideEnd();
12206 aAnimatedElementMap
= theMetaDoc
.aMetaSlideSet
[nNewSlideIndex
].aSlideAnimationsHandler
.aAnimatedElementMap
;
12207 for( sId
in aAnimatedElementMap
)
12208 aAnimatedElementMap
[ sId
].notifySlideStart( this.aContext
);
12211 SlideShow
.prototype.notifyTransitionEnd = function( nSlideIndex
)
12213 theMetaDoc
.setCurrentSlide( nSlideIndex
);
12214 if( this.isEnabled() )
12216 // clear all queues
12219 theMetaDoc
.getCurrentSlide().aSlideAnimationsHandler
.start();
12224 SlideShow
.prototype.notifyInteractiveAnimationSequenceStart = function( nNodeId
)
12226 ++this.nTotalInteractivePlayingEffects
;
12227 var aEffect
= new Effect( nNodeId
);
12229 this.aStartedEffectIndexMap
[ nNodeId
] = this.aStartedEffectList
.length
;
12230 this.aStartedEffectList
.push( aEffect
);
12233 SlideShow
.prototype.notifyInteractiveAnimationSequenceEnd = function( nNodeId
)
12235 assert( this.isInteractiveEffectPlaying(),
12236 'SlideShow.notifyInteractiveAnimationSequenceEnd: no interactive effect playing.' )
12238 this.aStartedEffectList
[ this.aStartedEffectIndexMap
[ nNodeId
] ].end();
12239 --this.nTotalInteractivePlayingEffects
;
12243 * Start the next effect belonging to the main animation sequence if any.
12244 * If there is an already playing effect belonging to any animation sequence
12247 * @return {Boolean}
12248 * False if there is no more effect to start, true otherwise.
12250 SlideShow
.prototype.nextEffect = function()
12252 if( !this.isEnabled() )
12255 if( this.isAnyEffectPlaying() )
12257 this.skipAllPlayingEffects();
12261 if( !this.aNextEffectEventArray
)
12264 if( this.nCurrentEffect
>= this.aNextEffectEventArray
.size() )
12267 this.notifyNextEffectStart();
12269 this.aNextEffectEventArray
.at( this.nCurrentEffect
).fire();
12270 ++this.nCurrentEffect
;
12275 /** skipAllPlayingEffects
12276 * Skip all playing effect, independently to which animation sequence they
12280 SlideShow
.prototype.skipAllPlayingEffects = function()
12282 if( this.bIsSkipping
|| this.bIsRewinding
)
12285 this.bIsSkipping
= true;
12286 // TODO: The correct order should be based on the left playing time.
12287 for( var i
= 0; i
< this.aStartedEffectList
.length
; ++i
)
12289 var aEffect
= this.aStartedEffectList
[i
];
12290 if( aEffect
.isPlaying() )
12292 if( aEffect
.isMainEffect() )
12293 this.aEventMultiplexer
.notifySkipEffectEvent();
12295 this.aEventMultiplexer
.notifySkipInteractiveEffectEvent( aEffect
.getId() );
12299 this.bIsSkipping
= false;
12304 * Skip the next effect to be played (if any) that belongs to the main
12305 * animation sequence.
12306 * Require: no effect is playing.
12308 * @return {Boolean}
12309 * False if there is no more effect to skip, true otherwise.
12311 SlideShow
.prototype.skipNextEffect = function()
12313 if( this.bIsSkipping
|| this.bIsRewinding
)
12316 assert( !this.isAnyEffectPlaying(),
12317 'SlideShow.skipNextEffect' );
12319 if( !this.aNextEffectEventArray
)
12322 if( this.nCurrentEffect
>= this.aNextEffectEventArray
.size() )
12325 this.notifyNextEffectStart();
12327 this.bIsSkipping
= true;
12328 this.aNextEffectEventArray
.at( this.nCurrentEffect
).fire();
12329 this.aEventMultiplexer
.notifySkipEffectEvent();
12330 ++this.nCurrentEffect
;
12332 this.bIsSkipping
= false;
12336 /** skipPlayingOrNextEffect
12337 * Skip the next effect to be played that belongs to the main animation
12338 * sequence or all playing effects.
12340 * @return {Boolean}
12341 * False if there is no more effect to skip, true otherwise.
12343 SlideShow
.prototype.skipPlayingOrNextEffect = function()
12345 if( this.isAnyEffectPlaying() )
12346 return this.skipAllPlayingEffects();
12348 return this.skipNextEffect();
12353 * Skip all left effects that belongs to the main animation sequence and all
12354 * playing effects on the current slide.
12356 * @return {Boolean}
12357 * True if it already skipping or when it has ended skipping,
12358 * false if the next slide needs to be displayed.
12360 SlideShow
.prototype.skipAllEffects = function()
12362 if( this.bIsSkippingAll
)
12365 this.bIsSkippingAll
= true;
12367 if( this.isAnyEffectPlaying() )
12369 this.skipAllPlayingEffects();
12371 else if( !this.aNextEffectEventArray
12372 || ( this.nCurrentEffect
>= this.aNextEffectEventArray
.size() ) )
12374 this.bIsSkippingAll
= false;
12378 // Pay attention here: a new next effect event is appended to
12379 // aNextEffectEventArray only after the related animation node has been
12380 // resolved, that is only after the animation node related to the previous
12381 // effect has notified to be deactivated to the main sequence time container.
12382 // So you should avoid any optimization here because the size of
12383 // aNextEffectEventArray will going on increasing after every skip action.
12384 while( this.nCurrentEffect
< this.aNextEffectEventArray
.size() )
12386 this.skipNextEffect();
12388 this.bIsSkippingAll
= false;
12393 * Rewind all the effects started after at least one of the current playing
12394 * effects. If there is no playing effect, it rewinds the last played one,
12395 * both in case it belongs to the main or to an interactive animation sequence.
12398 SlideShow
.prototype.rewindEffect = function()
12400 if( this.bIsSkipping
|| this.bIsRewinding
)
12403 if( !this.hasAnyEffectStarted() )
12405 this.rewindToPreviousSlide();
12409 this.bIsRewinding
= true;
12411 var nFirstPlayingEffectIndex
= undefined;
12414 for( ; i
< this.aStartedEffectList
.length
; ++i
)
12416 var aEffect
= this.aStartedEffectList
[i
];
12417 if( aEffect
.isPlaying() )
12419 nFirstPlayingEffectIndex
= i
;
12424 // There is at least one playing effect.
12425 if( nFirstPlayingEffectIndex
!== undefined )
12427 i
= this.aStartedEffectList
.length
- 1;
12428 for( ; i
>= nFirstPlayingEffectIndex
; --i
)
12430 aEffect
= this.aStartedEffectList
[i
];
12431 if( aEffect
.isPlaying() )
12433 if( aEffect
.isMainEffect() )
12435 this.aEventMultiplexer
.notifyRewindCurrentEffectEvent();
12436 if( this.nCurrentEffect
> 0 )
12437 --this.nCurrentEffect
;
12441 this.aEventMultiplexer
.notifyRewindRunningInteractiveEffectEvent( aEffect
.getId() );
12444 else if( aEffect
.isEnded() )
12446 if( aEffect
.isMainEffect() )
12448 this.aEventMultiplexer
.notifyRewindLastEffectEvent();
12449 if( this.nCurrentEffect
> 0 )
12450 --this.nCurrentEffect
;
12454 this.aEventMultiplexer
.notifyRewindEndedInteractiveEffectEvent( aEffect
.getId() );
12460 // Pay attention here: we need to remove all rewinded effects from
12461 // the started effect list only after updating.
12462 i
= this.aStartedEffectList
.length
- 1;
12463 for( ; i
>= nFirstPlayingEffectIndex
; --i
)
12465 aEffect
= this.aStartedEffectList
.pop();
12466 if( !aEffect
.isMainEffect() )
12467 delete this.aStartedEffectIndexMap
[ aEffect
.getId() ];
12470 else // there is no playing effect
12472 aEffect
= this.aStartedEffectList
.pop();
12473 if( !aEffect
.isMainEffect() )
12474 delete this.aStartedEffectIndexMap
[ aEffect
.getId() ];
12475 if( aEffect
.isEnded() ) // Well that is almost an assertion.
12477 if( aEffect
.isMainEffect() )
12479 this.aEventMultiplexer
.notifyRewindLastEffectEvent();
12480 if( this.nCurrentEffect
> 0 )
12481 --this.nCurrentEffect
;
12485 this.aEventMultiplexer
.notifyRewindEndedInteractiveEffectEvent( aEffect
.getId() );
12491 this.bIsRewinding
= false;
12494 /** rewindToPreviousSlide
12495 * Displays the previous slide with all effects, that belong to the main
12496 * animation sequence, played.
12499 SlideShow
.prototype.rewindToPreviousSlide = function()
12501 if( this.isAnyEffectPlaying() )
12503 var nNewSlide
= nCurSlide
- 1;
12504 this.displaySlide( nNewSlide
, true );
12505 this.skipAllEffects();
12508 /** rewindAllEffects
12509 * Rewind all effects already played on the current slide.
12512 SlideShow
.prototype.rewindAllEffects = function()
12514 if( !this.hasAnyEffectStarted() )
12516 this.rewindToPreviousSlide();
12520 while( this.hasAnyEffectStarted() )
12522 this.rewindEffect();
12526 SlideShow
.prototype.displaySlide = function( nNewSlide
, bSkipSlideTransition
)
12528 var aMetaDoc
= theMetaDoc
;
12529 var nSlides
= aMetaDoc
.nNumberOfSlides
;
12530 if( nNewSlide
< 0 && nSlides
> 0 )
12531 nNewSlide
= nSlides
- 1;
12532 else if( nNewSlide
>= nSlides
)
12535 if( ( currentMode
=== INDEX_MODE
) && ( nNewSlide
=== nCurSlide
) )
12537 aMetaDoc
.getCurrentSlide().show();
12541 // handle current slide
12542 var nOldSlide
= nCurSlide
;
12543 if( nOldSlide
!== undefined )
12545 var oldMetaSlide
= aMetaDoc
.aMetaSlideSet
[nOldSlide
];
12546 if( this.isEnabled() )
12548 if( oldMetaSlide
.aSlideAnimationsHandler
.isAnimated() )
12550 // force end animations
12551 oldMetaSlide
.aSlideAnimationsHandler
.end( bSkipSlideTransition
);
12553 // clear all queues
12559 this.notifySlideStart( nNewSlide
, nOldSlide
);
12561 if( this.isEnabled() && !bSkipSlideTransition
)
12563 // create slide transition and add to activity queue
12564 if ( ( nOldSlide
!== undefined ) &&
12565 ( ( nNewSlide
> nOldSlide
) ||
12566 ( ( nNewSlide
== 0) && ( nOldSlide
== (aMetaDoc
.nNumberOfSlides
- 1) ) ) ) )
12568 var aOldMetaSlide
= aMetaDoc
.aMetaSlideSet
[nOldSlide
];
12569 var aNewMetaSlide
= aMetaDoc
.aMetaSlideSet
[nNewSlide
];
12571 var aSlideTransitionHandler
= aNewMetaSlide
.aTransitionHandler
;
12572 if( aSlideTransitionHandler
&& aSlideTransitionHandler
.isValid() )
12574 var aLeavingSlide
= aOldMetaSlide
;
12575 var aEnteringSlide
= aNewMetaSlide
;
12576 var aTransitionEndEvent
= makeEvent( bind2( this.notifyTransitionEnd
, this, nNewSlide
) );
12578 var aTransitionActivity
=
12579 this.createSlideTransition( aSlideTransitionHandler
, aLeavingSlide
,
12580 aEnteringSlide
, aTransitionEndEvent
);
12582 if( aTransitionActivity
)
12584 this.aActivityQueue
.addActivity( aTransitionActivity
);
12589 this.notifyTransitionEnd( nNewSlide
);
12594 this.notifyTransitionEnd( nNewSlide
);
12599 this.notifyTransitionEnd( nNewSlide
);
12604 this.notifyTransitionEnd( nNewSlide
);
12609 SlideShow
.prototype.update = function()
12611 this.aTimer
.holdTimer();
12612 //var suspendHandle = ROOT_NODE.suspendRedraw( PREFERRED_FRAME_RATE * 1000 );
12615 this.aTimerEventQueue
.process();
12616 this.aActivityQueue
.process();
12618 this.aFrameSynchronization
.synchronize();
12620 this.aActivityQueue
.processDequeued();
12622 //ROOT_NODE.unsuspendRedraw(suspendHandle);
12623 //ROOT_NODE.forceRedraw();
12624 this.aTimer
.releaseTimer();
12626 var bActivitiesLeft
= ( ! this.aActivityQueue
.isEmpty() );
12627 var bTimerEventsLeft
= ( ! this.aTimerEventQueue
.isEmpty() );
12628 var bEventsLeft
= ( bActivitiesLeft
|| bTimerEventsLeft
);
12634 if( bActivitiesLeft
)
12636 nNextTimeout
= MINIMUM_TIMEOUT
;
12637 this.aFrameSynchronization
.activate();
12641 nNextTimeout
= this.aTimerEventQueue
.nextTimeout();
12642 if( nNextTimeout
< MINIMUM_TIMEOUT
)
12643 nNextTimeout
= MINIMUM_TIMEOUT
;
12644 else if( nNextTimeout
> MAXIMUM_TIMEOUT
)
12645 nNextTimeout
= MAXIMUM_TIMEOUT
;
12646 this.aFrameSynchronization
.deactivate();
12649 this.bIsIdle
= false;
12650 window
.setTimeout( 'aSlideShow.update()', nNextTimeout
* 1000 );
12654 this.bIsIdle
= true;
12658 SlideShow
.prototype.dispose = function()
12660 // clear all queues
12661 this.aTimerEventQueue
.clear();
12662 this.aActivityQueue
.clear();
12663 this.aNextEffectEventArray
= null;
12664 this.aEventMultiplexer
= null;
12667 SlideShow
.prototype.getContext = function()
12669 return this.aContext
;
12672 // the SlideShow global instance
12673 var aSlideShow
= null;
12677 // ------------------------------------------------------------------------------------------ //
12678 function SlideShowContext( aTimerEventQueue
, aEventMultiplexer
, aNextEffectEventArray
, aInteractiveAnimationSequenceMap
, aActivityQueue
)
12680 this.aTimerEventQueue
= aTimerEventQueue
;
12681 this.aEventMultiplexer
= aEventMultiplexer
;
12682 this.aNextEffectEventArray
= aNextEffectEventArray
;
12683 this.aInteractiveAnimationSequenceMap
= aInteractiveAnimationSequenceMap
;
12684 this.aActivityQueue
= aActivityQueue
;
12685 this.bIsSkipping
= false;
12690 // ------------------------------------------------------------------------------------------ //
12691 function FrameSynchronization( nFrameDuration
)
12693 this.nFrameDuration
= nFrameDuration
;
12694 this.aTimer
= new ElapsedTime();
12695 this.nNextFrameTargetTime
= 0.0;
12696 this.bIsActive
= false;
12698 this.markCurrentFrame();
12702 FrameSynchronization
.prototype.markCurrentFrame = function()
12704 this.nNextFrameTargetTime
= this.aTimer
.getElapsedTime() + this.nFrameDuration
;
12707 FrameSynchronization
.prototype.synchronize = function()
12709 if( this.bIsActive
)
12711 // Do busy waiting for now.
12712 while( this.aTimer
.getElapsedTime() < this.nNextFrameTargetTime
)
12716 this.markCurrentFrame();
12720 FrameSynchronization
.prototype.activate = function()
12722 this.bIsActive
= true;
12725 FrameSynchronization
.prototype.deactivate = function()
12727 this.bIsActive
= false;
12732 /**********************************************************************************************
12733 * TimerEventQueue, ActivityQueue and ElapsedTime
12734 **********************************************************************************************/
12736 //------------------------------------------------------------------------------------------- //
12737 function NextEffectEventArray()
12739 this.aEventArray
= new Array();
12743 NextEffectEventArray
.prototype.size = function()
12745 return this.aEventArray
.length
;
12748 NextEffectEventArray
.prototype.at = function( nIndex
)
12750 return this.aEventArray
[ nIndex
];
12753 NextEffectEventArray
.prototype.appendEvent = function( aEvent
)
12755 var nSize
= this.size();
12756 for( var i
= 0; i
< nSize
; ++i
)
12758 if( this.aEventArray
[i
].getId() == aEvent
.getId() )
12760 aNextEffectEventArrayDebugPrinter
.print( 'NextEffectEventArray.appendEvent: event(' + aEvent
.getId() + ') already present' );
12764 this.aEventArray
.push( aEvent
);
12765 aNextEffectEventArrayDebugPrinter
.print( 'NextEffectEventArray.appendEvent: event(' + aEvent
.getId() + ') appended' );
12769 NextEffectEventArray
.prototype.clear = function( )
12771 this.aEventArray
= new Array();
12776 //------------------------------------------------------------------------------------------- //
12777 function TimerEventQueue( aTimer
)
12779 this.aTimer
= aTimer
;
12780 this.aEventSet
= new PriorityQueue( EventEntry
.compare
);
12784 TimerEventQueue
.prototype.addEvent = function( aEvent
)
12786 this.DBG( 'TimerEventQueue.addEvent event(' + aEvent
.getId() + ') appended.' );
12789 log( 'TimerEventQueue.addEvent: null event' );
12793 var nTime
= aEvent
.getActivationTime( this.aTimer
.getElapsedTime() );
12794 var aEventEntry
= new EventEntry( aEvent
, nTime
);
12795 this.aEventSet
.push( aEventEntry
);
12800 TimerEventQueue
.prototype.forceEmpty = function()
12802 this.process_(true);
12806 TimerEventQueue
.prototype.process = function()
12808 this.process_(false);
12811 TimerEventQueue
.prototype.process_ = function( bFireAllEvents
)
12813 var nCurrentTime
= this.aTimer
.getElapsedTime();
12815 while( !this.isEmpty() && ( bFireAllEvents
|| ( this.aEventSet
.top().nActivationTime
<= nCurrentTime
) ) )
12817 var aEventEntry
= this.aEventSet
.top();
12818 this.aEventSet
.pop();
12820 var aEvent
= aEventEntry
.aEvent
;
12821 if( aEvent
.isCharged() )
12826 TimerEventQueue
.prototype.isEmpty = function()
12828 return this.aEventSet
.isEmpty();
12831 TimerEventQueue
.prototype.nextTimeout = function()
12833 var nTimeout
= Number
.MAX_VALUE
;
12834 var nCurrentTime
= this.aTimer
.getElapsedTime();
12835 if( !this.isEmpty() )
12836 nTimeout
= this.aEventSet
.top().nActivationTime
- nCurrentTime
;
12840 TimerEventQueue
.prototype.clear = function()
12842 this.DBG( 'TimerEventQueue.clear invoked' );
12843 this.aEventSet
.clear();
12846 TimerEventQueue
.prototype.getTimer = function()
12848 return this.aTimer
;
12851 TimerEventQueue
.prototype.DBG = function( sMessage
, nTime
)
12853 aTimerEventQueueDebugPrinter
.print( sMessage
, nTime
);
12857 TimerEventQueue
.prototype.insert = function( aEventEntry
)
12859 var nHoleIndex
= this.aEventSet
.length
;
12860 var nParent
= Math
.floor( ( nHoleIndex
- 1 ) / 2 );
12862 while( ( nHoleIndex
> 0 ) && this.aEventSet
[ nParent
].compare( aEventEntry
) )
12864 this.aEventSet
[ nHoleIndex
] = this.aEventSet
[ nParent
];
12865 nHoleIndex
= nParent
;
12866 nParent
= Math
.floor( ( nHoleIndex
- 1 ) / 2 );
12868 this.aEventSet
[ nHoleIndex
] = aEventEntry
;
12873 // ------------------------------------------------------------------------------------------ //
12874 function EventEntry( aEvent
, nTime
)
12876 this.aEvent
= aEvent
;
12877 this.nActivationTime
= nTime
;
12881 EventEntry
.compare = function( aLhsEventEntry
, aRhsEventEntry
)
12883 return ( aLhsEventEntry
.nActivationTime
> aRhsEventEntry
.nActivationTime
);
12888 // ------------------------------------------------------------------------------------------ //
12889 function ActivityQueue( aTimer
)
12891 this.aTimer
= aTimer
;
12892 this.aCurrentActivityWaitingSet
= new Array();
12893 this.aCurrentActivityReinsertSet
= new Array();
12894 this.aDequeuedActivitySet
= new Array();
12898 ActivityQueue
.prototype.dispose = function()
12900 var nSize
= this.aCurrentActivityWaitingSet
.length
;
12902 for( i
= 0; i
< nSize
; ++i
)
12903 this.aCurrentActivityWaitingSet
[i
].dispose();
12905 nSize
= this.aCurrentActivityReinsertSet
.length
;
12906 for( i
= 0; i
< nSize
; ++i
)
12907 this.aCurrentActivityReinsertSet
[i
].dispose();
12910 ActivityQueue
.prototype.addActivity = function( aActivity
)
12914 log( 'ActivityQueue.addActivity: activity is not valid' );
12918 this.aCurrentActivityWaitingSet
.push( aActivity
);
12919 aActivityQueueDebugPrinter
.print( 'ActivityQueue.addActivity: activity appended' );
12923 ActivityQueue
.prototype.process = function()
12925 var nSize
= this.aCurrentActivityWaitingSet
.length
;
12927 for( var i
= 0; i
< nSize
; ++i
)
12929 nLag
= Math
.max( nLag
,this.aCurrentActivityWaitingSet
[i
].calcTimeLag() );
12933 this.aTimer
.adjustTimer( -nLag
, true );
12936 while( this.aCurrentActivityWaitingSet
.length
!= 0 )
12938 var aActivity
= this.aCurrentActivityWaitingSet
.shift();
12939 var bReinsert
= false;
12941 bReinsert
= aActivity
.perform();
12945 this.aCurrentActivityReinsertSet
.push( aActivity
);
12949 this.aDequeuedActivitySet
.push( aActivity
);
12953 if( this.aCurrentActivityReinsertSet
.length
!= 0 )
12955 // TODO: optimization, try to swap reference here
12956 this.aCurrentActivityWaitingSet
= this.aCurrentActivityReinsertSet
;
12957 this.aCurrentActivityReinsertSet
= new Array();
12961 ActivityQueue
.prototype.processDequeued = function()
12963 // notify all dequeued activities from last round
12964 var nSize
= this.aDequeuedActivitySet
.length
;
12965 for( var i
= 0; i
< nSize
; ++i
)
12966 this.aDequeuedActivitySet
[i
].dequeued();
12968 this.aDequeuedActivitySet
= new Array();
12971 ActivityQueue
.prototype.isEmpty = function()
12973 return ( ( this.aCurrentActivityWaitingSet
.length
== 0 ) &&
12974 ( this.aCurrentActivityReinsertSet
.length
== 0 ) );
12977 ActivityQueue
.prototype.clear = function()
12979 aActivityQueueDebugPrinter
.print( 'ActivityQueue.clear invoked' );
12980 var nSize
= this.aCurrentActivityWaitingSet
.length
;
12982 for( i
= 0; i
< nSize
; ++i
)
12983 this.aCurrentActivityWaitingSet
[i
].dequeued();
12984 this.aCurrentActivityWaitingSet
= new Array();
12986 nSize
= this.aCurrentActivityReinsertSet
.length
;
12987 for( i
= 0; i
< nSize
; ++i
)
12988 this.aCurrentActivityReinsertSet
[i
].dequeued();
12989 this.aCurrentActivityReinsertSet
= new Array();
12992 ActivityQueue
.prototype.endAll = function()
12994 aActivityQueueDebugPrinter
.print( 'ActivityQueue.endAll invoked' );
12995 var nSize
= this.aCurrentActivityWaitingSet
.length
;
12997 for( i
= 0; i
< nSize
; ++i
)
12998 this.aCurrentActivityWaitingSet
[i
].end();
12999 this.aCurrentActivityWaitingSet
= new Array();
13001 nSize
= this.aCurrentActivityReinsertSet
.length
;
13002 for( i
= 0; i
< nSize
; ++i
)
13003 this.aCurrentActivityReinsertSet
[i
].end();
13004 this.aCurrentActivityReinsertSet
= new Array();
13007 ActivityQueue
.prototype.getTimer = function()
13009 return this.aTimer
;
13012 ActivityQueue
.prototype.size = function()
13014 return ( this.aCurrentActivityWaitingSet
.length
+
13015 this.aCurrentActivityReinsertSet
.length
+
13016 this.aDequeuedActivitySet
.length
);
13021 // ------------------------------------------------------------------------------------------ //
13022 function ElapsedTime( aTimeBase
)
13024 this.aTimeBase
= aTimeBase
;
13025 this.nLastQueriedTime
= 0.0;
13026 this.nStartTime
= this.getCurrentTime();
13027 this.nFrozenTime
= 0.0;
13028 this.bInPauseMode
= false;
13029 this.bInHoldMode
= false;
13033 ElapsedTime
.prototype.getTimeBase = function()
13035 return this.aTimeBase
;
13038 ElapsedTime
.prototype.reset = function()
13040 this.nLastQueriedTime
= 0.0;
13041 this.nStartTime
= this.getCurrentTime();
13042 this.nFrozenTime
= 0.0;
13043 this.bInPauseMode
= false;
13044 this.bInHoldMode
= false;
13047 ElapsedTime
.prototype.getElapsedTime = function()
13049 this.nLastQueriedTime
= this.getElapsedTimeImpl();
13050 return this.nLastQueriedTime
;
13053 ElapsedTime
.prototype.pauseTimer = function()
13055 this.nFrozenTime
= this.getElapsedTimeImpl();
13056 this.bInPauseMode
= true;
13059 ElapsedTime
.prototype.continueTimer = function()
13061 this.bInPauseMode
= false;
13063 // stop pausing, time runs again. Note that
13064 // getElapsedTimeImpl() honors hold mode, i.e. a
13065 // continueTimer() in hold mode will preserve the latter
13066 var nPauseDuration
= this.getElapsedTimeImpl() - this.nFrozenTime
;
13068 // adjust start time, such that subsequent getElapsedTime() calls
13069 // will virtually start from m_fFrozenTime.
13070 this.nStartTime
+= nPauseDuration
;
13073 ElapsedTime
.prototype.adjustTimer = function( nOffset
, bLimitToLastQueriedTime
)
13075 if( bLimitToLastQueriedTime
== undefined )
13076 bLimitToLastQueriedTime
= true;
13078 // to make getElapsedTime() become _larger_, have to reduce nStartTime.
13079 this.nStartTime
-= nOffset
;
13081 // also adjust frozen time, this method must _always_ affect the
13082 // value returned by getElapsedTime()!
13083 if( this.bInHoldMode
|| this.bInPauseMode
)
13084 this.nFrozenTime
+= nOffset
;
13087 ElapsedTime
.prototype.holdTimer = function()
13089 // when called during hold mode (e.g. more than once per time
13090 // object), the original hold time will be maintained.
13091 this.nFrozenTime
= this.getElapsedTimeImpl();
13092 this.bInHoldMode
= true;
13095 ElapsedTime
.prototype.releaseTimer = function()
13097 this.bInHoldMode
= false;
13100 ElapsedTime
.prototype.getSystemTime = function()
13102 return ( getCurrentSystemTime() / 1000.0 );
13105 ElapsedTime
.prototype.getCurrentTime = function()
13108 if ( !this.aTimeBase
)
13110 nCurrentTime
= this.getSystemTime();
13111 // if( !isFinite(nCurrentTime) )
13113 // log( 'ElapsedTime.getCurrentTime: this.getSystemTime() == ' + nCurrentTime );
13118 nCurrentTime
= this.aTimeBase
.getElapsedTimeImpl();
13119 // if( !isFinite(nCurrentTime) )
13121 // log( 'ElapsedTime.getCurrentTime: this.aTimeBase.getElapsedTimeImpl() == ' + nCurrentTime );
13125 assert( ( typeof( nCurrentTime
) === typeof( 0 ) ) && isFinite( nCurrentTime
),
13126 'ElapsedTime.getCurrentTime: assertion failed: nCurrentTime == ' + nCurrentTime
);
13129 return nCurrentTime
;
13132 ElapsedTime
.prototype.getElapsedTimeImpl = function()
13134 if( this.bInHoldMode
|| this.bInPauseMode
)
13136 // if( !isFinite(this.nFrozenTime) )
13138 // log( 'ElapsedTime.getElapsedTimeImpl: nFrozenTime == ' + this.nFrozenTime );
13141 return this.nFrozenTime
;
13144 var nCurTime
= this.getCurrentTime();
13145 return ( nCurTime
- this.nStartTime
);
13152 * Several parts of the above code are the result of the porting,
13153 * started on August 2011, of the C++ code included in the source files
13154 * placed under the folder '/slideshow/source' and subfolders.
13155 * @source http://cgit.freedesktop.org/libreoffice/core/tree/slideshow/source
13159 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */