1
// Copyright 2008, 2009 Hannes Hochreiner
2
// This program is free software: you can redistribute it and/or modify
3
// it under the terms of the GNU General Public License as published by
4
// the Free Software Foundation, either version 3 of the License, or
5
// (at your option) any later version.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License for more details.
12
// You should have received a copy of the GNU General Public License
13
// along with this program. If not, see http://www.gnu.org/licenses/.
15
// Set onload event handler.
16
window.onload = jessyInkInit;
18
// Creating a namespace dictionary. The standard Inkscape namespaces are taken from inkex.py.
19
var NSS = new Object();
20
NSS['sodipodi']='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd';
21
NSS['cc']='http://web.resource.org/cc/';
22
NSS['svg']='http://www.w3.org/2000/svg';
23
NSS['dc']='http://purl.org/dc/elements/1.1/';
24
NSS['rdf']='http://www.w3.org/1999/02/22-rdf-syntax-ns#';
25
NSS['inkscape']='http://www.inkscape.org/namespaces/inkscape';
26
NSS['xlink']='http://www.w3.org/1999/xlink';
27
NSS['xml']='http://www.w3.org/XML/1998/namespace';
28
NSS['jessyink']='https://launchpad.net/jessyink';
31
var LEFT_KEY = 37; // cursor left keycode
32
var UP_KEY = 38; // cursor up keycode
33
var RIGHT_KEY = 39; // cursor right keycode
34
var DOWN_KEY = 40; // cursor down keycode
35
var PAGE_UP_KEY = 33; // page up keycode
36
var PAGE_DOWN_KEY = 34; // page down keycode
37
var HOME_KEY = 36; // home keycode
38
var END_KEY = 35; // end keycode
39
var ENTER_KEY = 13; // next slide
43
// Presentation modes.
48
// Mouse handler actions.
55
var ROOT_NODE = document.getElementsByTagNameNS(NSS["svg"], "svg")[0];
58
var INDEX_COLUMNS_DEFAULT = 4;
59
var INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
63
var BACKGROUND_COLOR = null;
64
var slides = new Array();
67
var currentMode = SLIDE_MODE;
68
var masterSlide = null;
71
var timeStep = 30; // 40 ms equal 25 frames per second.
72
var lastFrameTime = null;
73
var processingEffect = false;
76
var defaultTransitionInDict = new Object();
77
defaultTransitionInDict["name"] = "appear";
78
var defaultTransitionOutDict = new Object();
79
defaultTransitionOutDict["name"] = "appear";
80
var jessyInkInitialised = false;
82
// Initialise char and key code dictionaries.
83
var charCodeDictionary = getDefaultCharCodeDictionary();
84
var keyCodeDictionary = getDefaultKeyCodeDictionary();
86
// Initialise mouse handler dictionary.
87
var mouseHandlerDictionary = getDefaultMouseHandlerDictionary();
89
var progress_bar_visible = false;
90
var timer_elapsed = 0;
91
var timer_start = timer_elapsed;
92
var timer_duration = 15; // 15 minutes
94
var history_counter = 0;
95
var history_original_elements = new Array();
96
var history_presentation_elements = new Array();
98
var mouse_original_path = null;
99
var mouse_presentation_path = null;
100
var mouse_last_x = -1;
101
var mouse_last_y = -1;
102
var mouse_min_dist_sqr = 3 * 3;
103
var path_colour = "red";
104
var path_width_default = 3;
105
var path_width = path_width_default;
106
var path_paint_width = path_width;
108
var number_of_added_slides = 0;
110
/** Initialisation function.
111
* The whole presentation is set-up in this function.
113
function jessyInkInit()
115
// Make sure we only execute this code once. Double execution can occur if the onload event handler is set
116
// in the main svg tag as well (as was recommended in earlier versions). Executing this function twice does
117
// not lead to any problems, but it takes more time.
118
if (jessyInkInitialised)
121
// Making the presentation scaleable.
122
var VIEWBOX = ROOT_NODE.getAttribute("viewBox");
126
WIDTH = ROOT_NODE.viewBox.animVal.width;
127
HEIGHT = ROOT_NODE.viewBox.animVal.height;
131
HEIGHT = parseFloat(ROOT_NODE.getAttribute("height"));
132
WIDTH = parseFloat(ROOT_NODE.getAttribute("width"));
133
ROOT_NODE.setAttribute("viewBox", "0 0 " + WIDTH + " " + HEIGHT);
136
ROOT_NODE.setAttribute("width", "100%");
137
ROOT_NODE.setAttribute("height", "100%");
139
// Setting the background color.
140
var namedViews = document.getElementsByTagNameNS(NSS["sodipodi"], "namedview");
142
for (var counter = 0; counter < namedViews.length; counter++)
144
if (namedViews[counter].hasAttribute("id") && namedViews[counter].hasAttribute("pagecolor"))
146
if (namedViews[counter].getAttribute("id") == "base")
148
BACKGROUND_COLOR = namedViews[counter].getAttribute("pagecolor");
149
var newAttribute = "background-color:" + BACKGROUND_COLOR + ";";
151
if (ROOT_NODE.hasAttribute("style"))
152
newAttribute += ROOT_NODE.getAttribute("style");
154
ROOT_NODE.setAttribute("style", newAttribute);
159
// Defining clip-path.
160
var defsNodes = document.getElementsByTagNameNS(NSS["svg"], "defs");
162
if (defsNodes.length > 0)
164
var existingClipPath = document.getElementById("jessyInkSlideClipPath");
166
if (!existingClipPath)
168
var rectNode = document.createElementNS(NSS["svg"], "rect");
169
var clipPath = document.createElementNS(NSS["svg"], "clipPath");
171
rectNode.setAttribute("x", 0);
172
rectNode.setAttribute("y", 0);
173
rectNode.setAttribute("width", WIDTH);
174
rectNode.setAttribute("height", HEIGHT);
176
clipPath.setAttribute("id", "jessyInkSlideClipPath");
177
clipPath.setAttribute("clipPathUnits", "userSpaceOnUse");
179
clipPath.appendChild(rectNode);
180
defsNodes[0].appendChild(clipPath);
184
// Making a list of the slide and finding the master slide.
185
var nodes = document.getElementsByTagNameNS(NSS["svg"], "g");
186
var tempSlides = new Array();
187
var existingJessyInkPresentationLayer = null;
189
for (var counter = 0; counter < nodes.length; counter++)
191
if (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") && (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") == "layer"))
193
if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "masterSlide") == "masterSlide")
194
masterSlide = nodes[counter];
195
else if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "presentationLayer") == "presentationLayer")
196
existingJessyInkPresentationLayer = nodes[counter];
198
tempSlides.push(nodes[counter].getAttribute("id"));
200
else if (nodes[counter].getAttributeNS(NSS['jessyink'], 'element'))
202
handleElement(nodes[counter]);
206
// Hide master slide set default transitions.
209
masterSlide.style.display = "none";
211
if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionIn"))
212
defaultTransitionInDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionIn"));
214
if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionOut"))
215
defaultTransitionOutDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionOut"));
218
if (existingJessyInkPresentationLayer != null)
220
existingJessyInkPresentationLayer.parentNode.removeChild(existingJessyInkPresentationLayer);
224
var hashObj = new LocationHash(window.location.hash);
226
activeSlide = hashObj.slideNumber;
227
activeEffect = hashObj.effectNumber;
231
else if (activeSlide >= tempSlides.length)
232
activeSlide = tempSlides.length - 1;
234
var originalNode = document.getElementById(tempSlides[counter]);
236
var JessyInkPresentationLayer = document.createElementNS(NSS["svg"], "g");
237
JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
238
JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "label", "JessyInk Presentation Layer");
239
JessyInkPresentationLayer.setAttributeNS(NSS["jessyink"], "presentationLayer", "presentationLayer");
240
JessyInkPresentationLayer.setAttribute("id", "jessyink_presentation_layer");
241
JessyInkPresentationLayer.style.display = "inherit";
242
ROOT_NODE.appendChild(JessyInkPresentationLayer);
244
// Gathering all the information about the transitions and effects of the slides, set the background
245
// from the master slide and substitute the auto-texts.
246
for (var counter = 0; counter < tempSlides.length; counter++)
248
var originalNode = document.getElementById(tempSlides[counter]);
249
originalNode.style.display = "none";
250
var node = suffixNodeIds(originalNode.cloneNode(true), "_" + counter);
251
JessyInkPresentationLayer.appendChild(node);
252
slides[counter] = new Object();
253
slides[counter]["original_element"] = originalNode;
254
slides[counter]["element"] = node;
256
// Set build in transition.
257
slides[counter]["transitionIn"] = new Object();
261
if (node.hasAttributeNS(NSS["jessyink"], "transitionIn"))
262
dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionIn"));
264
dict = defaultTransitionInDict;
266
slides[counter]["transitionIn"]["name"] = dict["name"];
267
slides[counter]["transitionIn"]["options"] = new Object();
271
slides[counter]["transitionIn"]["options"][key] = dict[key];
273
// Set build out transition.
274
slides[counter]["transitionOut"] = new Object();
276
if (node.hasAttributeNS(NSS["jessyink"], "transitionOut"))
277
dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionOut"));
279
dict = defaultTransitionOutDict;
281
slides[counter]["transitionOut"]["name"] = dict["name"];
282
slides[counter]["transitionOut"]["options"] = new Object();
286
slides[counter]["transitionOut"]["options"][key] = dict[key];
288
// Copy master slide content.
291
var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + counter);
292
clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode");
293
clonedNode.removeAttributeNS(NSS["inkscape"], "label");
294
clonedNode.style.display = "inherit";
296
node.insertBefore(clonedNode, node.firstChild);
299
// Setting clip path.
300
node.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
302
// Substitute auto texts.
303
substituteAutoTexts(node, node.getAttributeNS(NSS["inkscape"], "label"), counter + 1, tempSlides.length);
305
node.removeAttributeNS(NSS["inkscape"], "groupmode");
306
node.removeAttributeNS(NSS["inkscape"], "label");
309
var tempEffects = new Array();
310
var groups = new Object();
312
for (var IOCounter = 0; IOCounter <= 1; IOCounter++)
319
propName = "effectIn";
322
else if (IOCounter == 1)
324
propName = "effectOut";
328
var effects = getElementsByPropertyNS(node, NSS["jessyink"], propName);
330
for (var effectCounter = 0; effectCounter < effects.length; effectCounter++)
332
var element = document.getElementById(effects[effectCounter]);
333
var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], propName));
335
// Put every element that has an effect associated with it, into its own group.
336
// Unless of course, we already put it into its own group.
337
if (!(groups[element.id]))
339
var newGroup = document.createElementNS(NSS["svg"], "g");
341
element.parentNode.insertBefore(newGroup, element);
342
newGroup.appendChild(element.parentNode.removeChild(element));
343
groups[element.id] = newGroup;
346
var effectDict = new Object();
348
effectDict["effect"] = dict["name"];
349
effectDict["dir"] = dir;
350
effectDict["element"] = groups[element.id];
352
for (var option in dict)
354
if ((option != "name") && (option != "order"))
356
if (!effectDict["options"])
357
effectDict["options"] = new Object();
359
effectDict["options"][option] = dict[option];
363
if (!tempEffects[dict["order"]])
364
tempEffects[dict["order"]] = new Array();
366
tempEffects[dict["order"]][tempEffects[dict["order"]].length] = effectDict;
370
// Make invisible, but keep in rendering tree to ensure that bounding box can be calculated.
371
node.setAttribute("opacity",0);
372
node.style.display = "inherit";
374
// Create a transform group.
375
var transformGroup = document.createElementNS(NSS["svg"], "g");
377
// Add content to transform group.
378
while (node.firstChild)
379
transformGroup.appendChild(node.firstChild);
381
// Transfer the transform attribute from the node to the transform group.
382
if (node.getAttribute("transform"))
384
transformGroup.setAttribute("transform", node.getAttribute("transform"));
385
node.removeAttribute("transform");
388
// Create a view group.
389
var viewGroup = document.createElementNS(NSS["svg"], "g");
391
viewGroup.appendChild(transformGroup);
392
slides[counter]["viewGroup"] = node.appendChild(viewGroup);
394
// Insert background.
395
if (BACKGROUND_COLOR != null)
397
var rectNode = document.createElementNS(NSS["svg"], "rect");
399
rectNode.setAttribute("x", 0);
400
rectNode.setAttribute("y", 0);
401
rectNode.setAttribute("width", WIDTH);
402
rectNode.setAttribute("height", HEIGHT);
403
rectNode.setAttribute("id", "jessyInkBackground" + counter);
404
rectNode.setAttribute("fill", BACKGROUND_COLOR);
406
slides[counter]["viewGroup"].insertBefore(rectNode, slides[counter]["viewGroup"].firstChild);
410
var tempViews = new Array();
411
var views = getElementsByPropertyNS(node, NSS["jessyink"], "view");
412
var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
414
// Set initial view even if there are no other views.
415
slides[counter]["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
416
slides[counter].initialView = matrixOld.toAttribute();
418
for (var viewCounter = 0; viewCounter < views.length; viewCounter++)
420
var element = document.getElementById(views[viewCounter]);
421
var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], "view"));
423
if (dict["order"] == 0)
425
matrixOld = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv());
426
slides[counter].initialView = matrixOld.toAttribute();
430
var effectDict = new Object();
432
effectDict["effect"] = dict["name"];
433
effectDict["dir"] = 1;
434
effectDict["element"] = slides[counter]["viewGroup"];
435
effectDict["order"] = dict["order"];
437
for (var option in dict)
439
if ((option != "name") && (option != "order"))
441
if (!effectDict["options"])
442
effectDict["options"] = new Object();
444
effectDict["options"][option] = dict[option];
448
effectDict["options"]["matrixNew"] = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv());
450
tempViews[dict["order"]] = effectDict;
454
element.parentNode.removeChild(element);
457
// Consolidate view array and append it to the effect array.
458
if (tempViews.length > 0)
460
for (var viewCounter = 0; viewCounter < tempViews.length; viewCounter++)
462
if (tempViews[viewCounter])
464
tempViews[viewCounter]["options"]["matrixOld"] = matrixOld;
465
matrixOld = tempViews[viewCounter]["options"]["matrixNew"];
467
if (!tempEffects[tempViews[viewCounter]["order"]])
468
tempEffects[tempViews[viewCounter]["order"]] = new Array();
470
tempEffects[tempViews[viewCounter]["order"]][tempEffects[tempViews[viewCounter]["order"]].length] = tempViews[viewCounter];
475
// Set consolidated effect array.
476
if (tempEffects.length > 0)
478
slides[counter]["effects"] = new Array();
480
for (var effectCounter = 0; effectCounter < tempEffects.length; effectCounter++)
482
if (tempEffects[effectCounter])
483
slides[counter]["effects"][slides[counter]["effects"].length] = tempEffects[effectCounter];
487
node.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
489
// Set visibility for initial state.
490
if (counter == activeSlide)
492
node.style.display = "inherit";
493
node.setAttribute("opacity",1);
497
node.style.display = "none";
498
node.setAttribute("opacity",0);
503
var jessyInkObjects = document.getElementsByTagNameNS(NSS["svg"], "g");
505
for (var counter = 0; counter < jessyInkObjects.length; counter++)
507
var elem = jessyInkObjects[counter];
509
if (elem.getAttributeNS(NSS["jessyink"], "customKeyBindings"))
511
if (elem.getCustomKeyBindings != undefined)
512
keyCodeDictionary = elem.getCustomKeyBindings();
514
if (elem.getCustomCharBindings != undefined)
515
charCodeDictionary = elem.getCustomCharBindings();
519
// Set mouse handler.
520
var jessyInkMouseHandler = document.getElementsByTagNameNS(NSS["jessyink"], "mousehandler");
522
for (var counter = 0; counter < jessyInkMouseHandler.length; counter++)
524
var elem = jessyInkMouseHandler[counter];
526
if (elem.getMouseHandler != undefined)
528
var tempDict = elem.getMouseHandler();
530
for (mode in tempDict)
532
if (!mouseHandlerDictionary[mode])
533
mouseHandlerDictionary[mode] = new Object();
535
for (handler in tempDict[mode])
536
mouseHandlerDictionary[mode][handler] = tempDict[mode][handler];
541
// Check effect number.
542
if ((activeEffect < 0) || (!slides[activeSlide].effects))
546
else if (activeEffect > slides[activeSlide].effects.length)
548
activeEffect = slides[activeSlide].effects.length;
551
createProgressBar(JessyInkPresentationLayer);
553
setProgressBarValue(activeSlide);
554
setTimeIndicatorValue(0);
555
setInterval("updateTimer()", 1000);
556
setSlideToState(activeSlide, activeEffect);
557
jessyInkInitialised = true;
560
/** Function to subtitute the auto-texts.
562
* @param node the node
563
* @param slideName name of the slide the node is on
564
* @param slideNumber number of the slide the node is on
565
* @param numberOfSlides number of slides in the presentation
567
function substituteAutoTexts(node, slideName, slideNumber, numberOfSlides)
569
var texts = node.getElementsByTagNameNS(NSS["svg"], "tspan");
571
for (var textCounter = 0; textCounter < texts.length; textCounter++)
573
if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideNumber")
574
texts[textCounter].firstChild.nodeValue = slideNumber;
575
else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "numberOfSlides")
576
texts[textCounter].firstChild.nodeValue = numberOfSlides;
577
else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideTitle")
578
texts[textCounter].firstChild.nodeValue = slideName;
582
/** Convenience function to get an element depending on whether it has a property with a particular name.
583
* This function emulates some dearly missed XPath functionality.
585
* @param node the node
586
* @param namespace namespace of the attribute
587
* @param name attribute name
589
function getElementsByPropertyNS(node, namespace, name)
591
var elems = new Array();
593
if (node.getAttributeNS(namespace, name))
594
elems.push(node.getAttribute("id"));
596
for (var counter = 0; counter < node.childNodes.length; counter++)
598
if (node.childNodes[counter].nodeType == 1)
599
elems = elems.concat(getElementsByPropertyNS(node.childNodes[counter], namespace, name));
605
/** Function to dispatch the next effect, if there is none left, change the slide.
607
* @param dir direction of the change (1 = forwards, -1 = backwards)
609
function dispatchEffects(dir)
611
if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
613
processingEffect = true;
617
effectArray = slides[activeSlide]["effects"][activeEffect];
623
effectArray = slides[activeSlide]["effects"][activeEffect];
627
startTime = (new Date()).getTime();
628
lastFrameTime = null;
631
else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
637
/** Function to skip effects and directly either put the slide into start or end state or change slides.
639
* @param dir direction of the change (1 = forwards, -1 = backwards)
641
function skipEffects(dir)
643
if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
645
processingEffect = true;
647
if (slides[activeSlide]["effects"] && (dir == 1))
648
activeEffect = slides[activeSlide]["effects"].length;
653
setSlideToState(activeSlide, STATE_END);
655
setSlideToState(activeSlide, STATE_START);
657
processingEffect = false;
659
else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
665
/** Function to change between slides.
667
* @param dir direction (1 = forwards, -1 = backwards)
669
function changeSlide(dir)
671
processingEffect = true;
672
effectArray = new Array();
674
effectArray[0] = new Object();
677
effectArray[0]["effect"] = slides[activeSlide]["transitionOut"]["name"];
678
effectArray[0]["options"] = slides[activeSlide]["transitionOut"]["options"];
679
effectArray[0]["dir"] = -1;
683
effectArray[0]["effect"] = slides[activeSlide]["transitionIn"]["name"];
684
effectArray[0]["options"] = slides[activeSlide]["transitionIn"]["options"];
685
effectArray[0]["dir"] = 1;
687
effectArray[0]["element"] = slides[activeSlide]["element"];
690
setProgressBarValue(activeSlide);
692
effectArray[1] = new Object();
696
effectArray[1]["effect"] = slides[activeSlide]["transitionIn"]["name"];
697
effectArray[1]["options"] = slides[activeSlide]["transitionIn"]["options"];
698
effectArray[1]["dir"] = 1;
702
effectArray[1]["effect"] = slides[activeSlide]["transitionOut"]["name"];
703
effectArray[1]["options"] = slides[activeSlide]["transitionOut"]["options"];
704
effectArray[1]["dir"] = -1;
707
effectArray[1]["element"] = slides[activeSlide]["element"];
709
if (slides[activeSlide]["effects"] && (dir == -1))
710
activeEffect = slides[activeSlide]["effects"].length;
715
setSlideToState(activeSlide, STATE_END);
717
setSlideToState(activeSlide, STATE_START);
720
startTime = (new Date()).getTime();
721
lastFrameTime = null;
725
/** Function to toggle between index and slide mode.
727
function toggleSlideIndex()
729
var suspendHandle = ROOT_NODE.suspendRedraw(500);
731
if (currentMode == SLIDE_MODE)
735
indexSetPageSlide(activeSlide);
736
currentMode = INDEX_MODE;
738
else if (currentMode == INDEX_MODE)
740
for (var counter = 0; counter < slides.length; counter++)
742
slides[counter]["element"].setAttribute("transform","scale(1)");
744
if (counter == activeSlide)
746
slides[counter]["element"].style.display = "inherit";
747
slides[counter]["element"].setAttribute("opacity",1);
752
slides[counter]["element"].setAttribute("opacity",0);
753
slides[counter]["element"].style.display = "none";
756
currentMode = SLIDE_MODE;
757
setSlideToState(activeSlide, STATE_START);
758
setProgressBarValue(activeSlide);
760
if (progress_bar_visible)
766
ROOT_NODE.unsuspendRedraw(suspendHandle);
767
ROOT_NODE.forceRedraw();
770
/** Function to run an effect.
772
* @param dir direction in which to play the effect (1 = forwards, -1 = backwards)
778
var suspendHandle = ROOT_NODE.suspendRedraw(200);
780
for (var counter = 0; counter < effectArray.length; counter++)
782
if (effectArray[counter]["effect"] == "fade")
783
done &= fade(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
784
else if (effectArray[counter]["effect"] == "appear")
785
done &= appear(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
786
else if (effectArray[counter]["effect"] == "pop")
787
done &= pop(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
788
else if (effectArray[counter]["effect"] == "view")
789
done &= view(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
792
ROOT_NODE.unsuspendRedraw(suspendHandle);
793
ROOT_NODE.forceRedraw();
797
var currentTime = (new Date()).getTime();
800
transCounter = currentTime - startTime;
802
if (lastFrameTime != null)
804
timeDiff = timeStep - (currentTime - lastFrameTime);
810
lastFrameTime = currentTime;
812
window.setTimeout("effect(" + dir + ")", timeDiff);
816
window.location.hash = (activeSlide + 1) + '_' + activeEffect;
817
processingEffect = false;
821
/** Function to display the index sheet.
823
* @param offsetNumber offset number
825
function displayIndex(offsetNumber)
830
if (offsetNumber < 0)
832
else if (offsetNumber >= slides.length)
833
offsetNumber = slides.length - 1;
835
for (var counter = 0; counter < slides.length; counter++)
837
if ((counter < offsetNumber) || (counter > offsetNumber + INDEX_COLUMNS * INDEX_COLUMNS - 1))
839
slides[counter]["element"].setAttribute("opacity",0);
840
slides[counter]["element"].style.display = "none";
844
offsetX = ((counter - offsetNumber) % INDEX_COLUMNS) * WIDTH;
845
offsetY = Math.floor((counter - offsetNumber) / INDEX_COLUMNS) * HEIGHT;
847
slides[counter]["element"].setAttribute("transform","scale("+1/INDEX_COLUMNS+") translate("+offsetX+","+offsetY+")");
848
slides[counter]["element"].style.display = "inherit";
849
slides[counter]["element"].setAttribute("opacity",0.5);
852
setSlideToState(counter, STATE_END);
855
//do we need to save the current offset?
856
if (INDEX_OFFSET != offsetNumber)
857
INDEX_OFFSET = offsetNumber;
860
/** Function to set the active slide in the slide view.
862
* @param nbr index of the active slide
864
function slideSetActiveSlide(nbr)
866
if (nbr >= slides.length)
867
nbr = slides.length - 1;
871
slides[activeSlide]["element"].setAttribute("opacity",0);
872
slides[activeSlide]["element"].style.display = "none";
874
activeSlide = parseInt(nbr);
876
setSlideToState(activeSlide, STATE_START);
877
slides[activeSlide]["element"].style.display = "inherit";
878
slides[activeSlide]["element"].setAttribute("opacity",1);
881
setProgressBarValue(nbr);
884
/** Function to set the active slide in the index view.
886
* @param nbr index of the active slide
888
function indexSetActiveSlide(nbr)
890
if (nbr >= slides.length)
891
nbr = slides.length - 1;
895
slides[activeSlide]["element"].setAttribute("opacity",0.5);
897
activeSlide = parseInt(nbr);
898
window.location.hash = (activeSlide + 1) + '_0';
900
slides[activeSlide]["element"].setAttribute("opacity",1);
903
/** Function to set the page and active slide in index view.
905
* @param nbr index of the active slide
907
* NOTE: To force a redraw,
908
* set INDEX_OFFSET to -1 before calling indexSetPageSlide().
910
* This is necessary for zooming (otherwise the index might not
911
* get redrawn) and when switching to index mode.
914
* indexSetPageSlide(activeSlide);
916
function indexSetPageSlide(nbr)
918
if (nbr >= slides.length)
919
nbr = slides.length - 1;
923
//calculate the offset
924
var offset = nbr - nbr % (INDEX_COLUMNS * INDEX_COLUMNS);
929
//if different from kept offset, then record and change the page
930
if (offset != INDEX_OFFSET)
932
INDEX_OFFSET = offset;
933
displayIndex(INDEX_OFFSET);
936
//set the active slide
937
indexSetActiveSlide(nbr);
940
/** Event handler for key press.
949
code = e.keyCode || e.charCode;
951
if (!processingEffect && keyCodeDictionary[currentMode] && keyCodeDictionary[currentMode][code])
952
return keyCodeDictionary[currentMode][code]();
954
document.onkeypress = keypress;
956
// Set event handler for key down.
957
document.onkeydown = keydown;
959
/** Event handler for key press.
965
document.onkeypress = null;
970
str = String.fromCharCode(e.keyCode || e.charCode);
972
if (!processingEffect && charCodeDictionary[currentMode] && charCodeDictionary[currentMode][str])
973
return charCodeDictionary[currentMode][str]();
976
/** Function to supply the default char code dictionary.
978
* @returns default char code dictionary
980
function getDefaultCharCodeDictionary()
982
var charCodeDict = new Object();
984
charCodeDict[SLIDE_MODE] = new Object();
985
charCodeDict[INDEX_MODE] = new Object();
986
charCodeDict[DRAWING_MODE] = new Object();
988
charCodeDict[SLIDE_MODE]["i"] = function () { return toggleSlideIndex(); };
989
charCodeDict[SLIDE_MODE]["d"] = function () { return slideSwitchToDrawingMode(); };
990
charCodeDict[SLIDE_MODE]["D"] = function () { return slideQueryDuration(); };
991
charCodeDict[SLIDE_MODE]["n"] = function () { return slideAddSlide(activeSlide); };
992
charCodeDict[SLIDE_MODE]["p"] = function () { return slideToggleProgressBarVisibility(); };
993
charCodeDict[SLIDE_MODE]["t"] = function () { return slideResetTimer(); };
994
charCodeDict[SLIDE_MODE]["e"] = function () { return slideUpdateExportLayer(); };
996
charCodeDict[DRAWING_MODE]["d"] = function () { return drawingSwitchToSlideMode(); };
997
charCodeDict[DRAWING_MODE]["0"] = function () { return drawingResetPathWidth(); };
998
charCodeDict[DRAWING_MODE]["1"] = function () { return drawingSetPathWidth(1.0); };
999
charCodeDict[DRAWING_MODE]["3"] = function () { return drawingSetPathWidth(3.0); };
1000
charCodeDict[DRAWING_MODE]["5"] = function () { return drawingSetPathWidth(5.0); };
1001
charCodeDict[DRAWING_MODE]["7"] = function () { return drawingSetPathWidth(7.0); };
1002
charCodeDict[DRAWING_MODE]["9"] = function () { return drawingSetPathWidth(9.0); };
1003
charCodeDict[DRAWING_MODE]["b"] = function () { return drawingSetPathColour("blue"); };
1004
charCodeDict[DRAWING_MODE]["c"] = function () { return drawingSetPathColour("cyan"); };
1005
charCodeDict[DRAWING_MODE]["g"] = function () { return drawingSetPathColour("green"); };
1006
charCodeDict[DRAWING_MODE]["k"] = function () { return drawingSetPathColour("black"); };
1007
charCodeDict[DRAWING_MODE]["m"] = function () { return drawingSetPathColour("magenta"); };
1008
charCodeDict[DRAWING_MODE]["o"] = function () { return drawingSetPathColour("orange"); };
1009
charCodeDict[DRAWING_MODE]["r"] = function () { return drawingSetPathColour("red"); };
1010
charCodeDict[DRAWING_MODE]["w"] = function () { return drawingSetPathColour("white"); };
1011
charCodeDict[DRAWING_MODE]["y"] = function () { return drawingSetPathColour("yellow"); };
1012
charCodeDict[DRAWING_MODE]["z"] = function () { return drawingUndo(); };
1014
charCodeDict[INDEX_MODE]["i"] = function () { return toggleSlideIndex(); };
1015
charCodeDict[INDEX_MODE]["-"] = function () { return indexDecreaseNumberOfColumns(); };
1016
charCodeDict[INDEX_MODE]["="] = function () { return indexIncreaseNumberOfColumns(); };
1017
charCodeDict[INDEX_MODE]["+"] = function () { return indexIncreaseNumberOfColumns(); };
1018
charCodeDict[INDEX_MODE]["0"] = function () { return indexResetNumberOfColumns(); };
1020
return charCodeDict;
1023
/** Function to supply the default key code dictionary.
1025
* @returns default key code dictionary
1027
function getDefaultKeyCodeDictionary()
1029
var keyCodeDict = new Object();
1031
keyCodeDict[SLIDE_MODE] = new Object();
1032
keyCodeDict[INDEX_MODE] = new Object();
1033
keyCodeDict[DRAWING_MODE] = new Object();
1035
keyCodeDict[SLIDE_MODE][LEFT_KEY] = function() { return dispatchEffects(-1); };
1036
keyCodeDict[SLIDE_MODE][RIGHT_KEY] = function() { return dispatchEffects(1); };
1037
keyCodeDict[SLIDE_MODE][UP_KEY] = function() { return skipEffects(-1); };
1038
keyCodeDict[SLIDE_MODE][DOWN_KEY] = function() { return skipEffects(1); };
1039
keyCodeDict[SLIDE_MODE][PAGE_UP_KEY] = function() { return dispatchEffects(-1); };
1040
keyCodeDict[SLIDE_MODE][PAGE_DOWN_KEY] = function() { return dispatchEffects(1); };
1041
keyCodeDict[SLIDE_MODE][HOME_KEY] = function() { return slideSetActiveSlide(0); };
1042
keyCodeDict[SLIDE_MODE][END_KEY] = function() { return slideSetActiveSlide(slides.length - 1); };
1043
keyCodeDict[SLIDE_MODE][SPACE_KEY] = function() { return dispatchEffects(1); };
1045
keyCodeDict[INDEX_MODE][LEFT_KEY] = function() { return indexSetPageSlide(activeSlide - 1); };
1046
keyCodeDict[INDEX_MODE][RIGHT_KEY] = function() { return indexSetPageSlide(activeSlide + 1); };
1047
keyCodeDict[INDEX_MODE][UP_KEY] = function() { return indexSetPageSlide(activeSlide - INDEX_COLUMNS); };
1048
keyCodeDict[INDEX_MODE][DOWN_KEY] = function() { return indexSetPageSlide(activeSlide + INDEX_COLUMNS); };
1049
keyCodeDict[INDEX_MODE][PAGE_UP_KEY] = function() { return indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS); };
1050
keyCodeDict[INDEX_MODE][PAGE_DOWN_KEY] = function() { return indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS); };
1051
keyCodeDict[INDEX_MODE][HOME_KEY] = function() { return indexSetPageSlide(0); };
1052
keyCodeDict[INDEX_MODE][END_KEY] = function() { return indexSetPageSlide(slides.length - 1); };
1053
keyCodeDict[INDEX_MODE][ENTER_KEY] = function() { return toggleSlideIndex(); };
1055
keyCodeDict[DRAWING_MODE][ESCAPE_KEY] = function () { return drawingSwitchToSlideMode(); };
1060
/** Function to handle all mouse events.
1063
* @param action type of event (e.g. mouse up, mouse wheel)
1065
function mouseHandlerDispatch(evnt, action)
1068
evnt = window.event;
1072
if (!processingEffect && mouseHandlerDictionary[currentMode] && mouseHandlerDictionary[currentMode][action])
1074
var subRetVal = mouseHandlerDictionary[currentMode][action](evnt);
1076
if (subRetVal != null && subRetVal != undefined)
1080
if (evnt.preventDefault && !retVal)
1081
evnt.preventDefault();
1083
evnt.returnValue = retVal;
1088
// Set mouse event handler.
1089
document.onmousedown = function(e) { return mouseHandlerDispatch(e, MOUSE_DOWN); };
1090
document.onmouseup = function(e) { return mouseHandlerDispatch(e, MOUSE_UP); };
1091
document.onmousemove = function(e) { return mouseHandlerDispatch(e, MOUSE_MOVE); };
1094
if (window.addEventListener)
1096
window.addEventListener('DOMMouseScroll', function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); }, false);
1099
// Opera Safari OK - may not work in IE
1100
window.onmousewheel = function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); };
1102
/** Function to supply the default mouse handler dictionary.
1104
* @returns default mouse handler dictionary
1106
function getDefaultMouseHandlerDictionary()
1108
var mouseHandlerDict = new Object();
1110
mouseHandlerDict[SLIDE_MODE] = new Object();
1111
mouseHandlerDict[INDEX_MODE] = new Object();
1112
mouseHandlerDict[DRAWING_MODE] = new Object();
1114
mouseHandlerDict[SLIDE_MODE][MOUSE_DOWN] = function(evnt) { return dispatchEffects(1); };
1115
mouseHandlerDict[SLIDE_MODE][MOUSE_WHEEL] = function(evnt) { return slideMousewheel(evnt); };
1117
mouseHandlerDict[INDEX_MODE][MOUSE_DOWN] = function(evnt) { return toggleSlideIndex(); };
1119
mouseHandlerDict[DRAWING_MODE][MOUSE_DOWN] = function(evnt) { return drawingMousedown(evnt); };
1120
mouseHandlerDict[DRAWING_MODE][MOUSE_UP] = function(evnt) { return drawingMouseup(evnt); };
1121
mouseHandlerDict[DRAWING_MODE][MOUSE_MOVE] = function(evnt) { return drawingMousemove(evnt); };
1123
return mouseHandlerDict;
1126
/** Function to switch from slide mode to drawing mode.
1128
function slideSwitchToDrawingMode()
1130
currentMode = DRAWING_MODE;
1134
if (ROOT_NODE.hasAttribute("style"))
1135
tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
1137
tempDict = new Object();
1139
tempDict["cursor"] = "crosshair";
1140
ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
1143
/** Function to switch from drawing mode to slide mode.
1145
function drawingSwitchToSlideMode()
1147
currentMode = SLIDE_MODE;
1151
if (ROOT_NODE.hasAttribute("style"))
1152
tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
1154
tempDict = new Object();
1156
tempDict["cursor"] = "auto";
1157
ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
1160
/** Function to decrease the number of columns in index mode.
1162
function indexDecreaseNumberOfColumns()
1164
if (INDEX_COLUMNS >= 3)
1168
indexSetPageSlide(activeSlide);
1172
/** Function to increase the number of columns in index mode.
1174
function indexIncreaseNumberOfColumns()
1176
if (INDEX_COLUMNS < 7)
1180
indexSetPageSlide(activeSlide);
1184
/** Function to reset the number of columns in index mode.
1186
function indexResetNumberOfColumns()
1188
if (INDEX_COLUMNS != INDEX_COLUMNS_DEFAULT)
1190
INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
1192
indexSetPageSlide(activeSlide);
1196
/** Function to reset path width in drawing mode.
1198
function drawingResetPathWidth()
1200
path_width = path_width_default;
1201
set_path_paint_width();
1204
/** Function to set path width in drawing mode.
1206
* @param width new path width
1208
function drawingSetPathWidth(width)
1211
set_path_paint_width();
1214
/** Function to set path colour in drawing mode.
1216
* @param colour new path colour
1218
function drawingSetPathColour(colour)
1220
path_colour = colour;
1223
/** Function to query the duration of the presentation from the user in slide mode.
1225
function slideQueryDuration()
1227
var new_duration = prompt("Length of presentation in minutes?", timer_duration);
1229
if ((new_duration != null) && (new_duration != ''))
1231
timer_duration = new_duration;
1237
/** Function to add new slide in slide mode.
1239
* @param afterSlide after which slide to insert the new one
1241
function slideAddSlide(afterSlide)
1243
addSlide(afterSlide);
1244
slideSetActiveSlide(afterSlide + 1);
1248
/** Function to toggle the visibility of the progress bar in slide mode.
1250
function slideToggleProgressBarVisibility()
1252
if (progress_bar_visible)
1254
progress_bar_visible = false;
1259
progress_bar_visible = true;
1264
/** Function to reset the timer in slide mode.
1266
function slideResetTimer()
1268
timer_start = timer_elapsed;
1272
/** Convenience function to pad a string with zero in front up to a certain length.
1274
function padString(str, len)
1278
while (outStr.length < len)
1280
outStr = '0' + outStr;
1286
/** Function to update the export layer.
1288
function slideUpdateExportLayer()
1290
// Suspend redraw since we are going to mess with the slides.
1291
var suspendHandle = ROOT_NODE.suspendRedraw(2000);
1293
var tmpActiveSlide = activeSlide;
1294
var tmpActiveEffect = activeEffect;
1295
var exportedLayers = new Array();
1297
for (var counterSlides = 0; counterSlides < slides.length; counterSlides++)
1301
setSlideToState(counterSlides, STATE_START);
1305
if (slides[counterSlides].effects)
1307
maxEffect = slides[counterSlides].effects.length;
1310
exportNode = slides[counterSlides].element.cloneNode(true);
1311
exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
1312
exportNode.setAttributeNS(NSS["inkscape"], "label", "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString("0", maxEffect.toString().length));
1314
exportedLayers.push(exportNode);
1316
if (slides[counterSlides]["effects"])
1318
for (var counter = 0; counter < slides[counterSlides]["effects"].length; counter++)
1320
for (var subCounter = 0; subCounter < slides[counterSlides]["effects"][counter].length; subCounter++)
1322
var effect = slides[counterSlides]["effects"][counter][subCounter];
1323
if (effect["effect"] == "fade")
1324
fade(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);
1325
else if (effect["effect"] == "appear")
1326
appear(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);
1327
else if (effect["effect"] == "pop")
1328
pop(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);
1329
else if (effect["effect"] == "view")
1330
view(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);
1333
var layerName = "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString((counter + 1).toString(), maxEffect.toString().length);
1334
exportNode = slides[counterSlides].element.cloneNode(true);
1335
exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
1336
exportNode.setAttributeNS(NSS["inkscape"], "label", layerName);
1337
exportNode.setAttribute("id", layerName);
1339
exportedLayers.push(exportNode);
1344
activeSlide = tmpActiveSlide;
1345
activeEffect = tmpActiveEffect;
1346
setSlideToState(activeSlide, activeEffect);
1349
var newDoc = document.documentElement.cloneNode(true);
1351
// Delete viewbox form new imag and set width and height.
1352
newDoc.removeAttribute('viewbox');
1353
newDoc.setAttribute('width', WIDTH);
1354
newDoc.setAttribute('height', HEIGHT);
1356
// Delete all layers and script elements.
1357
var nodesToBeRemoved = new Array();
1359
for (var childCounter = 0; childCounter < newDoc.childNodes.length; childCounter++)
1361
var child = newDoc.childNodes[childCounter];
1363
if (child.nodeType == 1)
1365
if ((child.nodeName.toUpperCase() == 'G') || (child.nodeName.toUpperCase() == 'SCRIPT'))
1367
nodesToBeRemoved.push(child);
1372
for (var ndCounter = 0; ndCounter < nodesToBeRemoved.length; ndCounter++)
1374
var nd = nodesToBeRemoved[ndCounter];
1376
nd.parentNode.removeChild(nd);
1379
// Set current layer.
1380
if (exportedLayers[0])
1384
for (var nodeCounter = 0; nodeCounter < newDoc.childNodes.length; nodeCounter++)
1386
if ((newDoc.childNodes[nodeCounter].nodeType == 1) && (newDoc.childNodes[nodeCounter].getAttribute('id') == 'base'))
1388
namedView = newDoc.childNodes[nodeCounter];
1394
namedView.setAttributeNS(NSS['inkscape'], 'current-layer', exportedLayers[0].getAttributeNS(NSS['inkscape'], 'label'));
1398
// Add exported layers.
1399
while (exportedLayers.length > 0)
1401
var nd = exportedLayers.pop();
1403
nd.setAttribute("opacity",1);
1404
nd.style.display = "inherit";
1406
newDoc.appendChild(nd);
1409
// Serialise the new document.
1410
var serializer = new XMLSerializer();
1414
close : function() {},
1415
flush : function() {},
1416
write : function(str, count) { this.content += str; }
1419
var xml = serializer.serializeToStream(newDoc, strm, 'UTF-8');
1421
window.open('data:image/svg+xml;base64;charset=utf-8,' + window.btoa(strm.content), '_blank');
1423
// Unsuspend redraw.
1424
ROOT_NODE.unsuspendRedraw(suspendHandle);
1425
ROOT_NODE.forceRedraw();
1428
/** Function to undo last drawing operation.
1430
function drawingUndo()
1432
mouse_presentation_path = null;
1433
mouse_original_path = null;
1435
if (history_presentation_elements.length > 0)
1437
var p = history_presentation_elements.pop();
1438
var parent = p.parentNode.removeChild(p);
1440
p = history_original_elements.pop();
1441
parent = p.parentNode.removeChild(p);
1445
/** Event handler for mouse down in drawing mode.
1447
* @param e the event
1449
function drawingMousedown(e)
1462
var p = calcCoord(e);
1464
mouse_last_x = e.clientX;
1465
mouse_last_y = e.clientY;
1466
mouse_original_path = document.createElementNS(NSS["svg"], "path");
1467
mouse_original_path.setAttribute("stroke", path_colour);
1468
mouse_original_path.setAttribute("stroke-width", path_paint_width);
1469
mouse_original_path.setAttribute("fill", "none");
1470
mouse_original_path.setAttribute("id", "path " + Date());
1471
mouse_original_path.setAttribute("d", "M" + p.x + "," + p.y);
1472
slides[activeSlide]["original_element"].appendChild(mouse_original_path);
1473
history_original_elements.push(mouse_original_path);
1475
mouse_presentation_path = document.createElementNS(NSS["svg"], "path");
1476
mouse_presentation_path.setAttribute("stroke", path_colour);
1477
mouse_presentation_path.setAttribute("stroke-width", path_paint_width);
1478
mouse_presentation_path.setAttribute("fill", "none");
1479
mouse_presentation_path.setAttribute("id", "path " + Date() + " presentation copy");
1480
mouse_presentation_path.setAttribute("d", "M" + p.x + "," + p.y);
1482
if (slides[activeSlide]["viewGroup"])
1483
slides[activeSlide]["viewGroup"].appendChild(mouse_presentation_path);
1485
slides[activeSlide]["element"].appendChild(mouse_presentation_path);
1487
history_presentation_elements.push(mouse_presentation_path);
1495
/** Event handler for mouse up in drawing mode.
1497
* @param e the event
1499
function drawingMouseup(e)
1504
if (mouse_presentation_path != null)
1506
var p = calcCoord(e);
1507
var d = mouse_presentation_path.getAttribute("d");
1508
d += " L" + p.x + "," + p.y;
1509
mouse_presentation_path.setAttribute("d", d);
1510
mouse_presentation_path = null;
1511
mouse_original_path.setAttribute("d", d);
1512
mouse_original_path = null;
1520
/** Event handler for mouse move in drawing mode.
1522
* @param e the event
1524
function drawingMousemove(e)
1529
var dist = (mouse_last_x - e.clientX) * (mouse_last_x - e.clientX) + (mouse_last_y - e.clientY) * (mouse_last_y - e.clientY);
1531
if (mouse_presentation_path == null)
1536
if (dist >= mouse_min_dist_sqr)
1538
var p = calcCoord(e);
1539
var d = mouse_presentation_path.getAttribute("d");
1540
d += " L" + p.x + "," + p.y;
1541
mouse_presentation_path.setAttribute("d", d);
1542
mouse_original_path.setAttribute("d", d);
1543
mouse_last_x = e.clientX;
1544
mouse_last_y = e.clientY;
1550
/** Event handler for mouse wheel events in slide mode.
1551
* based on http://adomas.org/javascript-mouse-wheel/
1553
* @param e the event
1555
function slideMousewheel(e)
1564
delta = e.wheelDelta/120;
1568
delta = -e.detail/3;
1576
if (e.preventDefault)
1579
e.returnValue = false;
1582
/** Event handler for mouse wheel events in index mode.
1583
* based on http://adomas.org/javascript-mouse-wheel/
1585
* @param e the event
1587
function indexMousewheel(e)
1596
delta = e.wheelDelta/120;
1600
delta = -e.detail/3;
1604
indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);
1606
indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);
1608
if (e.preventDefault)
1611
e.returnValue = false;
1614
/** Function to set the path paint width.
1616
function set_path_paint_width()
1618
var svgPoint1 = document.documentElement.createSVGPoint();
1619
var svgPoint2 = document.documentElement.createSVGPoint();
1626
var matrix = slides[activeSlide]["element"].getTransformToElement(ROOT_NODE);
1628
if (slides[activeSlide]["viewGroup"])
1629
matrix = slides[activeSlide]["viewGroup"].getTransformToElement(ROOT_NODE);
1631
svgPoint1 = svgPoint1.matrixTransform(matrix);
1632
svgPoint2 = svgPoint2.matrixTransform(matrix);
1634
path_paint_width = path_width / Math.sqrt((svgPoint2.x - svgPoint1.x) * (svgPoint2.x - svgPoint1.x) + (svgPoint2.y - svgPoint1.y) * (svgPoint2.y - svgPoint1.y));
1637
/** The view effect.
1639
* @param dir direction the effect should be played (1 = forwards, -1 = backwards)
1640
* @param element the element the effect should be applied to
1641
* @param time the time that has elapsed since the beginning of the effect
1642
* @param options a dictionary with additional options (e.g. length of the effect); for the view effect the options need to contain the old and the new matrix.
1644
function view(dir, element, time, options)
1649
if (!options["matrixInitial"])
1651
var tempString = slides[activeSlide]["viewGroup"].getAttribute("transform");
1654
options["matrixInitial"] = (new matrixSVG()).fromAttribute(tempString);
1656
options["matrixInitial"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
1659
if ((time == STATE_END) || (time == STATE_START))
1663
if (options && options["length"])
1664
length = options["length"];
1666
fraction = time / length;
1673
element.setAttribute("transform", options["matrixInitial"].toAttribute());
1675
else if (fraction >= 1)
1677
element.setAttribute("transform", options["matrixNew"].toAttribute());
1679
set_path_paint_width();
1681
options["matrixInitial"] = null;
1686
element.setAttribute("transform", options["matrixInitial"].mix(options["matrixNew"], fraction).toAttribute());
1693
element.setAttribute("transform", options["matrixInitial"].toAttribute());
1695
else if (fraction >= 1)
1697
element.setAttribute("transform", options["matrixOld"].toAttribute());
1698
set_path_paint_width();
1700
options["matrixInitial"] = null;
1705
element.setAttribute("transform", options["matrixInitial"].mix(options["matrixOld"], fraction).toAttribute());
1712
/** The fade effect.
1714
* @param dir direction the effect should be played (1 = forwards, -1 = backwards)
1715
* @param element the element the effect should be applied to
1716
* @param time the time that has elapsed since the beginning of the effect
1717
* @param options a dictionary with additional options (e.g. length of the effect)
1719
function fade(dir, element, time, options)
1724
if ((time == STATE_END) || (time == STATE_START))
1728
if (options && options["length"])
1729
length = options["length"];
1731
fraction = time / length;
1738
element.style.display = "none";
1739
element.setAttribute("opacity", 0);
1741
else if (fraction >= 1)
1743
element.style.display = "inherit";
1744
element.setAttribute("opacity", 1);
1749
element.style.display = "inherit";
1750
element.setAttribute("opacity", fraction);
1757
element.style.display = "inherit";
1758
element.setAttribute("opacity", 1);
1760
else if (fraction >= 1)
1762
element.setAttribute("opacity", 0);
1763
element.style.display = "none";
1768
element.style.display = "inherit";
1769
element.setAttribute("opacity", 1 - fraction);
1775
/** The appear effect.
1777
* @param dir direction the effect should be played (1 = forwards, -1 = backwards)
1778
* @param element the element the effect should be applied to
1779
* @param time the time that has elapsed since the beginning of the effect
1780
* @param options a dictionary with additional options (e.g. length of the effect)
1782
function appear(dir, element, time, options)
1786
element.style.display = "inherit";
1787
element.setAttribute("opacity",1);
1791
element.style.display = "none";
1792
element.setAttribute("opacity",0);
1799
* @param dir direction the effect should be played (1 = forwards, -1 = backwards)
1800
* @param element the element the effect should be applied to
1801
* @param time the time that has elapsed since the beginning of the effect
1802
* @param options a dictionary with additional options (e.g. length of the effect)
1804
function pop(dir, element, time, options)
1809
if ((time == STATE_END) || (time == STATE_START))
1813
if (options && options["length"])
1814
length = options["length"];
1816
fraction = time / length;
1823
element.setAttribute("opacity", 0);
1824
element.setAttribute("transform", "scale(0)");
1825
element.style.display = "none";
1827
else if (fraction >= 1)
1829
element.setAttribute("opacity", 1);
1830
element.removeAttribute("transform");
1831
element.style.display = "inherit";
1836
element.style.display = "inherit";
1837
var opacityFraction = fraction * 3;
1838
if (opacityFraction > 1)
1839
opacityFraction = 1;
1840
element.setAttribute("opacity", opacityFraction);
1841
var offsetX = WIDTH * (1.0 - fraction) / 2.0;
1842
var offsetY = HEIGHT * (1.0 - fraction) / 2.0;
1843
element.setAttribute("transform", "translate(" + offsetX + "," + offsetY + ") scale(" + fraction + ")");
1850
element.setAttribute("opacity", 1);
1851
element.setAttribute("transform", "scale(1)");
1852
element.style.display = "inherit";
1854
else if (fraction >= 1)
1856
element.setAttribute("opacity", 0);
1857
element.removeAttribute("transform");
1858
element.style.display = "none";
1863
element.setAttribute("opacity", 1 - fraction);
1864
element.setAttribute("transform", "scale(" + 1 - fraction + ")");
1865
element.style.display = "inherit";
1871
/** Function to set a slide either to the start or the end state.
1873
* @param slide the slide to use
1874
* @param state the state into which the slide should be set
1876
function setSlideToState(slide, state)
1878
slides[slide]["viewGroup"].setAttribute("transform", slides[slide].initialView);
1880
if (slides[slide]["effects"])
1882
if (state == STATE_END)
1884
for (var counter = 0; counter < slides[slide]["effects"].length; counter++)
1886
for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
1888
var effect = slides[slide]["effects"][counter][subCounter];
1889
if (effect["effect"] == "fade")
1890
fade(effect["dir"], effect["element"], STATE_END, effect["options"]);
1891
else if (effect["effect"] == "appear")
1892
appear(effect["dir"], effect["element"], STATE_END, effect["options"]);
1893
else if (effect["effect"] == "pop")
1894
pop(effect["dir"], effect["element"], STATE_END, effect["options"]);
1895
else if (effect["effect"] == "view")
1896
view(effect["dir"], effect["element"], STATE_END, effect["options"]);
1900
else if (state == STATE_START)
1902
for (var counter = slides[slide]["effects"].length - 1; counter >= 0; counter--)
1904
for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
1906
var effect = slides[slide]["effects"][counter][subCounter];
1907
if (effect["effect"] == "fade")
1908
fade(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);
1909
else if (effect["effect"] == "appear")
1910
appear(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);
1911
else if (effect["effect"] == "pop")
1912
pop(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);
1913
else if (effect["effect"] == "view")
1914
view(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);
1920
setSlideToState(slide, STATE_START);
1922
for (var counter = 0; counter < slides[slide]["effects"].length && counter < state; counter++)
1924
for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
1926
var effect = slides[slide]["effects"][counter][subCounter];
1927
if (effect["effect"] == "fade")
1928
fade(effect["dir"], effect["element"], STATE_END, effect["options"]);
1929
else if (effect["effect"] == "appear")
1930
appear(effect["dir"], effect["element"], STATE_END, effect["options"]);
1931
else if (effect["effect"] == "pop")
1932
pop(effect["dir"], effect["element"], STATE_END, effect["options"]);
1933
else if (effect["effect"] == "view")
1934
view(effect["dir"], effect["element"], STATE_END, effect["options"]);
1940
window.location.hash = (activeSlide + 1) + '_' + activeEffect;
1943
/** Convenience function to translate a attribute string into a dictionary.
1945
* @param str the attribute string
1946
* @return a dictionary
1947
* @see dictToPropStr
1949
function propStrToDict(str)
1951
var list = str.split(";");
1952
var obj = new Object();
1954
for (var counter = 0; counter < list.length; counter++)
1956
var subStr = list[counter];
1957
var subList = subStr.split(":");
1958
if (subList.length == 2)
1960
obj[subList[0]] = subList[1];
1967
/** Convenience function to translate a dictionary into a string that can be used as an attribute.
1969
* @param dict the dictionary to convert
1970
* @return a string that can be used as an attribute
1971
* @see propStrToDict
1973
function dictToPropStr(dict)
1977
for (var key in dict)
1979
str += key + ":" + dict[key] + ";";
1985
/** Sub-function to add a suffix to the ids of the node and all its children.
1987
* @param node the node to change
1988
* @param suffix the suffix to add
1989
* @param replace dictionary of replaced ids
1990
* @see suffixNodeIds
1992
function suffixNoneIds_sub(node, suffix, replace)
1994
if (node.nodeType == 1)
1996
if (node.getAttribute("id"))
1998
var id = node.getAttribute("id")
1999
replace["#" + id] = id + suffix;
2000
node.setAttribute("id", id + suffix);
2003
if ((node.nodeName == "use") && (node.getAttributeNS(NSS["xlink"], "href")) && (replace[node.getAttribute(NSS["xlink"], "href")]))
2004
node.setAttribute(NSS["xlink"], "href", node.getAttribute(NSS["xlink"], "href") + suffix);
2006
if (node.childNodes)
2008
for (var counter = 0; counter < node.childNodes.length; counter++)
2009
suffixNoneIds_sub(node.childNodes[counter], suffix, replace);
2014
/** Function to add a suffix to the ids of the node and all its children.
2016
* @param node the node to change
2017
* @param suffix the suffix to add
2018
* @return the changed node
2019
* @see suffixNodeIds_sub
2021
function suffixNodeIds(node, suffix)
2023
var replace = new Object();
2025
suffixNoneIds_sub(node, suffix, replace);
2030
/** Function to build a progress bar.
2032
* @param parent node to attach the progress bar to
2034
function createProgressBar(parent_node)
2036
var g = document.createElementNS(NSS["svg"], "g");
2037
g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
2038
g.setAttribute("id", "layer_progress_bar");
2039
g.setAttribute("style", "display: none;");
2041
var rect_progress_bar = document.createElementNS(NSS["svg"], "rect");
2042
rect_progress_bar.setAttribute("style", "marker: none; fill: rgb(128, 128, 128); stroke: none;");
2043
rect_progress_bar.setAttribute("id", "rect_progress_bar");
2044
rect_progress_bar.setAttribute("x", 0);
2045
rect_progress_bar.setAttribute("y", 0.99 * HEIGHT);
2046
rect_progress_bar.setAttribute("width", 0);
2047
rect_progress_bar.setAttribute("height", 0.01 * HEIGHT);
2048
g.appendChild(rect_progress_bar);
2050
var circle_timer_indicator = document.createElementNS(NSS["svg"], "circle");
2051
circle_timer_indicator.setAttribute("style", "marker: none; fill: rgb(255, 0, 0); stroke: none;");
2052
circle_timer_indicator.setAttribute("id", "circle_timer_indicator");
2053
circle_timer_indicator.setAttribute("cx", 0.005 * HEIGHT);
2054
circle_timer_indicator.setAttribute("cy", 0.995 * HEIGHT);
2055
circle_timer_indicator.setAttribute("r", 0.005 * HEIGHT);
2056
g.appendChild(circle_timer_indicator);
2058
parent_node.appendChild(g);
2061
/** Function to hide the progress bar.
2064
function hideProgressBar()
2066
var progress_bar = document.getElementById("layer_progress_bar");
2073
progress_bar.setAttribute("style", "display: none;");
2076
/** Function to show the progress bar.
2079
function showProgressBar()
2081
var progress_bar = document.getElementById("layer_progress_bar");
2088
progress_bar.setAttribute("style", "display: inherit;");
2091
/** Set progress bar value.
2093
* @param value the current slide number
2096
function setProgressBarValue(value)
2098
var rect_progress_bar = document.getElementById("rect_progress_bar");
2100
if (!rect_progress_bar)
2107
// First slide, assumed to be the title of the presentation
2109
var w = 0.01 * HEIGHT;
2111
else if (value >= slides.length - 1)
2113
// Last slide, assumed to be the end of the presentation
2114
var x = WIDTH - 0.01 * HEIGHT;
2115
var w = 0.01 * HEIGHT;
2120
value /= (slides.length - 2);
2122
var x = WIDTH * value;
2123
var w = WIDTH / (slides.length - 2);
2126
rect_progress_bar.setAttribute("x", x);
2127
rect_progress_bar.setAttribute("width", w);
2130
/** Set time indicator.
2132
* @param value the percentage of time elapse so far between 0.0 and 1.0
2135
function setTimeIndicatorValue(value)
2137
var circle_timer_indicator = document.getElementById("circle_timer_indicator");
2139
if (!circle_timer_indicator)
2154
var cx = (WIDTH - 0.01 * HEIGHT) * value + 0.005 * HEIGHT;
2155
circle_timer_indicator.setAttribute("cx", cx);
2161
function updateTimer()
2164
setTimeIndicatorValue((timer_elapsed - timer_start) / (60 * timer_duration));
2167
/** Convert screen coordinates to document coordinates.
2169
* @param e event with screen coordinates
2171
* @return coordinates in SVG file coordinate system
2173
function calcCoord(e)
2175
var svgPoint = document.documentElement.createSVGPoint();
2176
svgPoint.x = e.clientX + window.pageXOffset;
2177
svgPoint.y = e.clientY + window.pageYOffset;
2179
var matrix = slides[activeSlide]["element"].getScreenCTM();
2181
if (slides[activeSlide]["viewGroup"])
2182
matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
2184
svgPoint = svgPoint.matrixTransform(matrix.inverse());
2190
* @param after_slide after which slide the new slide should be inserted into the presentation
2192
function addSlide(after_slide)
2194
number_of_added_slides++;
2196
var g = document.createElementNS(NSS["svg"], "g");
2197
g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
2198
g.setAttribute("id", "Whiteboard " + Date() + " presentation copy");
2199
g.setAttribute("style", "display: none;");
2201
var new_slide = new Object();
2202
new_slide["element"] = g;
2204
// Set build in transition.
2205
new_slide["transitionIn"] = new Object();
2206
var dict = defaultTransitionInDict;
2207
new_slide["transitionIn"]["name"] = dict["name"];
2208
new_slide["transitionIn"]["options"] = new Object();
2212
new_slide["transitionIn"]["options"][key] = dict[key];
2214
// Set build out transition.
2215
new_slide["transitionOut"] = new Object();
2216
dict = defaultTransitionOutDict;
2217
new_slide["transitionOut"]["name"] = dict["name"];
2218
new_slide["transitionOut"]["options"] = new Object();
2222
new_slide["transitionOut"]["options"][key] = dict[key];
2224
// Copy master slide content.
2227
var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + Date() + " presentation_copy");
2228
clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode");
2229
clonedNode.removeAttributeNS(NSS["inkscape"], "label");
2230
clonedNode.style.display = "inherit";
2232
g.appendChild(clonedNode);
2235
// Substitute auto texts.
2236
substituteAutoTexts(g, "Whiteboard " + number_of_added_slides, "W" + number_of_added_slides, slides.length);
2238
g.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + (after_slide + 1) + ")) { indexSetActiveSlide(" + (after_slide + 1) + "); };");
2240
// Create a transform group.
2241
var transformGroup = document.createElementNS(NSS["svg"], "g");
2243
// Add content to transform group.
2244
while (g.firstChild)
2245
transformGroup.appendChild(g.firstChild);
2247
// Transfer the transform attribute from the node to the transform group.
2248
if (g.getAttribute("transform"))
2250
transformGroup.setAttribute("transform", g.getAttribute("transform"));
2251
g.removeAttribute("transform");
2254
// Create a view group.
2255
var viewGroup = document.createElementNS(NSS["svg"], "g");
2257
viewGroup.appendChild(transformGroup);
2258
new_slide["viewGroup"] = g.appendChild(viewGroup);
2260
// Insert background.
2261
if (BACKGROUND_COLOR != null)
2263
var rectNode = document.createElementNS(NSS["svg"], "rect");
2265
rectNode.setAttribute("x", 0);
2266
rectNode.setAttribute("y", 0);
2267
rectNode.setAttribute("width", WIDTH);
2268
rectNode.setAttribute("height", HEIGHT);
2269
rectNode.setAttribute("id", "jessyInkBackground" + Date());
2270
rectNode.setAttribute("fill", BACKGROUND_COLOR);
2272
new_slide["viewGroup"].insertBefore(rectNode, new_slide["viewGroup"].firstChild);
2275
// Set initial view even if there are no other views.
2276
var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
2278
new_slide["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
2279
new_slide.initialView = matrixOld.toAttribute();
2282
var node = slides[after_slide]["element"];
2283
var next_node = node.nextSibling;
2284
var parent_node = node.parentNode;
2288
parent_node.insertBefore(g, next_node);
2292
parent_node.appendChild(g);
2295
g = document.createElementNS(NSS["svg"], "g");
2296
g.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
2297
g.setAttributeNS(NSS["inkscape"], "label", "Whiteboard " + number_of_added_slides);
2298
g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
2299
g.setAttribute("id", "Whiteboard " + Date());
2300
g.setAttribute("style", "display: none;");
2302
new_slide["original_element"] = g;
2304
node = slides[after_slide]["original_element"];
2305
next_node = node.nextSibling;
2306
parent_node = node.parentNode;
2310
parent_node.insertBefore(g, next_node);
2314
parent_node.appendChild(g);
2317
before_new_slide = slides.slice(0, after_slide + 1);
2318
after_new_slide = slides.slice(after_slide + 1);
2319
slides = before_new_slide.concat(new_slide, after_new_slide);
2321
//resetting the counter attributes on the slides that follow the new slide...
2322
for (var counter = after_slide+2; counter < slides.length; counter++)
2324
slides[counter]["element"].setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
2328
/** Convenience function to obtain a transformation matrix from a point matrix.
2330
* @param mPoints Point matrix.
2331
* @return A transformation matrix.
2333
function pointMatrixToTransformation(mPoints)
2335
mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
2337
return mPointsOld.mult(mPoints.inv());
2340
/** Convenience function to obtain a matrix with three corners of a rectangle.
2342
* @param rect an svg rectangle
2343
* @return a matrixSVG containing three corners of the rectangle
2345
function rectToMatrix(rect)
2347
rectWidth = rect.getBBox().width;
2348
rectHeight = rect.getBBox().height;
2349
rectX = rect.getBBox().x;
2350
rectY = rect.getBBox().y;
2354
scaleX = WIDTH / rectWidth;
2355
scaleY = HEIGHT / rectHeight;
2357
if (scaleX > scaleY)
2360
rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
2361
rectWidth = WIDTH / scaleX;
2366
rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
2367
rectHeight = HEIGHT / scaleY;
2370
if (rect.transform.baseVal.numberOfItems < 1)
2372
mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
2376
mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
2379
newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
2380
newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
2382
return mRectTrans.mult(newBasePoints.add(newVectors));
2385
/** Function to handle JessyInk elements.
2387
* @param node Element node.
2389
function handleElement(node)
2391
if (node.getAttributeNS(NSS['jessyink'], 'element') == 'core.video')
2400
var tspans = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "tspan");
2402
for (var tspanCounter = 0; tspanCounter < tspans.length; tspanCounter++)
2404
if (tspans[tspanCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "url")
2406
url = tspans[tspanCounter].firstChild.nodeValue;
2410
var rects = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "rect");
2412
for (var rectCounter = 0; rectCounter < rects.length; rectCounter++)
2414
if (rects[rectCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "rect")
2416
x = rects[rectCounter].getAttribute("x");
2417
y = rects[rectCounter].getAttribute("y");
2418
width = rects[rectCounter].getAttribute("width");
2419
height = rects[rectCounter].getAttribute("height");
2420
transform = rects[rectCounter].getAttribute("transform");
2424
for (var childCounter = 0; childCounter < node.childNodes.length; childCounter++)
2426
if (node.childNodes[childCounter].nodeType == 1)
2428
if (node.childNodes[childCounter].style)
2430
node.childNodes[childCounter].style.display = 'none';
2434
node.childNodes[childCounter].setAttribute("style", "display: none;");
2439
var foreignNode = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
2440
foreignNode.setAttribute("x", x);
2441
foreignNode.setAttribute("y", y);
2442
foreignNode.setAttribute("width", width);
2443
foreignNode.setAttribute("height", height);
2444
foreignNode.setAttribute("transform", transform);
2446
var videoNode = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
2447
videoNode.setAttribute("src", url);
2449
foreignNode.appendChild(videoNode);
2450
node.appendChild(foreignNode);
2454
/** Class processing the location hash.
2456
* @param str location hash
2458
function LocationHash(str)
2460
this.slideNumber = 0;
2461
this.effectNumber = 0;
2463
str = str.substr(1, str.length - 1);
2465
var parts = str.split('_');
2467
// Try to extract slide number.
2468
if (parts.length >= 1)
2472
var slideNumber = parseInt(parts[0]);
2474
if (!isNaN(slideNumber))
2476
this.slideNumber = slideNumber - 1;
2484
// Try to extract effect number.
2485
if (parts.length >= 2)
2489
var effectNumber = parseInt(parts[1]);
2491
if (!isNaN(effectNumber))
2493
this.effectNumber = effectNumber;
2502
/** Class representing an svg matrix.
2504
function matrixSVG()
2517
/** Constructor function.
2519
* @param a element a (i.e. 1, 1) as described in the svg standard.
2520
* @param b element b (i.e. 2, 1) as described in the svg standard.
2521
* @param c element c (i.e. 1, 2) as described in the svg standard.
2522
* @param d element d (i.e. 2, 2) as described in the svg standard.
2523
* @param e element e (i.e. 1, 3) as described in the svg standard.
2524
* @param f element f (i.e. 2, 3) as described in the svg standard.
2526
matrixSVG.prototype.fromSVGElements = function(a, b, c, d, e, f)
2541
/** Constructor function.
2543
* @param matrix an svg matrix as described in the svg standard.
2545
matrixSVG.prototype.fromSVGMatrix = function(m)
2560
/** Constructor function.
2562
* @param e11 element 1, 1 of the matrix.
2563
* @param e12 element 1, 2 of the matrix.
2564
* @param e13 element 1, 3 of the matrix.
2565
* @param e21 element 2, 1 of the matrix.
2566
* @param e22 element 2, 2 of the matrix.
2567
* @param e23 element 2, 3 of the matrix.
2568
* @param e31 element 3, 1 of the matrix.
2569
* @param e32 element 3, 2 of the matrix.
2570
* @param e33 element 3, 3 of the matrix.
2572
matrixSVG.prototype.fromElements = function(e11, e12, e13, e21, e22, e23, e31, e32, e33)
2587
/** Constructor function.
2589
* @param attrString string value of the "transform" attribute (currently only "matrix" is accepted)
2591
matrixSVG.prototype.fromAttribute = function(attrString)
2593
str = attrString.substr(7, attrString.length - 8);
2597
strArray = str.split(",");
2599
// Opera does not use commas to separate the values of the matrix, only spaces.
2600
if (strArray.length != 6)
2601
strArray = str.split(" ");
2603
this.e11 = parseFloat(strArray[0]);
2604
this.e21 = parseFloat(strArray[1]);
2606
this.e12 = parseFloat(strArray[2]);
2607
this.e22 = parseFloat(strArray[3]);
2609
this.e13 = parseFloat(strArray[4]);
2610
this.e23 = parseFloat(strArray[5]);
2618
* @return a string that can be used as the "transform" attribute.
2620
matrixSVG.prototype.toAttribute = function()
2622
return "matrix(" + this.e11 + ", " + this.e21 + ", " + this.e12 + ", " + this.e22 + ", " + this.e13 + ", " + this.e23 + ")";
2625
/** Matrix nversion.
2627
* @return the inverse of the matrix
2629
matrixSVG.prototype.inv = function()
2631
out = new matrixSVG();
2633
det = this.e11 * (this.e33 * this.e22 - this.e32 * this.e23) - this.e21 * (this.e33 * this.e12 - this.e32 * this.e13) + this.e31 * (this.e23 * this.e12 - this.e22 * this.e13);
2635
out.e11 = (this.e33 * this.e22 - this.e32 * this.e23) / det;
2636
out.e12 = -(this.e33 * this.e12 - this.e32 * this.e13) / det;
2637
out.e13 = (this.e23 * this.e12 - this.e22 * this.e13) / det;
2638
out.e21 = -(this.e33 * this.e21 - this.e31 * this.e23) / det;
2639
out.e22 = (this.e33 * this.e11 - this.e31 * this.e13) / det;
2640
out.e23 = -(this.e23 * this.e11 - this.e21 * this.e13) / det;
2641
out.e31 = (this.e32 * this.e21 - this.e31 * this.e22) / det;
2642
out.e32 = -(this.e32 * this.e11 - this.e31 * this.e12) / det;
2643
out.e33 = (this.e22 * this.e11 - this.e21 * this.e12) / det;
2648
/** Matrix multiplication.
2650
* @param op another svg matrix
2653
matrixSVG.prototype.mult = function(op)
2655
out = new matrixSVG();
2657
out.e11 = this.e11 * op.e11 + this.e12 * op.e21 + this.e13 * op.e31;
2658
out.e12 = this.e11 * op.e12 + this.e12 * op.e22 + this.e13 * op.e32;
2659
out.e13 = this.e11 * op.e13 + this.e12 * op.e23 + this.e13 * op.e33;
2660
out.e21 = this.e21 * op.e11 + this.e22 * op.e21 + this.e23 * op.e31;
2661
out.e22 = this.e21 * op.e12 + this.e22 * op.e22 + this.e23 * op.e32;
2662
out.e23 = this.e21 * op.e13 + this.e22 * op.e23 + this.e23 * op.e33;
2663
out.e31 = this.e31 * op.e11 + this.e32 * op.e21 + this.e33 * op.e31;
2664
out.e32 = this.e31 * op.e12 + this.e32 * op.e22 + this.e33 * op.e32;
2665
out.e33 = this.e31 * op.e13 + this.e32 * op.e23 + this.e33 * op.e33;
2670
/** Matrix addition.
2672
* @param op another svg matrix
2675
matrixSVG.prototype.add = function(op)
2677
out = new matrixSVG();
2679
out.e11 = this.e11 + op.e11;
2680
out.e12 = this.e12 + op.e12;
2681
out.e13 = this.e13 + op.e13;
2682
out.e21 = this.e21 + op.e21;
2683
out.e22 = this.e22 + op.e22;
2684
out.e23 = this.e23 + op.e23;
2685
out.e31 = this.e31 + op.e31;
2686
out.e32 = this.e32 + op.e32;
2687
out.e33 = this.e33 + op.e33;
2694
* @param op another svg matrix
2695
* @parma contribOp contribution of the other matrix (0 <= contribOp <= 1)
2696
* @return (1 - contribOp) * this + contribOp * op
2698
matrixSVG.prototype.mix = function(op, contribOp)
2700
contribThis = 1.0 - contribOp;
2701
out = new matrixSVG();
2703
out.e11 = contribThis * this.e11 + contribOp * op.e11;
2704
out.e12 = contribThis * this.e12 + contribOp * op.e12;
2705
out.e13 = contribThis * this.e13 + contribOp * op.e13;
2706
out.e21 = contribThis * this.e21 + contribOp * op.e21;
2707
out.e22 = contribThis * this.e22 + contribOp * op.e22;
2708
out.e23 = contribThis * this.e23 + contribOp * op.e23;
2709
out.e31 = contribThis * this.e31 + contribOp * op.e31;
2710
out.e32 = contribThis * this.e32 + contribOp * op.e32;
2711
out.e33 = contribThis * this.e33 + contribOp * op.e33;
2716
/** Trimming function for strings.
2718
String.prototype.trim = function()
2720
return this.replace(/^\s+|\s+$/g, '');