~ubuntu-branches/ubuntu/natty/inkscape/natty

« back to all changes in this revision

Viewing changes to share/extensions/jessyInk.js

  • Committer: Bazaar Package Importer
  • Author(s): Alex Valavanis
  • Date: 2010-09-12 19:44:58 UTC
  • mfrom: (1.1.12 upstream) (45.1.3 maverick)
  • Revision ID: james.westby@ubuntu.com-20100912194458-4sjwmbl7dlsrk5dc
Tags: 0.48.0-1ubuntu1
* Merge with Debian unstable (LP: #628048, LP: #401567, LP: #456248, 
  LP: #463602, LP: #591986)
* debian/control: 
  - Ubuntu maintainers
  - Promote python-lxml, python-numpy, python-uniconvertor to Recommends.
  - Demote pstoedit to Suggests (universe package).
  - Suggests ttf-dejavu instead of ttf-bitstream-vera (LP: #513319)
* debian/rules:
  - Run intltool-update on build (Ubuntu-specific).
  - Add translation domain to .desktop files (Ubuntu-specific).
* debian/dirs:
  - Add usr/share/pixmaps.  Allow inkscape.xpm installation
* drop 50-poppler-API.dpatch (now upstream)
* drop 51-paste-in-unwritable-directory.dpatch (now upstream) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
6
//
 
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.
 
11
//
 
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/.
 
14
 
 
15
// Set onload event handler.
 
16
window.onload = jessyInkInit;
 
17
 
 
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';
 
29
 
 
30
// Keycodes.
 
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
 
40
var SPACE_KEY = 32;
 
41
var ESCAPE_KEY = 27;
 
42
 
 
43
// Presentation modes.
 
44
var SLIDE_MODE = 1;
 
45
var INDEX_MODE = 2;
 
46
var DRAWING_MODE = 3;
 
47
 
 
48
// Mouse handler actions.
 
49
var MOUSE_UP = 1;
 
50
var MOUSE_DOWN = 2;
 
51
var MOUSE_MOVE = 3;
 
52
var MOUSE_WHEEL = 4;
 
53
 
 
54
// Parameters.
 
55
var ROOT_NODE = document.getElementsByTagNameNS(NSS["svg"], "svg")[0];
 
56
var HEIGHT = 0;
 
57
var WIDTH = 0;
 
58
var INDEX_COLUMNS_DEFAULT = 4;
 
59
var INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
 
60
var INDEX_OFFSET = 0;
 
61
var STATE_START = -1;
 
62
var STATE_END = -2;
 
63
var BACKGROUND_COLOR = null;
 
64
var slides = new Array();
 
65
 
 
66
// Initialisation.
 
67
var currentMode = SLIDE_MODE;
 
68
var masterSlide = null;
 
69
var activeSlide = 0;
 
70
var activeEffect = 0;
 
71
var timeStep = 30; // 40 ms equal 25 frames per second.
 
72
var lastFrameTime = null;
 
73
var processingEffect = false;
 
74
var transCounter = 0;
 
75
var effectArray = 0;
 
76
var defaultTransitionInDict = new Object();
 
77
defaultTransitionInDict["name"] = "appear";
 
78
var defaultTransitionOutDict = new Object();
 
79
defaultTransitionOutDict["name"] = "appear";
 
80
var jessyInkInitialised = false;
 
81
 
 
82
// Initialise char and key code dictionaries.
 
83
var charCodeDictionary = getDefaultCharCodeDictionary();
 
84
var keyCodeDictionary = getDefaultKeyCodeDictionary();
 
85
 
 
86
// Initialise mouse handler dictionary.
 
87
var mouseHandlerDictionary = getDefaultMouseHandlerDictionary();
 
88
 
 
89
var progress_bar_visible = false;
 
90
var timer_elapsed = 0;
 
91
var timer_start = timer_elapsed;
 
92
var timer_duration = 15; // 15 minutes
 
93
 
 
94
var history_counter = 0;
 
95
var history_original_elements = new Array();
 
96
var history_presentation_elements = new Array();
 
97
 
 
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;
 
107
 
 
108
var number_of_added_slides = 0;
 
109
 
 
110
/** Initialisation function.
 
111
 *  The whole presentation is set-up in this function.
 
112
 */
 
113
function jessyInkInit()
 
114
{
 
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)
 
119
                return;
 
120
 
 
121
        // Making the presentation scaleable.
 
122
        var VIEWBOX = ROOT_NODE.getAttribute("viewBox");
 
123
 
 
124
        if (VIEWBOX)
 
125
        {
 
126
                WIDTH = ROOT_NODE.viewBox.animVal.width;
 
127
                HEIGHT = ROOT_NODE.viewBox.animVal.height;
 
128
        }
 
129
        else
 
130
        {
 
131
                HEIGHT = parseFloat(ROOT_NODE.getAttribute("height"));
 
132
                WIDTH = parseFloat(ROOT_NODE.getAttribute("width"));
 
133
                ROOT_NODE.setAttribute("viewBox", "0 0 " + WIDTH + " " + HEIGHT);
 
134
        }
 
135
 
 
136
        ROOT_NODE.setAttribute("width", "100%");
 
137
        ROOT_NODE.setAttribute("height", "100%");
 
138
 
 
139
        // Setting the background color.
 
140
        var namedViews = document.getElementsByTagNameNS(NSS["sodipodi"], "namedview");
 
141
 
 
142
        for (var counter = 0; counter < namedViews.length; counter++)
 
143
        {
 
144
                if (namedViews[counter].hasAttribute("id") && namedViews[counter].hasAttribute("pagecolor"))
 
145
                {
 
146
                        if (namedViews[counter].getAttribute("id") == "base")
 
147
                        {
 
148
                                BACKGROUND_COLOR = namedViews[counter].getAttribute("pagecolor");
 
149
                                var newAttribute = "background-color:" + BACKGROUND_COLOR + ";";
 
150
 
 
151
                                if (ROOT_NODE.hasAttribute("style"))
 
152
                                        newAttribute += ROOT_NODE.getAttribute("style");
 
153
 
 
154
                                ROOT_NODE.setAttribute("style", newAttribute);
 
155
                        }
 
156
                }
 
157
        }
 
158
 
 
159
        // Defining clip-path.
 
160
        var defsNodes = document.getElementsByTagNameNS(NSS["svg"], "defs");
 
161
 
 
162
        if (defsNodes.length > 0)
 
163
        {
 
164
                var existingClipPath = document.getElementById("jessyInkSlideClipPath");
 
165
 
 
166
                if (!existingClipPath)
 
167
                {
 
168
                        var rectNode = document.createElementNS(NSS["svg"], "rect");
 
169
                        var clipPath = document.createElementNS(NSS["svg"], "clipPath");
 
170
 
 
171
                        rectNode.setAttribute("x", 0);
 
172
                        rectNode.setAttribute("y", 0);
 
173
                        rectNode.setAttribute("width", WIDTH);
 
174
                        rectNode.setAttribute("height", HEIGHT);
 
175
 
 
176
                        clipPath.setAttribute("id", "jessyInkSlideClipPath");
 
177
                        clipPath.setAttribute("clipPathUnits", "userSpaceOnUse");
 
178
 
 
179
                        clipPath.appendChild(rectNode);
 
180
                        defsNodes[0].appendChild(clipPath);
 
181
                }
 
182
        }
 
183
 
 
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;
 
188
 
 
189
        for (var counter = 0; counter < nodes.length; counter++)
 
190
        {
 
191
                if (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") && (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") == "layer"))
 
192
                {
 
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];
 
197
                        else
 
198
                                tempSlides.push(nodes[counter].getAttribute("id"));
 
199
                }
 
200
                else if (nodes[counter].getAttributeNS(NSS['jessyink'], 'element'))
 
201
                {
 
202
                        handleElement(nodes[counter]);
 
203
                }
 
204
        }
 
205
 
 
206
        // Hide master slide set default transitions.
 
207
        if (masterSlide)
 
208
        {
 
209
                masterSlide.style.display = "none";
 
210
 
 
211
                if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionIn"))
 
212
                        defaultTransitionInDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionIn"));
 
213
 
 
214
                if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionOut"))
 
215
                        defaultTransitionOutDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionOut"));
 
216
        }
 
217
 
 
218
        if (existingJessyInkPresentationLayer != null)
 
219
        {
 
220
                existingJessyInkPresentationLayer.parentNode.removeChild(existingJessyInkPresentationLayer);
 
221
        }
 
222
 
 
223
        // Set start slide.
 
224
        var hashObj = new LocationHash(window.location.hash);
 
225
 
 
226
        activeSlide = hashObj.slideNumber;
 
227
        activeEffect = hashObj.effectNumber;
 
228
 
 
229
        if (activeSlide < 0)
 
230
                activeSlide = 0;
 
231
        else if (activeSlide >= tempSlides.length)
 
232
                activeSlide = tempSlides.length - 1;
 
233
 
 
234
        var originalNode = document.getElementById(tempSlides[counter]);
 
235
 
 
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);
 
243
 
 
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++)
 
247
        {
 
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;
 
255
 
 
256
                // Set build in transition.
 
257
                slides[counter]["transitionIn"] = new Object();
 
258
 
 
259
                var dict;
 
260
 
 
261
                if (node.hasAttributeNS(NSS["jessyink"], "transitionIn"))
 
262
                        dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionIn"));
 
263
                else
 
264
                        dict = defaultTransitionInDict;
 
265
 
 
266
                slides[counter]["transitionIn"]["name"] = dict["name"];
 
267
                slides[counter]["transitionIn"]["options"] = new Object();
 
268
 
 
269
                for (key in dict)
 
270
                        if (key != "name")
 
271
                                slides[counter]["transitionIn"]["options"][key] = dict[key];
 
272
 
 
273
                // Set build out transition.
 
274
                slides[counter]["transitionOut"] = new Object();
 
275
 
 
276
                if (node.hasAttributeNS(NSS["jessyink"], "transitionOut"))
 
277
                        dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionOut"));
 
278
                else
 
279
                        dict = defaultTransitionOutDict;
 
280
 
 
281
                slides[counter]["transitionOut"]["name"] = dict["name"];
 
282
                slides[counter]["transitionOut"]["options"] = new Object();
 
283
 
 
284
                for (key in dict)
 
285
                        if (key != "name")
 
286
                                slides[counter]["transitionOut"]["options"][key] = dict[key];
 
287
 
 
288
                // Copy master slide content.
 
289
                if (masterSlide)
 
290
                {
 
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";
 
295
 
 
296
                        node.insertBefore(clonedNode, node.firstChild);
 
297
                }
 
298
 
 
299
                // Setting clip path.
 
300
                node.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
 
301
 
 
302
                // Substitute auto texts.
 
303
                substituteAutoTexts(node, node.getAttributeNS(NSS["inkscape"], "label"), counter + 1, tempSlides.length);
 
304
 
 
305
                node.removeAttributeNS(NSS["inkscape"], "groupmode");
 
306
                node.removeAttributeNS(NSS["inkscape"], "label");
 
307
 
 
308
                // Set effects.
 
309
                var tempEffects = new Array();
 
310
                var groups = new Object();
 
311
 
 
312
                for (var IOCounter = 0; IOCounter <= 1; IOCounter++)
 
313
                {
 
314
                        var propName = "";
 
315
                        var dir = 0;
 
316
 
 
317
                        if (IOCounter == 0)
 
318
                        {
 
319
                                propName = "effectIn";
 
320
                                dir = 1;
 
321
                        }
 
322
                        else if (IOCounter == 1)
 
323
                        {
 
324
                                propName = "effectOut";
 
325
                                dir = -1;
 
326
                        }
 
327
 
 
328
                        var effects = getElementsByPropertyNS(node, NSS["jessyink"], propName);
 
329
 
 
330
                        for (var effectCounter = 0; effectCounter < effects.length; effectCounter++)
 
331
                        {
 
332
                                var element = document.getElementById(effects[effectCounter]);
 
333
                                var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], propName));
 
334
 
 
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]))
 
338
                                {
 
339
                                        var newGroup = document.createElementNS(NSS["svg"], "g");
 
340
 
 
341
                                        element.parentNode.insertBefore(newGroup, element);
 
342
                                        newGroup.appendChild(element.parentNode.removeChild(element));
 
343
                                        groups[element.id] = newGroup;
 
344
                                }
 
345
 
 
346
                                var effectDict = new Object();
 
347
 
 
348
                                effectDict["effect"] = dict["name"];
 
349
                                effectDict["dir"] = dir;
 
350
                                effectDict["element"] = groups[element.id];
 
351
 
 
352
                                for (var option in dict)
 
353
                                {
 
354
                                        if ((option != "name") && (option != "order"))
 
355
                                        {
 
356
                                                if (!effectDict["options"])
 
357
                                                        effectDict["options"] = new Object();
 
358
 
 
359
                                                effectDict["options"][option] = dict[option];
 
360
                                        }
 
361
                                }
 
362
 
 
363
                                if (!tempEffects[dict["order"]])
 
364
                                        tempEffects[dict["order"]] = new Array();
 
365
 
 
366
                                tempEffects[dict["order"]][tempEffects[dict["order"]].length] = effectDict;
 
367
                        }
 
368
                }
 
369
 
 
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";
 
373
 
 
374
                // Create a transform group.
 
375
                var transformGroup = document.createElementNS(NSS["svg"], "g");
 
376
 
 
377
                // Add content to transform group.
 
378
                while (node.firstChild)
 
379
                        transformGroup.appendChild(node.firstChild);
 
380
 
 
381
                // Transfer the transform attribute from the node to the transform group.
 
382
                if (node.getAttribute("transform"))
 
383
                {
 
384
                        transformGroup.setAttribute("transform", node.getAttribute("transform"));
 
385
                        node.removeAttribute("transform");
 
386
                }
 
387
 
 
388
                // Create a view group.
 
389
                var viewGroup = document.createElementNS(NSS["svg"], "g");
 
390
 
 
391
                viewGroup.appendChild(transformGroup);
 
392
                slides[counter]["viewGroup"] = node.appendChild(viewGroup);
 
393
 
 
394
                // Insert background.
 
395
                if (BACKGROUND_COLOR != null)
 
396
                {
 
397
                        var rectNode = document.createElementNS(NSS["svg"], "rect");
 
398
 
 
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);
 
405
 
 
406
                        slides[counter]["viewGroup"].insertBefore(rectNode, slides[counter]["viewGroup"].firstChild);
 
407
                }
 
408
 
 
409
                // Set views.
 
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);
 
413
 
 
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();
 
417
 
 
418
                for (var viewCounter = 0; viewCounter < views.length; viewCounter++)
 
419
                {
 
420
                        var element = document.getElementById(views[viewCounter]);
 
421
                        var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], "view"));
 
422
 
 
423
                        if (dict["order"] == 0)
 
424
                        {
 
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();
 
427
                        }
 
428
                        else
 
429
                        {
 
430
                                var effectDict = new Object();
 
431
 
 
432
                                effectDict["effect"] = dict["name"];
 
433
                                effectDict["dir"] = 1;
 
434
                                effectDict["element"] = slides[counter]["viewGroup"];
 
435
                                effectDict["order"] = dict["order"];
 
436
 
 
437
                                for (var option in dict)
 
438
                                {
 
439
                                        if ((option != "name") && (option != "order"))
 
440
                                        {
 
441
                                                if (!effectDict["options"])
 
442
                                                        effectDict["options"] = new Object();
 
443
 
 
444
                                                effectDict["options"][option] = dict[option];
 
445
                                        }
 
446
                                }
 
447
 
 
448
                                effectDict["options"]["matrixNew"] = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv());
 
449
 
 
450
                                tempViews[dict["order"]] = effectDict;
 
451
                        }
 
452
 
 
453
                        // Remove element.
 
454
                        element.parentNode.removeChild(element);
 
455
                }
 
456
 
 
457
                // Consolidate view array and append it to the effect array.
 
458
                if (tempViews.length > 0)
 
459
                {
 
460
                        for (var viewCounter = 0; viewCounter < tempViews.length; viewCounter++)
 
461
                        {
 
462
                                if (tempViews[viewCounter])
 
463
                                {
 
464
                                        tempViews[viewCounter]["options"]["matrixOld"] = matrixOld;
 
465
                                        matrixOld = tempViews[viewCounter]["options"]["matrixNew"];
 
466
 
 
467
                                        if (!tempEffects[tempViews[viewCounter]["order"]])
 
468
                                                tempEffects[tempViews[viewCounter]["order"]] = new Array();
 
469
 
 
470
                                        tempEffects[tempViews[viewCounter]["order"]][tempEffects[tempViews[viewCounter]["order"]].length] = tempViews[viewCounter];
 
471
                                }
 
472
                        }
 
473
                }
 
474
 
 
475
                // Set consolidated effect array.
 
476
                if (tempEffects.length > 0)
 
477
                {
 
478
                        slides[counter]["effects"] = new Array();
 
479
 
 
480
                        for (var effectCounter = 0; effectCounter < tempEffects.length; effectCounter++)
 
481
                        {
 
482
                                if (tempEffects[effectCounter])
 
483
                                        slides[counter]["effects"][slides[counter]["effects"].length] = tempEffects[effectCounter];
 
484
                        }
 
485
                }
 
486
 
 
487
                node.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
 
488
 
 
489
                // Set visibility for initial state.
 
490
                if (counter == activeSlide)
 
491
                {
 
492
                        node.style.display = "inherit";
 
493
                        node.setAttribute("opacity",1);
 
494
                }
 
495
                else
 
496
                {
 
497
                        node.style.display = "none";
 
498
                        node.setAttribute("opacity",0);
 
499
                }
 
500
        }
 
501
 
 
502
        // Set key handler.
 
503
        var jessyInkObjects = document.getElementsByTagNameNS(NSS["svg"], "g");
 
504
 
 
505
        for (var counter = 0; counter < jessyInkObjects.length; counter++)
 
506
        {
 
507
                var elem = jessyInkObjects[counter];
 
508
 
 
509
                if (elem.getAttributeNS(NSS["jessyink"], "customKeyBindings"))
 
510
                {
 
511
                        if (elem.getCustomKeyBindings != undefined)
 
512
                                keyCodeDictionary = elem.getCustomKeyBindings();
 
513
 
 
514
                        if (elem.getCustomCharBindings != undefined)
 
515
                                charCodeDictionary = elem.getCustomCharBindings();
 
516
                }
 
517
        }
 
518
 
 
519
        // Set mouse handler.
 
520
        var jessyInkMouseHandler = document.getElementsByTagNameNS(NSS["jessyink"], "mousehandler");
 
521
 
 
522
        for (var counter = 0; counter < jessyInkMouseHandler.length; counter++)
 
523
        {
 
524
                var elem = jessyInkMouseHandler[counter];
 
525
 
 
526
                if (elem.getMouseHandler != undefined)
 
527
                {
 
528
                        var tempDict = elem.getMouseHandler();
 
529
 
 
530
                        for (mode in tempDict)
 
531
                        {
 
532
                                if (!mouseHandlerDictionary[mode])
 
533
                                        mouseHandlerDictionary[mode] = new Object();
 
534
 
 
535
                                for (handler in tempDict[mode])
 
536
                                        mouseHandlerDictionary[mode][handler] = tempDict[mode][handler];
 
537
                        }
 
538
                }
 
539
        }
 
540
 
 
541
        // Check effect number.
 
542
        if ((activeEffect < 0) || (!slides[activeSlide].effects))
 
543
        {
 
544
                activeEffect = 0;
 
545
        }
 
546
        else if (activeEffect > slides[activeSlide].effects.length)
 
547
        {
 
548
                activeEffect = slides[activeSlide].effects.length;
 
549
        }
 
550
 
 
551
        createProgressBar(JessyInkPresentationLayer);
 
552
        hideProgressBar();
 
553
        setProgressBarValue(activeSlide);
 
554
        setTimeIndicatorValue(0);
 
555
        setInterval("updateTimer()", 1000);
 
556
        setSlideToState(activeSlide, activeEffect);
 
557
        jessyInkInitialised = true;
 
558
}
 
559
 
 
560
/** Function to subtitute the auto-texts.
 
561
 *
 
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
 
566
 */
 
567
function substituteAutoTexts(node, slideName, slideNumber, numberOfSlides)
 
568
{
 
569
        var texts = node.getElementsByTagNameNS(NSS["svg"], "tspan");
 
570
 
 
571
        for (var textCounter = 0; textCounter < texts.length; textCounter++)
 
572
        {
 
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;
 
579
        }
 
580
}
 
581
 
 
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.
 
584
 *
 
585
 *  @param node the node
 
586
 *  @param namespace namespace of the attribute
 
587
 *  @param name attribute name
 
588
 */
 
589
function getElementsByPropertyNS(node, namespace, name)
 
590
{
 
591
        var elems = new Array();
 
592
 
 
593
        if (node.getAttributeNS(namespace, name))
 
594
                elems.push(node.getAttribute("id"));
 
595
 
 
596
        for (var counter = 0; counter < node.childNodes.length; counter++)
 
597
        {
 
598
                if (node.childNodes[counter].nodeType == 1)
 
599
                        elems = elems.concat(getElementsByPropertyNS(node.childNodes[counter], namespace, name));
 
600
        }
 
601
 
 
602
        return elems;
 
603
}
 
604
 
 
605
/** Function to dispatch the next effect, if there is none left, change the slide.
 
606
 *
 
607
 *  @param dir direction of the change (1 = forwards, -1 = backwards)
 
608
 */
 
609
function dispatchEffects(dir)
 
610
{
 
611
        if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
 
612
        {
 
613
                processingEffect = true;
 
614
 
 
615
                if (dir == 1)
 
616
                {
 
617
                        effectArray = slides[activeSlide]["effects"][activeEffect];
 
618
                        activeEffect += dir;
 
619
                }
 
620
                else if (dir == -1)
 
621
                {
 
622
                        activeEffect += dir;
 
623
                        effectArray = slides[activeSlide]["effects"][activeEffect];
 
624
                }
 
625
 
 
626
                transCounter = 0;
 
627
                startTime = (new Date()).getTime();
 
628
                lastFrameTime = null;
 
629
                effect(dir);
 
630
        }
 
631
        else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
 
632
        {
 
633
                changeSlide(dir);
 
634
        }
 
635
}
 
636
 
 
637
/** Function to skip effects and directly either put the slide into start or end state or change slides.
 
638
 *
 
639
 *  @param dir direction of the change (1 = forwards, -1 = backwards)
 
640
 */
 
641
function skipEffects(dir)
 
642
{
 
643
        if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
 
644
        {
 
645
                processingEffect = true;
 
646
 
 
647
                if (slides[activeSlide]["effects"] && (dir == 1))
 
648
                        activeEffect = slides[activeSlide]["effects"].length;
 
649
                else
 
650
                        activeEffect = 0;
 
651
 
 
652
                if (dir == 1)
 
653
                        setSlideToState(activeSlide, STATE_END);
 
654
                else
 
655
                        setSlideToState(activeSlide, STATE_START);
 
656
 
 
657
                processingEffect = false;
 
658
        }
 
659
        else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
 
660
        {
 
661
                changeSlide(dir);
 
662
        }
 
663
}
 
664
 
 
665
/** Function to change between slides.
 
666
 *
 
667
 *  @param dir direction (1 = forwards, -1 = backwards)
 
668
 */
 
669
function changeSlide(dir)
 
670
{
 
671
        processingEffect = true;
 
672
        effectArray = new Array();
 
673
 
 
674
        effectArray[0] = new Object();
 
675
        if (dir == 1)
 
676
        {
 
677
                effectArray[0]["effect"] = slides[activeSlide]["transitionOut"]["name"];
 
678
                effectArray[0]["options"] = slides[activeSlide]["transitionOut"]["options"];
 
679
                effectArray[0]["dir"] = -1;
 
680
        }
 
681
        else if (dir == -1)
 
682
        {
 
683
                effectArray[0]["effect"] = slides[activeSlide]["transitionIn"]["name"];
 
684
                effectArray[0]["options"] = slides[activeSlide]["transitionIn"]["options"];
 
685
                effectArray[0]["dir"] = 1;
 
686
        }
 
687
        effectArray[0]["element"] = slides[activeSlide]["element"];
 
688
 
 
689
        activeSlide += dir;
 
690
        setProgressBarValue(activeSlide);
 
691
 
 
692
        effectArray[1] = new Object();
 
693
 
 
694
        if (dir == 1)
 
695
        {
 
696
                effectArray[1]["effect"] = slides[activeSlide]["transitionIn"]["name"];
 
697
                effectArray[1]["options"] = slides[activeSlide]["transitionIn"]["options"];
 
698
                effectArray[1]["dir"] = 1;
 
699
        }
 
700
        else if (dir == -1)
 
701
        {
 
702
                effectArray[1]["effect"] = slides[activeSlide]["transitionOut"]["name"];
 
703
                effectArray[1]["options"] = slides[activeSlide]["transitionOut"]["options"];
 
704
                effectArray[1]["dir"] = -1;
 
705
        }
 
706
 
 
707
        effectArray[1]["element"] = slides[activeSlide]["element"];
 
708
 
 
709
        if (slides[activeSlide]["effects"] && (dir == -1))
 
710
                activeEffect = slides[activeSlide]["effects"].length;
 
711
        else
 
712
                activeEffect = 0;
 
713
 
 
714
        if (dir == -1)
 
715
                setSlideToState(activeSlide, STATE_END);
 
716
        else
 
717
                setSlideToState(activeSlide, STATE_START);
 
718
 
 
719
        transCounter = 0;
 
720
        startTime = (new Date()).getTime();
 
721
        lastFrameTime = null;
 
722
        effect(dir);
 
723
}
 
724
 
 
725
/** Function to toggle between index and slide mode.
 
726
*/
 
727
function toggleSlideIndex()
 
728
{
 
729
        var suspendHandle = ROOT_NODE.suspendRedraw(500);
 
730
 
 
731
        if (currentMode == SLIDE_MODE)
 
732
        {
 
733
                hideProgressBar();              
 
734
                INDEX_OFFSET = -1;
 
735
                indexSetPageSlide(activeSlide);
 
736
                currentMode = INDEX_MODE;
 
737
        }
 
738
        else if (currentMode == INDEX_MODE)
 
739
        {
 
740
                for (var counter = 0; counter < slides.length; counter++)
 
741
                {
 
742
                        slides[counter]["element"].setAttribute("transform","scale(1)");
 
743
 
 
744
                        if (counter == activeSlide)
 
745
                        {
 
746
                                slides[counter]["element"].style.display = "inherit";
 
747
                                slides[counter]["element"].setAttribute("opacity",1);
 
748
                                activeEffect = 0;
 
749
                        }
 
750
                        else
 
751
                        {
 
752
                                slides[counter]["element"].setAttribute("opacity",0);
 
753
                                slides[counter]["element"].style.display = "none";
 
754
                        }
 
755
                }
 
756
                currentMode = SLIDE_MODE;
 
757
                setSlideToState(activeSlide, STATE_START);
 
758
                setProgressBarValue(activeSlide);
 
759
 
 
760
                if (progress_bar_visible)
 
761
                {
 
762
                        showProgressBar();
 
763
                }
 
764
        }
 
765
 
 
766
        ROOT_NODE.unsuspendRedraw(suspendHandle);
 
767
        ROOT_NODE.forceRedraw();
 
768
}
 
769
 
 
770
/** Function to run an effect.
 
771
 *
 
772
 *  @param dir direction in which to play the effect (1 = forwards, -1 = backwards)
 
773
 */
 
774
function effect(dir)
 
775
{
 
776
        var done = true;
 
777
 
 
778
        var suspendHandle = ROOT_NODE.suspendRedraw(200);
 
779
 
 
780
        for (var counter = 0; counter < effectArray.length; counter++)
 
781
        {
 
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"]);
 
790
        }
 
791
 
 
792
        ROOT_NODE.unsuspendRedraw(suspendHandle);
 
793
        ROOT_NODE.forceRedraw();
 
794
 
 
795
        if (!done)
 
796
        {
 
797
                var currentTime = (new Date()).getTime();
 
798
                var timeDiff = 1;
 
799
 
 
800
                transCounter = currentTime - startTime;
 
801
 
 
802
                if (lastFrameTime != null)
 
803
                {
 
804
                        timeDiff = timeStep - (currentTime - lastFrameTime);
 
805
 
 
806
                        if (timeDiff <= 0)
 
807
                                timeDiff = 1;
 
808
                }
 
809
 
 
810
                lastFrameTime = currentTime;
 
811
 
 
812
                window.setTimeout("effect(" + dir + ")", timeDiff);
 
813
        }
 
814
        else
 
815
        {
 
816
                window.location.hash = (activeSlide + 1) + '_' + activeEffect;
 
817
                processingEffect = false;
 
818
        }
 
819
}
 
820
 
 
821
/** Function to display the index sheet.
 
822
 *
 
823
 *  @param offsetNumber offset number
 
824
 */
 
825
function displayIndex(offsetNumber)
 
826
{
 
827
        var offsetX = 0;
 
828
        var offsetY = 0;
 
829
 
 
830
        if (offsetNumber < 0)
 
831
                offsetNumber = 0;
 
832
        else if (offsetNumber >= slides.length)
 
833
                offsetNumber = slides.length - 1;
 
834
 
 
835
        for (var counter = 0; counter < slides.length; counter++)
 
836
        {
 
837
                if ((counter < offsetNumber) || (counter > offsetNumber + INDEX_COLUMNS * INDEX_COLUMNS - 1))
 
838
                {
 
839
                        slides[counter]["element"].setAttribute("opacity",0);
 
840
                        slides[counter]["element"].style.display = "none";
 
841
                }
 
842
                else
 
843
                {
 
844
                        offsetX = ((counter - offsetNumber) % INDEX_COLUMNS) * WIDTH;
 
845
                        offsetY = Math.floor((counter - offsetNumber) / INDEX_COLUMNS) * HEIGHT;
 
846
 
 
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);
 
850
                }
 
851
 
 
852
                setSlideToState(counter, STATE_END);
 
853
        }
 
854
 
 
855
        //do we need to save the current offset?
 
856
        if (INDEX_OFFSET != offsetNumber)
 
857
                INDEX_OFFSET = offsetNumber;
 
858
}
 
859
 
 
860
/** Function to set the active slide in the slide view.
 
861
 *
 
862
 *  @param nbr index of the active slide
 
863
 */
 
864
function slideSetActiveSlide(nbr)
 
865
{
 
866
        if (nbr >= slides.length)
 
867
                nbr = slides.length - 1;
 
868
        else if (nbr < 0)
 
869
                nbr = 0;
 
870
 
 
871
        slides[activeSlide]["element"].setAttribute("opacity",0);
 
872
        slides[activeSlide]["element"].style.display = "none";
 
873
 
 
874
        activeSlide = parseInt(nbr);
 
875
 
 
876
        setSlideToState(activeSlide, STATE_START);
 
877
        slides[activeSlide]["element"].style.display = "inherit";
 
878
        slides[activeSlide]["element"].setAttribute("opacity",1);
 
879
 
 
880
        activeEffect = 0;
 
881
        setProgressBarValue(nbr);
 
882
}
 
883
 
 
884
/** Function to set the active slide in the index view.
 
885
 *
 
886
 *  @param nbr index of the active slide
 
887
 */
 
888
function indexSetActiveSlide(nbr)
 
889
{
 
890
        if (nbr >= slides.length)
 
891
                nbr = slides.length - 1;
 
892
        else if (nbr < 0)
 
893
                nbr = 0;
 
894
 
 
895
        slides[activeSlide]["element"].setAttribute("opacity",0.5);
 
896
 
 
897
        activeSlide = parseInt(nbr);
 
898
        window.location.hash = (activeSlide + 1) + '_0';
 
899
 
 
900
        slides[activeSlide]["element"].setAttribute("opacity",1);
 
901
}
 
902
 
 
903
/** Function to set the page and active slide in index view. 
 
904
 *
 
905
 *  @param nbr index of the active slide
 
906
 *
 
907
 *  NOTE: To force a redraw,
 
908
 *  set INDEX_OFFSET to -1 before calling indexSetPageSlide().
 
909
 *
 
910
 *  This is necessary for zooming (otherwise the index might not
 
911
 *  get redrawn) and when switching to index mode.
 
912
 *
 
913
 *  INDEX_OFFSET = -1
 
914
 *  indexSetPageSlide(activeSlide);
 
915
 */
 
916
function indexSetPageSlide(nbr)
 
917
{
 
918
        if (nbr >= slides.length)
 
919
                nbr = slides.length - 1;
 
920
        else if (nbr < 0)
 
921
                nbr = 0;
 
922
 
 
923
        //calculate the offset
 
924
        var offset = nbr - nbr % (INDEX_COLUMNS * INDEX_COLUMNS);
 
925
 
 
926
        if (offset < 0)
 
927
                offset = 0;
 
928
 
 
929
        //if different from kept offset, then record and change the page
 
930
        if (offset != INDEX_OFFSET)
 
931
        {
 
932
                INDEX_OFFSET = offset;
 
933
                displayIndex(INDEX_OFFSET);
 
934
        }
 
935
 
 
936
        //set the active slide
 
937
        indexSetActiveSlide(nbr);
 
938
}
 
939
 
 
940
/** Event handler for key press.
 
941
 *
 
942
 *  @param e the event
 
943
 */
 
944
function keydown(e)
 
945
{
 
946
        if (!e)
 
947
                e = window.event;
 
948
 
 
949
        code = e.keyCode || e.charCode;
 
950
 
 
951
        if (!processingEffect && keyCodeDictionary[currentMode] && keyCodeDictionary[currentMode][code])
 
952
                return keyCodeDictionary[currentMode][code]();
 
953
        else
 
954
                document.onkeypress = keypress;
 
955
}
 
956
// Set event handler for key down.
 
957
document.onkeydown = keydown;
 
958
 
 
959
/** Event handler for key press.
 
960
 *
 
961
 *  @param e the event
 
962
 */
 
963
function keypress(e)
 
964
{
 
965
        document.onkeypress = null;
 
966
 
 
967
        if (!e)
 
968
                e = window.event;
 
969
 
 
970
        str = String.fromCharCode(e.keyCode || e.charCode);
 
971
 
 
972
        if (!processingEffect && charCodeDictionary[currentMode] && charCodeDictionary[currentMode][str])
 
973
                return charCodeDictionary[currentMode][str]();
 
974
}
 
975
 
 
976
/** Function to supply the default char code dictionary.
 
977
 *
 
978
 * @returns default char code dictionary
 
979
 */
 
980
function getDefaultCharCodeDictionary()
 
981
{
 
982
        var charCodeDict = new Object();
 
983
 
 
984
        charCodeDict[SLIDE_MODE] = new Object();
 
985
        charCodeDict[INDEX_MODE] = new Object();
 
986
        charCodeDict[DRAWING_MODE] = new Object();
 
987
 
 
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(); };
 
995
 
 
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(); };
 
1013
 
 
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(); };
 
1019
 
 
1020
        return charCodeDict;
 
1021
}
 
1022
 
 
1023
/** Function to supply the default key code dictionary.
 
1024
 *
 
1025
 * @returns default key code dictionary
 
1026
 */
 
1027
function getDefaultKeyCodeDictionary()
 
1028
{
 
1029
        var keyCodeDict = new Object();
 
1030
 
 
1031
        keyCodeDict[SLIDE_MODE] = new Object();
 
1032
        keyCodeDict[INDEX_MODE] = new Object();
 
1033
        keyCodeDict[DRAWING_MODE] = new Object();
 
1034
 
 
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); };
 
1044
 
 
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(); };
 
1054
 
 
1055
        keyCodeDict[DRAWING_MODE][ESCAPE_KEY] = function () { return drawingSwitchToSlideMode(); };
 
1056
 
 
1057
        return keyCodeDict;
 
1058
}
 
1059
 
 
1060
/** Function to handle all mouse events.
 
1061
 *
 
1062
 *      @param  evnt    event
 
1063
 *      @param  action  type of event (e.g. mouse up, mouse wheel)
 
1064
 */
 
1065
function mouseHandlerDispatch(evnt, action)
 
1066
{
 
1067
        if (!evnt)
 
1068
                evnt = window.event;
 
1069
 
 
1070
        var retVal = true;
 
1071
 
 
1072
        if (!processingEffect && mouseHandlerDictionary[currentMode] && mouseHandlerDictionary[currentMode][action])
 
1073
        {
 
1074
                var subRetVal = mouseHandlerDictionary[currentMode][action](evnt);
 
1075
 
 
1076
                if (subRetVal != null && subRetVal != undefined)
 
1077
                        retVal = subRetVal;
 
1078
        }
 
1079
 
 
1080
        if (evnt.preventDefault && !retVal)
 
1081
                evnt.preventDefault();
 
1082
 
 
1083
        evnt.returnValue = retVal;
 
1084
 
 
1085
        return retVal;
 
1086
}
 
1087
 
 
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); };
 
1092
 
 
1093
// Moz
 
1094
if (window.addEventListener)
 
1095
{
 
1096
        window.addEventListener('DOMMouseScroll', function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); }, false);
 
1097
}
 
1098
 
 
1099
// Opera Safari OK - may not work in IE
 
1100
window.onmousewheel = function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); };
 
1101
 
 
1102
/** Function to supply the default mouse handler dictionary.
 
1103
 *
 
1104
 * @returns default mouse handler dictionary
 
1105
 */
 
1106
function getDefaultMouseHandlerDictionary()
 
1107
{
 
1108
        var mouseHandlerDict = new Object();
 
1109
 
 
1110
        mouseHandlerDict[SLIDE_MODE] = new Object();
 
1111
        mouseHandlerDict[INDEX_MODE] = new Object();
 
1112
        mouseHandlerDict[DRAWING_MODE] = new Object();
 
1113
 
 
1114
        mouseHandlerDict[SLIDE_MODE][MOUSE_DOWN] = function(evnt) { return dispatchEffects(1); };
 
1115
        mouseHandlerDict[SLIDE_MODE][MOUSE_WHEEL] = function(evnt) { return slideMousewheel(evnt); };
 
1116
 
 
1117
        mouseHandlerDict[INDEX_MODE][MOUSE_DOWN] = function(evnt) { return toggleSlideIndex(); };
 
1118
 
 
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); };
 
1122
 
 
1123
        return mouseHandlerDict;
 
1124
}
 
1125
 
 
1126
/** Function to switch from slide mode to drawing mode.
 
1127
*/
 
1128
function slideSwitchToDrawingMode()
 
1129
{
 
1130
        currentMode = DRAWING_MODE;
 
1131
 
 
1132
        var tempDict;
 
1133
 
 
1134
        if (ROOT_NODE.hasAttribute("style"))
 
1135
                tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
 
1136
        else
 
1137
                tempDict = new Object();
 
1138
 
 
1139
        tempDict["cursor"] = "crosshair";
 
1140
        ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
 
1141
}
 
1142
 
 
1143
/** Function to switch from drawing mode to slide mode.
 
1144
*/
 
1145
function drawingSwitchToSlideMode()
 
1146
{
 
1147
        currentMode = SLIDE_MODE;
 
1148
 
 
1149
        var tempDict;
 
1150
 
 
1151
        if (ROOT_NODE.hasAttribute("style"))
 
1152
                tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
 
1153
        else
 
1154
                tempDict = new Object();
 
1155
 
 
1156
        tempDict["cursor"] = "auto";
 
1157
        ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
 
1158
}
 
1159
 
 
1160
/** Function to decrease the number of columns in index mode.
 
1161
*/
 
1162
function indexDecreaseNumberOfColumns()
 
1163
{
 
1164
        if (INDEX_COLUMNS >= 3)
 
1165
        {
 
1166
                INDEX_COLUMNS -= 1;
 
1167
                INDEX_OFFSET = -1
 
1168
                        indexSetPageSlide(activeSlide);
 
1169
        }
 
1170
}
 
1171
 
 
1172
/** Function to increase the number of columns in index mode.
 
1173
*/
 
1174
function indexIncreaseNumberOfColumns()
 
1175
{
 
1176
        if (INDEX_COLUMNS < 7)
 
1177
        {
 
1178
                INDEX_COLUMNS += 1;
 
1179
                INDEX_OFFSET = -1
 
1180
                        indexSetPageSlide(activeSlide);
 
1181
        }
 
1182
}
 
1183
 
 
1184
/** Function to reset the number of columns in index mode.
 
1185
*/
 
1186
function indexResetNumberOfColumns()
 
1187
{
 
1188
        if (INDEX_COLUMNS != INDEX_COLUMNS_DEFAULT)
 
1189
        {
 
1190
                INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
 
1191
                INDEX_OFFSET = -1
 
1192
                        indexSetPageSlide(activeSlide);
 
1193
        }
 
1194
}
 
1195
 
 
1196
/** Function to reset path width in drawing mode.
 
1197
*/
 
1198
function drawingResetPathWidth()
 
1199
{
 
1200
        path_width = path_width_default;
 
1201
        set_path_paint_width();
 
1202
}
 
1203
 
 
1204
/** Function to set path width in drawing mode.
 
1205
 *
 
1206
 * @param width new path width
 
1207
 */
 
1208
function drawingSetPathWidth(width)
 
1209
{
 
1210
        path_width = width;
 
1211
        set_path_paint_width();
 
1212
}
 
1213
 
 
1214
/** Function to set path colour in drawing mode.
 
1215
 *
 
1216
 * @param colour new path colour
 
1217
 */
 
1218
function drawingSetPathColour(colour)
 
1219
{
 
1220
        path_colour = colour;
 
1221
}
 
1222
 
 
1223
/** Function to query the duration of the presentation from the user in slide mode.
 
1224
*/
 
1225
function slideQueryDuration()
 
1226
{
 
1227
        var new_duration = prompt("Length of presentation in minutes?", timer_duration);
 
1228
 
 
1229
        if ((new_duration != null) && (new_duration != ''))
 
1230
        {
 
1231
                timer_duration = new_duration;
 
1232
        }
 
1233
 
 
1234
        updateTimer();
 
1235
}
 
1236
 
 
1237
/** Function to add new slide in slide mode.
 
1238
 *
 
1239
 * @param afterSlide after which slide to insert the new one
 
1240
 */
 
1241
function slideAddSlide(afterSlide)
 
1242
{
 
1243
        addSlide(afterSlide);
 
1244
        slideSetActiveSlide(afterSlide + 1);
 
1245
        updateTimer();
 
1246
}
 
1247
 
 
1248
/** Function to toggle the visibility of the progress bar in slide mode.
 
1249
*/
 
1250
function slideToggleProgressBarVisibility()
 
1251
{
 
1252
        if (progress_bar_visible)
 
1253
        {
 
1254
                progress_bar_visible = false;
 
1255
                hideProgressBar();
 
1256
        }
 
1257
        else
 
1258
        {
 
1259
                progress_bar_visible = true;
 
1260
                showProgressBar();
 
1261
        }
 
1262
}
 
1263
 
 
1264
/** Function to reset the timer in slide mode.
 
1265
*/
 
1266
function slideResetTimer()
 
1267
{
 
1268
        timer_start = timer_elapsed;
 
1269
        updateTimer();
 
1270
}
 
1271
 
 
1272
/** Convenience function to pad a string with zero in front up to a certain length.
 
1273
 */
 
1274
function padString(str, len)
 
1275
{
 
1276
        var outStr = str;
 
1277
 
 
1278
        while (outStr.length < len)
 
1279
        {
 
1280
                outStr = '0' + outStr;
 
1281
        }
 
1282
 
 
1283
        return outStr;
 
1284
}
 
1285
 
 
1286
/** Function to update the export layer.
 
1287
 */
 
1288
function slideUpdateExportLayer()
 
1289
{
 
1290
        // Suspend redraw since we are going to mess with the slides.
 
1291
        var suspendHandle = ROOT_NODE.suspendRedraw(2000);
 
1292
 
 
1293
        var tmpActiveSlide = activeSlide;
 
1294
        var tmpActiveEffect = activeEffect;
 
1295
        var exportedLayers = new Array();
 
1296
 
 
1297
        for (var counterSlides = 0; counterSlides < slides.length; counterSlides++)
 
1298
        {
 
1299
                var exportNode;
 
1300
 
 
1301
                setSlideToState(counterSlides, STATE_START);
 
1302
 
 
1303
                var maxEffect = 0;
 
1304
 
 
1305
                if (slides[counterSlides].effects)
 
1306
                {
 
1307
                        maxEffect = slides[counterSlides].effects.length;
 
1308
                }
 
1309
 
 
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));
 
1313
 
 
1314
                exportedLayers.push(exportNode);
 
1315
 
 
1316
                if (slides[counterSlides]["effects"])
 
1317
                {       
 
1318
                        for (var counter = 0; counter < slides[counterSlides]["effects"].length; counter++)
 
1319
                        {
 
1320
                                for (var subCounter = 0; subCounter < slides[counterSlides]["effects"][counter].length; subCounter++)
 
1321
                                {
 
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"]); 
 
1331
                                }
 
1332
 
 
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);
 
1338
 
 
1339
                                exportedLayers.push(exportNode);
 
1340
                        }
 
1341
                }
 
1342
        }
 
1343
 
 
1344
        activeSlide = tmpActiveSlide;
 
1345
        activeEffect = tmpActiveEffect;
 
1346
        setSlideToState(activeSlide, activeEffect);
 
1347
 
 
1348
        // Copy image.
 
1349
        var newDoc = document.documentElement.cloneNode(true);
 
1350
 
 
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);
 
1355
 
 
1356
        // Delete all layers and script elements.
 
1357
        var nodesToBeRemoved = new Array();
 
1358
 
 
1359
        for (var childCounter = 0; childCounter <  newDoc.childNodes.length; childCounter++)
 
1360
        {
 
1361
                var child = newDoc.childNodes[childCounter];
 
1362
 
 
1363
                if (child.nodeType == 1)
 
1364
                {
 
1365
                        if ((child.nodeName.toUpperCase() == 'G') || (child.nodeName.toUpperCase() == 'SCRIPT'))
 
1366
                        {
 
1367
                                nodesToBeRemoved.push(child);
 
1368
                        }
 
1369
                }
 
1370
        }
 
1371
 
 
1372
        for (var ndCounter = 0; ndCounter < nodesToBeRemoved.length; ndCounter++)
 
1373
        {
 
1374
                var nd = nodesToBeRemoved[ndCounter];
 
1375
 
 
1376
                nd.parentNode.removeChild(nd);
 
1377
        }
 
1378
 
 
1379
        // Set current layer.
 
1380
        if (exportedLayers[0])
 
1381
        {
 
1382
                var namedView;
 
1383
 
 
1384
                for (var nodeCounter = 0; nodeCounter < newDoc.childNodes.length; nodeCounter++)
 
1385
                {
 
1386
                        if ((newDoc.childNodes[nodeCounter].nodeType == 1) && (newDoc.childNodes[nodeCounter].getAttribute('id') == 'base'))
 
1387
                        {
 
1388
                                namedView = newDoc.childNodes[nodeCounter];
 
1389
                        }
 
1390
                }
 
1391
 
 
1392
                if (namedView)
 
1393
                {
 
1394
                        namedView.setAttributeNS(NSS['inkscape'], 'current-layer', exportedLayers[0].getAttributeNS(NSS['inkscape'], 'label'));
 
1395
                }
 
1396
        }
 
1397
 
 
1398
        // Add exported layers.
 
1399
        while (exportedLayers.length > 0)
 
1400
        {
 
1401
                var nd = exportedLayers.pop();
 
1402
 
 
1403
                nd.setAttribute("opacity",1);
 
1404
                nd.style.display = "inherit";
 
1405
 
 
1406
                newDoc.appendChild(nd);
 
1407
        }
 
1408
 
 
1409
        // Serialise the new document.
 
1410
        var serializer = new XMLSerializer();
 
1411
        var strm = 
 
1412
        {
 
1413
                content : "",
 
1414
                close : function() {},  
 
1415
                flush : function() {},  
 
1416
                write : function(str, count) { this.content += str; }  
 
1417
        };
 
1418
 
 
1419
        var xml = serializer.serializeToStream(newDoc, strm, 'UTF-8');
 
1420
 
 
1421
        window.open('data:image/svg+xml;base64;charset=utf-8,' + window.btoa(strm.content), '_blank');
 
1422
 
 
1423
        // Unsuspend redraw.
 
1424
        ROOT_NODE.unsuspendRedraw(suspendHandle);
 
1425
        ROOT_NODE.forceRedraw();
 
1426
}
 
1427
 
 
1428
/** Function to undo last drawing operation.
 
1429
*/
 
1430
function drawingUndo()
 
1431
{
 
1432
        mouse_presentation_path = null;
 
1433
        mouse_original_path = null;
 
1434
 
 
1435
        if (history_presentation_elements.length > 0)
 
1436
        {
 
1437
                var p = history_presentation_elements.pop();
 
1438
                var parent = p.parentNode.removeChild(p);
 
1439
 
 
1440
                p = history_original_elements.pop();
 
1441
                parent = p.parentNode.removeChild(p);
 
1442
        }
 
1443
}
 
1444
 
 
1445
/** Event handler for mouse down in drawing mode.
 
1446
 *
 
1447
 *  @param e the event
 
1448
 */
 
1449
function drawingMousedown(e)
 
1450
{
 
1451
        var value = 0;
 
1452
 
 
1453
        if (e.button)
 
1454
                value = e.button;
 
1455
        else if (e.which)
 
1456
                value = e.which;
 
1457
 
 
1458
        if (value == 1)
 
1459
        {
 
1460
                history_counter++;
 
1461
 
 
1462
                var p = calcCoord(e);
 
1463
 
 
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);
 
1474
 
 
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);
 
1481
 
 
1482
                if (slides[activeSlide]["viewGroup"])
 
1483
                        slides[activeSlide]["viewGroup"].appendChild(mouse_presentation_path);
 
1484
                else
 
1485
                        slides[activeSlide]["element"].appendChild(mouse_presentation_path);
 
1486
 
 
1487
                history_presentation_elements.push(mouse_presentation_path);
 
1488
 
 
1489
                return false;
 
1490
        }
 
1491
 
 
1492
        return true;
 
1493
}
 
1494
 
 
1495
/** Event handler for mouse up in drawing mode.
 
1496
 *
 
1497
 *  @param e the event
 
1498
 */
 
1499
function drawingMouseup(e)
 
1500
{
 
1501
        if(!e)
 
1502
                e = window.event;
 
1503
 
 
1504
        if (mouse_presentation_path != null)
 
1505
        {
 
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;
 
1513
 
 
1514
                return false;
 
1515
        }
 
1516
 
 
1517
        return true;
 
1518
}
 
1519
 
 
1520
/** Event handler for mouse move in drawing mode.
 
1521
 *
 
1522
 *  @param e the event
 
1523
 */
 
1524
function drawingMousemove(e)
 
1525
{
 
1526
        if(!e)
 
1527
                e = window.event;
 
1528
 
 
1529
        var dist = (mouse_last_x - e.clientX) * (mouse_last_x - e.clientX) + (mouse_last_y - e.clientY) * (mouse_last_y - e.clientY);
 
1530
 
 
1531
        if (mouse_presentation_path == null)
 
1532
        {
 
1533
                return true;
 
1534
        }
 
1535
 
 
1536
        if (dist >= mouse_min_dist_sqr)
 
1537
        {
 
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;
 
1545
        }
 
1546
 
 
1547
        return false;
 
1548
}
 
1549
 
 
1550
/** Event handler for mouse wheel events in slide mode.
 
1551
 *  based on http://adomas.org/javascript-mouse-wheel/
 
1552
 *
 
1553
 *  @param e the event
 
1554
 */
 
1555
function slideMousewheel(e)
 
1556
{
 
1557
        var delta = 0;
 
1558
 
 
1559
        if (!e)
 
1560
                e = window.event;
 
1561
 
 
1562
        if (e.wheelDelta)
 
1563
        { // IE Opera
 
1564
                delta = e.wheelDelta/120;
 
1565
        }
 
1566
        else if (e.detail)
 
1567
        { // MOZ
 
1568
                delta = -e.detail/3;
 
1569
        }
 
1570
 
 
1571
        if (delta > 0)
 
1572
                skipEffects(-1);
 
1573
        else if (delta < 0)
 
1574
                skipEffects(1);
 
1575
 
 
1576
        if (e.preventDefault)
 
1577
                e.preventDefault();
 
1578
 
 
1579
        e.returnValue = false;
 
1580
}
 
1581
 
 
1582
/** Event handler for mouse wheel events in index mode.
 
1583
 *  based on http://adomas.org/javascript-mouse-wheel/
 
1584
 *
 
1585
 *  @param e the event
 
1586
 */
 
1587
function indexMousewheel(e)
 
1588
{
 
1589
        var delta = 0;
 
1590
 
 
1591
        if (!e)
 
1592
                e = window.event;
 
1593
 
 
1594
        if (e.wheelDelta)
 
1595
        { // IE Opera
 
1596
                delta = e.wheelDelta/120;
 
1597
        }
 
1598
        else if (e.detail)
 
1599
        { // MOZ
 
1600
                delta = -e.detail/3;
 
1601
        }
 
1602
 
 
1603
        if (delta > 0)
 
1604
                indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);
 
1605
        else if (delta < 0)
 
1606
                indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);
 
1607
 
 
1608
        if (e.preventDefault)
 
1609
                e.preventDefault();
 
1610
 
 
1611
        e.returnValue = false;
 
1612
}
 
1613
 
 
1614
/** Function to set the path paint width.
 
1615
*/
 
1616
function set_path_paint_width()
 
1617
{
 
1618
        var svgPoint1 = document.documentElement.createSVGPoint();
 
1619
        var svgPoint2 = document.documentElement.createSVGPoint();
 
1620
 
 
1621
        svgPoint1.x = 0.0;
 
1622
        svgPoint1.y = 0.0;
 
1623
        svgPoint2.x = 1.0;
 
1624
        svgPoint2.y = 0.0;
 
1625
 
 
1626
        var matrix = slides[activeSlide]["element"].getTransformToElement(ROOT_NODE);
 
1627
 
 
1628
        if (slides[activeSlide]["viewGroup"])
 
1629
                matrix = slides[activeSlide]["viewGroup"].getTransformToElement(ROOT_NODE);
 
1630
 
 
1631
        svgPoint1 = svgPoint1.matrixTransform(matrix);
 
1632
        svgPoint2 = svgPoint2.matrixTransform(matrix);
 
1633
 
 
1634
        path_paint_width = path_width / Math.sqrt((svgPoint2.x - svgPoint1.x) * (svgPoint2.x - svgPoint1.x) + (svgPoint2.y - svgPoint1.y) * (svgPoint2.y - svgPoint1.y));
 
1635
}
 
1636
 
 
1637
/** The view effect.
 
1638
 *
 
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.
 
1643
 */
 
1644
function view(dir, element, time, options)
 
1645
{
 
1646
        var length = 250;
 
1647
        var fraction;
 
1648
 
 
1649
        if (!options["matrixInitial"])
 
1650
        {
 
1651
                var tempString = slides[activeSlide]["viewGroup"].getAttribute("transform");
 
1652
 
 
1653
                if (tempString)
 
1654
                        options["matrixInitial"] = (new matrixSVG()).fromAttribute(tempString);
 
1655
                else
 
1656
                        options["matrixInitial"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
 
1657
        }
 
1658
 
 
1659
        if ((time == STATE_END) || (time == STATE_START))
 
1660
                fraction = 1;
 
1661
        else
 
1662
        {
 
1663
                if (options && options["length"])
 
1664
                        length = options["length"];
 
1665
 
 
1666
                fraction = time / length;
 
1667
        }
 
1668
 
 
1669
        if (dir == 1)
 
1670
        {
 
1671
                if (fraction <= 0)
 
1672
                {
 
1673
                        element.setAttribute("transform", options["matrixInitial"].toAttribute());
 
1674
                }
 
1675
                else if (fraction >= 1)
 
1676
                {
 
1677
                        element.setAttribute("transform", options["matrixNew"].toAttribute());
 
1678
 
 
1679
                        set_path_paint_width();
 
1680
 
 
1681
                        options["matrixInitial"] = null;
 
1682
                        return true;
 
1683
                }
 
1684
                else
 
1685
                {
 
1686
                        element.setAttribute("transform", options["matrixInitial"].mix(options["matrixNew"], fraction).toAttribute());
 
1687
                }
 
1688
        }
 
1689
        else if (dir == -1)
 
1690
        {
 
1691
                if (fraction <= 0)
 
1692
                {
 
1693
                        element.setAttribute("transform", options["matrixInitial"].toAttribute());
 
1694
                }
 
1695
                else if (fraction >= 1)
 
1696
                {
 
1697
                        element.setAttribute("transform", options["matrixOld"].toAttribute());
 
1698
                        set_path_paint_width();
 
1699
 
 
1700
                        options["matrixInitial"] = null;
 
1701
                        return true;
 
1702
                }
 
1703
                else
 
1704
                {
 
1705
                        element.setAttribute("transform", options["matrixInitial"].mix(options["matrixOld"], fraction).toAttribute());
 
1706
                }
 
1707
        }
 
1708
 
 
1709
        return false;
 
1710
}
 
1711
 
 
1712
/** The fade effect.
 
1713
 *
 
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)
 
1718
 */
 
1719
function fade(dir, element, time, options)
 
1720
{
 
1721
        var length = 250;
 
1722
        var fraction;
 
1723
 
 
1724
        if ((time == STATE_END) || (time == STATE_START))
 
1725
                fraction = 1;
 
1726
        else
 
1727
        {
 
1728
                if (options && options["length"])
 
1729
                        length = options["length"];
 
1730
 
 
1731
                fraction = time / length;
 
1732
        }
 
1733
 
 
1734
        if (dir == 1)
 
1735
        {
 
1736
                if (fraction <= 0)
 
1737
                {
 
1738
                        element.style.display = "none";
 
1739
                        element.setAttribute("opacity", 0);
 
1740
                }
 
1741
                else if (fraction >= 1)
 
1742
                {
 
1743
                        element.style.display = "inherit";
 
1744
                        element.setAttribute("opacity", 1);
 
1745
                        return true;
 
1746
                }
 
1747
                else
 
1748
                {
 
1749
                        element.style.display = "inherit";
 
1750
                        element.setAttribute("opacity", fraction);
 
1751
                }
 
1752
        }
 
1753
        else if (dir == -1)
 
1754
        {
 
1755
                if (fraction <= 0)
 
1756
                {
 
1757
                        element.style.display = "inherit";
 
1758
                        element.setAttribute("opacity", 1);
 
1759
                }
 
1760
                else if (fraction >= 1)
 
1761
                {
 
1762
                        element.setAttribute("opacity", 0);
 
1763
                        element.style.display = "none";
 
1764
                        return true;
 
1765
                }
 
1766
                else
 
1767
                {
 
1768
                        element.style.display = "inherit";
 
1769
                        element.setAttribute("opacity", 1 - fraction);
 
1770
                }
 
1771
        }
 
1772
        return false;
 
1773
}
 
1774
 
 
1775
/** The appear effect.
 
1776
 *
 
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)
 
1781
 */
 
1782
function appear(dir, element, time, options)
 
1783
{
 
1784
        if (dir == 1)
 
1785
        {
 
1786
                element.style.display = "inherit";
 
1787
                element.setAttribute("opacity",1);
 
1788
        }
 
1789
        else if (dir == -1)
 
1790
        {
 
1791
                element.style.display = "none";
 
1792
                element.setAttribute("opacity",0);
 
1793
        }
 
1794
        return true;
 
1795
}
 
1796
 
 
1797
/** The pop effect.
 
1798
 *
 
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)
 
1803
 */
 
1804
function pop(dir, element, time, options)
 
1805
{
 
1806
        var length = 500;
 
1807
        var fraction;
 
1808
 
 
1809
        if ((time == STATE_END) || (time == STATE_START))
 
1810
                fraction = 1;
 
1811
        else
 
1812
        {
 
1813
                if (options && options["length"])
 
1814
                        length = options["length"];
 
1815
 
 
1816
                fraction = time / length;
 
1817
        }
 
1818
 
 
1819
        if (dir == 1)
 
1820
        {
 
1821
                if (fraction <= 0)
 
1822
                {
 
1823
                        element.setAttribute("opacity", 0);
 
1824
                        element.setAttribute("transform", "scale(0)");
 
1825
                        element.style.display = "none";
 
1826
                }
 
1827
                else if (fraction >= 1)
 
1828
                {
 
1829
                        element.setAttribute("opacity", 1);
 
1830
                        element.removeAttribute("transform");
 
1831
                        element.style.display = "inherit";
 
1832
                        return true;
 
1833
                }
 
1834
                else
 
1835
                {
 
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 + ")");
 
1844
                }
 
1845
        }
 
1846
        else if (dir == -1)
 
1847
        {
 
1848
                if (fraction <= 0)
 
1849
                {
 
1850
                        element.setAttribute("opacity", 1);
 
1851
                        element.setAttribute("transform", "scale(1)");
 
1852
                        element.style.display = "inherit";
 
1853
                }
 
1854
                else if (fraction >= 1)
 
1855
                {
 
1856
                        element.setAttribute("opacity", 0);
 
1857
                        element.removeAttribute("transform");
 
1858
                        element.style.display = "none";
 
1859
                        return true;
 
1860
                }
 
1861
                else
 
1862
                {
 
1863
                        element.setAttribute("opacity", 1 - fraction);
 
1864
                        element.setAttribute("transform", "scale(" + 1 - fraction + ")");
 
1865
                        element.style.display = "inherit";
 
1866
                }
 
1867
        }
 
1868
        return false;
 
1869
}
 
1870
 
 
1871
/** Function to set a slide either to the start or the end state.
 
1872
 *  
 
1873
 *  @param slide the slide to use
 
1874
 *  @param state the state into which the slide should be set
 
1875
 */
 
1876
function setSlideToState(slide, state)
 
1877
{
 
1878
        slides[slide]["viewGroup"].setAttribute("transform", slides[slide].initialView);
 
1879
 
 
1880
        if (slides[slide]["effects"])
 
1881
        {       
 
1882
                if (state == STATE_END)
 
1883
                {
 
1884
                        for (var counter = 0; counter < slides[slide]["effects"].length; counter++)
 
1885
                        {
 
1886
                                for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
 
1887
                                {
 
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"]);   
 
1897
                                }
 
1898
                        }
 
1899
                }
 
1900
                else if (state == STATE_START)
 
1901
                {
 
1902
                        for (var counter = slides[slide]["effects"].length - 1; counter >= 0; counter--)
 
1903
                        {
 
1904
                                for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
 
1905
                                {
 
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"]);  
 
1915
                                }
 
1916
                        }
 
1917
                }
 
1918
                else
 
1919
                {
 
1920
                        setSlideToState(slide, STATE_START);
 
1921
 
 
1922
                        for (var counter = 0; counter < slides[slide]["effects"].length && counter < state; counter++)
 
1923
                        {
 
1924
                                for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
 
1925
                                {
 
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"]);   
 
1935
                                }
 
1936
                        }
 
1937
                }
 
1938
        }
 
1939
 
 
1940
        window.location.hash = (activeSlide + 1) + '_' + activeEffect;
 
1941
}
 
1942
 
 
1943
/** Convenience function to translate a attribute string into a dictionary.
 
1944
 *
 
1945
 *      @param str the attribute string
 
1946
 *  @return a dictionary
 
1947
 *  @see dictToPropStr
 
1948
 */
 
1949
function propStrToDict(str)
 
1950
{
 
1951
        var list = str.split(";");
 
1952
        var obj = new Object();
 
1953
 
 
1954
        for (var counter = 0; counter < list.length; counter++)
 
1955
        {
 
1956
                var subStr = list[counter];
 
1957
                var subList = subStr.split(":");
 
1958
                if (subList.length == 2)
 
1959
                {
 
1960
                        obj[subList[0]] = subList[1];
 
1961
                }       
 
1962
        }
 
1963
 
 
1964
        return obj;
 
1965
}
 
1966
 
 
1967
/** Convenience function to translate a dictionary into a string that can be used as an attribute.
 
1968
 *
 
1969
 *  @param dict the dictionary to convert
 
1970
 *  @return a string that can be used as an attribute
 
1971
 *  @see propStrToDict
 
1972
 */
 
1973
function dictToPropStr(dict)
 
1974
{
 
1975
        var str = "";
 
1976
 
 
1977
        for (var key in dict)
 
1978
        {
 
1979
                str += key + ":" + dict[key] + ";";
 
1980
        }
 
1981
 
 
1982
        return str;
 
1983
}
 
1984
 
 
1985
/** Sub-function to add a suffix to the ids of the node and all its children.
 
1986
 *      
 
1987
 *      @param node the node to change
 
1988
 *      @param suffix the suffix to add
 
1989
 *      @param replace dictionary of replaced ids
 
1990
 *  @see suffixNodeIds
 
1991
 */
 
1992
function suffixNoneIds_sub(node, suffix, replace)
 
1993
{
 
1994
        if (node.nodeType == 1)
 
1995
        {
 
1996
                if (node.getAttribute("id"))
 
1997
                {
 
1998
                        var id = node.getAttribute("id")
 
1999
                                replace["#" + id] = id + suffix;
 
2000
                        node.setAttribute("id", id + suffix);
 
2001
                }
 
2002
 
 
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);
 
2005
 
 
2006
                if (node.childNodes)
 
2007
                {
 
2008
                        for (var counter = 0; counter < node.childNodes.length; counter++)
 
2009
                                suffixNoneIds_sub(node.childNodes[counter], suffix, replace);
 
2010
                }
 
2011
        }
 
2012
}
 
2013
 
 
2014
/** Function to add a suffix to the ids of the node and all its children.
 
2015
 *      
 
2016
 *      @param node the node to change
 
2017
 *      @param suffix the suffix to add
 
2018
 *  @return the changed node
 
2019
 *  @see suffixNodeIds_sub
 
2020
 */
 
2021
function suffixNodeIds(node, suffix)
 
2022
{
 
2023
        var replace = new Object();
 
2024
 
 
2025
        suffixNoneIds_sub(node, suffix, replace);
 
2026
 
 
2027
        return node;
 
2028
}
 
2029
 
 
2030
/** Function to build a progress bar.
 
2031
 *      
 
2032
 *  @param parent node to attach the progress bar to
 
2033
 */
 
2034
function createProgressBar(parent_node)
 
2035
{
 
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;");
 
2040
 
 
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);
 
2049
 
 
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);
 
2057
 
 
2058
        parent_node.appendChild(g);
 
2059
}
 
2060
 
 
2061
/** Function to hide the progress bar.
 
2062
 *      
 
2063
 */
 
2064
function hideProgressBar()
 
2065
{
 
2066
        var progress_bar = document.getElementById("layer_progress_bar");
 
2067
 
 
2068
        if (!progress_bar)
 
2069
        {
 
2070
                return;
 
2071
        }
 
2072
 
 
2073
        progress_bar.setAttribute("style", "display: none;");
 
2074
}
 
2075
 
 
2076
/** Function to show the progress bar.
 
2077
 *      
 
2078
 */
 
2079
function showProgressBar()
 
2080
{
 
2081
        var progress_bar = document.getElementById("layer_progress_bar");
 
2082
 
 
2083
        if (!progress_bar)
 
2084
        {
 
2085
                return;
 
2086
        }
 
2087
 
 
2088
        progress_bar.setAttribute("style", "display: inherit;");
 
2089
}
 
2090
 
 
2091
/** Set progress bar value.
 
2092
 *      
 
2093
 *      @param value the current slide number
 
2094
 *
 
2095
 */
 
2096
function setProgressBarValue(value)
 
2097
{
 
2098
        var rect_progress_bar = document.getElementById("rect_progress_bar");
 
2099
 
 
2100
        if (!rect_progress_bar)
 
2101
        {
 
2102
                return;
 
2103
        }
 
2104
 
 
2105
        if (value < 1)
 
2106
        {
 
2107
                // First slide, assumed to be the title of the presentation
 
2108
                var x = 0;
 
2109
                var w = 0.01 * HEIGHT;
 
2110
        }
 
2111
        else if (value >= slides.length - 1)
 
2112
        {
 
2113
                // Last slide, assumed to be the end of the presentation
 
2114
                var x = WIDTH - 0.01 * HEIGHT;
 
2115
                var w = 0.01 * HEIGHT;
 
2116
        }
 
2117
        else
 
2118
        {
 
2119
                value -= 1;
 
2120
                value /= (slides.length - 2);
 
2121
 
 
2122
                var x = WIDTH * value;
 
2123
                var w = WIDTH / (slides.length - 2);
 
2124
        }
 
2125
 
 
2126
        rect_progress_bar.setAttribute("x", x);
 
2127
        rect_progress_bar.setAttribute("width", w);
 
2128
}
 
2129
 
 
2130
/** Set time indicator.
 
2131
 *      
 
2132
 *      @param value the percentage of time elapse so far between 0.0 and 1.0
 
2133
 *
 
2134
 */
 
2135
function setTimeIndicatorValue(value)
 
2136
{
 
2137
        var circle_timer_indicator = document.getElementById("circle_timer_indicator");
 
2138
 
 
2139
        if (!circle_timer_indicator)
 
2140
        {
 
2141
                return;
 
2142
        }
 
2143
 
 
2144
        if (value < 0.0)
 
2145
        {
 
2146
                value = 0.0;
 
2147
        }
 
2148
 
 
2149
        if (value > 1.0)
 
2150
        {
 
2151
                value = 1.0;
 
2152
        }
 
2153
 
 
2154
        var cx = (WIDTH - 0.01 * HEIGHT) * value + 0.005 * HEIGHT;
 
2155
        circle_timer_indicator.setAttribute("cx", cx);
 
2156
}
 
2157
 
 
2158
/** Update timer.
 
2159
 *      
 
2160
 */
 
2161
function updateTimer()
 
2162
{
 
2163
        timer_elapsed += 1;
 
2164
        setTimeIndicatorValue((timer_elapsed - timer_start) / (60 * timer_duration));
 
2165
}
 
2166
 
 
2167
/** Convert screen coordinates to document coordinates.
 
2168
 *
 
2169
 *  @param e event with screen coordinates
 
2170
 *
 
2171
 *  @return coordinates in SVG file coordinate system   
 
2172
 */
 
2173
function calcCoord(e)
 
2174
{
 
2175
        var svgPoint = document.documentElement.createSVGPoint();
 
2176
        svgPoint.x = e.clientX + window.pageXOffset;
 
2177
        svgPoint.y = e.clientY + window.pageYOffset;
 
2178
 
 
2179
        var matrix = slides[activeSlide]["element"].getScreenCTM();
 
2180
 
 
2181
        if (slides[activeSlide]["viewGroup"])
 
2182
                matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
 
2183
 
 
2184
        svgPoint = svgPoint.matrixTransform(matrix.inverse());
 
2185
        return svgPoint;
 
2186
}
 
2187
 
 
2188
/** Add slide.
 
2189
 *
 
2190
 *      @param after_slide after which slide the new slide should be inserted into the presentation
 
2191
 */
 
2192
function addSlide(after_slide)
 
2193
{
 
2194
        number_of_added_slides++;
 
2195
 
 
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;");
 
2200
 
 
2201
        var new_slide = new Object();
 
2202
        new_slide["element"] = g;
 
2203
 
 
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();
 
2209
 
 
2210
        for (key in dict)
 
2211
                if (key != "name")
 
2212
                        new_slide["transitionIn"]["options"][key] = dict[key];
 
2213
 
 
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();
 
2219
 
 
2220
        for (key in dict)
 
2221
                if (key != "name")
 
2222
                        new_slide["transitionOut"]["options"][key] = dict[key];
 
2223
 
 
2224
        // Copy master slide content.
 
2225
        if (masterSlide)
 
2226
        {
 
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";
 
2231
 
 
2232
                g.appendChild(clonedNode);
 
2233
        }
 
2234
 
 
2235
        // Substitute auto texts.
 
2236
        substituteAutoTexts(g, "Whiteboard " + number_of_added_slides, "W" + number_of_added_slides, slides.length);
 
2237
 
 
2238
        g.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + (after_slide + 1) + ")) { indexSetActiveSlide(" + (after_slide + 1) + "); };");
 
2239
 
 
2240
        // Create a transform group.
 
2241
        var transformGroup = document.createElementNS(NSS["svg"], "g");
 
2242
 
 
2243
        // Add content to transform group.
 
2244
        while (g.firstChild)
 
2245
                transformGroup.appendChild(g.firstChild);
 
2246
 
 
2247
        // Transfer the transform attribute from the node to the transform group.
 
2248
        if (g.getAttribute("transform"))
 
2249
        {
 
2250
                transformGroup.setAttribute("transform", g.getAttribute("transform"));
 
2251
                g.removeAttribute("transform");
 
2252
        }
 
2253
 
 
2254
        // Create a view group.
 
2255
        var viewGroup = document.createElementNS(NSS["svg"], "g");
 
2256
 
 
2257
        viewGroup.appendChild(transformGroup);
 
2258
        new_slide["viewGroup"] = g.appendChild(viewGroup);
 
2259
 
 
2260
        // Insert background.
 
2261
        if (BACKGROUND_COLOR != null)
 
2262
        {
 
2263
                var rectNode = document.createElementNS(NSS["svg"], "rect");
 
2264
 
 
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);
 
2271
 
 
2272
                new_slide["viewGroup"].insertBefore(rectNode, new_slide["viewGroup"].firstChild);
 
2273
        }
 
2274
 
 
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);
 
2277
 
 
2278
        new_slide["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
 
2279
        new_slide.initialView = matrixOld.toAttribute();
 
2280
 
 
2281
        // Insert slide
 
2282
        var node = slides[after_slide]["element"];
 
2283
        var next_node = node.nextSibling;
 
2284
        var parent_node = node.parentNode;
 
2285
 
 
2286
        if (next_node)
 
2287
        {
 
2288
                parent_node.insertBefore(g, next_node);
 
2289
        }
 
2290
        else
 
2291
        {
 
2292
                parent_node.appendChild(g);
 
2293
        }
 
2294
 
 
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;");
 
2301
 
 
2302
        new_slide["original_element"] = g;
 
2303
 
 
2304
        node = slides[after_slide]["original_element"];
 
2305
        next_node = node.nextSibling;
 
2306
        parent_node = node.parentNode;
 
2307
 
 
2308
        if (next_node)
 
2309
        {
 
2310
                parent_node.insertBefore(g, next_node);
 
2311
        }
 
2312
        else
 
2313
        {
 
2314
                parent_node.appendChild(g);
 
2315
        }
 
2316
 
 
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);
 
2320
 
 
2321
        //resetting the counter attributes on the slides that follow the new slide...
 
2322
        for (var counter = after_slide+2; counter < slides.length; counter++)
 
2323
        {
 
2324
                slides[counter]["element"].setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
 
2325
        }
 
2326
}
 
2327
 
 
2328
/** Convenience function to obtain a transformation matrix from a point matrix.
 
2329
 *
 
2330
 *      @param mPoints Point matrix.
 
2331
 *      @return A transformation matrix.
 
2332
 */
 
2333
function pointMatrixToTransformation(mPoints)
 
2334
{
 
2335
        mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
 
2336
 
 
2337
        return mPointsOld.mult(mPoints.inv());
 
2338
}
 
2339
 
 
2340
/** Convenience function to obtain a matrix with three corners of a rectangle.
 
2341
 *
 
2342
 *      @param rect an svg rectangle
 
2343
 *      @return a matrixSVG containing three corners of the rectangle
 
2344
 */
 
2345
function rectToMatrix(rect)
 
2346
{
 
2347
        rectWidth = rect.getBBox().width;
 
2348
        rectHeight = rect.getBBox().height;
 
2349
        rectX = rect.getBBox().x;
 
2350
        rectY = rect.getBBox().y;
 
2351
        rectXcorr = 0;
 
2352
        rectYcorr = 0;
 
2353
 
 
2354
        scaleX = WIDTH / rectWidth;
 
2355
        scaleY = HEIGHT / rectHeight;
 
2356
 
 
2357
        if (scaleX > scaleY)
 
2358
        {
 
2359
                scaleX = scaleY;
 
2360
                rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
 
2361
                rectWidth = WIDTH / scaleX;
 
2362
        }       
 
2363
        else
 
2364
        {
 
2365
                scaleY = scaleX;
 
2366
                rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
 
2367
                rectHeight = HEIGHT / scaleY;
 
2368
        }
 
2369
 
 
2370
        if (rect.transform.baseVal.numberOfItems < 1)
 
2371
        {
 
2372
                mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
 
2373
        }
 
2374
        else
 
2375
        {
 
2376
                mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
 
2377
        }
 
2378
 
 
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);
 
2381
 
 
2382
        return mRectTrans.mult(newBasePoints.add(newVectors));
 
2383
}
 
2384
 
 
2385
/** Function to handle JessyInk elements.
 
2386
 *
 
2387
 *      @param  node    Element node.
 
2388
 */
 
2389
function handleElement(node)
 
2390
{
 
2391
        if (node.getAttributeNS(NSS['jessyink'], 'element') == 'core.video')
 
2392
        {
 
2393
                var url;
 
2394
                var width;
 
2395
                var height;
 
2396
                var x;
 
2397
                var y;
 
2398
                var transform;
 
2399
 
 
2400
                var tspans = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "tspan");
 
2401
 
 
2402
                for (var tspanCounter = 0; tspanCounter < tspans.length; tspanCounter++)
 
2403
                {
 
2404
                        if (tspans[tspanCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "url")
 
2405
                        {
 
2406
                                url = tspans[tspanCounter].firstChild.nodeValue;
 
2407
                        }
 
2408
                }
 
2409
 
 
2410
                var rects = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "rect");
 
2411
 
 
2412
                for (var rectCounter = 0; rectCounter < rects.length; rectCounter++)
 
2413
                {
 
2414
                        if (rects[rectCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "rect")
 
2415
                        {
 
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");
 
2421
                        }
 
2422
                }
 
2423
 
 
2424
                for (var childCounter = 0; childCounter < node.childNodes.length; childCounter++)
 
2425
                {
 
2426
                        if (node.childNodes[childCounter].nodeType == 1)
 
2427
                        {
 
2428
                                if (node.childNodes[childCounter].style)
 
2429
                                {
 
2430
                                        node.childNodes[childCounter].style.display = 'none';
 
2431
                                }
 
2432
                                else
 
2433
                                {
 
2434
                                        node.childNodes[childCounter].setAttribute("style", "display: none;");
 
2435
                                }
 
2436
                        }
 
2437
                }
 
2438
 
 
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);
 
2445
 
 
2446
                var videoNode = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
 
2447
                videoNode.setAttribute("src", url);
 
2448
 
 
2449
                foreignNode.appendChild(videoNode);
 
2450
                node.appendChild(foreignNode);
 
2451
        }
 
2452
}
 
2453
 
 
2454
/** Class processing the location hash.
 
2455
 *
 
2456
 *      @param str location hash
 
2457
 */
 
2458
function LocationHash(str)
 
2459
{
 
2460
        this.slideNumber = 0;
 
2461
        this.effectNumber = 0;
 
2462
 
 
2463
        str = str.substr(1, str.length - 1);
 
2464
 
 
2465
        var parts = str.split('_');
 
2466
 
 
2467
        // Try to extract slide number.
 
2468
        if (parts.length >= 1)
 
2469
        {
 
2470
                try
 
2471
                {
 
2472
                        var slideNumber = parseInt(parts[0]);
 
2473
 
 
2474
                        if (!isNaN(slideNumber))
 
2475
                        {
 
2476
                                this.slideNumber = slideNumber - 1;
 
2477
                        }
 
2478
                }
 
2479
                catch (e)
 
2480
                {
 
2481
                }
 
2482
        }
 
2483
        
 
2484
        // Try to extract effect number.
 
2485
        if (parts.length >= 2)
 
2486
        {
 
2487
                try
 
2488
                {
 
2489
                        var effectNumber = parseInt(parts[1]);
 
2490
 
 
2491
                        if (!isNaN(effectNumber))
 
2492
                        {
 
2493
                                this.effectNumber = effectNumber;
 
2494
                        }
 
2495
                }
 
2496
                catch (e)
 
2497
                {
 
2498
                }
 
2499
        }
 
2500
}
 
2501
 
 
2502
/** Class representing an svg matrix.
 
2503
*/
 
2504
function matrixSVG()
 
2505
{
 
2506
        this.e11 = 0; // a
 
2507
        this.e12 = 0; // c
 
2508
        this.e13 = 0; // e
 
2509
        this.e21 = 0; // b
 
2510
        this.e22 = 0; // d
 
2511
        this.e23 = 0; // f
 
2512
        this.e31 = 0;
 
2513
        this.e32 = 0;
 
2514
        this.e33 = 0;
 
2515
}
 
2516
 
 
2517
/** Constructor function.
 
2518
 *
 
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.
 
2525
 */
 
2526
matrixSVG.prototype.fromSVGElements = function(a, b, c, d, e, f)
 
2527
{
 
2528
        this.e11 = a;
 
2529
        this.e12 = c;
 
2530
        this.e13 = e;
 
2531
        this.e21 = b;
 
2532
        this.e22 = d;
 
2533
        this.e23 = f;
 
2534
        this.e31 = 0;
 
2535
        this.e32 = 0;
 
2536
        this.e33 = 1;
 
2537
 
 
2538
        return this;
 
2539
}
 
2540
 
 
2541
/** Constructor function.
 
2542
 *
 
2543
 *      @param matrix an svg matrix as described in the svg standard.
 
2544
 */
 
2545
matrixSVG.prototype.fromSVGMatrix = function(m)
 
2546
{
 
2547
        this.e11 = m.a;
 
2548
        this.e12 = m.c;
 
2549
        this.e13 = m.e;
 
2550
        this.e21 = m.b;
 
2551
        this.e22 = m.d;
 
2552
        this.e23 = m.f;
 
2553
        this.e31 = 0;
 
2554
        this.e32 = 0;
 
2555
        this.e33 = 1;
 
2556
 
 
2557
        return this;
 
2558
}
 
2559
 
 
2560
/** Constructor function.
 
2561
 *
 
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.
 
2571
 */
 
2572
matrixSVG.prototype.fromElements = function(e11, e12, e13, e21, e22, e23, e31, e32, e33)
 
2573
{
 
2574
        this.e11 = e11;
 
2575
        this.e12 = e12;
 
2576
        this.e13 = e13;
 
2577
        this.e21 = e21;
 
2578
        this.e22 = e22;
 
2579
        this.e23 = e23;
 
2580
        this.e31 = e31;
 
2581
        this.e32 = e32;
 
2582
        this.e33 = e33;
 
2583
 
 
2584
        return this;
 
2585
}
 
2586
 
 
2587
/** Constructor function.
 
2588
 *
 
2589
 *      @param attrString string value of the "transform" attribute (currently only "matrix" is accepted)
 
2590
 */
 
2591
matrixSVG.prototype.fromAttribute = function(attrString)
 
2592
{
 
2593
        str = attrString.substr(7, attrString.length - 8);
 
2594
 
 
2595
        str = str.trim();
 
2596
 
 
2597
        strArray = str.split(",");
 
2598
 
 
2599
        // Opera does not use commas to separate the values of the matrix, only spaces.
 
2600
        if (strArray.length != 6)
 
2601
                strArray = str.split(" ");
 
2602
 
 
2603
        this.e11 = parseFloat(strArray[0]);
 
2604
        this.e21 = parseFloat(strArray[1]);
 
2605
        this.e31 = 0;
 
2606
        this.e12 = parseFloat(strArray[2]);
 
2607
        this.e22 = parseFloat(strArray[3]);
 
2608
        this.e32 = 0;
 
2609
        this.e13 = parseFloat(strArray[4]);
 
2610
        this.e23 = parseFloat(strArray[5]);
 
2611
        this.e33 = 1;
 
2612
 
 
2613
        return this;
 
2614
}
 
2615
 
 
2616
/** Output function
 
2617
 *
 
2618
 *      @return a string that can be used as the "transform" attribute.
 
2619
 */
 
2620
matrixSVG.prototype.toAttribute = function()
 
2621
{
 
2622
        return "matrix(" + this.e11 + ", " + this.e21 + ", " + this.e12 + ", " + this.e22 + ", " + this.e13 + ", " + this.e23 + ")";
 
2623
}
 
2624
 
 
2625
/** Matrix nversion.
 
2626
 *
 
2627
 *      @return the inverse of the matrix
 
2628
 */
 
2629
matrixSVG.prototype.inv = function()
 
2630
{
 
2631
        out = new matrixSVG();
 
2632
 
 
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);
 
2634
 
 
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;
 
2644
 
 
2645
        return out;
 
2646
}
 
2647
 
 
2648
/** Matrix multiplication.
 
2649
 *
 
2650
 *      @param op another svg matrix
 
2651
 *      @return this * op
 
2652
 */
 
2653
matrixSVG.prototype.mult = function(op)
 
2654
{
 
2655
        out = new matrixSVG();
 
2656
 
 
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;
 
2666
 
 
2667
        return out;
 
2668
}
 
2669
 
 
2670
/** Matrix addition.
 
2671
 *
 
2672
 *      @param op another svg matrix
 
2673
 *      @return this + op
 
2674
 */
 
2675
matrixSVG.prototype.add = function(op)
 
2676
{
 
2677
        out = new matrixSVG();
 
2678
 
 
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;
 
2688
 
 
2689
        return out;
 
2690
}
 
2691
 
 
2692
/** Matrix mixing.
 
2693
 *
 
2694
 *      @param op another svg matrix
 
2695
 *      @parma contribOp contribution of the other matrix (0 <= contribOp <= 1)
 
2696
 *      @return (1 - contribOp) * this + contribOp * op
 
2697
 */
 
2698
matrixSVG.prototype.mix = function(op, contribOp)
 
2699
{
 
2700
        contribThis = 1.0 - contribOp;
 
2701
        out = new matrixSVG();
 
2702
 
 
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;
 
2712
 
 
2713
        return out;
 
2714
}
 
2715
 
 
2716
/** Trimming function for strings.
 
2717
*/
 
2718
String.prototype.trim = function()
 
2719
{
 
2720
        return this.replace(/^\s+|\s+$/g, '');
 
2721
}
 
2722