home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / share / extensions / jessyInk.js < prev    next >
Text File  |  2011-07-08  |  78KB  |  2,735 lines

  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.         // Before removing the node, check whether it contains any definitions.
  1377.         var defs = nd.getElementsByTagNameNS(NSS["svg"], "defs");
  1378.  
  1379.         for (var defsCounter = 0; defsCounter < defs.length; defsCounter++)
  1380.         {
  1381.             if (defs[defsCounter].id)
  1382.             {
  1383.                 newDoc.appendChild(defs[defsCounter].cloneNode(true));
  1384.             }
  1385.         }
  1386.  
  1387.         // Remove node.
  1388.         nd.parentNode.removeChild(nd);
  1389.     }
  1390.  
  1391.     // Set current layer.
  1392.     if (exportedLayers[0])
  1393.     {
  1394.         var namedView;
  1395.  
  1396.         for (var nodeCounter = 0; nodeCounter < newDoc.childNodes.length; nodeCounter++)
  1397.         {
  1398.             if ((newDoc.childNodes[nodeCounter].nodeType == 1) && (newDoc.childNodes[nodeCounter].getAttribute('id') == 'base'))
  1399.             {
  1400.                 namedView = newDoc.childNodes[nodeCounter];
  1401.             }
  1402.         }
  1403.  
  1404.         if (namedView)
  1405.         {
  1406.             namedView.setAttributeNS(NSS['inkscape'], 'current-layer', exportedLayers[0].getAttributeNS(NSS['inkscape'], 'label'));
  1407.         }
  1408.     }
  1409.  
  1410.     // Add exported layers.
  1411.     while (exportedLayers.length > 0)
  1412.     {
  1413.         var nd = exportedLayers.pop();
  1414.  
  1415.         nd.setAttribute("opacity",1);
  1416.         nd.style.display = "inherit";
  1417.  
  1418.         newDoc.appendChild(nd);
  1419.     }
  1420.  
  1421.     // Serialise the new document.
  1422.     var serializer = new XMLSerializer();
  1423.     var strm = 
  1424.     {
  1425.         content : "",
  1426.         close : function() {},  
  1427.         flush : function() {},  
  1428.         write : function(str, count) { this.content += str; }  
  1429.     };
  1430.  
  1431.     var xml = serializer.serializeToStream(newDoc, strm, 'UTF-8');
  1432.  
  1433.     window.location = 'data:application/svg+xml;base64;charset=utf-8,' + window.btoa(strm.content);
  1434.  
  1435.     // Unsuspend redraw.
  1436.     ROOT_NODE.unsuspendRedraw(suspendHandle);
  1437.     ROOT_NODE.forceRedraw();
  1438. }
  1439.  
  1440. /** Function to undo last drawing operation.
  1441. */
  1442. function drawingUndo()
  1443. {
  1444.     mouse_presentation_path = null;
  1445.     mouse_original_path = null;
  1446.  
  1447.     if (history_presentation_elements.length > 0)
  1448.     {
  1449.         var p = history_presentation_elements.pop();
  1450.         var parent = p.parentNode.removeChild(p);
  1451.  
  1452.         p = history_original_elements.pop();
  1453.         parent = p.parentNode.removeChild(p);
  1454.     }
  1455. }
  1456.  
  1457. /** Event handler for mouse down in drawing mode.
  1458.  *
  1459.  *  @param e the event
  1460.  */
  1461. function drawingMousedown(e)
  1462. {
  1463.     var value = 0;
  1464.  
  1465.     if (e.button)
  1466.         value = e.button;
  1467.     else if (e.which)
  1468.         value = e.which;
  1469.  
  1470.     if (value == 1)
  1471.     {
  1472.         history_counter++;
  1473.  
  1474.         var p = calcCoord(e);
  1475.  
  1476.         mouse_last_x = e.clientX;
  1477.         mouse_last_y = e.clientY;
  1478.         mouse_original_path = document.createElementNS(NSS["svg"], "path");
  1479.         mouse_original_path.setAttribute("stroke", path_colour);
  1480.         mouse_original_path.setAttribute("stroke-width", path_paint_width);
  1481.         mouse_original_path.setAttribute("fill", "none");
  1482.         mouse_original_path.setAttribute("id", "path " + Date());
  1483.         mouse_original_path.setAttribute("d", "M" + p.x + "," + p.y);
  1484.         slides[activeSlide]["original_element"].appendChild(mouse_original_path);
  1485.         history_original_elements.push(mouse_original_path);
  1486.  
  1487.         mouse_presentation_path = document.createElementNS(NSS["svg"], "path");
  1488.         mouse_presentation_path.setAttribute("stroke", path_colour);
  1489.         mouse_presentation_path.setAttribute("stroke-width", path_paint_width);
  1490.         mouse_presentation_path.setAttribute("fill", "none");
  1491.         mouse_presentation_path.setAttribute("id", "path " + Date() + " presentation copy");
  1492.         mouse_presentation_path.setAttribute("d", "M" + p.x + "," + p.y);
  1493.  
  1494.         if (slides[activeSlide]["viewGroup"])
  1495.             slides[activeSlide]["viewGroup"].appendChild(mouse_presentation_path);
  1496.         else
  1497.             slides[activeSlide]["element"].appendChild(mouse_presentation_path);
  1498.  
  1499.         history_presentation_elements.push(mouse_presentation_path);
  1500.  
  1501.         return false;
  1502.     }
  1503.  
  1504.     return true;
  1505. }
  1506.  
  1507. /** Event handler for mouse up in drawing mode.
  1508.  *
  1509.  *  @param e the event
  1510.  */
  1511. function drawingMouseup(e)
  1512. {
  1513.     if(!e)
  1514.         e = window.event;
  1515.  
  1516.     if (mouse_presentation_path != null)
  1517.     {
  1518.         var p = calcCoord(e);
  1519.         var d = mouse_presentation_path.getAttribute("d");
  1520.         d += " L" + p.x + "," + p.y;
  1521.         mouse_presentation_path.setAttribute("d", d);
  1522.         mouse_presentation_path = null;
  1523.         mouse_original_path.setAttribute("d", d);
  1524.         mouse_original_path = null;
  1525.  
  1526.         return false;
  1527.     }
  1528.  
  1529.     return true;
  1530. }
  1531.  
  1532. /** Event handler for mouse move in drawing mode.
  1533.  *
  1534.  *  @param e the event
  1535.  */
  1536. function drawingMousemove(e)
  1537. {
  1538.     if(!e)
  1539.         e = window.event;
  1540.  
  1541.     var dist = (mouse_last_x - e.clientX) * (mouse_last_x - e.clientX) + (mouse_last_y - e.clientY) * (mouse_last_y - e.clientY);
  1542.  
  1543.     if (mouse_presentation_path == null)
  1544.     {
  1545.         return true;
  1546.     }
  1547.  
  1548.     if (dist >= mouse_min_dist_sqr)
  1549.     {
  1550.         var p = calcCoord(e);
  1551.         var d = mouse_presentation_path.getAttribute("d");
  1552.         d += " L" + p.x + "," + p.y;
  1553.         mouse_presentation_path.setAttribute("d", d);
  1554.         mouse_original_path.setAttribute("d", d);
  1555.         mouse_last_x = e.clientX;
  1556.         mouse_last_y = e.clientY;
  1557.     }
  1558.  
  1559.     return false;
  1560. }
  1561.  
  1562. /** Event handler for mouse wheel events in slide mode.
  1563.  *  based on http://adomas.org/javascript-mouse-wheel/
  1564.  *
  1565.  *  @param e the event
  1566.  */
  1567. function slideMousewheel(e)
  1568. {
  1569.     var delta = 0;
  1570.  
  1571.     if (!e)
  1572.         e = window.event;
  1573.  
  1574.     if (e.wheelDelta)
  1575.     { // IE Opera
  1576.         delta = e.wheelDelta/120;
  1577.     }
  1578.     else if (e.detail)
  1579.     { // MOZ
  1580.         delta = -e.detail/3;
  1581.     }
  1582.  
  1583.     if (delta > 0)
  1584.         skipEffects(-1);
  1585.     else if (delta < 0)
  1586.         skipEffects(1);
  1587.  
  1588.     if (e.preventDefault)
  1589.         e.preventDefault();
  1590.  
  1591.     e.returnValue = false;
  1592. }
  1593.  
  1594. /** Event handler for mouse wheel events in index mode.
  1595.  *  based on http://adomas.org/javascript-mouse-wheel/
  1596.  *
  1597.  *  @param e the event
  1598.  */
  1599. function indexMousewheel(e)
  1600. {
  1601.     var delta = 0;
  1602.  
  1603.     if (!e)
  1604.         e = window.event;
  1605.  
  1606.     if (e.wheelDelta)
  1607.     { // IE Opera
  1608.         delta = e.wheelDelta/120;
  1609.     }
  1610.     else if (e.detail)
  1611.     { // MOZ
  1612.         delta = -e.detail/3;
  1613.     }
  1614.  
  1615.     if (delta > 0)
  1616.         indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);
  1617.     else if (delta < 0)
  1618.         indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);
  1619.  
  1620.     if (e.preventDefault)
  1621.         e.preventDefault();
  1622.  
  1623.     e.returnValue = false;
  1624. }
  1625.  
  1626. /** Function to set the path paint width.
  1627. */
  1628. function set_path_paint_width()
  1629. {
  1630.     var svgPoint1 = document.documentElement.createSVGPoint();
  1631.     var svgPoint2 = document.documentElement.createSVGPoint();
  1632.  
  1633.     svgPoint1.x = 0.0;
  1634.     svgPoint1.y = 0.0;
  1635.     svgPoint2.x = 1.0;
  1636.     svgPoint2.y = 0.0;
  1637.  
  1638.     var matrix = slides[activeSlide]["element"].getTransformToElement(ROOT_NODE);
  1639.  
  1640.     if (slides[activeSlide]["viewGroup"])
  1641.         matrix = slides[activeSlide]["viewGroup"].getTransformToElement(ROOT_NODE);
  1642.  
  1643.     svgPoint1 = svgPoint1.matrixTransform(matrix);
  1644.     svgPoint2 = svgPoint2.matrixTransform(matrix);
  1645.  
  1646.     path_paint_width = path_width / Math.sqrt((svgPoint2.x - svgPoint1.x) * (svgPoint2.x - svgPoint1.x) + (svgPoint2.y - svgPoint1.y) * (svgPoint2.y - svgPoint1.y));
  1647. }
  1648.  
  1649. /** The view effect.
  1650.  *
  1651.  *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
  1652.  *  @param element the element the effect should be applied to
  1653.  *  @param time the time that has elapsed since the beginning of the effect
  1654.  *  @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.
  1655.  */
  1656. function view(dir, element, time, options)
  1657. {
  1658.     var length = 250;
  1659.     var fraction;
  1660.  
  1661.     if (!options["matrixInitial"])
  1662.     {
  1663.         var tempString = slides[activeSlide]["viewGroup"].getAttribute("transform");
  1664.  
  1665.         if (tempString)
  1666.             options["matrixInitial"] = (new matrixSVG()).fromAttribute(tempString);
  1667.         else
  1668.             options["matrixInitial"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
  1669.     }
  1670.  
  1671.     if ((time == STATE_END) || (time == STATE_START))
  1672.         fraction = 1;
  1673.     else
  1674.     {
  1675.         if (options && options["length"])
  1676.             length = options["length"];
  1677.  
  1678.         fraction = time / length;
  1679.     }
  1680.  
  1681.     if (dir == 1)
  1682.     {
  1683.         if (fraction <= 0)
  1684.         {
  1685.             element.setAttribute("transform", options["matrixInitial"].toAttribute());
  1686.         }
  1687.         else if (fraction >= 1)
  1688.         {
  1689.             element.setAttribute("transform", options["matrixNew"].toAttribute());
  1690.  
  1691.             set_path_paint_width();
  1692.  
  1693.             options["matrixInitial"] = null;
  1694.             return true;
  1695.         }
  1696.         else
  1697.         {
  1698.             element.setAttribute("transform", options["matrixInitial"].mix(options["matrixNew"], fraction).toAttribute());
  1699.         }
  1700.     }
  1701.     else if (dir == -1)
  1702.     {
  1703.         if (fraction <= 0)
  1704.         {
  1705.             element.setAttribute("transform", options["matrixInitial"].toAttribute());
  1706.         }
  1707.         else if (fraction >= 1)
  1708.         {
  1709.             element.setAttribute("transform", options["matrixOld"].toAttribute());
  1710.             set_path_paint_width();
  1711.  
  1712.             options["matrixInitial"] = null;
  1713.             return true;
  1714.         }
  1715.         else
  1716.         {
  1717.             element.setAttribute("transform", options["matrixInitial"].mix(options["matrixOld"], fraction).toAttribute());
  1718.         }
  1719.     }
  1720.  
  1721.     return false;
  1722. }
  1723.  
  1724. /** The fade effect.
  1725.  *
  1726.  *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
  1727.  *  @param element the element the effect should be applied to
  1728.  *  @param time the time that has elapsed since the beginning of the effect
  1729.  *  @param options a dictionary with additional options (e.g. length of the effect)
  1730.  */
  1731. function fade(dir, element, time, options)
  1732. {
  1733.     var length = 250;
  1734.     var fraction;
  1735.  
  1736.     if ((time == STATE_END) || (time == STATE_START))
  1737.         fraction = 1;
  1738.     else
  1739.     {
  1740.         if (options && options["length"])
  1741.             length = options["length"];
  1742.  
  1743.         fraction = time / length;
  1744.     }
  1745.  
  1746.     if (dir == 1)
  1747.     {
  1748.         if (fraction <= 0)
  1749.         {
  1750.             element.style.display = "none";
  1751.             element.setAttribute("opacity", 0);
  1752.         }
  1753.         else if (fraction >= 1)
  1754.         {
  1755.             element.style.display = "inherit";
  1756.             element.setAttribute("opacity", 1);
  1757.             return true;
  1758.         }
  1759.         else
  1760.         {
  1761.             element.style.display = "inherit";
  1762.             element.setAttribute("opacity", fraction);
  1763.         }
  1764.     }
  1765.     else if (dir == -1)
  1766.     {
  1767.         if (fraction <= 0)
  1768.         {
  1769.             element.style.display = "inherit";
  1770.             element.setAttribute("opacity", 1);
  1771.         }
  1772.         else if (fraction >= 1)
  1773.         {
  1774.             element.setAttribute("opacity", 0);
  1775.             element.style.display = "none";
  1776.             return true;
  1777.         }
  1778.         else
  1779.         {
  1780.             element.style.display = "inherit";
  1781.             element.setAttribute("opacity", 1 - fraction);
  1782.         }
  1783.     }
  1784.     return false;
  1785. }
  1786.  
  1787. /** The appear effect.
  1788.  *
  1789.  *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
  1790.  *  @param element the element the effect should be applied to
  1791.  *  @param time the time that has elapsed since the beginning of the effect
  1792.  *  @param options a dictionary with additional options (e.g. length of the effect)
  1793.  */
  1794. function appear(dir, element, time, options)
  1795. {
  1796.     if (dir == 1)
  1797.     {
  1798.         element.style.display = "inherit";
  1799.         element.setAttribute("opacity",1);
  1800.     }
  1801.     else if (dir == -1)
  1802.     {
  1803.         element.style.display = "none";
  1804.         element.setAttribute("opacity",0);
  1805.     }
  1806.     return true;
  1807. }
  1808.  
  1809. /** The pop effect.
  1810.  *
  1811.  *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
  1812.  *  @param element the element the effect should be applied to
  1813.  *  @param time the time that has elapsed since the beginning of the effect
  1814.  *  @param options a dictionary with additional options (e.g. length of the effect)
  1815.  */
  1816. function pop(dir, element, time, options)
  1817. {
  1818.     var length = 500;
  1819.     var fraction;
  1820.  
  1821.     if ((time == STATE_END) || (time == STATE_START))
  1822.         fraction = 1;
  1823.     else
  1824.     {
  1825.         if (options && options["length"])
  1826.             length = options["length"];
  1827.  
  1828.         fraction = time / length;
  1829.     }
  1830.  
  1831.     if (dir == 1)
  1832.     {
  1833.         if (fraction <= 0)
  1834.         {
  1835.             element.setAttribute("opacity", 0);
  1836.             element.setAttribute("transform", "scale(0)");
  1837.             element.style.display = "none";
  1838.         }
  1839.         else if (fraction >= 1)
  1840.         {
  1841.             element.setAttribute("opacity", 1);
  1842.             element.removeAttribute("transform");
  1843.             element.style.display = "inherit";
  1844.             return true;
  1845.         }
  1846.         else
  1847.         {
  1848.             element.style.display = "inherit";
  1849.             var opacityFraction = fraction * 3;
  1850.             if (opacityFraction > 1)
  1851.                 opacityFraction = 1;
  1852.             element.setAttribute("opacity", opacityFraction);
  1853.             var offsetX = WIDTH * (1.0 - fraction) / 2.0;
  1854.             var offsetY = HEIGHT * (1.0 - fraction) / 2.0;
  1855.             element.setAttribute("transform", "translate(" + offsetX + "," + offsetY + ") scale(" + fraction + ")");
  1856.         }
  1857.     }
  1858.     else if (dir == -1)
  1859.     {
  1860.         if (fraction <= 0)
  1861.         {
  1862.             element.setAttribute("opacity", 1);
  1863.             element.setAttribute("transform", "scale(1)");
  1864.             element.style.display = "inherit";
  1865.         }
  1866.         else if (fraction >= 1)
  1867.         {
  1868.             element.setAttribute("opacity", 0);
  1869.             element.removeAttribute("transform");
  1870.             element.style.display = "none";
  1871.             return true;
  1872.         }
  1873.         else
  1874.         {
  1875.             element.setAttribute("opacity", 1 - fraction);
  1876.             element.setAttribute("transform", "scale(" + 1 - fraction + ")");
  1877.             element.style.display = "inherit";
  1878.         }
  1879.     }
  1880.     return false;
  1881. }
  1882.  
  1883. /** Function to set a slide either to the start or the end state.
  1884.  *  
  1885.  *  @param slide the slide to use
  1886.  *  @param state the state into which the slide should be set
  1887.  */
  1888. function setSlideToState(slide, state)
  1889. {
  1890.     slides[slide]["viewGroup"].setAttribute("transform", slides[slide].initialView);
  1891.  
  1892.     if (slides[slide]["effects"])
  1893.     {    
  1894.         if (state == STATE_END)
  1895.         {
  1896.             for (var counter = 0; counter < slides[slide]["effects"].length; counter++)
  1897.             {
  1898.                 for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
  1899.                 {
  1900.                     var effect = slides[slide]["effects"][counter][subCounter];
  1901.                     if (effect["effect"] == "fade")
  1902.                         fade(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1903.                     else if (effect["effect"] == "appear")
  1904.                         appear(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1905.                     else if (effect["effect"] == "pop")
  1906.                         pop(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1907.                     else if (effect["effect"] == "view")
  1908.                         view(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1909.                 }
  1910.             }
  1911.         }
  1912.         else if (state == STATE_START)
  1913.         {
  1914.             for (var counter = slides[slide]["effects"].length - 1; counter >= 0; counter--)
  1915.             {
  1916.                 for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
  1917.                 {
  1918.                     var effect = slides[slide]["effects"][counter][subCounter];
  1919.                     if (effect["effect"] == "fade")
  1920.                         fade(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);    
  1921.                     else if (effect["effect"] == "appear")
  1922.                         appear(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);    
  1923.                     else if (effect["effect"] == "pop")
  1924.                         pop(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);    
  1925.                     else if (effect["effect"] == "view")
  1926.                         view(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);    
  1927.                 }
  1928.             }
  1929.         }
  1930.         else
  1931.         {
  1932.             setSlideToState(slide, STATE_START);
  1933.  
  1934.             for (var counter = 0; counter < slides[slide]["effects"].length && counter < state; counter++)
  1935.             {
  1936.                 for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
  1937.                 {
  1938.                     var effect = slides[slide]["effects"][counter][subCounter];
  1939.                     if (effect["effect"] == "fade")
  1940.                         fade(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1941.                     else if (effect["effect"] == "appear")
  1942.                         appear(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1943.                     else if (effect["effect"] == "pop")
  1944.                         pop(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1945.                     else if (effect["effect"] == "view")
  1946.                         view(effect["dir"], effect["element"], STATE_END, effect["options"]);    
  1947.                 }
  1948.             }
  1949.         }
  1950.     }
  1951.  
  1952.     window.location.hash = (activeSlide + 1) + '_' + activeEffect;
  1953. }
  1954.  
  1955. /** Convenience function to translate a attribute string into a dictionary.
  1956.  *
  1957.  *    @param str the attribute string
  1958.  *  @return a dictionary
  1959.  *  @see dictToPropStr
  1960.  */
  1961. function propStrToDict(str)
  1962. {
  1963.     var list = str.split(";");
  1964.     var obj = new Object();
  1965.  
  1966.     for (var counter = 0; counter < list.length; counter++)
  1967.     {
  1968.         var subStr = list[counter];
  1969.         var subList = subStr.split(":");
  1970.         if (subList.length == 2)
  1971.         {
  1972.             obj[subList[0]] = subList[1];
  1973.         }    
  1974.     }
  1975.  
  1976.     return obj;
  1977. }
  1978.  
  1979. /** Convenience function to translate a dictionary into a string that can be used as an attribute.
  1980.  *
  1981.  *  @param dict the dictionary to convert
  1982.  *  @return a string that can be used as an attribute
  1983.  *  @see propStrToDict
  1984.  */
  1985. function dictToPropStr(dict)
  1986. {
  1987.     var str = "";
  1988.  
  1989.     for (var key in dict)
  1990.     {
  1991.         str += key + ":" + dict[key] + ";";
  1992.     }
  1993.  
  1994.     return str;
  1995. }
  1996.  
  1997. /** Sub-function to add a suffix to the ids of the node and all its children.
  1998.  *    
  1999.  *    @param node the node to change
  2000.  *    @param suffix the suffix to add
  2001.  *    @param replace dictionary of replaced ids
  2002.  *  @see suffixNodeIds
  2003.  */
  2004. function suffixNoneIds_sub(node, suffix, replace)
  2005. {
  2006.     if (node.nodeType == 1)
  2007.     {
  2008.         if (node.getAttribute("id"))
  2009.         {
  2010.             var id = node.getAttribute("id")
  2011.                 replace["#" + id] = id + suffix;
  2012.             node.setAttribute("id", id + suffix);
  2013.         }
  2014.  
  2015.         if ((node.nodeName == "use") && (node.getAttributeNS(NSS["xlink"], "href")) && (replace[node.getAttribute(NSS["xlink"], "href")]))
  2016.             node.setAttribute(NSS["xlink"], "href", node.getAttribute(NSS["xlink"], "href") + suffix);
  2017.  
  2018.         if (node.childNodes)
  2019.         {
  2020.             for (var counter = 0; counter < node.childNodes.length; counter++)
  2021.                 suffixNoneIds_sub(node.childNodes[counter], suffix, replace);
  2022.         }
  2023.     }
  2024. }
  2025.  
  2026. /** Function to add a suffix to the ids of the node and all its children.
  2027.  *    
  2028.  *    @param node the node to change
  2029.  *    @param suffix the suffix to add
  2030.  *  @return the changed node
  2031.  *  @see suffixNodeIds_sub
  2032.  */
  2033. function suffixNodeIds(node, suffix)
  2034. {
  2035.     var replace = new Object();
  2036.  
  2037.     suffixNoneIds_sub(node, suffix, replace);
  2038.  
  2039.     return node;
  2040. }
  2041.  
  2042. /** Function to build a progress bar.
  2043.  *    
  2044.  *  @param parent node to attach the progress bar to
  2045.  */
  2046. function createProgressBar(parent_node)
  2047. {
  2048.     var g = document.createElementNS(NSS["svg"], "g");
  2049.     g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
  2050.     g.setAttribute("id", "layer_progress_bar");
  2051.     g.setAttribute("style", "display: none;");
  2052.  
  2053.     var rect_progress_bar = document.createElementNS(NSS["svg"], "rect");
  2054.     rect_progress_bar.setAttribute("style", "marker: none; fill: rgb(128, 128, 128); stroke: none;");
  2055.     rect_progress_bar.setAttribute("id", "rect_progress_bar");
  2056.     rect_progress_bar.setAttribute("x", 0);
  2057.     rect_progress_bar.setAttribute("y", 0.99 * HEIGHT);
  2058.     rect_progress_bar.setAttribute("width", 0);
  2059.     rect_progress_bar.setAttribute("height", 0.01 * HEIGHT);
  2060.     g.appendChild(rect_progress_bar);
  2061.  
  2062.     var circle_timer_indicator = document.createElementNS(NSS["svg"], "circle");
  2063.     circle_timer_indicator.setAttribute("style", "marker: none; fill: rgb(255, 0, 0); stroke: none;");
  2064.     circle_timer_indicator.setAttribute("id", "circle_timer_indicator");
  2065.     circle_timer_indicator.setAttribute("cx", 0.005 * HEIGHT);
  2066.     circle_timer_indicator.setAttribute("cy", 0.995 * HEIGHT);
  2067.     circle_timer_indicator.setAttribute("r", 0.005 * HEIGHT);
  2068.     g.appendChild(circle_timer_indicator);
  2069.  
  2070.     parent_node.appendChild(g);
  2071. }
  2072.  
  2073. /** Function to hide the progress bar.
  2074.  *    
  2075.  */
  2076. function hideProgressBar()
  2077. {
  2078.     var progress_bar = document.getElementById("layer_progress_bar");
  2079.  
  2080.     if (!progress_bar)
  2081.     {
  2082.         return;
  2083.     }
  2084.  
  2085.     progress_bar.setAttribute("style", "display: none;");
  2086. }
  2087.  
  2088. /** Function to show the progress bar.
  2089.  *    
  2090.  */
  2091. function showProgressBar()
  2092. {
  2093.     var progress_bar = document.getElementById("layer_progress_bar");
  2094.  
  2095.     if (!progress_bar)
  2096.     {
  2097.         return;
  2098.     }
  2099.  
  2100.     progress_bar.setAttribute("style", "display: inherit;");
  2101. }
  2102.  
  2103. /** Set progress bar value.
  2104.  *    
  2105.  *    @param value the current slide number
  2106.  *
  2107.  */
  2108. function setProgressBarValue(value)
  2109. {
  2110.     var rect_progress_bar = document.getElementById("rect_progress_bar");
  2111.  
  2112.     if (!rect_progress_bar)
  2113.     {
  2114.         return;
  2115.     }
  2116.  
  2117.     if (value < 1)
  2118.     {
  2119.         // First slide, assumed to be the title of the presentation
  2120.         var x = 0;
  2121.         var w = 0.01 * HEIGHT;
  2122.     }
  2123.     else if (value >= slides.length - 1)
  2124.     {
  2125.         // Last slide, assumed to be the end of the presentation
  2126.         var x = WIDTH - 0.01 * HEIGHT;
  2127.         var w = 0.01 * HEIGHT;
  2128.     }
  2129.     else
  2130.     {
  2131.         value -= 1;
  2132.         value /= (slides.length - 2);
  2133.  
  2134.         var x = WIDTH * value;
  2135.         var w = WIDTH / (slides.length - 2);
  2136.     }
  2137.  
  2138.     rect_progress_bar.setAttribute("x", x);
  2139.     rect_progress_bar.setAttribute("width", w);
  2140. }
  2141.  
  2142. /** Set time indicator.
  2143.  *    
  2144.  *    @param value the percentage of time elapse so far between 0.0 and 1.0
  2145.  *
  2146.  */
  2147. function setTimeIndicatorValue(value)
  2148. {
  2149.     var circle_timer_indicator = document.getElementById("circle_timer_indicator");
  2150.  
  2151.     if (!circle_timer_indicator)
  2152.     {
  2153.         return;
  2154.     }
  2155.  
  2156.     if (value < 0.0)
  2157.     {
  2158.         value = 0.0;
  2159.     }
  2160.  
  2161.     if (value > 1.0)
  2162.     {
  2163.         value = 1.0;
  2164.     }
  2165.  
  2166.     var cx = (WIDTH - 0.01 * HEIGHT) * value + 0.005 * HEIGHT;
  2167.     circle_timer_indicator.setAttribute("cx", cx);
  2168. }
  2169.  
  2170. /** Update timer.
  2171.  *    
  2172.  */
  2173. function updateTimer()
  2174. {
  2175.     timer_elapsed += 1;
  2176.     setTimeIndicatorValue((timer_elapsed - timer_start) / (60 * timer_duration));
  2177. }
  2178.  
  2179. /** Convert screen coordinates to document coordinates.
  2180.  *
  2181.  *  @param e event with screen coordinates
  2182.  *
  2183.  *  @return coordinates in SVG file coordinate system    
  2184.  */
  2185. function calcCoord(e)
  2186. {
  2187.     var svgPoint = document.documentElement.createSVGPoint();
  2188.     svgPoint.x = e.clientX + window.pageXOffset;
  2189.     svgPoint.y = e.clientY + window.pageYOffset;
  2190.  
  2191.     var matrix = slides[activeSlide]["element"].getScreenCTM();
  2192.  
  2193.     if (slides[activeSlide]["viewGroup"])
  2194.         matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
  2195.  
  2196.     svgPoint = svgPoint.matrixTransform(matrix.inverse());
  2197.     return svgPoint;
  2198. }
  2199.  
  2200. /** Add slide.
  2201.  *
  2202.  *    @param after_slide after which slide the new slide should be inserted into the presentation
  2203.  */
  2204. function addSlide(after_slide)
  2205. {
  2206.     number_of_added_slides++;
  2207.  
  2208.     var g = document.createElementNS(NSS["svg"], "g");
  2209.     g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
  2210.     g.setAttribute("id", "Whiteboard " + Date() + " presentation copy");
  2211.     g.setAttribute("style", "display: none;");
  2212.  
  2213.     var new_slide = new Object();
  2214.     new_slide["element"] = g;
  2215.  
  2216.     // Set build in transition.
  2217.     new_slide["transitionIn"] = new Object();
  2218.     var dict = defaultTransitionInDict;
  2219.     new_slide["transitionIn"]["name"] = dict["name"];
  2220.     new_slide["transitionIn"]["options"] = new Object();
  2221.  
  2222.     for (key in dict)
  2223.         if (key != "name")
  2224.             new_slide["transitionIn"]["options"][key] = dict[key];
  2225.  
  2226.     // Set build out transition.
  2227.     new_slide["transitionOut"] = new Object();
  2228.     dict = defaultTransitionOutDict;
  2229.     new_slide["transitionOut"]["name"] = dict["name"];
  2230.     new_slide["transitionOut"]["options"] = new Object();
  2231.  
  2232.     for (key in dict)
  2233.         if (key != "name")
  2234.             new_slide["transitionOut"]["options"][key] = dict[key];
  2235.  
  2236.     // Copy master slide content.
  2237.     if (masterSlide)
  2238.     {
  2239.         var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + Date() + " presentation_copy");
  2240.         clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode");
  2241.         clonedNode.removeAttributeNS(NSS["inkscape"], "label");
  2242.         clonedNode.style.display = "inherit";
  2243.  
  2244.         g.appendChild(clonedNode);
  2245.     }
  2246.  
  2247.     // Substitute auto texts.
  2248.     substituteAutoTexts(g, "Whiteboard " + number_of_added_slides, "W" + number_of_added_slides, slides.length);
  2249.  
  2250.     g.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + (after_slide + 1) + ")) { indexSetActiveSlide(" + (after_slide + 1) + "); };");
  2251.  
  2252.     // Create a transform group.
  2253.     var transformGroup = document.createElementNS(NSS["svg"], "g");
  2254.  
  2255.     // Add content to transform group.
  2256.     while (g.firstChild)
  2257.         transformGroup.appendChild(g.firstChild);
  2258.  
  2259.     // Transfer the transform attribute from the node to the transform group.
  2260.     if (g.getAttribute("transform"))
  2261.     {
  2262.         transformGroup.setAttribute("transform", g.getAttribute("transform"));
  2263.         g.removeAttribute("transform");
  2264.     }
  2265.  
  2266.     // Create a view group.
  2267.     var viewGroup = document.createElementNS(NSS["svg"], "g");
  2268.  
  2269.     viewGroup.appendChild(transformGroup);
  2270.     new_slide["viewGroup"] = g.appendChild(viewGroup);
  2271.  
  2272.     // Insert background.
  2273.     if (BACKGROUND_COLOR != null)
  2274.     {
  2275.         var rectNode = document.createElementNS(NSS["svg"], "rect");
  2276.  
  2277.         rectNode.setAttribute("x", 0);
  2278.         rectNode.setAttribute("y", 0);
  2279.         rectNode.setAttribute("width", WIDTH);
  2280.         rectNode.setAttribute("height", HEIGHT);
  2281.         rectNode.setAttribute("id", "jessyInkBackground" + Date());
  2282.         rectNode.setAttribute("fill", BACKGROUND_COLOR);
  2283.  
  2284.         new_slide["viewGroup"].insertBefore(rectNode, new_slide["viewGroup"].firstChild);
  2285.     }
  2286.  
  2287.     // Set initial view even if there are no other views.
  2288.     var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
  2289.  
  2290.     new_slide["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
  2291.     new_slide.initialView = matrixOld.toAttribute();
  2292.  
  2293.     // Insert slide
  2294.     var node = slides[after_slide]["element"];
  2295.     var next_node = node.nextSibling;
  2296.     var parent_node = node.parentNode;
  2297.  
  2298.     if (next_node)
  2299.     {
  2300.         parent_node.insertBefore(g, next_node);
  2301.     }
  2302.     else
  2303.     {
  2304.         parent_node.appendChild(g);
  2305.     }
  2306.  
  2307.     g = document.createElementNS(NSS["svg"], "g");
  2308.     g.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
  2309.     g.setAttributeNS(NSS["inkscape"], "label", "Whiteboard " + number_of_added_slides);
  2310.     g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
  2311.     g.setAttribute("id", "Whiteboard " + Date());
  2312.     g.setAttribute("style", "display: none;");
  2313.  
  2314.     new_slide["original_element"] = g;
  2315.  
  2316.     node = slides[after_slide]["original_element"];
  2317.     next_node = node.nextSibling;
  2318.     parent_node = node.parentNode;
  2319.  
  2320.     if (next_node)
  2321.     {
  2322.         parent_node.insertBefore(g, next_node);
  2323.     }
  2324.     else
  2325.     {
  2326.         parent_node.appendChild(g);
  2327.     }
  2328.  
  2329.     before_new_slide = slides.slice(0, after_slide + 1);
  2330.     after_new_slide = slides.slice(after_slide + 1);
  2331.     slides = before_new_slide.concat(new_slide, after_new_slide);
  2332.  
  2333.     //resetting the counter attributes on the slides that follow the new slide...
  2334.     for (var counter = after_slide+2; counter < slides.length; counter++)
  2335.     {
  2336.         slides[counter]["element"].setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
  2337.     }
  2338. }
  2339.  
  2340. /** Convenience function to obtain a transformation matrix from a point matrix.
  2341.  *
  2342.  *    @param mPoints Point matrix.
  2343.  *    @return A transformation matrix.
  2344.  */
  2345. function pointMatrixToTransformation(mPoints)
  2346. {
  2347.     mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
  2348.  
  2349.     return mPointsOld.mult(mPoints.inv());
  2350. }
  2351.  
  2352. /** Convenience function to obtain a matrix with three corners of a rectangle.
  2353.  *
  2354.  *    @param rect an svg rectangle
  2355.  *    @return a matrixSVG containing three corners of the rectangle
  2356.  */
  2357. function rectToMatrix(rect)
  2358. {
  2359.     rectWidth = rect.getBBox().width;
  2360.     rectHeight = rect.getBBox().height;
  2361.     rectX = rect.getBBox().x;
  2362.     rectY = rect.getBBox().y;
  2363.     rectXcorr = 0;
  2364.     rectYcorr = 0;
  2365.  
  2366.     scaleX = WIDTH / rectWidth;
  2367.     scaleY = HEIGHT / rectHeight;
  2368.  
  2369.     if (scaleX > scaleY)
  2370.     {
  2371.         scaleX = scaleY;
  2372.         rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
  2373.         rectWidth = WIDTH / scaleX;
  2374.     }    
  2375.     else
  2376.     {
  2377.         scaleY = scaleX;
  2378.         rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
  2379.         rectHeight = HEIGHT / scaleY;
  2380.     }
  2381.  
  2382.     if (rect.transform.baseVal.numberOfItems < 1)
  2383.     {
  2384.         mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
  2385.     }
  2386.     else
  2387.     {
  2388.         mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
  2389.     }
  2390.  
  2391.     newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
  2392.     newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
  2393.  
  2394.     return mRectTrans.mult(newBasePoints.add(newVectors));
  2395. }
  2396.  
  2397. /** Function to handle JessyInk elements.
  2398.  *
  2399.  *    @param    node    Element node.
  2400.  */
  2401. function handleElement(node)
  2402. {
  2403.     if (node.getAttributeNS(NSS['jessyink'], 'element') == 'core.video')
  2404.     {
  2405.         var url;
  2406.         var width;
  2407.         var height;
  2408.         var x;
  2409.         var y;
  2410.         var transform;
  2411.  
  2412.         var tspans = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "tspan");
  2413.  
  2414.         for (var tspanCounter = 0; tspanCounter < tspans.length; tspanCounter++)
  2415.         {
  2416.             if (tspans[tspanCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "url")
  2417.             {
  2418.                 url = tspans[tspanCounter].firstChild.nodeValue;
  2419.             }
  2420.         }
  2421.  
  2422.         var rects = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "rect");
  2423.  
  2424.         for (var rectCounter = 0; rectCounter < rects.length; rectCounter++)
  2425.         {
  2426.             if (rects[rectCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "rect")
  2427.             {
  2428.                 x = rects[rectCounter].getAttribute("x");
  2429.                 y = rects[rectCounter].getAttribute("y");
  2430.                 width = rects[rectCounter].getAttribute("width");
  2431.                 height = rects[rectCounter].getAttribute("height");
  2432.                 transform = rects[rectCounter].getAttribute("transform");
  2433.             }
  2434.         }
  2435.  
  2436.         for (var childCounter = 0; childCounter < node.childNodes.length; childCounter++)
  2437.         {
  2438.             if (node.childNodes[childCounter].nodeType == 1)
  2439.             {
  2440.                 if (node.childNodes[childCounter].style)
  2441.                 {
  2442.                     node.childNodes[childCounter].style.display = 'none';
  2443.                 }
  2444.                 else
  2445.                 {
  2446.                     node.childNodes[childCounter].setAttribute("style", "display: none;");
  2447.                 }
  2448.             }
  2449.         }
  2450.  
  2451.         var foreignNode = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
  2452.         foreignNode.setAttribute("x", x);
  2453.         foreignNode.setAttribute("y", y);
  2454.         foreignNode.setAttribute("width", width);
  2455.         foreignNode.setAttribute("height", height);
  2456.         foreignNode.setAttribute("transform", transform);
  2457.  
  2458.         var videoNode = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
  2459.         videoNode.setAttribute("src", url);
  2460.  
  2461.         foreignNode.appendChild(videoNode);
  2462.         node.appendChild(foreignNode);
  2463.     }
  2464. }
  2465.  
  2466. /** Class processing the location hash.
  2467.  *
  2468.  *    @param str location hash
  2469.  */
  2470. function LocationHash(str)
  2471. {
  2472.     this.slideNumber = 0;
  2473.     this.effectNumber = 0;
  2474.  
  2475.     str = str.substr(1, str.length - 1);
  2476.  
  2477.     var parts = str.split('_');
  2478.  
  2479.     // Try to extract slide number.
  2480.     if (parts.length >= 1)
  2481.     {
  2482.         try
  2483.         {
  2484.             var slideNumber = parseInt(parts[0]);
  2485.  
  2486.             if (!isNaN(slideNumber))
  2487.             {
  2488.                 this.slideNumber = slideNumber - 1;
  2489.             }
  2490.         }
  2491.         catch (e)
  2492.         {
  2493.         }
  2494.     }
  2495.     
  2496.     // Try to extract effect number.
  2497.     if (parts.length >= 2)
  2498.     {
  2499.         try
  2500.         {
  2501.             var effectNumber = parseInt(parts[1]);
  2502.  
  2503.             if (!isNaN(effectNumber))
  2504.             {
  2505.                 this.effectNumber = effectNumber;
  2506.             }
  2507.         }
  2508.         catch (e)
  2509.         {
  2510.         }
  2511.     }
  2512. }
  2513.  
  2514. /** Class representing an svg matrix.
  2515. */
  2516. function matrixSVG()
  2517. {
  2518.     this.e11 = 0; // a
  2519.     this.e12 = 0; // c
  2520.     this.e13 = 0; // e
  2521.     this.e21 = 0; // b
  2522.     this.e22 = 0; // d
  2523.     this.e23 = 0; // f
  2524.     this.e31 = 0;
  2525.     this.e32 = 0;
  2526.     this.e33 = 0;
  2527. }
  2528.  
  2529. /** Constructor function.
  2530.  *
  2531.  *    @param a element a (i.e. 1, 1) as described in the svg standard.
  2532.  *    @param b element b (i.e. 2, 1) as described in the svg standard.
  2533.  *    @param c element c (i.e. 1, 2) as described in the svg standard.
  2534.  *    @param d element d (i.e. 2, 2) as described in the svg standard.
  2535.  *    @param e element e (i.e. 1, 3) as described in the svg standard.
  2536.  *    @param f element f (i.e. 2, 3) as described in the svg standard.
  2537.  */
  2538. matrixSVG.prototype.fromSVGElements = function(a, b, c, d, e, f)
  2539. {
  2540.     this.e11 = a;
  2541.     this.e12 = c;
  2542.     this.e13 = e;
  2543.     this.e21 = b;
  2544.     this.e22 = d;
  2545.     this.e23 = f;
  2546.     this.e31 = 0;
  2547.     this.e32 = 0;
  2548.     this.e33 = 1;
  2549.  
  2550.     return this;
  2551. }
  2552.  
  2553. /** Constructor function.
  2554.  *
  2555.  *    @param matrix an svg matrix as described in the svg standard.
  2556.  */
  2557. matrixSVG.prototype.fromSVGMatrix = function(m)
  2558. {
  2559.     this.e11 = m.a;
  2560.     this.e12 = m.c;
  2561.     this.e13 = m.e;
  2562.     this.e21 = m.b;
  2563.     this.e22 = m.d;
  2564.     this.e23 = m.f;
  2565.     this.e31 = 0;
  2566.     this.e32 = 0;
  2567.     this.e33 = 1;
  2568.  
  2569.     return this;
  2570. }
  2571.  
  2572. /** Constructor function.
  2573.  *
  2574.  *    @param e11 element 1, 1 of the matrix.
  2575.  *    @param e12 element 1, 2 of the matrix.
  2576.  *    @param e13 element 1, 3 of the matrix.
  2577.  *    @param e21 element 2, 1 of the matrix.
  2578.  *    @param e22 element 2, 2 of the matrix.
  2579.  *    @param e23 element 2, 3 of the matrix.
  2580.  *    @param e31 element 3, 1 of the matrix.
  2581.  *    @param e32 element 3, 2 of the matrix.
  2582.  *    @param e33 element 3, 3 of the matrix.
  2583.  */
  2584. matrixSVG.prototype.fromElements = function(e11, e12, e13, e21, e22, e23, e31, e32, e33)
  2585. {
  2586.     this.e11 = e11;
  2587.     this.e12 = e12;
  2588.     this.e13 = e13;
  2589.     this.e21 = e21;
  2590.     this.e22 = e22;
  2591.     this.e23 = e23;
  2592.     this.e31 = e31;
  2593.     this.e32 = e32;
  2594.     this.e33 = e33;
  2595.  
  2596.     return this;
  2597. }
  2598.  
  2599. /** Constructor function.
  2600.  *
  2601.  *    @param attrString string value of the "transform" attribute (currently only "matrix" is accepted)
  2602.  */
  2603. matrixSVG.prototype.fromAttribute = function(attrString)
  2604. {
  2605.     str = attrString.substr(7, attrString.length - 8);
  2606.  
  2607.     str = str.trim();
  2608.  
  2609.     strArray = str.split(",");
  2610.  
  2611.     // Opera does not use commas to separate the values of the matrix, only spaces.
  2612.     if (strArray.length != 6)
  2613.         strArray = str.split(" ");
  2614.  
  2615.     this.e11 = parseFloat(strArray[0]);
  2616.     this.e21 = parseFloat(strArray[1]);
  2617.     this.e31 = 0;
  2618.     this.e12 = parseFloat(strArray[2]);
  2619.     this.e22 = parseFloat(strArray[3]);
  2620.     this.e32 = 0;
  2621.     this.e13 = parseFloat(strArray[4]);
  2622.     this.e23 = parseFloat(strArray[5]);
  2623.     this.e33 = 1;
  2624.  
  2625.     return this;
  2626. }
  2627.  
  2628. /** Output function
  2629.  *
  2630.  *    @return a string that can be used as the "transform" attribute.
  2631.  */
  2632. matrixSVG.prototype.toAttribute = function()
  2633. {
  2634.     return "matrix(" + this.e11 + ", " + this.e21 + ", " + this.e12 + ", " + this.e22 + ", " + this.e13 + ", " + this.e23 + ")";
  2635. }
  2636.  
  2637. /** Matrix nversion.
  2638.  *
  2639.  *    @return the inverse of the matrix
  2640.  */
  2641. matrixSVG.prototype.inv = function()
  2642. {
  2643.     out = new matrixSVG();
  2644.  
  2645.     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);
  2646.  
  2647.     out.e11 =  (this.e33 * this.e22 - this.e32 * this.e23) / det;
  2648.     out.e12 = -(this.e33 * this.e12 - this.e32 * this.e13) / det;
  2649.     out.e13 =  (this.e23 * this.e12 - this.e22 * this.e13) / det;
  2650.     out.e21 = -(this.e33 * this.e21 - this.e31 * this.e23) / det;
  2651.     out.e22 =  (this.e33 * this.e11 - this.e31 * this.e13) / det;
  2652.     out.e23 = -(this.e23 * this.e11 - this.e21 * this.e13) / det;
  2653.     out.e31 =  (this.e32 * this.e21 - this.e31 * this.e22) / det;
  2654.     out.e32 = -(this.e32 * this.e11 - this.e31 * this.e12) / det;
  2655.     out.e33 =  (this.e22 * this.e11 - this.e21 * this.e12) / det;
  2656.  
  2657.     return out;
  2658. }
  2659.  
  2660. /** Matrix multiplication.
  2661.  *
  2662.  *    @param op another svg matrix
  2663.  *    @return this * op
  2664.  */
  2665. matrixSVG.prototype.mult = function(op)
  2666. {
  2667.     out = new matrixSVG();
  2668.  
  2669.     out.e11 = this.e11 * op.e11 + this.e12 * op.e21 + this.e13 * op.e31;
  2670.     out.e12 = this.e11 * op.e12 + this.e12 * op.e22 + this.e13 * op.e32;
  2671.     out.e13 = this.e11 * op.e13 + this.e12 * op.e23 + this.e13 * op.e33;
  2672.     out.e21 = this.e21 * op.e11 + this.e22 * op.e21 + this.e23 * op.e31;
  2673.     out.e22 = this.e21 * op.e12 + this.e22 * op.e22 + this.e23 * op.e32;
  2674.     out.e23 = this.e21 * op.e13 + this.e22 * op.e23 + this.e23 * op.e33;
  2675.     out.e31 = this.e31 * op.e11 + this.e32 * op.e21 + this.e33 * op.e31;
  2676.     out.e32 = this.e31 * op.e12 + this.e32 * op.e22 + this.e33 * op.e32;
  2677.     out.e33 = this.e31 * op.e13 + this.e32 * op.e23 + this.e33 * op.e33;
  2678.  
  2679.     return out;
  2680. }
  2681.  
  2682. /** Matrix addition.
  2683.  *
  2684.  *    @param op another svg matrix
  2685.  *    @return this + op
  2686.  */
  2687. matrixSVG.prototype.add = function(op)
  2688. {
  2689.     out = new matrixSVG();
  2690.  
  2691.     out.e11 = this.e11 + op.e11;
  2692.     out.e12 = this.e12 + op.e12;
  2693.     out.e13 = this.e13 + op.e13;
  2694.     out.e21 = this.e21 + op.e21;
  2695.     out.e22 = this.e22 + op.e22;
  2696.     out.e23 = this.e23 + op.e23;
  2697.     out.e31 = this.e31 + op.e31;
  2698.     out.e32 = this.e32 + op.e32;
  2699.     out.e33 = this.e33 + op.e33;
  2700.  
  2701.     return out;
  2702. }
  2703.  
  2704. /** Matrix mixing.
  2705.  *
  2706.  *    @param op another svg matrix
  2707.  *    @parma contribOp contribution of the other matrix (0 <= contribOp <= 1)
  2708.  *    @return (1 - contribOp) * this + contribOp * op
  2709.  */
  2710. matrixSVG.prototype.mix = function(op, contribOp)
  2711. {
  2712.     contribThis = 1.0 - contribOp;
  2713.     out = new matrixSVG();
  2714.  
  2715.     out.e11 = contribThis * this.e11 + contribOp * op.e11;
  2716.     out.e12 = contribThis * this.e12 + contribOp * op.e12;
  2717.     out.e13 = contribThis * this.e13 + contribOp * op.e13;
  2718.     out.e21 = contribThis * this.e21 + contribOp * op.e21;
  2719.     out.e22 = contribThis * this.e22 + contribOp * op.e22;
  2720.     out.e23 = contribThis * this.e23 + contribOp * op.e23;
  2721.     out.e31 = contribThis * this.e31 + contribOp * op.e31;
  2722.     out.e32 = contribThis * this.e32 + contribOp * op.e32;
  2723.     out.e33 = contribThis * this.e33 + contribOp * op.e33;
  2724.  
  2725.     return out;
  2726. }
  2727.  
  2728. /** Trimming function for strings.
  2729. */
  2730. String.prototype.trim = function()
  2731. {
  2732.     return this.replace(/^\s+|\s+$/g, '');
  2733. }
  2734.  
  2735.