Code

Adding JessyInk 1.5.1 extension set by Hannes Hochreiner
authorJosh Andler <scislac@gmail.com>
Fri, 4 Jun 2010 22:18:31 +0000 (15:18 -0700)
committerJosh Andler <scislac@gmail.com>
Fri, 4 Jun 2010 22:18:31 +0000 (15:18 -0700)
30 files changed:
AUTHORS
share/extensions/jessyInk.js [new file with mode: 0755]
share/extensions/jessyInk_autoTexts.inx [new file with mode: 0644]
share/extensions/jessyInk_autoTexts.py [new file with mode: 0755]
share/extensions/jessyInk_core_mouseHandler_noclick.js [new file with mode: 0644]
share/extensions/jessyInk_core_mouseHandler_zoomControl.js [new file with mode: 0644]
share/extensions/jessyInk_effects.inx [new file with mode: 0644]
share/extensions/jessyInk_effects.py [new file with mode: 0755]
share/extensions/jessyInk_export.inx [new file with mode: 0644]
share/extensions/jessyInk_export.py [new file with mode: 0755]
share/extensions/jessyInk_install.inx [new file with mode: 0644]
share/extensions/jessyInk_install.py [new file with mode: 0755]
share/extensions/jessyInk_keyBindings.inx [new file with mode: 0644]
share/extensions/jessyInk_keyBindings.py [new file with mode: 0755]
share/extensions/jessyInk_masterSlide.inx [new file with mode: 0644]
share/extensions/jessyInk_masterSlide.py [new file with mode: 0755]
share/extensions/jessyInk_mouseHandler.inx [new file with mode: 0644]
share/extensions/jessyInk_mouseHandler.py [new file with mode: 0755]
share/extensions/jessyInk_summary.inx [new file with mode: 0644]
share/extensions/jessyInk_summary.py [new file with mode: 0755]
share/extensions/jessyInk_transitions.inx [new file with mode: 0644]
share/extensions/jessyInk_transitions.py [new file with mode: 0755]
share/extensions/jessyInk_uninstall.inx [new file with mode: 0644]
share/extensions/jessyInk_uninstall.py [new file with mode: 0755]
share/extensions/jessyInk_video.inx [new file with mode: 0644]
share/extensions/jessyInk_video.py [new file with mode: 0755]
share/extensions/jessyInk_video.svg [new file with mode: 0644]
share/extensions/jessyInk_view.inx [new file with mode: 0644]
share/extensions/jessyInk_view.py [new file with mode: 0755]
src/ui/dialog/aboutbox.cpp

diff --git a/AUTHORS b/AUTHORS
index ea6ddcc03dac1b080eaedf4432222a3319596b50..736c46352d6cbbe9c829f4ca2ac57493b26c1f52 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -61,6 +61,7 @@ Dale Harvey
 AurĂ©lio Heckert
 Carl Hetherington
 Jos Hirth
+Hannes Hochreiner
 Thomas Holder
 Joel Holdsworth
 Alan Horkan
diff --git a/share/extensions/jessyInk.js b/share/extensions/jessyInk.js
new file mode 100755 (executable)
index 0000000..51736ad
--- /dev/null
@@ -0,0 +1,2722 @@
+// Copyright 2008, 2009 Hannes Hochreiner
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/.
+
+// Set onload event handler.
+window.onload = jessyInkInit;
+
+// Creating a namespace dictionary. The standard Inkscape namespaces are taken from inkex.py.
+var NSS = new Object();
+NSS['sodipodi']='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd';
+NSS['cc']='http://web.resource.org/cc/';
+NSS['svg']='http://www.w3.org/2000/svg';
+NSS['dc']='http://purl.org/dc/elements/1.1/';
+NSS['rdf']='http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+NSS['inkscape']='http://www.inkscape.org/namespaces/inkscape';
+NSS['xlink']='http://www.w3.org/1999/xlink';
+NSS['xml']='http://www.w3.org/XML/1998/namespace';
+NSS['jessyink']='https://launchpad.net/jessyink';
+
+// Keycodes.
+var LEFT_KEY = 37; // cursor left keycode
+var UP_KEY = 38; // cursor up keycode
+var RIGHT_KEY = 39; // cursor right keycode
+var DOWN_KEY = 40; // cursor down keycode
+var PAGE_UP_KEY = 33; // page up keycode
+var PAGE_DOWN_KEY = 34; // page down keycode
+var HOME_KEY = 36; // home keycode
+var END_KEY = 35; // end keycode
+var ENTER_KEY = 13; // next slide
+var SPACE_KEY = 32;
+
+// Presentation modes.
+var SLIDE_MODE = 1;
+var INDEX_MODE = 2;
+var DRAWING_MODE = 3;
+
+// Mouse handler actions.
+var MOUSE_UP = 1;
+var MOUSE_DOWN = 2;
+var MOUSE_MOVE = 3;
+var MOUSE_WHEEL = 4;
+
+// Parameters.
+var ROOT_NODE = document.getElementsByTagNameNS(NSS["svg"], "svg")[0];
+var HEIGHT = 0;
+var WIDTH = 0;
+var INDEX_COLUMNS_DEFAULT = 4;
+var INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
+var INDEX_OFFSET = 0;
+var STATE_START = -1;
+var STATE_END = -2;
+var BACKGROUND_COLOR = null;
+var slides = new Array();
+
+// Initialisation.
+var currentMode = SLIDE_MODE;
+var masterSlide = null;
+var activeSlide = 0;
+var activeEffect = 0;
+var timeStep = 30; // 40 ms equal 25 frames per second.
+var lastFrameTime = null;
+var processingEffect = false;
+var transCounter = 0;
+var effectArray = 0;
+var defaultTransitionInDict = new Object();
+defaultTransitionInDict["name"] = "appear";
+var defaultTransitionOutDict = new Object();
+defaultTransitionOutDict["name"] = "appear";
+var jessyInkInitialised = false;
+
+// Initialise char and key code dictionaries.
+var charCodeDictionary = getDefaultCharCodeDictionary();
+var keyCodeDictionary = getDefaultKeyCodeDictionary();
+
+// Initialise mouse handler dictionary.
+var mouseHandlerDictionary = getDefaultMouseHandlerDictionary();
+
+var progress_bar_visible = false;
+var timer_elapsed = 0;
+var timer_start = timer_elapsed;
+var timer_duration = 15; // 15 minutes
+
+var history_counter = 0;
+var history_original_elements = new Array();
+var history_presentation_elements = new Array();
+
+var mouse_original_path = null;
+var mouse_presentation_path = null;
+var mouse_last_x = -1;
+var mouse_last_y = -1;
+var mouse_min_dist_sqr = 3 * 3;
+var path_colour = "red";
+var path_width_default = 3;
+var path_width = path_width_default;
+var path_paint_width = path_width;
+
+var number_of_added_slides = 0;
+
+/** Initialisation function.
+ *  The whole presentation is set-up in this function.
+ */
+function jessyInkInit()
+{
+       // Make sure we only execute this code once. Double execution can occur if the onload event handler is set
+       // in the main svg tag as well (as was recommended in earlier versions). Executing this function twice does
+       // not lead to any problems, but it takes more time.
+       if (jessyInkInitialised)
+               return;
+
+       // Making the presentation scaleable.
+       var VIEWBOX = ROOT_NODE.getAttribute("viewBox");
+
+       if (VIEWBOX)
+       {
+               WIDTH = ROOT_NODE.viewBox.animVal.width;
+               HEIGHT = ROOT_NODE.viewBox.animVal.height;
+       }
+       else
+       {
+               HEIGHT = parseFloat(ROOT_NODE.getAttribute("height"));
+               WIDTH = parseFloat(ROOT_NODE.getAttribute("width"));
+               ROOT_NODE.setAttribute("viewBox", "0 0 " + WIDTH + " " + HEIGHT);
+       }
+
+       ROOT_NODE.setAttribute("width", "100%");
+       ROOT_NODE.setAttribute("height", "100%");
+
+       // Setting the background color.
+       var namedViews = document.getElementsByTagNameNS(NSS["sodipodi"], "namedview");
+
+       for (var counter = 0; counter < namedViews.length; counter++)
+       {
+               if (namedViews[counter].hasAttribute("id") && namedViews[counter].hasAttribute("pagecolor"))
+               {
+                       if (namedViews[counter].getAttribute("id") == "base")
+                       {
+                               BACKGROUND_COLOR = namedViews[counter].getAttribute("pagecolor");
+                               var newAttribute = "background-color:" + BACKGROUND_COLOR + ";";
+
+                               if (ROOT_NODE.hasAttribute("style"))
+                                       newAttribute += ROOT_NODE.getAttribute("style");
+
+                               ROOT_NODE.setAttribute("style", newAttribute);
+                       }
+               }
+       }
+
+       // Defining clip-path.
+       var defsNodes = document.getElementsByTagNameNS(NSS["svg"], "defs");
+
+       if (defsNodes.length > 0)
+       {
+               var existingClipPath = document.getElementById("jessyInkSlideClipPath");
+
+               if (!existingClipPath)
+               {
+                       var rectNode = document.createElementNS(NSS["svg"], "rect");
+                       var clipPath = document.createElementNS(NSS["svg"], "clipPath");
+
+                       rectNode.setAttribute("x", 0);
+                       rectNode.setAttribute("y", 0);
+                       rectNode.setAttribute("width", WIDTH);
+                       rectNode.setAttribute("height", HEIGHT);
+
+                       clipPath.setAttribute("id", "jessyInkSlideClipPath");
+                       clipPath.setAttribute("clipPathUnits", "userSpaceOnUse");
+
+                       clipPath.appendChild(rectNode);
+                       defsNodes[0].appendChild(clipPath);
+               }
+       }
+
+       // Making a list of the slide and finding the master slide.
+       var nodes = document.getElementsByTagNameNS(NSS["svg"], "g");
+       var tempSlides = new Array();
+       var existingJessyInkPresentationLayer = null;
+
+       for (var counter = 0; counter < nodes.length; counter++)
+       {
+               if (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") && (nodes[counter].getAttributeNS(NSS["inkscape"], "groupmode") == "layer"))
+               {
+                       if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "masterSlide") == "masterSlide")
+                               masterSlide = nodes[counter];
+                       else if (nodes[counter].getAttributeNS(NSS["inkscape"], "label") && nodes[counter].getAttributeNS(NSS["jessyink"], "presentationLayer") == "presentationLayer")
+                               existingJessyInkPresentationLayer = nodes[counter];
+                       else
+                               tempSlides.push(nodes[counter].getAttribute("id"));
+               }
+               else if (nodes[counter].getAttributeNS(NSS['jessyink'], 'element'))
+               {
+                       handleElement(nodes[counter]);
+               }
+       }
+
+       // Hide master slide set default transitions.
+       if (masterSlide)
+       {
+               masterSlide.style.display = "none";
+
+               if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionIn"))
+                       defaultTransitionInDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionIn"));
+
+               if (masterSlide.hasAttributeNS(NSS["jessyink"], "transitionOut"))
+                       defaultTransitionOutDict = propStrToDict(masterSlide.getAttributeNS(NSS["jessyink"], "transitionOut"));
+       }
+
+       if (existingJessyInkPresentationLayer != null)
+       {
+               existingJessyInkPresentationLayer.parentNode.removeChild(existingJessyInkPresentationLayer);
+       }
+
+       // Set start slide.
+       var hashObj = new LocationHash(window.location.hash);
+
+       activeSlide = hashObj.slideNumber;
+       activeEffect = hashObj.effectNumber;
+
+       if (activeSlide < 0)
+               activeSlide = 0;
+       else if (activeSlide >= tempSlides.length)
+               activeSlide = tempSlides.length - 1;
+
+       var originalNode = document.getElementById(tempSlides[counter]);
+
+       var JessyInkPresentationLayer = document.createElementNS(NSS["svg"], "g");
+       JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
+       JessyInkPresentationLayer.setAttributeNS(NSS["inkscape"], "label", "JessyInk Presentation Layer");
+       JessyInkPresentationLayer.setAttributeNS(NSS["jessyink"], "presentationLayer", "presentationLayer");
+       JessyInkPresentationLayer.setAttribute("id", "jessyink_presentation_layer");
+       JessyInkPresentationLayer.style.display = "inherit";
+       ROOT_NODE.appendChild(JessyInkPresentationLayer);
+
+       // Gathering all the information about the transitions and effects of the slides, set the background
+       // from the master slide and substitute the auto-texts.
+       for (var counter = 0; counter < tempSlides.length; counter++)
+       {
+               var originalNode = document.getElementById(tempSlides[counter]);
+               originalNode.style.display = "none";
+               var node = suffixNodeIds(originalNode.cloneNode(true), "_" + counter);
+               JessyInkPresentationLayer.appendChild(node);
+               slides[counter] = new Object();
+               slides[counter]["original_element"] = originalNode;
+               slides[counter]["element"] = node;
+
+               // Set build in transition.
+               slides[counter]["transitionIn"] = new Object();
+
+               var dict;
+
+               if (node.hasAttributeNS(NSS["jessyink"], "transitionIn"))
+                       dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionIn"));
+               else
+                       dict = defaultTransitionInDict;
+
+               slides[counter]["transitionIn"]["name"] = dict["name"];
+               slides[counter]["transitionIn"]["options"] = new Object();
+
+               for (key in dict)
+                       if (key != "name")
+                               slides[counter]["transitionIn"]["options"][key] = dict[key];
+
+               // Set build out transition.
+               slides[counter]["transitionOut"] = new Object();
+
+               if (node.hasAttributeNS(NSS["jessyink"], "transitionOut"))
+                       dict = propStrToDict(node.getAttributeNS(NSS["jessyink"], "transitionOut"));
+               else
+                       dict = defaultTransitionOutDict;
+
+               slides[counter]["transitionOut"]["name"] = dict["name"];
+               slides[counter]["transitionOut"]["options"] = new Object();
+
+               for (key in dict)
+                       if (key != "name")
+                               slides[counter]["transitionOut"]["options"][key] = dict[key];
+
+               // Copy master slide content.
+               if (masterSlide)
+               {
+                       var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + counter);
+                       clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode");
+                       clonedNode.removeAttributeNS(NSS["inkscape"], "label");
+                       clonedNode.style.display = "inherit";
+
+                       node.insertBefore(clonedNode, node.firstChild);
+               }
+
+               // Setting clip path.
+               node.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
+
+               // Substitute auto texts.
+               substituteAutoTexts(node, node.getAttributeNS(NSS["inkscape"], "label"), counter + 1, tempSlides.length);
+
+               node.removeAttributeNS(NSS["inkscape"], "groupmode");
+               node.removeAttributeNS(NSS["inkscape"], "label");
+
+               // Set effects.
+               var tempEffects = new Array();
+               var groups = new Object();
+
+               for (var IOCounter = 0; IOCounter <= 1; IOCounter++)
+               {
+                       var propName = "";
+                       var dir = 0;
+
+                       if (IOCounter == 0)
+                       {
+                               propName = "effectIn";
+                               dir = 1;
+                       }
+                       else if (IOCounter == 1)
+                       {
+                               propName = "effectOut";
+                               dir = -1;
+                       }
+
+                       var effects = getElementsByPropertyNS(node, NSS["jessyink"], propName);
+
+                       for (var effectCounter = 0; effectCounter < effects.length; effectCounter++)
+                       {
+                               var element = document.getElementById(effects[effectCounter]);
+                               var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], propName));
+
+                               // Put every element that has an effect associated with it, into its own group.
+                               // Unless of course, we already put it into its own group.
+                               if (!(groups[element.id]))
+                               {
+                                       var newGroup = document.createElementNS(NSS["svg"], "g");
+
+                                       element.parentNode.insertBefore(newGroup, element);
+                                       newGroup.appendChild(element.parentNode.removeChild(element));
+                                       groups[element.id] = newGroup;
+                               }
+
+                               var effectDict = new Object();
+
+                               effectDict["effect"] = dict["name"];
+                               effectDict["dir"] = dir;
+                               effectDict["element"] = groups[element.id];
+
+                               for (var option in dict)
+                               {
+                                       if ((option != "name") && (option != "order"))
+                                       {
+                                               if (!effectDict["options"])
+                                                       effectDict["options"] = new Object();
+
+                                               effectDict["options"][option] = dict[option];
+                                       }
+                               }
+
+                               if (!tempEffects[dict["order"]])
+                                       tempEffects[dict["order"]] = new Array();
+
+                               tempEffects[dict["order"]][tempEffects[dict["order"]].length] = effectDict;
+                       }
+               }
+
+               // Make invisible, but keep in rendering tree to ensure that bounding box can be calculated.
+               node.setAttribute("opacity",0);
+               node.style.display = "inherit";
+
+               // Create a transform group.
+               var transformGroup = document.createElementNS(NSS["svg"], "g");
+
+               // Add content to transform group.
+               while (node.firstChild)
+                       transformGroup.appendChild(node.firstChild);
+
+               // Transfer the transform attribute from the node to the transform group.
+               if (node.getAttribute("transform"))
+               {
+                       transformGroup.setAttribute("transform", node.getAttribute("transform"));
+                       node.removeAttribute("transform");
+               }
+
+               // Create a view group.
+               var viewGroup = document.createElementNS(NSS["svg"], "g");
+
+               viewGroup.appendChild(transformGroup);
+               slides[counter]["viewGroup"] = node.appendChild(viewGroup);
+
+               // Insert background.
+               if (BACKGROUND_COLOR != null)
+               {
+                       var rectNode = document.createElementNS(NSS["svg"], "rect");
+
+                       rectNode.setAttribute("x", 0);
+                       rectNode.setAttribute("y", 0);
+                       rectNode.setAttribute("width", WIDTH);
+                       rectNode.setAttribute("height", HEIGHT);
+                       rectNode.setAttribute("id", "jessyInkBackground" + counter);
+                       rectNode.setAttribute("fill", BACKGROUND_COLOR);
+
+                       slides[counter]["viewGroup"].insertBefore(rectNode, slides[counter]["viewGroup"].firstChild);
+               }
+
+               // Set views.
+               var tempViews = new Array();
+               var views = getElementsByPropertyNS(node, NSS["jessyink"], "view");
+               var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+
+               // Set initial view even if there are no other views.
+               slides[counter]["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
+               slides[counter].initialView = matrixOld.toAttribute();
+
+               for (var viewCounter = 0; viewCounter < views.length; viewCounter++)
+               {
+                       var element = document.getElementById(views[viewCounter]);
+                       var dict = propStrToDict(element.getAttributeNS(NSS["jessyink"], "view"));
+
+                       if (dict["order"] == 0)
+                       {
+                               matrixOld = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv());
+                               slides[counter].initialView = matrixOld.toAttribute();
+                       }
+                       else
+                       {
+                               var effectDict = new Object();
+
+                               effectDict["effect"] = dict["name"];
+                               effectDict["dir"] = 1;
+                               effectDict["element"] = slides[counter]["viewGroup"];
+                               effectDict["order"] = dict["order"];
+
+                               for (var option in dict)
+                               {
+                                       if ((option != "name") && (option != "order"))
+                                       {
+                                               if (!effectDict["options"])
+                                                       effectDict["options"] = new Object();
+
+                                               effectDict["options"][option] = dict[option];
+                                       }
+                               }
+
+                               effectDict["options"]["matrixNew"] = pointMatrixToTransformation(rectToMatrix(element)).mult((new matrixSVG()).fromSVGMatrix(slides[counter].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(element.parentNode.getScreenCTM())).inv());
+
+                               tempViews[dict["order"]] = effectDict;
+                       }
+
+                       // Remove element.
+                       element.parentNode.removeChild(element);
+               }
+
+               // Consolidate view array and append it to the effect array.
+               if (tempViews.length > 0)
+               {
+                       for (var viewCounter = 0; viewCounter < tempViews.length; viewCounter++)
+                       {
+                               if (tempViews[viewCounter])
+                               {
+                                       tempViews[viewCounter]["options"]["matrixOld"] = matrixOld;
+                                       matrixOld = tempViews[viewCounter]["options"]["matrixNew"];
+
+                                       if (!tempEffects[tempViews[viewCounter]["order"]])
+                                               tempEffects[tempViews[viewCounter]["order"]] = new Array();
+
+                                       tempEffects[tempViews[viewCounter]["order"]][tempEffects[tempViews[viewCounter]["order"]].length] = tempViews[viewCounter];
+                               }
+                       }
+               }
+
+               // Set consolidated effect array.
+               if (tempEffects.length > 0)
+               {
+                       slides[counter]["effects"] = new Array();
+
+                       for (var effectCounter = 0; effectCounter < tempEffects.length; effectCounter++)
+                       {
+                               if (tempEffects[effectCounter])
+                                       slides[counter]["effects"][slides[counter]["effects"].length] = tempEffects[effectCounter];
+                       }
+               }
+
+               node.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
+
+               // Set visibility for initial state.
+               if (counter == activeSlide)
+               {
+                       node.style.display = "inherit";
+                       node.setAttribute("opacity",1);
+               }
+               else
+               {
+                       node.style.display = "none";
+                       node.setAttribute("opacity",0);
+               }
+       }
+
+       // Set key handler.
+       var jessyInkObjects = document.getElementsByTagNameNS(NSS["svg"], "g");
+
+       for (var counter = 0; counter < jessyInkObjects.length; counter++)
+       {
+               var elem = jessyInkObjects[counter];
+
+               if (elem.getAttributeNS(NSS["jessyink"], "customKeyBindings"))
+               {
+                       if (elem.getCustomKeyBindings != undefined)
+                               keyCodeDictionary = elem.getCustomKeyBindings();
+
+                       if (elem.getCustomCharBindings != undefined)
+                               charCodeDictionary = elem.getCustomCharBindings();
+               }
+       }
+
+       // Set mouse handler.
+       var jessyInkMouseHandler = document.getElementsByTagNameNS(NSS["jessyink"], "mousehandler");
+
+       for (var counter = 0; counter < jessyInkMouseHandler.length; counter++)
+       {
+               var elem = jessyInkMouseHandler[counter];
+
+               if (elem.getMouseHandler != undefined)
+               {
+                       var tempDict = elem.getMouseHandler();
+
+                       for (mode in tempDict)
+                       {
+                               if (!mouseHandlerDictionary[mode])
+                                       mouseHandlerDictionary[mode] = new Object();
+
+                               for (handler in tempDict[mode])
+                                       mouseHandlerDictionary[mode][handler] = tempDict[mode][handler];
+                       }
+               }
+       }
+
+       // Check effect number.
+       if ((activeEffect < 0) || (!slides[activeSlide].effects))
+       {
+               activeEffect = 0;
+       }
+       else if (activeEffect > slides[activeSlide].effects.length)
+       {
+               activeEffect = slides[activeSlide].effects.length;
+       }
+
+       createProgressBar(JessyInkPresentationLayer);
+       hideProgressBar();
+       setProgressBarValue(activeSlide);
+       setTimeIndicatorValue(0);
+       setInterval("updateTimer()", 1000);
+       setSlideToState(activeSlide, activeEffect);
+       jessyInkInitialised = true;
+}
+
+/** Function to subtitute the auto-texts.
+ *
+ *  @param node the node
+ *  @param slideName name of the slide the node is on
+ *  @param slideNumber number of the slide the node is on
+ *  @param numberOfSlides number of slides in the presentation
+ */
+function substituteAutoTexts(node, slideName, slideNumber, numberOfSlides)
+{
+       var texts = node.getElementsByTagNameNS(NSS["svg"], "tspan");
+
+       for (var textCounter = 0; textCounter < texts.length; textCounter++)
+       {
+               if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideNumber")
+                       texts[textCounter].firstChild.nodeValue = slideNumber;
+               else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "numberOfSlides")
+                       texts[textCounter].firstChild.nodeValue = numberOfSlides;
+               else if (texts[textCounter].getAttributeNS(NSS["jessyink"], "autoText") == "slideTitle")
+                       texts[textCounter].firstChild.nodeValue = slideName;
+       }
+}
+
+/** Convenience function to get an element depending on whether it has a property with a particular name.
+ *     This function emulates some dearly missed XPath functionality.
+ *
+ *  @param node the node
+ *  @param namespace namespace of the attribute
+ *  @param name attribute name
+ */
+function getElementsByPropertyNS(node, namespace, name)
+{
+       var elems = new Array();
+
+       if (node.getAttributeNS(namespace, name))
+               elems.push(node.getAttribute("id"));
+
+       for (var counter = 0; counter < node.childNodes.length; counter++)
+       {
+               if (node.childNodes[counter].nodeType == 1)
+                       elems = elems.concat(getElementsByPropertyNS(node.childNodes[counter], namespace, name));
+       }
+
+       return elems;
+}
+
+/** Function to dispatch the next effect, if there is none left, change the slide.
+ *
+ *  @param dir direction of the change (1 = forwards, -1 = backwards)
+ */
+function dispatchEffects(dir)
+{
+       if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
+       {
+               processingEffect = true;
+
+               if (dir == 1)
+               {
+                       effectArray = slides[activeSlide]["effects"][activeEffect];
+                       activeEffect += dir;
+               }
+               else if (dir == -1)
+               {
+                       activeEffect += dir;
+                       effectArray = slides[activeSlide]["effects"][activeEffect];
+               }
+
+               transCounter = 0;
+               startTime = (new Date()).getTime();
+               lastFrameTime = null;
+               effect(dir);
+       }
+       else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
+       {
+               changeSlide(dir);
+       }
+}
+
+/** Function to skip effects and directly either put the slide into start or end state or change slides.
+ *
+ *  @param dir direction of the change (1 = forwards, -1 = backwards)
+ */
+function skipEffects(dir)
+{
+       if (slides[activeSlide]["effects"] && (((dir == 1) && (activeEffect < slides[activeSlide]["effects"].length)) || ((dir == -1) && (activeEffect > 0))))
+       {
+               processingEffect = true;
+
+               if (slides[activeSlide]["effects"] && (dir == 1))
+                       activeEffect = slides[activeSlide]["effects"].length;
+               else
+                       activeEffect = 0;
+
+               if (dir == 1)
+                       setSlideToState(activeSlide, STATE_END);
+               else
+                       setSlideToState(activeSlide, STATE_START);
+
+               processingEffect = false;
+       }
+       else if (((dir == 1) && (activeSlide < (slides.length - 1))) || (((dir == -1) && (activeSlide > 0))))
+       {
+               changeSlide(dir);
+       }
+}
+
+/** Function to change between slides.
+ *
+ *  @param dir direction (1 = forwards, -1 = backwards)
+ */
+function changeSlide(dir)
+{
+       processingEffect = true;
+       effectArray = new Array();
+
+       effectArray[0] = new Object();
+       if (dir == 1)
+       {
+               effectArray[0]["effect"] = slides[activeSlide]["transitionOut"]["name"];
+               effectArray[0]["options"] = slides[activeSlide]["transitionOut"]["options"];
+               effectArray[0]["dir"] = -1;
+       }
+       else if (dir == -1)
+       {
+               effectArray[0]["effect"] = slides[activeSlide]["transitionIn"]["name"];
+               effectArray[0]["options"] = slides[activeSlide]["transitionIn"]["options"];
+               effectArray[0]["dir"] = 1;
+       }
+       effectArray[0]["element"] = slides[activeSlide]["element"];
+
+       activeSlide += dir;
+       setProgressBarValue(activeSlide);
+
+       effectArray[1] = new Object();
+
+       if (dir == 1)
+       {
+               effectArray[1]["effect"] = slides[activeSlide]["transitionIn"]["name"];
+               effectArray[1]["options"] = slides[activeSlide]["transitionIn"]["options"];
+               effectArray[1]["dir"] = 1;
+       }
+       else if (dir == -1)
+       {
+               effectArray[1]["effect"] = slides[activeSlide]["transitionOut"]["name"];
+               effectArray[1]["options"] = slides[activeSlide]["transitionOut"]["options"];
+               effectArray[1]["dir"] = -1;
+       }
+
+       effectArray[1]["element"] = slides[activeSlide]["element"];
+
+       if (slides[activeSlide]["effects"] && (dir == -1))
+               activeEffect = slides[activeSlide]["effects"].length;
+       else
+               activeEffect = 0;
+
+       if (dir == -1)
+               setSlideToState(activeSlide, STATE_END);
+       else
+               setSlideToState(activeSlide, STATE_START);
+
+       transCounter = 0;
+       startTime = (new Date()).getTime();
+       lastFrameTime = null;
+       effect(dir);
+}
+
+/** Function to toggle between index and slide mode.
+*/
+function toggleSlideIndex()
+{
+       var suspendHandle = ROOT_NODE.suspendRedraw(500);
+
+       if (currentMode == SLIDE_MODE)
+       {
+               hideProgressBar();              
+               INDEX_OFFSET = -1;
+               indexSetPageSlide(activeSlide);
+               currentMode = INDEX_MODE;
+       }
+       else if (currentMode == INDEX_MODE)
+       {
+               for (var counter = 0; counter < slides.length; counter++)
+               {
+                       slides[counter]["element"].setAttribute("transform","scale(1)");
+
+                       if (counter == activeSlide)
+                       {
+                               slides[counter]["element"].style.display = "inherit";
+                               slides[counter]["element"].setAttribute("opacity",1);
+                               activeEffect = 0;
+                       }
+                       else
+                       {
+                               slides[counter]["element"].setAttribute("opacity",0);
+                               slides[counter]["element"].style.display = "none";
+                       }
+               }
+               currentMode = SLIDE_MODE;
+               setSlideToState(activeSlide, STATE_START);
+               setProgressBarValue(activeSlide);
+
+               if (progress_bar_visible)
+               {
+                       showProgressBar();
+               }
+       }
+
+       ROOT_NODE.unsuspendRedraw(suspendHandle);
+       ROOT_NODE.forceRedraw();
+}
+
+/** Function to run an effect.
+ *
+ *  @param dir direction in which to play the effect (1 = forwards, -1 = backwards)
+ */
+function effect(dir)
+{
+       var done = true;
+
+       var suspendHandle = ROOT_NODE.suspendRedraw(200);
+
+       for (var counter = 0; counter < effectArray.length; counter++)
+       {
+               if (effectArray[counter]["effect"] == "fade")
+                       done &= fade(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
+               else if (effectArray[counter]["effect"] == "appear")
+                       done &= appear(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
+               else if (effectArray[counter]["effect"] == "pop")
+                       done &= pop(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
+               else if (effectArray[counter]["effect"] == "view")
+                       done &= view(parseInt(effectArray[counter]["dir"]) * dir, effectArray[counter]["element"], transCounter, effectArray[counter]["options"]);
+       }
+
+       ROOT_NODE.unsuspendRedraw(suspendHandle);
+       ROOT_NODE.forceRedraw();
+
+       if (!done)
+       {
+               var currentTime = (new Date()).getTime();
+               var timeDiff = 1;
+
+               transCounter = currentTime - startTime;
+
+               if (lastFrameTime != null)
+               {
+                       timeDiff = timeStep - (currentTime - lastFrameTime);
+
+                       if (timeDiff <= 0)
+                               timeDiff = 1;
+               }
+
+               lastFrameTime = currentTime;
+
+               window.setTimeout("effect(" + dir + ")", timeDiff);
+       }
+       else
+       {
+               window.location.hash = (activeSlide + 1) + '_' + activeEffect;
+               processingEffect = false;
+       }
+}
+
+/** Function to display the index sheet.
+ *
+ *  @param offsetNumber offset number
+ */
+function displayIndex(offsetNumber)
+{
+       var offsetX = 0;
+       var offsetY = 0;
+
+       if (offsetNumber < 0)
+               offsetNumber = 0;
+       else if (offsetNumber >= slides.length)
+               offsetNumber = slides.length - 1;
+
+       for (var counter = 0; counter < slides.length; counter++)
+       {
+               if ((counter < offsetNumber) || (counter > offsetNumber + INDEX_COLUMNS * INDEX_COLUMNS - 1))
+               {
+                       slides[counter]["element"].setAttribute("opacity",0);
+                       slides[counter]["element"].style.display = "none";
+               }
+               else
+               {
+                       offsetX = ((counter - offsetNumber) % INDEX_COLUMNS) * WIDTH;
+                       offsetY = Math.floor((counter - offsetNumber) / INDEX_COLUMNS) * HEIGHT;
+
+                       slides[counter]["element"].setAttribute("transform","scale("+1/INDEX_COLUMNS+") translate("+offsetX+","+offsetY+")");
+                       slides[counter]["element"].style.display = "inherit";
+                       slides[counter]["element"].setAttribute("opacity",0.5);
+               }
+
+               setSlideToState(counter, STATE_END);
+       }
+
+       //do we need to save the current offset?
+       if (INDEX_OFFSET != offsetNumber)
+               INDEX_OFFSET = offsetNumber;
+}
+
+/** Function to set the active slide in the slide view.
+ *
+ *  @param nbr index of the active slide
+ */
+function slideSetActiveSlide(nbr)
+{
+       if (nbr >= slides.length)
+               nbr = slides.length - 1;
+       else if (nbr < 0)
+               nbr = 0;
+
+       slides[activeSlide]["element"].setAttribute("opacity",0);
+       slides[activeSlide]["element"].style.display = "none";
+
+       activeSlide = parseInt(nbr);
+
+       setSlideToState(activeSlide, STATE_START);
+       slides[activeSlide]["element"].style.display = "inherit";
+       slides[activeSlide]["element"].setAttribute("opacity",1);
+
+       activeEffect = 0;
+       setProgressBarValue(nbr);
+}
+
+/** Function to set the active slide in the index view.
+ *
+ *  @param nbr index of the active slide
+ */
+function indexSetActiveSlide(nbr)
+{
+       if (nbr >= slides.length)
+               nbr = slides.length - 1;
+       else if (nbr < 0)
+               nbr = 0;
+
+       slides[activeSlide]["element"].setAttribute("opacity",0.5);
+
+       activeSlide = parseInt(nbr);
+       window.location.hash = (activeSlide + 1) + '_0';
+
+       slides[activeSlide]["element"].setAttribute("opacity",1);
+}
+
+/** Function to set the page and active slide in index view. 
+ *
+ *  @param nbr index of the active slide
+ *
+ *  NOTE: To force a redraw,
+ *  set INDEX_OFFSET to -1 before calling indexSetPageSlide().
+ *
+ *  This is necessary for zooming (otherwise the index might not
+ *  get redrawn) and when switching to index mode.
+ *
+ *  INDEX_OFFSET = -1
+ *  indexSetPageSlide(activeSlide);
+ */
+function indexSetPageSlide(nbr)
+{
+       if (nbr >= slides.length)
+               nbr = slides.length - 1;
+       else if (nbr < 0)
+               nbr = 0;
+
+       //calculate the offset
+       var offset = nbr - nbr % (INDEX_COLUMNS * INDEX_COLUMNS);
+
+       if (offset < 0)
+               offset = 0;
+
+       //if different from kept offset, then record and change the page
+       if (offset != INDEX_OFFSET)
+       {
+               INDEX_OFFSET = offset;
+               displayIndex(INDEX_OFFSET);
+       }
+
+       //set the active slide
+       indexSetActiveSlide(nbr);
+}
+
+/** Event handler for key press.
+ *
+ *  @param e the event
+ */
+function keydown(e)
+{
+       if (!e)
+               e = window.event;
+
+       code = e.keyCode || e.charCode;
+
+       if (!processingEffect && keyCodeDictionary[currentMode] && keyCodeDictionary[currentMode][code])
+               keyCodeDictionary[currentMode][code]();
+       else
+               document.onkeypress = keypress;
+}
+// Set event handler for key down.
+document.onkeydown = keydown;
+
+/** Event handler for key press.
+ *
+ *  @param e the event
+ */
+function keypress(e)
+{
+       document.onkeypress = null;
+
+       if (!e)
+               e = window.event;
+
+       str = String.fromCharCode(e.keyCode || e.charCode);
+
+       if (!processingEffect && charCodeDictionary[currentMode] && charCodeDictionary[currentMode][str])
+               charCodeDictionary[currentMode][str]();
+}
+
+/** Function to supply the default char code dictionary.
+ *
+ * @returns default char code dictionary
+ */
+function getDefaultCharCodeDictionary()
+{
+       var charCodeDict = new Object();
+
+       charCodeDict[SLIDE_MODE] = new Object();
+       charCodeDict[INDEX_MODE] = new Object();
+       charCodeDict[DRAWING_MODE] = new Object();
+
+       charCodeDict[SLIDE_MODE]["i"] = function () { toggleSlideIndex(); };
+       charCodeDict[SLIDE_MODE]["d"] = function () { slideSwitchToDrawingMode(); };
+       charCodeDict[SLIDE_MODE]["D"] = function () { slideQueryDuration(); };
+       charCodeDict[SLIDE_MODE]["n"] = function () { slideAddSlide(activeSlide); };
+       charCodeDict[SLIDE_MODE]["p"] = function () { slideToggleProgressBarVisibility(); };
+       charCodeDict[SLIDE_MODE]["t"] = function () { slideResetTimer(); };
+       charCodeDict[SLIDE_MODE]["e"] = function () { slideUpdateExportLayer(); };
+
+       charCodeDict[DRAWING_MODE]["d"] = function () { drawingSwitchToSlideMode(); };
+       charCodeDict[DRAWING_MODE]["0"] = function () { drawingResetPathWidth(); };
+       charCodeDict[DRAWING_MODE]["1"] = function () { drawingSetPathWidth(1.0); };
+       charCodeDict[DRAWING_MODE]["3"] = function () { drawingSetPathWidth(3.0); };
+       charCodeDict[DRAWING_MODE]["5"] = function () { drawingSetPathWidth(5.0); };
+       charCodeDict[DRAWING_MODE]["7"] = function () { drawingSetPathWidth(7.0); };
+       charCodeDict[DRAWING_MODE]["9"] = function () { drawingSetPathWidth(9.0); };
+       charCodeDict[DRAWING_MODE]["b"] = function () { drawingSetPathColour("blue"); };
+       charCodeDict[DRAWING_MODE]["c"] = function () { drawingSetPathColour("cyan"); };
+       charCodeDict[DRAWING_MODE]["g"] = function () { drawingSetPathColour("green"); };
+       charCodeDict[DRAWING_MODE]["k"] = function () { drawingSetPathColour("black"); };
+       charCodeDict[DRAWING_MODE]["m"] = function () { drawingSetPathColour("magenta"); };
+       charCodeDict[DRAWING_MODE]["o"] = function () { drawingSetPathColour("orange"); };
+       charCodeDict[DRAWING_MODE]["r"] = function () { drawingSetPathColour("red"); };
+       charCodeDict[DRAWING_MODE]["w"] = function () { drawingSetPathColour("white"); };
+       charCodeDict[DRAWING_MODE]["y"] = function () { drawingSetPathColour("yellow"); };
+       charCodeDict[DRAWING_MODE]["z"] = function () { drawingUndo(); };
+
+       charCodeDict[INDEX_MODE]["i"] = function () { toggleSlideIndex(); };
+       charCodeDict[INDEX_MODE]["-"] = function () { indexDecreaseNumberOfColumns(); };
+       charCodeDict[INDEX_MODE]["="] = function () { indexIncreaseNumberOfColumns(); };
+       charCodeDict[INDEX_MODE]["+"] = function () { indexIncreaseNumberOfColumns(); };
+       charCodeDict[INDEX_MODE]["0"] = function () { indexResetNumberOfColumns(); };
+
+       return charCodeDict;
+}
+
+/** Function to supply the default key code dictionary.
+ *
+ * @returns default key code dictionary
+ */
+function getDefaultKeyCodeDictionary()
+{
+       var keyCodeDict = new Object();
+
+       keyCodeDict[SLIDE_MODE] = new Object();
+       keyCodeDict[INDEX_MODE] = new Object();
+       keyCodeDict[DRAWING_MODE] = new Object();
+
+       keyCodeDict[SLIDE_MODE][LEFT_KEY] = function() { dispatchEffects(-1); };
+       keyCodeDict[SLIDE_MODE][RIGHT_KEY] = function() { dispatchEffects(1); };
+       keyCodeDict[SLIDE_MODE][UP_KEY] = function() { skipEffects(-1); };
+       keyCodeDict[SLIDE_MODE][DOWN_KEY] = function() { skipEffects(1); };
+       keyCodeDict[SLIDE_MODE][PAGE_UP_KEY] = function() { dispatchEffects(-1); };
+       keyCodeDict[SLIDE_MODE][PAGE_DOWN_KEY] = function() { dispatchEffects(1); };
+       keyCodeDict[SLIDE_MODE][HOME_KEY] = function() { slideSetActiveSlide(0); };
+       keyCodeDict[SLIDE_MODE][END_KEY] = function() { slideSetActiveSlide(slides.length - 1); };
+       keyCodeDict[SLIDE_MODE][SPACE_KEY] = function() { dispatchEffects(1); };
+
+       keyCodeDict[INDEX_MODE][LEFT_KEY] = function() { indexSetPageSlide(activeSlide - 1); };
+       keyCodeDict[INDEX_MODE][RIGHT_KEY] = function() { indexSetPageSlide(activeSlide + 1); };
+       keyCodeDict[INDEX_MODE][UP_KEY] = function() { indexSetPageSlide(activeSlide - INDEX_COLUMNS); };
+       keyCodeDict[INDEX_MODE][DOWN_KEY] = function() { indexSetPageSlide(activeSlide + INDEX_COLUMNS); };
+       keyCodeDict[INDEX_MODE][PAGE_UP_KEY] = function() { indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS); };
+       keyCodeDict[INDEX_MODE][PAGE_DOWN_KEY] = function() { indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS); };
+       keyCodeDict[INDEX_MODE][HOME_KEY] = function() { indexSetPageSlide(0); };
+       keyCodeDict[INDEX_MODE][END_KEY] = function() { indexSetPageSlide(slides.length - 1); };
+       keyCodeDict[INDEX_MODE][ENTER_KEY] = function() { toggleSlideIndex(); };
+
+       return keyCodeDict;
+}
+
+/** Function to handle all mouse events.
+ *
+ *     @param  evnt    event
+ *     @param  action  type of event (e.g. mouse up, mouse wheel)
+ */
+function mouseHandlerDispatch(evnt, action)
+{
+       if (!evnt)
+               evnt = window.event;
+
+       var retVal = true;
+
+       if (!processingEffect && mouseHandlerDictionary[currentMode] && mouseHandlerDictionary[currentMode][action])
+       {
+               var subRetVal = mouseHandlerDictionary[currentMode][action](evnt);
+
+               if (subRetVal != null && subRetVal != undefined)
+                       retVal = subRetVal;
+       }
+
+       if (evnt.preventDefault && !retVal)
+               evnt.preventDefault();
+
+       evnt.returnValue = retVal;
+
+       return retVal;
+}
+
+// Set mouse event handler.
+document.onmousedown = function(e) { return mouseHandlerDispatch(e, MOUSE_DOWN); };
+document.onmouseup = function(e) { return mouseHandlerDispatch(e, MOUSE_UP); };
+document.onmousemove = function(e) { return mouseHandlerDispatch(e, MOUSE_MOVE); };
+
+// Moz
+if (window.addEventListener)
+{
+       window.addEventListener('DOMMouseScroll', function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); }, false);
+}
+
+// Opera Safari OK - may not work in IE
+window.onmousewheel = function(e) { return mouseHandlerDispatch(e, MOUSE_WHEEL); };
+
+/** Function to supply the default mouse handler dictionary.
+ *
+ * @returns default mouse handler dictionary
+ */
+function getDefaultMouseHandlerDictionary()
+{
+       var mouseHandlerDict = new Object();
+
+       mouseHandlerDict[SLIDE_MODE] = new Object();
+       mouseHandlerDict[INDEX_MODE] = new Object();
+       mouseHandlerDict[DRAWING_MODE] = new Object();
+
+       mouseHandlerDict[SLIDE_MODE][MOUSE_DOWN] = function(evnt) { dispatchEffects(1); };
+       mouseHandlerDict[SLIDE_MODE][MOUSE_WHEEL] = function(evnt) { slideMousewheel(evnt); };
+
+       mouseHandlerDict[INDEX_MODE][MOUSE_DOWN] = function(evnt) { toggleSlideIndex(); };
+
+       mouseHandlerDict[DRAWING_MODE][MOUSE_DOWN] = function(evnt) { drawingMousedown(evnt); };
+       mouseHandlerDict[DRAWING_MODE][MOUSE_UP] = function(evnt) { drawingMouseup(evnt); };
+       mouseHandlerDict[DRAWING_MODE][MOUSE_MOVE] = function(evnt) { drawingMousemove(evnt); };
+
+       return mouseHandlerDict;
+}
+
+/** Function to switch from slide mode to drawing mode.
+*/
+function slideSwitchToDrawingMode()
+{
+       currentMode = DRAWING_MODE;
+
+       var tempDict;
+
+       if (ROOT_NODE.hasAttribute("style"))
+               tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
+       else
+               tempDict = new Object();
+
+       tempDict["cursor"] = "crosshair";
+       ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
+}
+
+/** Function to switch from drawing mode to slide mode.
+*/
+function drawingSwitchToSlideMode()
+{
+       currentMode = SLIDE_MODE;
+
+       var tempDict;
+
+       if (ROOT_NODE.hasAttribute("style"))
+               tempDict = propStrToDict(ROOT_NODE.getAttribute("style"));
+       else
+               tempDict = new Object();
+
+       tempDict["cursor"] = "auto";
+       ROOT_NODE.setAttribute("style", dictToPropStr(tempDict));
+}
+
+/** Function to decrease the number of columns in index mode.
+*/
+function indexDecreaseNumberOfColumns()
+{
+       if (INDEX_COLUMNS >= 3)
+       {
+               INDEX_COLUMNS -= 1;
+               INDEX_OFFSET = -1
+                       indexSetPageSlide(activeSlide);
+       }
+}
+
+/** Function to increase the number of columns in index mode.
+*/
+function indexIncreaseNumberOfColumns()
+{
+       if (INDEX_COLUMNS < 7)
+       {
+               INDEX_COLUMNS += 1;
+               INDEX_OFFSET = -1
+                       indexSetPageSlide(activeSlide);
+       }
+}
+
+/** Function to reset the number of columns in index mode.
+*/
+function indexResetNumberOfColumns()
+{
+       if (INDEX_COLUMNS != INDEX_COLUMNS_DEFAULT)
+       {
+               INDEX_COLUMNS = INDEX_COLUMNS_DEFAULT;
+               INDEX_OFFSET = -1
+                       indexSetPageSlide(activeSlide);
+       }
+}
+
+/** Function to reset path width in drawing mode.
+*/
+function drawingResetPathWidth()
+{
+       path_width = path_width_default;
+       set_path_paint_width();
+}
+
+/** Function to set path width in drawing mode.
+ *
+ * @param width new path width
+ */
+function drawingSetPathWidth(width)
+{
+       path_width = width;
+       set_path_paint_width();
+}
+
+/** Function to set path colour in drawing mode.
+ *
+ * @param colour new path colour
+ */
+function drawingSetPathColour(colour)
+{
+       path_colour = colour;
+}
+
+/** Function to query the duration of the presentation from the user in slide mode.
+*/
+function slideQueryDuration()
+{
+       var new_duration = prompt("Length of presentation in minutes?", timer_duration);
+
+       if ((new_duration != null) && (new_duration != ''))
+       {
+               timer_duration = new_duration;
+       }
+
+       updateTimer();
+}
+
+/** Function to add new slide in slide mode.
+ *
+ * @param afterSlide after which slide to insert the new one
+ */
+function slideAddSlide(afterSlide)
+{
+       addSlide(afterSlide);
+       slideSetActiveSlide(afterSlide + 1);
+       updateTimer();
+}
+
+/** Function to toggle the visibility of the progress bar in slide mode.
+*/
+function slideToggleProgressBarVisibility()
+{
+       if (progress_bar_visible)
+       {
+               progress_bar_visible = false;
+               hideProgressBar();
+       }
+       else
+       {
+               progress_bar_visible = true;
+               showProgressBar();
+       }
+}
+
+/** Function to reset the timer in slide mode.
+*/
+function slideResetTimer()
+{
+       timer_start = timer_elapsed;
+       updateTimer();
+}
+
+/** Convenience function to pad a string with zero in front up to a certain length.
+ */
+function padString(str, len)
+{
+       var outStr = str;
+
+       while (outStr.length < len)
+       {
+               outStr = '0' + outStr;
+       }
+
+       return outStr;
+}
+
+/** Function to update the export layer.
+ */
+function slideUpdateExportLayer()
+{
+       // Suspend redraw since we are going to mess with the slides.
+       var suspendHandle = ROOT_NODE.suspendRedraw(2000);
+
+       var tmpActiveSlide = activeSlide;
+       var tmpActiveEffect = activeEffect;
+       var exportedLayers = new Array();
+
+       for (var counterSlides = 0; counterSlides < slides.length; counterSlides++)
+       {
+               var exportNode;
+
+               setSlideToState(counterSlides, STATE_START);
+
+               var maxEffect = 0;
+
+               if (slides[counterSlides].effects)
+               {
+                       maxEffect = slides[counterSlides].effects.length;
+               }
+
+               exportNode = slides[counterSlides].element.cloneNode(true);
+               exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
+               exportNode.setAttributeNS(NSS["inkscape"], "label", "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString("0", maxEffect.toString().length));
+
+               exportedLayers.push(exportNode);
+
+               if (slides[counterSlides]["effects"])
+               {       
+                       for (var counter = 0; counter < slides[counterSlides]["effects"].length; counter++)
+                       {
+                               for (var subCounter = 0; subCounter < slides[counterSlides]["effects"][counter].length; subCounter++)
+                               {
+                                       var effect = slides[counterSlides]["effects"][counter][subCounter];
+                                       if (effect["effect"] == "fade")
+                                               fade(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); 
+                                       else if (effect["effect"] == "appear")
+                                               appear(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);       
+                                       else if (effect["effect"] == "pop")
+                                               pop(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]);  
+                                       else if (effect["effect"] == "view")
+                                               view(parseInt(effect["dir"]), effect["element"], STATE_END, effect["options"]); 
+                               }
+
+                               var layerName = "slide_" + padString((counterSlides + 1).toString(), slides.length.toString().length) + "_effect_" + padString((counter + 1).toString(), maxEffect.toString().length);
+                               exportNode = slides[counterSlides].element.cloneNode(true);
+                               exportNode.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
+                               exportNode.setAttributeNS(NSS["inkscape"], "label", layerName);
+                               exportNode.setAttribute("id", layerName);
+
+                               exportedLayers.push(exportNode);
+                       }
+               }
+       }
+
+       activeSlide = tmpActiveSlide;
+       activeEffect = tmpActiveEffect;
+       setSlideToState(activeSlide, activeEffect);
+
+       // Copy image.
+       var newDoc = document.documentElement.cloneNode(true);
+
+       // Delete viewbox form new imag and set width and height.
+       newDoc.removeAttribute('viewbox');
+       newDoc.setAttribute('width', WIDTH);
+       newDoc.setAttribute('height', HEIGHT);
+
+       // Delete all layers and script elements.
+       var nodesToBeRemoved = new Array();
+
+       for (var childCounter = 0; childCounter <  newDoc.childNodes.length; childCounter++)
+       {
+               var child = newDoc.childNodes[childCounter];
+
+               if (child.nodeType == 1)
+               {
+                       if ((child.nodeName.toUpperCase() == 'G') || (child.nodeName.toUpperCase() == 'SCRIPT'))
+                       {
+                               nodesToBeRemoved.push(child);
+                       }
+               }
+       }
+
+       for (var ndCounter = 0; ndCounter < nodesToBeRemoved.length; ndCounter++)
+       {
+               var nd = nodesToBeRemoved[ndCounter];
+
+               nd.parentNode.removeChild(nd);
+       }
+
+       // Set current layer.
+       if (exportedLayers[0])
+       {
+               var namedView;
+
+               for (var nodeCounter = 0; nodeCounter < newDoc.childNodes.length; nodeCounter++)
+               {
+                       if ((newDoc.childNodes[nodeCounter].nodeType == 1) && (newDoc.childNodes[nodeCounter].getAttribute('id') == 'base'))
+                       {
+                               namedView = newDoc.childNodes[nodeCounter];
+                       }
+               }
+
+               if (namedView)
+               {
+                       namedView.setAttributeNS(NSS['inkscape'], 'current-layer', exportedLayers[0].getAttributeNS(NSS['inkscape'], 'label'));
+               }
+       }
+
+       // Add exported layers.
+       while (exportedLayers.length > 0)
+       {
+               var nd = exportedLayers.pop();
+
+               nd.setAttribute("opacity",1);
+               nd.style.display = "inherit";
+
+               newDoc.appendChild(nd);
+       }
+
+       // Serialise the new document.
+       var serializer = new XMLSerializer();
+       var xml = serializer.serializeToString(newDoc);
+
+       window.open('data:image/svg+xml;base64,' + window.btoa(xml), '_blank');
+
+       // Unsuspend redraw.
+       ROOT_NODE.unsuspendRedraw(suspendHandle);
+       ROOT_NODE.forceRedraw();
+}
+
+/** Function to undo last drawing operation.
+*/
+function drawingUndo()
+{
+       mouse_presentation_path = null;
+       mouse_original_path = null;
+
+       if (history_presentation_elements.length > 0)
+       {
+               var p = history_presentation_elements.pop();
+               var parent = p.parentNode.removeChild(p);
+
+               p = history_original_elements.pop();
+               parent = p.parentNode.removeChild(p);
+       }
+}
+
+/** Event handler for mouse down in drawing mode.
+ *
+ *  @param e the event
+ */
+function drawingMousedown(e)
+{
+       var value = 0;
+
+       if (e.button)
+               value = e.button;
+       else if (e.which)
+               value = e.which;
+
+       if (value == 1)
+       {
+               history_counter++;
+
+               var p = calcCoord(e);
+
+               mouse_last_x = e.clientX;
+               mouse_last_y = e.clientY;
+               mouse_original_path = document.createElementNS(NSS["svg"], "path");
+               mouse_original_path.setAttribute("stroke", path_colour);
+               mouse_original_path.setAttribute("stroke-width", path_paint_width);
+               mouse_original_path.setAttribute("fill", "none");
+               mouse_original_path.setAttribute("id", "path " + Date());
+               mouse_original_path.setAttribute("d", "M" + p.x + "," + p.y);
+               slides[activeSlide]["original_element"].appendChild(mouse_original_path);
+               history_original_elements.push(mouse_original_path);
+
+               mouse_presentation_path = document.createElementNS(NSS["svg"], "path");
+               mouse_presentation_path.setAttribute("stroke", path_colour);
+               mouse_presentation_path.setAttribute("stroke-width", path_paint_width);
+               mouse_presentation_path.setAttribute("fill", "none");
+               mouse_presentation_path.setAttribute("id", "path " + Date() + " presentation copy");
+               mouse_presentation_path.setAttribute("d", "M" + p.x + "," + p.y);
+
+               if (slides[activeSlide]["viewGroup"])
+                       slides[activeSlide]["viewGroup"].appendChild(mouse_presentation_path);
+               else
+                       slides[activeSlide]["element"].appendChild(mouse_presentation_path);
+
+               history_presentation_elements.push(mouse_presentation_path);
+
+               return false;
+       }
+
+       return true;
+}
+
+/** Event handler for mouse up in drawing mode.
+ *
+ *  @param e the event
+ */
+function drawingMouseup(e)
+{
+       if(!e)
+               e = window.event;
+
+       if (mouse_presentation_path != null)
+       {
+               var p = calcCoord(e);
+               var d = mouse_presentation_path.getAttribute("d");
+               d += " L" + p.x + "," + p.y;
+               mouse_presentation_path.setAttribute("d", d);
+               mouse_presentation_path = null;
+               mouse_original_path.setAttribute("d", d);
+               mouse_original_path = null;
+
+               return false;
+       }
+
+       return true;
+}
+
+/** Event handler for mouse move in drawing mode.
+ *
+ *  @param e the event
+ */
+function drawingMousemove(e)
+{
+       if(!e)
+               e = window.event;
+
+       var dist = (mouse_last_x - e.clientX) * (mouse_last_x - e.clientX) + (mouse_last_y - e.clientY) * (mouse_last_y - e.clientY);
+
+       if (mouse_presentation_path == null)
+       {
+               return true;
+       }
+
+       if (dist >= mouse_min_dist_sqr)
+       {
+               var p = calcCoord(e);
+               var d = mouse_presentation_path.getAttribute("d");
+               d += " L" + p.x + "," + p.y;
+               mouse_presentation_path.setAttribute("d", d);
+               mouse_original_path.setAttribute("d", d);
+               mouse_last_x = e.clientX;
+               mouse_last_y = e.clientY;
+       }
+
+       return false;
+}
+
+/** Event handler for mouse wheel events in slide mode.
+ *  based on http://adomas.org/javascript-mouse-wheel/
+ *
+ *  @param e the event
+ */
+function slideMousewheel(e)
+{
+       var delta = 0;
+
+       if (!e)
+               e = window.event;
+
+       if (e.wheelDelta)
+       { // IE Opera
+               delta = e.wheelDelta/120;
+       }
+       else if (e.detail)
+       { // MOZ
+               delta = -e.detail/3;
+       }
+
+       if (delta > 0)
+               skipEffects(-1);
+       else if (delta < 0)
+               skipEffects(1);
+
+       if (e.preventDefault)
+               e.preventDefault();
+
+       e.returnValue = false;
+}
+
+/** Event handler for mouse wheel events in index mode.
+ *  based on http://adomas.org/javascript-mouse-wheel/
+ *
+ *  @param e the event
+ */
+function indexMousewheel(e)
+{
+       var delta = 0;
+
+       if (!e)
+               e = window.event;
+
+       if (e.wheelDelta)
+       { // IE Opera
+               delta = e.wheelDelta/120;
+       }
+       else if (e.detail)
+       { // MOZ
+               delta = -e.detail/3;
+       }
+
+       if (delta > 0)
+               indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);
+       else if (delta < 0)
+               indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);
+
+       if (e.preventDefault)
+               e.preventDefault();
+
+       e.returnValue = false;
+}
+
+/** Function to set the path paint width.
+*/
+function set_path_paint_width()
+{
+       var svgPoint1 = document.documentElement.createSVGPoint();
+       var svgPoint2 = document.documentElement.createSVGPoint();
+
+       svgPoint1.x = 0.0;
+       svgPoint1.y = 0.0;
+       svgPoint2.x = 1.0;
+       svgPoint2.y = 0.0;
+
+       var matrix = slides[activeSlide]["element"].getTransformToElement(ROOT_NODE);
+
+       if (slides[activeSlide]["viewGroup"])
+               matrix = slides[activeSlide]["viewGroup"].getTransformToElement(ROOT_NODE);
+
+       svgPoint1 = svgPoint1.matrixTransform(matrix);
+       svgPoint2 = svgPoint2.matrixTransform(matrix);
+
+       path_paint_width = path_width / Math.sqrt((svgPoint2.x - svgPoint1.x) * (svgPoint2.x - svgPoint1.x) + (svgPoint2.y - svgPoint1.y) * (svgPoint2.y - svgPoint1.y));
+}
+
+/** The view effect.
+ *
+ *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
+ *  @param element the element the effect should be applied to
+ *  @param time the time that has elapsed since the beginning of the effect
+ *  @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.
+ */
+function view(dir, element, time, options)
+{
+       var length = 250;
+       var fraction;
+
+       if (!options["matrixInitial"])
+       {
+               var tempString = slides[activeSlide]["viewGroup"].getAttribute("transform");
+
+               if (tempString)
+                       options["matrixInitial"] = (new matrixSVG()).fromAttribute(tempString);
+               else
+                       options["matrixInitial"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
+       }
+
+       if ((time == STATE_END) || (time == STATE_START))
+               fraction = 1;
+       else
+       {
+               if (options && options["length"])
+                       length = options["length"];
+
+               fraction = time / length;
+       }
+
+       if (dir == 1)
+       {
+               if (fraction <= 0)
+               {
+                       element.setAttribute("transform", options["matrixInitial"].toAttribute());
+               }
+               else if (fraction >= 1)
+               {
+                       element.setAttribute("transform", options["matrixNew"].toAttribute());
+
+                       set_path_paint_width();
+
+                       options["matrixInitial"] = null;
+                       return true;
+               }
+               else
+               {
+                       element.setAttribute("transform", options["matrixInitial"].mix(options["matrixNew"], fraction).toAttribute());
+               }
+       }
+       else if (dir == -1)
+       {
+               if (fraction <= 0)
+               {
+                       element.setAttribute("transform", options["matrixInitial"].toAttribute());
+               }
+               else if (fraction >= 1)
+               {
+                       element.setAttribute("transform", options["matrixOld"].toAttribute());
+                       set_path_paint_width();
+
+                       options["matrixInitial"] = null;
+                       return true;
+               }
+               else
+               {
+                       element.setAttribute("transform", options["matrixInitial"].mix(options["matrixOld"], fraction).toAttribute());
+               }
+       }
+
+       return false;
+}
+
+/** The fade effect.
+ *
+ *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
+ *  @param element the element the effect should be applied to
+ *  @param time the time that has elapsed since the beginning of the effect
+ *  @param options a dictionary with additional options (e.g. length of the effect)
+ */
+function fade(dir, element, time, options)
+{
+       var length = 250;
+       var fraction;
+
+       if ((time == STATE_END) || (time == STATE_START))
+               fraction = 1;
+       else
+       {
+               if (options && options["length"])
+                       length = options["length"];
+
+               fraction = time / length;
+       }
+
+       if (dir == 1)
+       {
+               if (fraction <= 0)
+               {
+                       element.style.display = "none";
+                       element.setAttribute("opacity", 0);
+               }
+               else if (fraction >= 1)
+               {
+                       element.style.display = "inherit";
+                       element.setAttribute("opacity", 1);
+                       return true;
+               }
+               else
+               {
+                       element.style.display = "inherit";
+                       element.setAttribute("opacity", fraction);
+               }
+       }
+       else if (dir == -1)
+       {
+               if (fraction <= 0)
+               {
+                       element.style.display = "inherit";
+                       element.setAttribute("opacity", 1);
+               }
+               else if (fraction >= 1)
+               {
+                       element.setAttribute("opacity", 0);
+                       element.style.display = "none";
+                       return true;
+               }
+               else
+               {
+                       element.style.display = "inherit";
+                       element.setAttribute("opacity", 1 - fraction);
+               }
+       }
+       return false;
+}
+
+/** The appear effect.
+ *
+ *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
+ *  @param element the element the effect should be applied to
+ *  @param time the time that has elapsed since the beginning of the effect
+ *  @param options a dictionary with additional options (e.g. length of the effect)
+ */
+function appear(dir, element, time, options)
+{
+       if (dir == 1)
+       {
+               element.style.display = "inherit";
+               element.setAttribute("opacity",1);
+       }
+       else if (dir == -1)
+       {
+               element.style.display = "none";
+               element.setAttribute("opacity",0);
+       }
+       return true;
+}
+
+/** The pop effect.
+ *
+ *  @param dir direction the effect should be played (1 = forwards, -1 = backwards)
+ *  @param element the element the effect should be applied to
+ *  @param time the time that has elapsed since the beginning of the effect
+ *  @param options a dictionary with additional options (e.g. length of the effect)
+ */
+function pop(dir, element, time, options)
+{
+       var length = 500;
+       var fraction;
+
+       if ((time == STATE_END) || (time == STATE_START))
+               fraction = 1;
+       else
+       {
+               if (options && options["length"])
+                       length = options["length"];
+
+               fraction = time / length;
+       }
+
+       if (dir == 1)
+       {
+               if (fraction <= 0)
+               {
+                       element.setAttribute("opacity", 0);
+                       element.setAttribute("transform", "scale(0)");
+                       element.style.display = "none";
+               }
+               else if (fraction >= 1)
+               {
+                       element.setAttribute("opacity", 1);
+                       element.removeAttribute("transform");
+                       element.style.display = "inherit";
+                       return true;
+               }
+               else
+               {
+                       element.style.display = "inherit";
+                       var opacityFraction = fraction * 3;
+                       if (opacityFraction > 1)
+                               opacityFraction = 1;
+                       element.setAttribute("opacity", opacityFraction);
+                       var offsetX = WIDTH * (1.0 - fraction) / 2.0;
+                       var offsetY = HEIGHT * (1.0 - fraction) / 2.0;
+                       element.setAttribute("transform", "translate(" + offsetX + "," + offsetY + ") scale(" + fraction + ")");
+               }
+       }
+       else if (dir == -1)
+       {
+               if (fraction <= 0)
+               {
+                       element.setAttribute("opacity", 1);
+                       element.setAttribute("transform", "scale(1)");
+                       element.style.display = "inherit";
+               }
+               else if (fraction >= 1)
+               {
+                       element.setAttribute("opacity", 0);
+                       element.removeAttribute("transform");
+                       element.style.display = "none";
+                       return true;
+               }
+               else
+               {
+                       element.setAttribute("opacity", 1 - fraction);
+                       element.setAttribute("transform", "scale(" + 1 - fraction + ")");
+                       element.style.display = "inherit";
+               }
+       }
+       return false;
+}
+
+/** Function to set a slide either to the start or the end state.
+ *  
+ *  @param slide the slide to use
+ *  @param state the state into which the slide should be set
+ */
+function setSlideToState(slide, state)
+{
+       slides[slide]["viewGroup"].setAttribute("transform", slides[slide].initialView);
+
+       if (slides[slide]["effects"])
+       {       
+               if (state == STATE_END)
+               {
+                       for (var counter = 0; counter < slides[slide]["effects"].length; counter++)
+                       {
+                               for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
+                               {
+                                       var effect = slides[slide]["effects"][counter][subCounter];
+                                       if (effect["effect"] == "fade")
+                                               fade(effect["dir"], effect["element"], STATE_END, effect["options"]);   
+                                       else if (effect["effect"] == "appear")
+                                               appear(effect["dir"], effect["element"], STATE_END, effect["options"]); 
+                                       else if (effect["effect"] == "pop")
+                                               pop(effect["dir"], effect["element"], STATE_END, effect["options"]);    
+                                       else if (effect["effect"] == "view")
+                                               view(effect["dir"], effect["element"], STATE_END, effect["options"]);   
+                               }
+                       }
+               }
+               else if (state == STATE_START)
+               {
+                       for (var counter = slides[slide]["effects"].length - 1; counter >= 0; counter--)
+                       {
+                               for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
+                               {
+                                       var effect = slides[slide]["effects"][counter][subCounter];
+                                       if (effect["effect"] == "fade")
+                                               fade(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);  
+                                       else if (effect["effect"] == "appear")
+                                               appear(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);        
+                                       else if (effect["effect"] == "pop")
+                                               pop(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);   
+                                       else if (effect["effect"] == "view")
+                                               view(parseInt(effect["dir"]) * -1, effect["element"], STATE_START, effect["options"]);  
+                               }
+                       }
+               }
+               else
+               {
+                       setSlideToState(slide, STATE_START);
+
+                       for (var counter = 0; counter < slides[slide]["effects"].length && counter < state; counter++)
+                       {
+                               for (var subCounter = 0; subCounter < slides[slide]["effects"][counter].length; subCounter++)
+                               {
+                                       var effect = slides[slide]["effects"][counter][subCounter];
+                                       if (effect["effect"] == "fade")
+                                               fade(effect["dir"], effect["element"], STATE_END, effect["options"]);   
+                                       else if (effect["effect"] == "appear")
+                                               appear(effect["dir"], effect["element"], STATE_END, effect["options"]); 
+                                       else if (effect["effect"] == "pop")
+                                               pop(effect["dir"], effect["element"], STATE_END, effect["options"]);    
+                                       else if (effect["effect"] == "view")
+                                               view(effect["dir"], effect["element"], STATE_END, effect["options"]);   
+                               }
+                       }
+               }
+       }
+
+       window.location.hash = (activeSlide + 1) + '_' + activeEffect;
+}
+
+/** Convenience function to translate a attribute string into a dictionary.
+ *
+ *     @param str the attribute string
+ *  @return a dictionary
+ *  @see dictToPropStr
+ */
+function propStrToDict(str)
+{
+       var list = str.split(";");
+       var obj = new Object();
+
+       for (var counter = 0; counter < list.length; counter++)
+       {
+               var subStr = list[counter];
+               var subList = subStr.split(":");
+               if (subList.length == 2)
+               {
+                       obj[subList[0]] = subList[1];
+               }       
+       }
+
+       return obj;
+}
+
+/** Convenience function to translate a dictionary into a string that can be used as an attribute.
+ *
+ *  @param dict the dictionary to convert
+ *  @return a string that can be used as an attribute
+ *  @see propStrToDict
+ */
+function dictToPropStr(dict)
+{
+       var str = "";
+
+       for (var key in dict)
+       {
+               str += key + ":" + dict[key] + ";";
+       }
+
+       return str;
+}
+
+/** Sub-function to add a suffix to the ids of the node and all its children.
+ *     
+ *     @param node the node to change
+ *     @param suffix the suffix to add
+ *     @param replace dictionary of replaced ids
+ *  @see suffixNodeIds
+ */
+function suffixNoneIds_sub(node, suffix, replace)
+{
+       if (node.nodeType == 1)
+       {
+               if (node.getAttribute("id"))
+               {
+                       var id = node.getAttribute("id")
+                               replace["#" + id] = id + suffix;
+                       node.setAttribute("id", id + suffix);
+               }
+
+               if ((node.nodeName == "use") && (node.getAttributeNS(NSS["xlink"], "href")) && (replace[node.getAttribute(NSS["xlink"], "href")]))
+                       node.setAttribute(NSS["xlink"], "href", node.getAttribute(NSS["xlink"], "href") + suffix);
+
+               if (node.childNodes)
+               {
+                       for (var counter = 0; counter < node.childNodes.length; counter++)
+                               suffixNoneIds_sub(node.childNodes[counter], suffix, replace);
+               }
+       }
+}
+
+/** Function to add a suffix to the ids of the node and all its children.
+ *     
+ *     @param node the node to change
+ *     @param suffix the suffix to add
+ *  @return the changed node
+ *  @see suffixNodeIds_sub
+ */
+function suffixNodeIds(node, suffix)
+{
+       var replace = new Object();
+
+       suffixNoneIds_sub(node, suffix, replace);
+
+       return node;
+}
+
+/** Function to build a progress bar.
+ *     
+ *  @param parent node to attach the progress bar to
+ */
+function createProgressBar(parent_node)
+{
+       var g = document.createElementNS(NSS["svg"], "g");
+       g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
+       g.setAttribute("id", "layer_progress_bar");
+       g.setAttribute("style", "display: none;");
+
+       var rect_progress_bar = document.createElementNS(NSS["svg"], "rect");
+       rect_progress_bar.setAttribute("style", "marker: none; fill: rgb(128, 128, 128); stroke: none;");
+       rect_progress_bar.setAttribute("id", "rect_progress_bar");
+       rect_progress_bar.setAttribute("x", 0);
+       rect_progress_bar.setAttribute("y", 0.99 * HEIGHT);
+       rect_progress_bar.setAttribute("width", 0);
+       rect_progress_bar.setAttribute("height", 0.01 * HEIGHT);
+       g.appendChild(rect_progress_bar);
+
+       var circle_timer_indicator = document.createElementNS(NSS["svg"], "circle");
+       circle_timer_indicator.setAttribute("style", "marker: none; fill: rgb(255, 0, 0); stroke: none;");
+       circle_timer_indicator.setAttribute("id", "circle_timer_indicator");
+       circle_timer_indicator.setAttribute("cx", 0.005 * HEIGHT);
+       circle_timer_indicator.setAttribute("cy", 0.995 * HEIGHT);
+       circle_timer_indicator.setAttribute("r", 0.005 * HEIGHT);
+       g.appendChild(circle_timer_indicator);
+
+       parent_node.appendChild(g);
+}
+
+/** Function to hide the progress bar.
+ *     
+ */
+function hideProgressBar()
+{
+       var progress_bar = document.getElementById("layer_progress_bar");
+
+       if (!progress_bar)
+       {
+               return;
+       }
+
+       progress_bar.setAttribute("style", "display: none;");
+}
+
+/** Function to show the progress bar.
+ *     
+ */
+function showProgressBar()
+{
+       var progress_bar = document.getElementById("layer_progress_bar");
+
+       if (!progress_bar)
+       {
+               return;
+       }
+
+       progress_bar.setAttribute("style", "display: inherit;");
+}
+
+/** Set progress bar value.
+ *     
+ *     @param value the current slide number
+ *
+ */
+function setProgressBarValue(value)
+{
+       var rect_progress_bar = document.getElementById("rect_progress_bar");
+
+       if (!rect_progress_bar)
+       {
+               return;
+       }
+
+       if (value < 1)
+       {
+               // First slide, assumed to be the title of the presentation
+               var x = 0;
+               var w = 0.01 * HEIGHT;
+       }
+       else if (value >= slides.length - 1)
+       {
+               // Last slide, assumed to be the end of the presentation
+               var x = WIDTH - 0.01 * HEIGHT;
+               var w = 0.01 * HEIGHT;
+       }
+       else
+       {
+               value -= 1;
+               value /= (slides.length - 2);
+
+               var x = WIDTH * value;
+               var w = WIDTH / (slides.length - 2);
+       }
+
+       rect_progress_bar.setAttribute("x", x);
+       rect_progress_bar.setAttribute("width", w);
+}
+
+/** Set time indicator.
+ *     
+ *     @param value the percentage of time elapse so far between 0.0 and 1.0
+ *
+ */
+function setTimeIndicatorValue(value)
+{
+       var circle_timer_indicator = document.getElementById("circle_timer_indicator");
+
+       if (!circle_timer_indicator)
+       {
+               return;
+       }
+
+       if (value < 0.0)
+       {
+               value = 0.0;
+       }
+
+       if (value > 1.0)
+       {
+               value = 1.0;
+       }
+
+       var cx = (WIDTH - 0.01 * HEIGHT) * value + 0.005 * HEIGHT;
+       circle_timer_indicator.setAttribute("cx", cx);
+}
+
+/** Update timer.
+ *     
+ */
+function updateTimer()
+{
+       timer_elapsed += 1;
+       setTimeIndicatorValue((timer_elapsed - timer_start) / (60 * timer_duration));
+}
+
+/** Convert screen coordinates to document coordinates.
+ *
+ *  @param e event with screen coordinates
+ *
+ *  @return coordinates in SVG file coordinate system  
+ */
+function calcCoord(e)
+{
+       var svgPoint = document.documentElement.createSVGPoint();
+       svgPoint.x = e.clientX + window.pageXOffset;
+       svgPoint.y = e.clientY + window.pageYOffset;
+
+       // The following is needed for Google Chrome, but causes problems
+       // with Firefox, as viewport is not implemented in Firefox 3.5.
+       try
+       {
+               svgPoint.x += document.rootElement.viewport.x;
+               svgPoint.y += document.rootElement.viewport.y;
+       }
+       catch (e)
+       {
+       }
+
+       var matrix = slides[activeSlide]["element"].getScreenCTM();
+
+       if (slides[activeSlide]["viewGroup"])
+               matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
+
+       svgPoint = svgPoint.matrixTransform(matrix.inverse());
+       return svgPoint;
+}
+
+/** Add slide.
+ *
+ *     @param after_slide after which slide the new slide should be inserted into the presentation
+ */
+function addSlide(after_slide)
+{
+       number_of_added_slides++;
+
+       var g = document.createElementNS(NSS["svg"], "g");
+       g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
+       g.setAttribute("id", "Whiteboard " + Date() + " presentation copy");
+       g.setAttribute("style", "display: none;");
+
+       var new_slide = new Object();
+       new_slide["element"] = g;
+
+       // Set build in transition.
+       new_slide["transitionIn"] = new Object();
+       var dict = defaultTransitionInDict;
+       new_slide["transitionIn"]["name"] = dict["name"];
+       new_slide["transitionIn"]["options"] = new Object();
+
+       for (key in dict)
+               if (key != "name")
+                       new_slide["transitionIn"]["options"][key] = dict[key];
+
+       // Set build out transition.
+       new_slide["transitionOut"] = new Object();
+       dict = defaultTransitionOutDict;
+       new_slide["transitionOut"]["name"] = dict["name"];
+       new_slide["transitionOut"]["options"] = new Object();
+
+       for (key in dict)
+               if (key != "name")
+                       new_slide["transitionOut"]["options"][key] = dict[key];
+
+       // Copy master slide content.
+       if (masterSlide)
+       {
+               var clonedNode = suffixNodeIds(masterSlide.cloneNode(true), "_" + Date() + " presentation_copy");
+               clonedNode.removeAttributeNS(NSS["inkscape"], "groupmode");
+               clonedNode.removeAttributeNS(NSS["inkscape"], "label");
+               clonedNode.style.display = "inherit";
+
+               g.appendChild(clonedNode);
+       }
+
+       // Substitute auto texts.
+       substituteAutoTexts(g, "Whiteboard " + number_of_added_slides, "W" + number_of_added_slides, slides.length);
+
+       g.setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + (after_slide + 1) + ")) { indexSetActiveSlide(" + (after_slide + 1) + "); };");
+
+       // Create a transform group.
+       var transformGroup = document.createElementNS(NSS["svg"], "g");
+
+       // Add content to transform group.
+       while (g.firstChild)
+               transformGroup.appendChild(g.firstChild);
+
+       // Transfer the transform attribute from the node to the transform group.
+       if (g.getAttribute("transform"))
+       {
+               transformGroup.setAttribute("transform", g.getAttribute("transform"));
+               g.removeAttribute("transform");
+       }
+
+       // Create a view group.
+       var viewGroup = document.createElementNS(NSS["svg"], "g");
+
+       viewGroup.appendChild(transformGroup);
+       new_slide["viewGroup"] = g.appendChild(viewGroup);
+
+       // Insert background.
+       if (BACKGROUND_COLOR != null)
+       {
+               var rectNode = document.createElementNS(NSS["svg"], "rect");
+
+               rectNode.setAttribute("x", 0);
+               rectNode.setAttribute("y", 0);
+               rectNode.setAttribute("width", WIDTH);
+               rectNode.setAttribute("height", HEIGHT);
+               rectNode.setAttribute("id", "jessyInkBackground" + Date());
+               rectNode.setAttribute("fill", BACKGROUND_COLOR);
+
+               new_slide["viewGroup"].insertBefore(rectNode, new_slide["viewGroup"].firstChild);
+       }
+
+       // Set initial view even if there are no other views.
+       var matrixOld = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+
+       new_slide["viewGroup"].setAttribute("transform", matrixOld.toAttribute());
+       new_slide.initialView = matrixOld.toAttribute();
+
+       // Insert slide
+       var node = slides[after_slide]["element"];
+       var next_node = node.nextSibling;
+       var parent_node = node.parentNode;
+
+       if (next_node)
+       {
+               parent_node.insertBefore(g, next_node);
+       }
+       else
+       {
+               parent_node.appendChild(g);
+       }
+
+       g = document.createElementNS(NSS["svg"], "g");
+       g.setAttributeNS(NSS["inkscape"], "groupmode", "layer");
+       g.setAttributeNS(NSS["inkscape"], "label", "Whiteboard " + number_of_added_slides);
+       g.setAttribute("clip-path", "url(#jessyInkSlideClipPath)");
+       g.setAttribute("id", "Whiteboard " + Date());
+       g.setAttribute("style", "display: none;");
+
+       new_slide["original_element"] = g;
+
+       node = slides[after_slide]["original_element"];
+       next_node = node.nextSibling;
+       parent_node = node.parentNode;
+
+       if (next_node)
+       {
+               parent_node.insertBefore(g, next_node);
+       }
+       else
+       {
+               parent_node.appendChild(g);
+       }
+
+       before_new_slide = slides.slice(0, after_slide + 1);
+       after_new_slide = slides.slice(after_slide + 1);
+       slides = before_new_slide.concat(new_slide, after_new_slide);
+
+       //resetting the counter attributes on the slides that follow the new slide...
+       for (var counter = after_slide+2; counter < slides.length; counter++)
+       {
+               slides[counter]["element"].setAttribute("onmouseover", "if ((currentMode == INDEX_MODE) && ( activeSlide != " + counter + ")) { indexSetActiveSlide(" + counter + "); };");
+       }
+}
+
+/** Convenience function to obtain a transformation matrix from a point matrix.
+ *
+ *     @param mPoints Point matrix.
+ *     @return A transformation matrix.
+ */
+function pointMatrixToTransformation(mPoints)
+{
+       mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
+
+       return mPointsOld.mult(mPoints.inv());
+}
+
+/** Convenience function to obtain a matrix with three corners of a rectangle.
+ *
+ *     @param rect an svg rectangle
+ *     @return a matrixSVG containing three corners of the rectangle
+ */
+function rectToMatrix(rect)
+{
+       rectWidth = rect.getBBox().width;
+       rectHeight = rect.getBBox().height;
+       rectX = rect.getBBox().x;
+       rectY = rect.getBBox().y;
+       rectXcorr = 0;
+       rectYcorr = 0;
+
+       scaleX = WIDTH / rectWidth;
+       scaleY = HEIGHT / rectHeight;
+
+       if (scaleX > scaleY)
+       {
+               scaleX = scaleY;
+               rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
+               rectWidth = WIDTH / scaleX;
+       }       
+       else
+       {
+               scaleY = scaleX;
+               rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
+               rectHeight = HEIGHT / scaleY;
+       }
+
+       if (rect.transform.baseVal.numberOfItems < 1)
+       {
+               mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+       }
+       else
+       {
+               mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
+       }
+
+       newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
+       newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
+
+       return mRectTrans.mult(newBasePoints.add(newVectors));
+}
+
+/** Function to handle JessyInk elements.
+ *
+ *     @param  node    Element node.
+ */
+function handleElement(node)
+{
+       if (node.getAttributeNS(NSS['jessyink'], 'element') == 'core.video')
+       {
+               var url;
+               var width;
+               var height;
+               var x;
+               var y;
+               var transform;
+
+               var tspans = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "tspan");
+
+               for (var tspanCounter = 0; tspanCounter < tspans.length; tspanCounter++)
+               {
+                       if (tspans[tspanCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "url")
+                       {
+                               url = tspans[tspanCounter].firstChild.nodeValue;
+                       }
+               }
+
+               var rects = node.getElementsByTagNameNS("http://www.w3.org/2000/svg", "rect");
+
+               for (var rectCounter = 0; rectCounter < rects.length; rectCounter++)
+               {
+                       if (rects[rectCounter].getAttributeNS("https://launchpad.net/jessyink", "video") == "rect")
+                       {
+                               x = rects[rectCounter].getAttribute("x");
+                               y = rects[rectCounter].getAttribute("y");
+                               width = rects[rectCounter].getAttribute("width");
+                               height = rects[rectCounter].getAttribute("height");
+                               transform = rects[rectCounter].getAttribute("transform");
+                       }
+               }
+
+               for (var childCounter = 0; childCounter < node.childNodes.length; childCounter++)
+               {
+                       if (node.childNodes[childCounter].nodeType == 1)
+                       {
+                               if (node.childNodes[childCounter].style)
+                               {
+                                       node.childNodes[childCounter].style.display = 'none';
+                               }
+                               else
+                               {
+                                       node.childNodes[childCounter].setAttribute("style", "display: none;");
+                               }
+                       }
+               }
+
+               var foreignNode = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
+               foreignNode.setAttribute("x", x);
+               foreignNode.setAttribute("y", y);
+               foreignNode.setAttribute("width", width);
+               foreignNode.setAttribute("height", height);
+               foreignNode.setAttribute("transform", transform);
+
+               var videoNode = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+               videoNode.setAttribute("src", url);
+
+               foreignNode.appendChild(videoNode);
+               node.appendChild(foreignNode);
+       }
+}
+
+/** Class processing the location hash.
+ *
+ *     @param str location hash
+ */
+function LocationHash(str)
+{
+       this.slideNumber = 0;
+       this.effectNumber = 0;
+
+       str = str.substr(1, str.length - 1);
+
+       var parts = str.split('_');
+
+       // Try to extract slide number.
+       if (parts.length >= 1)
+       {
+               try
+               {
+                       var slideNumber = parseInt(parts[0]);
+
+                       if (!isNaN(slideNumber))
+                       {
+                               this.slideNumber = slideNumber - 1;
+                       }
+               }
+               catch (e)
+               {
+               }
+       }
+       
+       // Try to extract effect number.
+       if (parts.length >= 2)
+       {
+               try
+               {
+                       var effectNumber = parseInt(parts[1]);
+
+                       if (!isNaN(effectNumber))
+                       {
+                               this.effectNumber = effectNumber;
+                       }
+               }
+               catch (e)
+               {
+               }
+       }
+}
+
+/** Class representing an svg matrix.
+*/
+function matrixSVG()
+{
+       this.e11 = 0; // a
+       this.e12 = 0; // c
+       this.e13 = 0; // e
+       this.e21 = 0; // b
+       this.e22 = 0; // d
+       this.e23 = 0; // f
+       this.e31 = 0;
+       this.e32 = 0;
+       this.e33 = 0;
+}
+
+/** Constructor function.
+ *
+ *     @param a element a (i.e. 1, 1) as described in the svg standard.
+ *     @param b element b (i.e. 2, 1) as described in the svg standard.
+ *     @param c element c (i.e. 1, 2) as described in the svg standard.
+ *     @param d element d (i.e. 2, 2) as described in the svg standard.
+ *     @param e element e (i.e. 1, 3) as described in the svg standard.
+ *     @param f element f (i.e. 2, 3) as described in the svg standard.
+ */
+matrixSVG.prototype.fromSVGElements = function(a, b, c, d, e, f)
+{
+       this.e11 = a;
+       this.e12 = c;
+       this.e13 = e;
+       this.e21 = b;
+       this.e22 = d;
+       this.e23 = f;
+       this.e31 = 0;
+       this.e32 = 0;
+       this.e33 = 1;
+
+       return this;
+}
+
+/** Constructor function.
+ *
+ *     @param matrix an svg matrix as described in the svg standard.
+ */
+matrixSVG.prototype.fromSVGMatrix = function(m)
+{
+       this.e11 = m.a;
+       this.e12 = m.c;
+       this.e13 = m.e;
+       this.e21 = m.b;
+       this.e22 = m.d;
+       this.e23 = m.f;
+       this.e31 = 0;
+       this.e32 = 0;
+       this.e33 = 1;
+
+       return this;
+}
+
+/** Constructor function.
+ *
+ *     @param e11 element 1, 1 of the matrix.
+ *     @param e12 element 1, 2 of the matrix.
+ *     @param e13 element 1, 3 of the matrix.
+ *     @param e21 element 2, 1 of the matrix.
+ *     @param e22 element 2, 2 of the matrix.
+ *     @param e23 element 2, 3 of the matrix.
+ *     @param e31 element 3, 1 of the matrix.
+ *     @param e32 element 3, 2 of the matrix.
+ *     @param e33 element 3, 3 of the matrix.
+ */
+matrixSVG.prototype.fromElements = function(e11, e12, e13, e21, e22, e23, e31, e32, e33)
+{
+       this.e11 = e11;
+       this.e12 = e12;
+       this.e13 = e13;
+       this.e21 = e21;
+       this.e22 = e22;
+       this.e23 = e23;
+       this.e31 = e31;
+       this.e32 = e32;
+       this.e33 = e33;
+
+       return this;
+}
+
+/** Constructor function.
+ *
+ *     @param attrString string value of the "transform" attribute (currently only "matrix" is accepted)
+ */
+matrixSVG.prototype.fromAttribute = function(attrString)
+{
+       str = attrString.substr(7, attrString.length - 8);
+
+       str = str.trim();
+
+       strArray = str.split(",");
+
+       // Opera does not use commas to separate the values of the matrix, only spaces.
+       if (strArray.length != 6)
+               strArray = str.split(" ");
+
+       this.e11 = parseFloat(strArray[0]);
+       this.e21 = parseFloat(strArray[1]);
+       this.e31 = 0;
+       this.e12 = parseFloat(strArray[2]);
+       this.e22 = parseFloat(strArray[3]);
+       this.e32 = 0;
+       this.e13 = parseFloat(strArray[4]);
+       this.e23 = parseFloat(strArray[5]);
+       this.e33 = 1;
+
+       return this;
+}
+
+/** Output function
+ *
+ *     @return a string that can be used as the "transform" attribute.
+ */
+matrixSVG.prototype.toAttribute = function()
+{
+       return "matrix(" + this.e11 + ", " + this.e21 + ", " + this.e12 + ", " + this.e22 + ", " + this.e13 + ", " + this.e23 + ")";
+}
+
+/** Matrix nversion.
+ *
+ *     @return the inverse of the matrix
+ */
+matrixSVG.prototype.inv = function()
+{
+       out = new matrixSVG();
+
+       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);
+
+       out.e11 =  (this.e33 * this.e22 - this.e32 * this.e23) / det;
+       out.e12 = -(this.e33 * this.e12 - this.e32 * this.e13) / det;
+       out.e13 =  (this.e23 * this.e12 - this.e22 * this.e13) / det;
+       out.e21 = -(this.e33 * this.e21 - this.e31 * this.e23) / det;
+       out.e22 =  (this.e33 * this.e11 - this.e31 * this.e13) / det;
+       out.e23 = -(this.e23 * this.e11 - this.e21 * this.e13) / det;
+       out.e31 =  (this.e32 * this.e21 - this.e31 * this.e22) / det;
+       out.e32 = -(this.e32 * this.e11 - this.e31 * this.e12) / det;
+       out.e33 =  (this.e22 * this.e11 - this.e21 * this.e12) / det;
+
+       return out;
+}
+
+/** Matrix multiplication.
+ *
+ *     @param op another svg matrix
+ *     @return this * op
+ */
+matrixSVG.prototype.mult = function(op)
+{
+       out = new matrixSVG();
+
+       out.e11 = this.e11 * op.e11 + this.e12 * op.e21 + this.e13 * op.e31;
+       out.e12 = this.e11 * op.e12 + this.e12 * op.e22 + this.e13 * op.e32;
+       out.e13 = this.e11 * op.e13 + this.e12 * op.e23 + this.e13 * op.e33;
+       out.e21 = this.e21 * op.e11 + this.e22 * op.e21 + this.e23 * op.e31;
+       out.e22 = this.e21 * op.e12 + this.e22 * op.e22 + this.e23 * op.e32;
+       out.e23 = this.e21 * op.e13 + this.e22 * op.e23 + this.e23 * op.e33;
+       out.e31 = this.e31 * op.e11 + this.e32 * op.e21 + this.e33 * op.e31;
+       out.e32 = this.e31 * op.e12 + this.e32 * op.e22 + this.e33 * op.e32;
+       out.e33 = this.e31 * op.e13 + this.e32 * op.e23 + this.e33 * op.e33;
+
+       return out;
+}
+
+/** Matrix addition.
+ *
+ *     @param op another svg matrix
+ *     @return this + op
+ */
+matrixSVG.prototype.add = function(op)
+{
+       out = new matrixSVG();
+
+       out.e11 = this.e11 + op.e11;
+       out.e12 = this.e12 + op.e12;
+       out.e13 = this.e13 + op.e13;
+       out.e21 = this.e21 + op.e21;
+       out.e22 = this.e22 + op.e22;
+       out.e23 = this.e23 + op.e23;
+       out.e31 = this.e31 + op.e31;
+       out.e32 = this.e32 + op.e32;
+       out.e33 = this.e33 + op.e33;
+
+       return out;
+}
+
+/** Matrix mixing.
+ *
+ *     @param op another svg matrix
+ *     @parma contribOp contribution of the other matrix (0 <= contribOp <= 1)
+ *     @return (1 - contribOp) * this + contribOp * op
+ */
+matrixSVG.prototype.mix = function(op, contribOp)
+{
+       contribThis = 1.0 - contribOp;
+       out = new matrixSVG();
+
+       out.e11 = contribThis * this.e11 + contribOp * op.e11;
+       out.e12 = contribThis * this.e12 + contribOp * op.e12;
+       out.e13 = contribThis * this.e13 + contribOp * op.e13;
+       out.e21 = contribThis * this.e21 + contribOp * op.e21;
+       out.e22 = contribThis * this.e22 + contribOp * op.e22;
+       out.e23 = contribThis * this.e23 + contribOp * op.e23;
+       out.e31 = contribThis * this.e31 + contribOp * op.e31;
+       out.e32 = contribThis * this.e32 + contribOp * op.e32;
+       out.e33 = contribThis * this.e33 + contribOp * op.e33;
+
+       return out;
+}
+
+/** Trimming function for strings.
+*/
+String.prototype.trim = function()
+{
+       return this.replace(/^\s+|\s+$/g, '');
+}
+
diff --git a/share/extensions/jessyInk_autoTexts.inx b/share/extensions/jessyInk_autoTexts.inx
new file mode 100644 (file)
index 0000000..f62005b
--- /dev/null
@@ -0,0 +1,29 @@
+<inkscape-extension>
+  <_name>auto-texts</_name>
+  <id>jessyink.autotexts</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_autoTexts.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="autoText" type="optiongroup" _gui-text="Auto-Text">
+        <_option value="none">none (remove)</_option>
+        <_option value="slideTitle">slide title</_option>
+        <_option value="slideNumber">slide number</_option>
+        <_option value="numberOfSlides">number of slides</_option>
+                       </param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to install, update and remove auto-texts for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_autoTexts.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_autoTexts.py b/share/extensions/jessyInk_autoTexts.py
new file mode 100755 (executable)
index 0000000..a9873e2
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class JessyInk_AutoTexts(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--autoText', action = 'store', type = 'string', dest = 'autoText', default = 'none')
+               
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               if len(self.selected) == 0:
+                       sys.stderr.write("To assign an effect, please select an object.\n")
+
+               for id, node in self.selected.items():
+                       nodes = node.xpath("./svg:tspan", namespaces=inkex.NSS)
+
+                       if len(nodes) != 1:
+                               sys.stderr.write("Node with id '" + str(id) + "' is not a suitable text node and was therefore ignored.\n")
+                       else:
+                               if self.options.autoText == "slideTitle":
+                                       nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","slideTitle")
+                               elif self.options.autoText == "slideNumber":
+                                       nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","slideNumber")
+                               elif self.options.autoText == "numberOfSlides":
+                                       nodes[0].set("{" + inkex.NSS["jessyink"] + "}autoText","numberOfSlides")
+                               else:
+                                       if nodes[0].attrib.has_key("{" + inkex.NSS["jessyink"] + "}autoText"):
+                                               del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}autoText"]
+
+# Create effect instance
+effect = JessyInk_AutoTexts()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_core_mouseHandler_noclick.js b/share/extensions/jessyInk_core_mouseHandler_noclick.js
new file mode 100644 (file)
index 0000000..8857975
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2008, 2009 Hannes Hochreiner
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/.
+
+// Add event listener for initialisation.
+document.addEventListener("DOMContentLoaded",  jessyInk_core_mouseHandler_noclick_init, false);
+
+/** Initialisation function.
+ *  
+ *  This function looks for the objects of the appropriate sub-type and hands them to another function that will add the required methods.
+ */
+function jessyInk_core_mouseHandler_noclick_init()
+{
+       var elems = document.getElementsByTagNameNS("https://launchpad.net/jessyink", "mousehandler");
+
+       for (var counter = 0; counter < elems.length; counter++)
+       {
+               if (elems[counter].getAttributeNS("https://launchpad.net/jessyink", "subtype") == "jessyInk_core_mouseHandler_noclick")
+                       jessyInk_core_mouseHandler_noclick(elems[counter]);
+       }
+}
+
+/** Function to initialise an object.
+ *
+ *  @param obj Object to be initialised.
+ */
+function jessyInk_core_mouseHandler_noclick(obj)
+{
+       /** Function supplying a custom mouse handler.
+        *
+        *  @returns A dictionary containing the new mouse handler functions.
+        */
+       obj.getMouseHandler = function ()
+       {
+               var handlerDictio = new Object();
+
+               handlerDictio[SLIDE_MODE] = new Object();
+               handlerDictio[SLIDE_MODE][MOUSE_DOWN] = null;
+
+               return handlerDictio;
+       }
+}
+
diff --git a/share/extensions/jessyInk_core_mouseHandler_zoomControl.js b/share/extensions/jessyInk_core_mouseHandler_zoomControl.js
new file mode 100644 (file)
index 0000000..7944ae8
--- /dev/null
@@ -0,0 +1,443 @@
+// Copyright 2008, 2009 Hannes Hochreiner
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see http://www.gnu.org/licenses/.
+
+// Add event listener for initialisation.
+document.addEventListener("DOMContentLoaded",  jessyInk_core_mouseHandler_zoomControl_init, false);
+
+/** Initialisation function.
+ *  
+ *  This function looks for the objects of the appropriate sub-type and hands them to another function that will add the required methods.
+ */
+function jessyInk_core_mouseHandler_zoomControl_init()
+{
+       var elems = document.getElementsByTagNameNS("https://launchpad.net/jessyink", "mousehandler");
+
+       for (var counter = 0; counter < elems.length; counter++)
+       {
+               if (elems[counter].getAttributeNS("https://launchpad.net/jessyink", "subtype") == "jessyInk_core_mouseHandler_zoomControl")
+                       jessyInk_core_mouseHandler_zoomControl(elems[counter]);
+       }
+}
+
+/** Function to initialise an object.
+ *
+ *  @param obj Object to be initialised.
+ */
+function jessyInk_core_mouseHandler_zoomControl(obj)
+{
+       // Last dragging position.
+       obj.dragging_last;
+       // Flag to indicate whether dragging is active currently.
+       obj.dragging_active = false;
+       // Flag to indicate whether dragging is working currently.
+       obj.dragging_working = false;
+       // Flag to indicate whether the user clicked.
+       obj.click = false;
+
+       /** Function supplying a custom mouse handler.
+        *
+        *  @returns A dictionary containing the new mouse handler functions.
+        */
+       obj.getMouseHandler = function ()
+       {
+               var handlerDictio = new Object();
+
+               handlerDictio[SLIDE_MODE] = new Object();
+               handlerDictio[SLIDE_MODE][MOUSE_DOWN] = obj.mousedown;
+               handlerDictio[SLIDE_MODE][MOUSE_MOVE] = obj.mousemove;
+               handlerDictio[SLIDE_MODE][MOUSE_UP] = obj.mouseup;
+               handlerDictio[SLIDE_MODE][MOUSE_WHEEL] = obj.mousewheel;
+
+               return handlerDictio;
+       }
+
+       /** Event handler for mouse clicks.
+        *
+        *  @param e Event object.
+        */
+       obj.mouseclick = function (e)
+       {
+               var elem = obj.getAdHocViewBbox(slides[activeSlide]["viewGroup"], obj.getCoords(e));
+
+               processingEffect = true;
+
+               effectArray = new Array();
+
+               effectArray[0] = new Object();
+               effectArray[0]["effect"] = "view";
+               effectArray[0]["dir"] = 1;
+               effectArray[0]["element"] = slides[activeSlide]["viewGroup"];
+               effectArray[0]["options"] = new Object();
+               effectArray[0]["options"]["length"] = 200;
+
+               if (elem == null)
+                       effectArray[0]["options"]["matrixNew"] = (new matrixSVG()).fromSVGElements(1, 0, 0, 1, 0, 0);
+               else
+                       effectArray[0]["options"]["matrixNew"] = obj.pointMatrixToTransformation(obj.rectToMatrix(elem)).mult((new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.getScreenCTM()).inv().mult((new matrixSVG()).fromSVGMatrix(elem.parentNode.getScreenCTM())).inv());
+
+               transCounter = 0;
+               startTime = (new Date()).getTime();
+               lastFrameTime = null;
+               effect(1);
+       }
+
+       /** Function to search for the element the user clicked on.
+        *
+        *      This function searches for the element with the highest z-order, which encloses the point the user clicked on
+        *      and which view box fits entierly into the currently visible part of the slide.
+        *
+        *  @param elem Element to start the search from.
+        *  @param pnt Point where the user clicked.
+        *  @returns The element the user clicked on or null, if no element could be found.
+        */
+       obj.getAdHocViewBbox = function (elem, pnt)
+       {
+               var children = elem.childNodes;
+               
+               for (var counter = 0; counter < children.length; counter++)
+               {
+                       if (children[counter].getBBox)
+                       {
+                               var childPointList = obj.projectRect(children[counter].getBBox(), children[counter].getScreenCTM());
+
+                               var viewBbox = document.documentElement.createSVGRect();
+
+                               viewBbox.x = 0.0;
+                               viewBbox.y = 0.0;
+                               viewBbox.width = WIDTH;
+                               viewBbox.height = HEIGHT;
+
+                               var screenPointList = obj.projectRect(viewBbox, slides[activeSlide]["element"].getScreenCTM());
+
+                               if (obj.pointsWithinRect([pnt], childPointList) && obj.pointsWithinRect(childPointList, screenPointList))
+                                       return children[counter];
+
+                               child = obj.getAdHocViewBbox(children[counter], pnt);
+
+                               if (child != null)
+                                       return child;
+                       }
+               }
+
+               return null;
+       }
+
+       /** Function to project a rectangle using the projection matrix supplied.
+        *
+        *  @param rect The rectangle to project.
+        *  @param projectionMatrix The projection matrix.
+        *  @returns A list of the four corners of the projected rectangle starting from the upper left corner and going counter-clockwise.
+        */
+       obj.projectRect = function (rect, projectionMatrix)
+       {
+               var pntUL = document.documentElement.createSVGPoint();
+               pntUL.x = rect.x;
+               pntUL.y = rect.y;
+               pntUL = pntUL.matrixTransform(projectionMatrix);
+
+               var pntLL = document.documentElement.createSVGPoint();
+               pntLL.x = rect.x;
+               pntLL.y = rect.y + rect.height;
+               pntLL = pntLL.matrixTransform(projectionMatrix);
+
+               var pntUR = document.documentElement.createSVGPoint();
+               pntUR.x = rect.x + rect.width;
+               pntUR.y = rect.y;
+               pntUR = pntUR.matrixTransform(projectionMatrix);
+
+               var pntLR = document.documentElement.createSVGPoint();
+               pntLR.x = rect.x + rect.width;
+               pntLR.y = rect.y + rect.height;
+               pntLR = pntLR.matrixTransform(projectionMatrix);
+
+               return [pntUL, pntLL, pntUR, pntLR];
+       }
+
+       /** Function to determine whether all the points supplied in a list are within a rectangle.
+        *
+        *  @param pnts List of points to check.
+        *  @param pointList List of points representing the four corners of the rectangle.
+        *  @return True, if all points are within the rectangle; false, otherwise.
+        */
+       obj.pointsWithinRect = function (pnts, pointList)
+       {
+               var pntUL = pointList[0];
+               var pntLL = pointList[1];
+               var pntUR = pointList[2];
+
+               var matrixOrig = (new matrixSVG()).fromElements(pntUL.x, pntLL.x, pntUR.x, pntUL.y, pntLL.y, pntUR.y, 1, 1, 1);
+               var matrixProj = (new matrixSVG()).fromElements(0, 0, 1, 0, 1, 0, 1, 1, 1);
+
+               var matrixProjection = matrixProj.mult(matrixOrig.inv());
+
+               for (var blockCounter = 0; blockCounter < Math.ceil(pnts.length / 3.0); blockCounter++)
+               {
+                       var subPnts = new Array();
+
+                       for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
+                       {
+                               if (blockCounter * 3.0 + pntCounter < pnts.length)
+                                       subPnts[pntCounter] = pnts[blockCounter * 3.0 + pntCounter];
+                               else
+                               {
+                                       var tmpPnt = document.documentElement.createSVGPoint();
+                                       
+                                       tmpPnt.x = 0.0;
+                                       tmpPnt.y = 0.0;
+
+                                       subPnts[pntCounter] = tmpPnt;
+                               }
+                       }
+
+                       var matrixPnt = (new matrixSVG).fromElements(subPnts[0].x, subPnts[1].x, subPnts[2].x, subPnts[0].y, subPnts[1].y, subPnts[2].y, 1, 1, 1);
+                       var matrixTrans = matrixProjection.mult(matrixPnt);
+
+                       for (var pntCounter = 0; pntCounter < 3.0; pntCounter++)
+                       {
+                               if (blockCounter * 3.0 + pntCounter < pnts.length)
+                               {
+                                       if ((pntCounter == 0) && !((matrixTrans.e11 > 0.01) && (matrixTrans.e11 < 0.99) && (matrixTrans.e21 > 0.01) && (matrixTrans.e21 < 0.99)))
+                                               return false;
+                                       else if ((pntCounter == 1) && !((matrixTrans.e12 > 0.01) && (matrixTrans.e12 < 0.99) && (matrixTrans.e22 > 0.01) && (matrixTrans.e22 < 0.99)))
+                                               return false;
+                                       else if ((pntCounter == 2) && !((matrixTrans.e13 > 0.01) && (matrixTrans.e13 < 0.99) && (matrixTrans.e23 > 0.01) && (matrixTrans.e23 < 0.99)))
+                                               return false;
+                               }
+                       }
+               }
+
+               return true;
+       }
+
+       /** Event handler for mouse movements.
+        *
+        *  @param e Event object.
+        */
+       obj.mousemove = function (e)
+       {
+               obj.click = false;
+
+               if (!obj.dragging_active || obj.dragging_working)
+                       return false;
+
+               obj.dragging_working = true;
+
+               var p = obj.getCoords(e);
+
+               if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
+               {
+                       var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+               }
+               else
+               {
+                       var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
+               }
+
+               matrix.e13 += p.x - obj.dragging_last.x;
+               matrix.e23 += p.y - obj.dragging_last.y;
+
+               slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
+
+               obj.dragging_last = p;
+               obj.dragging_working = false;
+
+               return false;
+       }
+
+       /** Event handler for mouse down.
+        *
+        *  @param e Event object.
+        */
+       obj.mousedown = function (e)
+       {
+               if (obj.dragging_active)
+                       return false;
+
+               var value = 0;
+
+               if (e.button)
+                       value = e.button;
+               else if (e.which)
+                       value = e.which;
+
+               if (value == 1)
+               {
+                       obj.dragging_last = obj.getCoords(e);
+                       obj.dragging_active = true;
+                       obj.click = true;
+               }
+
+               return false;
+       }
+
+       /** Event handler for mouse up.
+        *
+        *  @param e Event object.
+        */
+       obj.mouseup = function (e)
+       {
+               obj.dragging_active = false;
+
+               if (obj.click)
+                       return obj.mouseclick(e);
+               else
+                       return false;
+       }
+
+       /** Function to get the coordinates of a point corrected for the offset of the viewport.
+        *
+        *  @param e Point.
+        *  @returns Coordinates of the point corrected for the offset of the viewport.
+        */
+       obj.getCoords = function (e)
+       {
+               var svgPoint = document.documentElement.createSVGPoint();
+               svgPoint.x = e.clientX + window.pageXOffset;
+               svgPoint.y = e.clientY + window.pageYOffset;
+
+               // The following is needed for Google Chrome, but causes problems
+               // with Firefox, as viewport is not implemented in Firefox 3.5.
+               try
+               {
+                       svgPoint.x += document.rootElement.viewport.x;
+                       svgPoint.y += document.rootElement.viewport.y;
+               }
+               catch (ex)
+               {
+               }
+
+               return svgPoint;
+       }
+
+       /** Event handler for scrolling.
+        *
+        *  @param e Event object.
+        */
+       obj.mousewheel = function(e)
+       {
+               var p = obj.projectCoords(obj.getCoords(e));
+
+               if (slides[activeSlide].viewGroup.transform.baseVal.numberOfItems < 1)
+               {
+                       var matrix = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+               }
+               else
+               {
+                       var matrix = (new matrixSVG()).fromSVGMatrix(slides[activeSlide].viewGroup.transform.baseVal.consolidate().matrix);
+               }
+
+               if (e.wheelDelta)
+               { // IE Opera
+                       delta = e.wheelDelta/120;
+               }
+               else if (e.detail)
+               { // MOZ
+                       delta = -e.detail/3;
+               }
+
+               var widthOld = p.x * matrix.e11 + p.y * matrix.e12;
+               var heightOld = p.x * matrix.e21 + p.y * matrix.e22;
+
+               matrix.e11 *= (1.0 - delta / 20.0);
+               matrix.e12 *= (1.0 - delta / 20.0);
+               matrix.e21 *= (1.0 - delta / 20.0);
+               matrix.e22 *= (1.0 - delta / 20.0);
+
+               var widthNew = p.x * matrix.e11 + p.y * matrix.e12;
+               var heightNew = p.x * matrix.e21 + p.y * matrix.e22;
+
+               matrix.e13 += (widthOld - widthNew);
+               matrix.e23 += (heightOld - heightNew);
+
+               slides[activeSlide]["viewGroup"].setAttribute("transform", matrix.toAttribute());
+
+               return false;
+       }
+
+       /** Function to project a point to screen coordinates.
+        *
+        *      @param Point.
+        *      @returns The point projected to screen coordinates.
+        */
+       obj.projectCoords = function(pnt)
+       {
+               var matrix = slides[activeSlide]["element"].getScreenCTM();
+
+               if (slides[activeSlide]["viewGroup"])
+                       matrix = slides[activeSlide]["viewGroup"].getScreenCTM();
+
+               pnt = pnt.matrixTransform(matrix.inverse());
+               return pnt;
+       }
+
+       /** Function to convert a rectangle into a point matrix.
+        *
+        *      The function figures out a rectangle that encloses the rectangle given and has the same width/height ratio as the viewport of the presentation.
+        *
+        *  @param rect Rectangle.
+        *  @return The upper left, upper right and lower right corner of the rectangle in a point matrix.
+        */
+       obj.rectToMatrix = function(rect)
+       {
+               rectWidth = rect.getBBox().width;
+               rectHeight = rect.getBBox().height;
+               rectX = rect.getBBox().x;
+               rectY = rect.getBBox().y;
+               rectXcorr = 0;
+               rectYcorr = 0;
+
+               scaleX = WIDTH / rectWidth;
+               scaleY = HEIGHT / rectHeight;
+
+               if (scaleX > scaleY)
+               {
+                       scaleX = scaleY;
+                       rectXcorr -= (WIDTH / scaleX - rectWidth) / 2;
+                       rectWidth = WIDTH / scaleX;
+               }       
+               else
+               {
+                       scaleY = scaleX;
+                       rectYcorr -= (HEIGHT / scaleY - rectHeight) / 2;
+                       rectHeight = HEIGHT / scaleY;
+               }
+
+               if (rect.transform.baseVal.numberOfItems < 1)
+               {
+                       mRectTrans = (new matrixSVG()).fromElements(1, 0, 0, 0, 1, 0, 0, 0, 1);
+               }
+               else
+               {
+                       mRectTrans = (new matrixSVG()).fromSVGMatrix(rect.transform.baseVal.consolidate().matrix);
+               }
+
+               newBasePoints = (new matrixSVG()).fromElements(rectX, rectX, rectX, rectY, rectY, rectY, 1, 1, 1);
+               newVectors = (new matrixSVG()).fromElements(rectXcorr, rectXcorr + rectWidth, rectXcorr + rectWidth, rectYcorr, rectYcorr, rectYcorr + rectHeight, 0, 0, 0);
+
+               return mRectTrans.mult(newBasePoints.add(newVectors));
+       }
+
+       /** Function to return a transformation matrix from a point matrix.
+        *
+        *      @param mPoints The point matrix.
+        *      @returns The transformation matrix.
+        */
+       obj.pointMatrixToTransformation = function(mPoints)
+       {
+               mPointsOld = (new matrixSVG()).fromElements(0, WIDTH, WIDTH, 0, 0, HEIGHT, 1, 1, 1);
+
+               return mPointsOld.mult(mPoints.inv());
+       }
+}
+
diff --git a/share/extensions/jessyInk_effects.inx b/share/extensions/jessyInk_effects.inx
new file mode 100644 (file)
index 0000000..86449e8
--- /dev/null
@@ -0,0 +1,41 @@
+<inkscape-extension>
+  <_name>effects</_name>
+  <id>jessyink.effects</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_effects.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="effectInLabel" type="description">Build-in effect:</param>
+                       <param name="effectInOrder" type="int" min="1" max="100" _gui-text="order">1</param>
+                       <param name="effectInDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="duration in seconds">0.8</param>
+                       <param name="effectIn" type="optiongroup" _gui-text="type">
+        <_option value="none">none (default)</_option>
+        <_option value="appear">appear</_option>
+        <_option value="fade">fade</_option>
+        <_option value="pop">pop</_option>
+                       </param>
+                       <param name="effectOutLabel" type="description">Build-out effect:</param>
+                       <param name="effectOutOrder" type="int" min="1" max="100" _gui-text="order">1</param>
+                       <param name="effectOutDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="duration in seconds">0.8</param>
+                       <param name="effectOut" type="optiongroup" _gui-text="type">
+        <_option value="none">none (default)</_option>
+        <_option value="appear">appear</_option>
+        <_option value="fade">fade</_option>
+        <_option value="pop">pop</_option>
+                       </param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to install, update and remove object effects for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_effects.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_effects.py b/share/extensions/jessyInk_effects.py
new file mode 100755 (executable)
index 0000000..862e550
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class JessyInk_Effects(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--effectInOrder', action = 'store', type = 'string', dest = 'effectInOrder', default = 1)
+               self.OptionParser.add_option('--effectInDuration', action = 'store', type = 'float', dest = 'effectInDuration', default = 0.8)
+               self.OptionParser.add_option('--effectIn', action = 'store', type = 'string', dest = 'effectIn', default = 'none')
+               self.OptionParser.add_option('--effectOutOrder', action = 'store', type = 'string', dest = 'effectOutOrder', default = 2)
+               self.OptionParser.add_option('--effectOutDuration', action = 'store', type = 'float', dest = 'effectOutDuration', default = 0.8)
+               self.OptionParser.add_option('--effectOut', action = 'store', type = 'string', dest = 'effectOut', default = 'none')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               if len(self.selected) == 0:
+                       sys.stderr.write("No object selected. Please select the object you want to assign an effect to and then press apply.\n")
+
+               for id, node in self.selected.items():
+                       if (self.options.effectIn == "appear") or (self.options.effectIn == "fade") or (self.options.effectIn == "pop"):
+                               node.set("{" + inkex.NSS["jessyink"] + "}effectIn","name:" + self.options.effectIn  + ";order:" + self.options.effectInOrder + ";length:" + str(int(self.options.effectInDuration * 1000)))
+                               # Remove possible view argument.
+                               if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"):
+                                       del node.attrib["{" + inkex.NSS["jessyink"] + "}view"]
+                       else:
+                               if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectIn"):
+                                       del node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"]
+               
+                       if (self.options.effectOut == "appear") or (self.options.effectOut == "fade") or (self.options.effectOut == "pop"):
+                               node.set("{" + inkex.NSS["jessyink"] + "}effectOut","name:" + self.options.effectOut  + ";order:" + self.options.effectOutOrder + ";length:" + str(int(self.options.effectOutDuration * 1000)))
+                               # Remove possible view argument.
+                               if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"):
+                                       del node.attrib["{" + inkex.NSS["jessyink"] + "}view"]
+                       else:
+                               if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectOut"):
+                                       del node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"]
+
+# Create effect instance
+effect = JessyInk_Effects()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_export.inx b/share/extensions/jessyInk_export.inx
new file mode 100644 (file)
index 0000000..585f418
--- /dev/null
@@ -0,0 +1,30 @@
+<inkscape-extension>
+       <_name>JessyInk zipped pdf or png output</_name>
+       <id>jessyink.export</id>
+       <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
+       <dependency type="executable" location="extensions">jessyInk_export.py</dependency>
+       <dependency type="executable" location="extensions">inkex.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="type" type="optiongroup" _gui-text="type">
+        <_option value="PDF">PDF</_option>
+        <_option value="PNG">PNG</_option>
+                       </param>
+                       <param name="resolution" type="int" min="50" max="1000" _gui-text="resolution">92</param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to export a JessyInk presentation once you created an export layer in your browser. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+       <output>
+               <extension>.zip</extension>
+               <mimetype>application/x-zip</mimetype>
+               <_filetypename>JessyInk zipped pdf or png output (*.zip)</_filetypename>
+               <_filetypetooltip>Creates a zip file containing pdfs or pngs of all slides of a JessyInk presentation.</_filetypetooltip>
+               <dataloss>TRUE</dataloss>
+       </output>
+       <script>
+               <command reldir="extensions" interpreter="python">jessyInk_export.py</command>
+               <helper_extension>org.inkscape.output.svg.inkscape</helper_extension>
+       </script>
+</inkscape-extension>
diff --git a/share/extensions/jessyInk_export.py b/share/extensions/jessyInk_export.py
new file mode 100755 (executable)
index 0000000..6f9bf9e
--- /dev/null
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+import inkex, os.path
+import subprocess
+import tempfile
+import os
+import zipfile
+import glob
+import re
+
+def propStrToDict(inStr):
+       dictio = {}
+
+       for prop in inStr.split(";"):
+               values = prop.split(":")
+
+               if (len(values) == 2):
+                       dictio[values[0].strip()] = values[1].strip()
+
+       return dictio
+
+def dictToPropStr(dictio):
+       str = ""
+
+       for key in dictio.keys():
+               str += " " + key + ":" + dictio[key] + ";" 
+
+       return str[1:]
+
+def setStyle(node, propKey, propValue):
+       props = {}
+
+       if node.attrib.has_key("style"):
+               props = propStrToDict(node.get("style"))
+       
+       props[propKey] = propValue
+       node.set("style", dictToPropStr(props))
+
+class MyEffect(inkex.Effect):
+       inkscapeCommand = None
+       zipFile = None
+
+       def __init__(self):
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--type', action = 'store', type = 'string', dest = 'type', default = '')
+               self.OptionParser.add_option('--resolution', action = 'store', type = 'string', dest = 'resolution', default = '')
+
+               # Register jessyink namespace.
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+               # Set inkscape command.
+               self.inkscapeCommand = self.findInkscapeCommand()
+
+               if (self.inkscapeCommand == None):
+                       sys.stderr.write("Could not find Inkscape command.\n")
+                       sys.exit(1)
+
+       def output(self):
+               pass
+
+       def effect(self):
+               # Remove any temporary files that might be left from last time.
+               self.removeJessyInkFilesInTempDir()
+
+               # Check whether the JessyInk-script is present (indicating that the presentation has not been properly exported).
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version]", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 0:
+                       sys.stderr.write("The JessyInk-script is present in this SVG file. This indicates that the presentation has not been properly exported. Please visit code.goolge.com/p/jessyink to find information on how to export a JessyInk presentation.\n\n")
+
+               zipFileDesc, zpFile = tempfile.mkstemp(suffix=".zip", prefix="jessyInk__")
+
+               output = zipfile.ZipFile(zpFile, "w", compression=zipfile.ZIP_STORED)
+
+               # Find layers.
+               exportNodes = self.document.xpath("//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS)
+
+               if len(exportNodes) < 1:
+                       sys.stderr.write("No layers found.")
+
+               for node in exportNodes:
+                       setStyle(node, "display", "none")
+
+               for node in exportNodes:
+                       setStyle(node, "display", "inherit")
+                       setStyle(node, "opacity", "1")
+                       self.takeSnapshot(output, node.attrib["{" + inkex.NSS["inkscape"] + "}label"])
+                       setStyle(node, "display", "none")
+                               
+               # Write temporary zip file to stdout.
+               output.close()
+               out = open(zpFile,'rb')
+
+               # Switch stdout to binary on Windows.
+               if sys.platform == "win32":
+                       import os, msvcrt
+                       msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+               # Output the file.
+               sys.stdout.write(out.read())
+               sys.stdout.close()
+               out.close()
+
+               # Delete temporary files.
+               self.removeJessyInkFilesInTempDir()
+
+       # Function to export the current state of the file using Inkscape.
+       def takeSnapshot(self, output, fileName):
+               # Write the svg file.
+               svgFileDesc, svgFile = tempfile.mkstemp(suffix=".svg", prefix="jessyInk__")
+               self.document.write(os.fdopen(svgFileDesc, "wb"))
+               
+               ext = str(self.options.type).lower()
+
+               # Prepare output file.
+               outFileDesc, outFile = tempfile.mkstemp(suffix="." + ext, prefix="jessyInk__")
+                               
+               proc = subprocess.Popen([self.inkscapeCommand + " --file=" + svgFile  + " --without-gui --export-dpi=" + str(self.options.resolution) + " --export-" + ext + "=" + outFile], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+               stdout_value, stderr_value = proc.communicate()
+               
+               output.write(outFile, fileName + "." + ext)
+
+       # Function to remove any temporary files created during the export.
+       def removeJessyInkFilesInTempDir(self):
+               for infile in glob.glob(os.path.join(tempfile.gettempdir(), 'jessyInk__*')):
+                       try:
+                               os.remove(infile)
+                       except:
+                               pass
+                               
+       # Function to try and find the correct command to invoke Inkscape.
+       def findInkscapeCommand(self):
+               commands = []
+               commands.append("inkscape")
+               commands.append("C:\Program Files\Inkscape\inkscape.exe")
+               commands.append("/Applications/Inkscape.app/Contents/Resources/bin/inkscape")
+
+               for command in commands:
+                       proc = subprocess.Popen([command + " --without-gui --version"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                       stdout_value, stderr_value = proc.communicate()
+
+                       if proc.returncode == 0:
+                               return command
+
+               return None
+
+e = MyEffect()
+e.affect()
diff --git a/share/extensions/jessyInk_install.inx b/share/extensions/jessyInk_install.inx
new file mode 100644 (file)
index 0000000..e8022e2
--- /dev/null
@@ -0,0 +1,22 @@
+<inkscape-extension>
+  <_name>install/update</_name>
+  <id>jessyink.install</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_install.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk.js</dependency>
+  <param name="tab" type="notebook">
+    <page name="help" _gui-text="Help">
+                       <param name="help_text" type="description">This extensions allows you to install or update the JessyInk script in order to turn your SVG file into a presentation. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+       </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_install.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_install.py b/share/extensions/jessyInk_install.py
new file mode 100755 (executable)
index 0000000..f966152
--- /dev/null
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+import os
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+def propStrToList(str):
+       list = []
+       propList = str.split(";")
+       for prop in propList:
+               if not (len(prop) == 0):
+                       list.append(prop.strip())
+       return list
+
+def listToPropStr(list):
+       str = ""
+       for prop in list:
+               str += " " + prop + ";" 
+       return str[1:]
+
+class JessyInk_Install(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Find and delete old script node
+               for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS):
+                       node.getparent().remove(node)
+       
+               # Create new script node
+               scriptElm = inkex.etree.Element(inkex.addNS("script", "svg"))
+               scriptElm.text = open(os.path.join(os.path.dirname(__file__),   "jessyInk.js")).read()
+               scriptElm.set("id","JessyInk")
+               scriptElm.set("{" + inkex.NSS["jessyink"] + "}version", "1.5.1")
+               self.document.getroot().append(scriptElm)
+
+               # Remove "jessyInkInit()" in the "onload" attribute, if present.
+               if self.document.getroot().get("onload"):
+                       propList = propStrToList(self.document.getroot().get("onload"))
+               else:
+                       propList = []
+       
+               for prop in propList:
+                       if prop == "jessyInkInit()":
+                               propList.remove("jessyInkInit()")
+       
+               if len(propList) > 0:
+                       self.document.getroot().set("onload", listToPropStr(propList))
+               else:
+                       if self.document.getroot().get("onload"):
+                               del self.document.getroot().attrib["onload"]
+
+               # Update effect attributes.
+               for node in self.document.xpath("//*[@jessyInk_effectIn]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] = node.attrib["jessyInk_effectIn"]
+                       del node.attrib["jessyInk_effectIn"]
+
+               for node in self.document.xpath("//*[@jessyink:effectIn]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"] = node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"].replace("=", ":")
+
+               for node in self.document.xpath("//*[@jessyInk_effectOut]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] = node.attrib["jessyInk_effectOut"]
+                       del node.attrib["jessyInk_effectOut"]
+
+               for node in self.document.xpath("//*[@jessyink:effectOut]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"] = node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"].replace("=", ":")
+
+               # Update master slide assignment.
+               for node in self.document.xpath("//*[@jessyInk_masterSlide]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] = node.attrib["jessyInk_masterSlide"]
+                       del node.attrib["jessyInk_masterSlide"]
+
+               for node in self.document.xpath("//*[@jessyink:masterSlide]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"] = node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"].replace("=", ":")
+
+               # Udpate transitions.
+               for node in self.document.xpath("//*[@jessyInk_transitionIn]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] = node.attrib["jessyInk_transitionIn"]
+                       del node.attrib["jessyInk_transitionIn"]
+
+               for node in self.document.xpath("//*[@jessyink:transitionIn]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"] = node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"].replace("=", ":")
+
+               for node in self.document.xpath("//*[@jessyInk_transitionOut]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] = node.attrib["jessyInk_transitionOut"]
+                       del node.attrib["jessyInk_transitionOut"]
+
+               for node in self.document.xpath("//*[@jessyink:transitionOut]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"] = node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"].replace("=", ":")
+
+               # Update auto texts.
+               for node in self.document.xpath("//*[@jessyInk_autoText]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"] = node.attrib["jessyInk_autoText"]
+                       del node.attrib["jessyInk_autoText"]
+
+               for node in self.document.xpath("//*[@jessyink:autoText]", namespaces=inkex.NSS):
+                       node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"] = node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"].replace("=", ":")
+
+# Create effect instance
+effect = JessyInk_Install()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_keyBindings.inx b/share/extensions/jessyInk_keyBindings.inx
new file mode 100644 (file)
index 0000000..1d58b88
--- /dev/null
@@ -0,0 +1,68 @@
+<inkscape-extension>
+  <_name>key bindings</_name>
+  <id>jessyink.keyBindings</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_keyBindings.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Slide mode">
+                       <param name="slide_backWithEffects" type="string" _gui-text="back (with effects)">LEFT, PAGE_UP</param>
+                       <param name="slide_nextWithEffects" type="string" _gui-text="next (with effects)">RIGHT, PAGE_DOWN, SPACE</param>
+                       <param name="slide_backWithoutEffects" type="string" _gui-text="back (without effects)">UP</param>
+                       <param name="slide_nextWithoutEffects" type="string" _gui-text="next (without effects)">DOWN</param>
+                       <param name="slide_firstSlide" type="string" _gui-text="first slide">HOME</param>
+                       <param name="slide_lastSlide" type="string" _gui-text="last slide">END</param>
+                       <param name="slide_switchToIndexMode" type="string" _gui-text="switch to index mode">i</param>
+                       <param name="slide_switchToDrawingMode" type="string" _gui-text="switch to drawing mode">d</param>
+                       <param name="slide_setDuration" type="string" _gui-text="set duration">D</param>
+                       <param name="slide_addSlide" type="string" _gui-text="add slide">n</param>
+                       <param name="slide_toggleProgressBar" type="string" _gui-text="toggle progress bar">p</param>
+                       <param name="slide_resetTimer" type="string" _gui-text="reset timer">t</param>
+               </page>
+    <page name="settings" _gui-text="Drawing mode">
+                       <param name="drawing_switchToSlideMode" type="string" _gui-text="switch to slide mode">d</param>
+                       <param name="drawing_pathWidthDefault" type="string" _gui-text="set path with to default">0</param>
+                       <param name="drawing_pathWidth1" type="string" _gui-text="set path with to 1">1</param>
+                       <param name="drawing_pathWidth3" type="string" _gui-text="set path with to 3">3</param>
+                       <param name="drawing_pathWidth5" type="string" _gui-text="set path with to 5">5</param>
+                       <param name="drawing_pathWidth7" type="string" _gui-text="set path with to 7">7</param>
+                       <param name="drawing_pathWidth9" type="string" _gui-text="set path with to 9">9</param>
+                       <param name="drawing_pathColourBlue" type="string" _gui-text="set path colour to blue">b</param>
+                       <param name="drawing_pathColourCyan" type="string" _gui-text="set path colour to cyan">c</param>
+                       <param name="drawing_pathColourGreen" type="string" _gui-text="set path colour to green">g</param>
+                       <param name="drawing_pathColourBlack" type="string" _gui-text="set path colour to black">k</param>
+                       <param name="drawing_pathColourMagenta" type="string" _gui-text="set path colour to magenta">m</param>
+                       <param name="drawing_pathColourOrange" type="string" _gui-text="set path colour to orange">o</param>
+                       <param name="drawing_pathColourRed" type="string" _gui-text="set path colour to red">r</param>
+                       <param name="drawing_pathColourWhite" type="string" _gui-text="set path colour to white">w</param>
+                       <param name="drawing_pathColourYellow" type="string" _gui-text="set path colour to yellow">y</param>
+                       <param name="drawing_undo" type="string" _gui-text="undo last path segment">z</param>
+               </page>
+    <page name="settings" _gui-text="Index mode">
+                       <param name="index_selectSlideToLeft" type="string" _gui-text="select the slide to the left">LEFT</param>
+                       <param name="index_selectSlideToRight" type="string" _gui-text="select the slide to the right">RIGHT</param>
+                       <param name="index_selectSlideAbove" type="string" _gui-text="select the slide above">UP</param>
+                       <param name="index_selectSlideBelow" type="string" _gui-text="select the slide below">DOWN</param>
+                       <param name="index_previousPage" type="string" _gui-text="previous page">PAGE_UP</param>
+                       <param name="index_nextPage" type="string" _gui-text="next page">PAGE_DOWN</param>
+                       <param name="index_firstSlide" type="string" _gui-text="first slide">HOME</param>
+                       <param name="index_lastSlide" type="string" _gui-text="last slide">END</param>
+                       <param name="index_switchToSlideMode" type="string" _gui-text="switch to slide mode">ENTER, i</param>
+                       <param name="index_decreaseNumberOfColumns" type="string" _gui-text="decrease number of columns">-</param>
+                       <param name="index_increaseNumberOfColumns" type="string" _gui-text="increase number of columns">+, =</param>
+                       <param name="index_setNumberOfColumnsToDefault" type="string" _gui-text="set number of columns to default">0</param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you customise the key bindings JessyInk uses. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>g</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_keyBindings.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_keyBindings.py b/share/extensions/jessyInk_keyBindings.py
new file mode 100755 (executable)
index 0000000..45c0fe2
--- /dev/null
@@ -0,0 +1,250 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class  JessyInk_CustomKeyBindings(inkex.Effect):
+       modes = ('slide', 'index', 'drawing')
+       keyCodes = ('LEFT', 'RIGHT', 'DOWN', 'UP', 'HOME', 'END', 'ENTER', 'SPACE', 'PAGE_UP', 'PAGE_DOWN')
+       slideActions = {}
+       slideCharCodes = {}
+       slideKeyCodes = {}
+       drawingActions = {}
+       drawingCharCodes = {}
+       drawingKeyCodes = {}
+       indexActions = {}
+       indexCharCodes = {}
+       indexKeyCodes = {}
+
+
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--slide_backWithEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_nextWithEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_backWithoutEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_nextWithoutEffects', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_firstSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_lastSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_switchToIndexMode', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_switchToDrawingMode', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_setDuration', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_addSlide', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_toggleProgressBar', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--slide_resetTimer', action = 'callback', type = 'string', callback = self.slideOptions, default = '')
+               self.OptionParser.add_option('--drawing_switchToSlideMode', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidthDefault', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth1', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth2', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth3', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth4', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth5', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth6', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth7', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth8', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathWidth9', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourBlue', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourCyan', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourGreen', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourBlack', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourMagenta', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourOrange', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourRed', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourWhite', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_pathColourYellow', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--drawing_undo', action = 'callback', type = 'string', callback = self.drawingOptions, default = '')
+               self.OptionParser.add_option('--index_selectSlideToLeft', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_selectSlideToRight', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_selectSlideAbove', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_selectSlideBelow', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_previousPage', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_nextPage', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_firstSlide', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_lastSlide', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_switchToSlideMode', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_decreaseNumberOfColumns', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_increaseNumberOfColumns', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+               self.OptionParser.add_option('--index_setNumberOfColumnsToDefault', action = 'callback', type = 'string', callback = self.indexOptions, default = '')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+               
+               self.slideActions["backWithEffects"] = "dispatchEffects(-1);"
+               self.slideActions["nextWithEffects"] = "dispatchEffects(1);"
+               self.slideActions["backWithoutEffects"] = "skipEffects(-1);"
+               self.slideActions["nextWithoutEffects"] = "skipEffects(1);"
+               self.slideActions["firstSlide"] = "slideSetActiveSlide(0);"
+               self.slideActions["lastSlide"] = "slideSetActiveSlide(slides.length - 1);"
+               self.slideActions["switchToIndexMode"] = "toggleSlideIndex();"
+               self.slideActions["switchToDrawingMode"] = "slideSwitchToDrawingMode();"
+               self.slideActions["setDuration"] = "slideQueryDuration();"
+               self.slideActions["addSlide"] = "slideAddSlide(activeSlide);"
+               self.slideActions["toggleProgressBar"] = "slideToggleProgressBarVisibility();"
+               self.slideActions["resetTimer"] = "slideResetTimer();"
+
+               self.drawingActions["switchToSlideMode"] = "drawingSwitchToSlideMode();"
+               self.drawingActions["pathWidthDefault"] = "drawingResetPathWidth();"
+               self.drawingActions["pathWidth1"] = "drawingSetPathWidth(1.0);"
+               self.drawingActions["pathWidth3"] = "drawingSetPathWidth(3.0);"
+               self.drawingActions["pathWidth5"] = "drawingSetPathWidth(5.0);"
+               self.drawingActions["pathWidth7"] = "drawingSetPathWidth(7.0);"
+               self.drawingActions["pathWidth9"] = "drawingSetPathWidth(9.0);"
+               self.drawingActions["pathColourBlue"] = "drawingSetPathColour(\"blue\");"
+               self.drawingActions["pathColourCyan"] = "drawingSetPathColour(\"cyan\");"
+               self.drawingActions["pathColourGreen"] = "drawingSetPathColour(\"green\");"
+               self.drawingActions["pathColourBlack"] = "drawingSetPathColour(\"black\");"
+               self.drawingActions["pathColourMagenta"] = "drawingSetPathColour(\"magenta\");"
+               self.drawingActions["pathColourOrange"] = "drawingSetPathColour(\"orange\");"
+               self.drawingActions["pathColourRed"] = "drawingSetPathColour(\"red\");"
+               self.drawingActions["pathColourWhite"] = "drawingSetPathColour(\"white\");"
+               self.drawingActions["pathColourYellow"] = "drawingSetPathColour(\"yellow\");"
+               self.drawingActions["undo"] = "drawingUndo();"
+
+               self.indexActions["selectSlideToLeft"] = "indexSetPageSlide(activeSlide - 1);"
+               self.indexActions["selectSlideToRight"] = "indexSetPageSlide(activeSlide + 1);"
+               self.indexActions["selectSlideAbove"] = "indexSetPageSlide(activeSlide - INDEX_COLUMNS);"
+               self.indexActions["selectSlideBelow"] = "indexSetPageSlide(activeSlide + INDEX_COLUMNS);"
+               self.indexActions["previousPage"] = "indexSetPageSlide(activeSlide - INDEX_COLUMNS * INDEX_COLUMNS);"
+               self.indexActions["nextPage"] = "indexSetPageSlide(activeSlide + INDEX_COLUMNS * INDEX_COLUMNS);"
+               self.indexActions["firstSlide"] = "indexSetPageSlide(0);"
+               self.indexActions["lastSlide"] = "indexSetPageSlide(slides.length - 1);"
+               self.indexActions["switchToSlideMode"] = "toggleSlideIndex();"
+               self.indexActions["decreaseNumberOfColumns"] = "indexDecreaseNumberOfColumns();"
+               self.indexActions["increaseNumberOfColumns"] = "indexIncreaseNumberOfColumns();"
+               self.indexActions["setNumberOfColumnsToDefault"] = "indexResetNumberOfColumns();"
+
+       def slideOptions(self, option, opt_str, value, parser):
+               action = self.getAction(opt_str)
+
+               valueArray = value.split(",")
+
+               for val in valueArray:
+                       val = val.strip()
+
+                       if val in self.keyCodes:
+                               self.slideKeyCodes[val + "_KEY"] = self.slideActions[action]
+                       elif len(val) == 1:
+                               self.slideCharCodes[val] = self.slideActions[action]
+
+       def drawingOptions(self, option, opt_str, value, parser):
+               action = self.getAction(opt_str)
+
+               valueArray = value.split(",")
+
+               for val in valueArray:
+                       val = val.strip()
+
+                       if val in self.keyCodes:
+                               self.drawingKeyCodes[val + "_KEY"] = self.drawingActions[action]
+                       elif len(val) == 1:
+                               self.drawingCharCodes[val] = self.drawingActions[action]
+
+       def indexOptions(self, option, opt_str, value, parser):
+               action = self.getAction(opt_str)
+
+               valueArray = value.split(",")
+
+               for val in valueArray:
+                       val = val.strip()
+
+                       if val in self.keyCodes:
+                               self.indexKeyCodes[val + "_KEY"] = self.indexActions[action]
+                       elif len(val) == 1:
+                               self.indexCharCodes[val] = self.indexActions[action]
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               # Remove old master slide property
+               for node in self.document.xpath("//svg:g[@jessyink:customKeyBindings='customKeyBindings']", namespaces=inkex.NSS):
+                       node.getparent().remove(node)
+
+               # Set custom key bindings.
+               nodeText = "function getCustomKeyBindingsSub()" + "\n"
+               nodeText += "{" + "\n"
+               nodeText += "   var keyDict = new Object();" + "\n"
+               nodeText += "   keyDict[SLIDE_MODE] = new Object();" + "\n"
+               nodeText += "   keyDict[INDEX_MODE] = new Object();" + "\n"
+               nodeText += "   keyDict[DRAWING_MODE] = new Object();" + "\n"
+
+               for key, value in self.slideKeyCodes.items():
+                       nodeText += "   keyDict[SLIDE_MODE][" + key + "] = function() { " + value + " };" + "\n"
+               
+               for key, value in self.drawingKeyCodes.items():
+                       nodeText += "   keyDict[DRAWING_MODE][" + key + "] = function() { " + value + " };" + "\n"
+               
+               for key, value in self.indexKeyCodes.items():
+                       nodeText += "   keyDict[INDEX_MODE][" + key + "] = function() { " + value + " };" + "\n"
+               
+               nodeText += "   return keyDict;" + "\n"
+               nodeText += "}" + "\n\n"
+
+               # Set custom char bindings.
+               nodeText += "function getCustomCharBindingsSub()" + "\n"
+               nodeText += "{" + "\n"
+               nodeText += "   var charDict = new Object();" + "\n"
+               nodeText += "   charDict[SLIDE_MODE] = new Object();" + "\n"
+               nodeText += "   charDict[INDEX_MODE] = new Object();" + "\n"
+               nodeText += "   charDict[DRAWING_MODE] = new Object();" + "\n"
+
+               for key, value in self.slideCharCodes.items():
+                       nodeText += "   charDict[SLIDE_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n"
+               
+               for key, value in self.drawingCharCodes.items():
+                       nodeText += "   charDict[DRAWING_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n"
+               
+               for key, value in self.indexCharCodes.items():
+                       nodeText += "   charDict[INDEX_MODE][\"" + key + "\"] = function() { " + value + " };" + "\n"
+               
+               nodeText += "   return charDict;" + "\n"
+               nodeText += "}" + "\n"
+
+               # Create new script node
+               scriptElm = inkex.etree.Element(inkex.addNS("script", "svg"))
+               scriptElm.text = nodeText
+               groupElm = inkex.etree.Element(inkex.addNS("g", "svg"))
+               groupElm.set("{" + inkex.NSS["jessyink"] + "}customKeyBindings", "customKeyBindings")
+               groupElm.set("onload", "this.getCustomCharBindings = function() { return getCustomCharBindingsSub(); }; this.getCustomKeyBindings = function() { return getCustomKeyBindingsSub(); };")
+               groupElm.append(scriptElm)
+               self.document.getroot().append(groupElm)
+
+       def getAction(self, varName):
+               parts = varName.split('_')
+               
+               if (len(parts) != 2):
+                       raise StandardException("Error parsing variable name.")
+               
+               return parts[1]
+
+# Create effect instance
+effect = JessyInk_CustomKeyBindings()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_masterSlide.inx b/share/extensions/jessyInk_masterSlide.inx
new file mode 100644 (file)
index 0000000..2f566c2
--- /dev/null
@@ -0,0 +1,25 @@
+<inkscape-extension>
+  <_name>master slide</_name>
+  <id>jessyink.masterSlide</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_masterSlide.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="layerName" type="string" _gui-text="Name of Layer"></param>
+      <param name="info_text" type="description">If no layer name is supplied, the master slide is unset.</param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to change the master slide JessyInk uses. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>g</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_masterSlide.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_masterSlide.py b/share/extensions/jessyInk_masterSlide.py
new file mode 100755 (executable)
index 0000000..9089215
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class JessyInk_MasterSlide(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--layerName', action = 'store', type = 'string', dest = 'layerName', default = '')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               # Remove old master slide property
+               for node in self.document.xpath("//*[@jessyink:masterSlide='masterSlide']", namespaces=inkex.NSS):
+                       del node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"]
+
+               # Set new master slide.
+               if self.options.layerName != "":
+                       nodes = self.document.xpath("//*[@inkscape:groupmode='layer' and @inkscape:label='" + self.options.layerName + "']", namespaces=inkex.NSS)
+                       if len(nodes) == 0:
+                               sys.stderr.write("Layer not found. Removed current master slide selection.\n")
+                       elif len(nodes) > 1:
+                               sys.stderr.write("More than one layer with this name found. Removed current master slide selection.\n")
+                       else:
+                               nodes[0].set("{" + inkex.NSS["jessyink"] + "}masterSlide","masterSlide")
+
+# Create effect instance
+effect = JessyInk_MasterSlide()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_mouseHandler.inx b/share/extensions/jessyInk_mouseHandler.inx
new file mode 100644 (file)
index 0000000..1564f01
--- /dev/null
@@ -0,0 +1,28 @@
+<inkscape-extension>
+  <_name>mouse handler</_name>
+  <id>jessyink.mouseHandler</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_mouseHandler.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Mouse handler">
+                       <param name="mouseSetting" type="optiongroup" _gui-text="Mouse settings">
+        <_option value="default">default</_option>
+        <_option value="noclick">no-click</_option>
+        <_option value="draggingZoom">dragging/zoom</_option>
+                       </param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you customise the mouse handler JessyInk uses. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>g</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_mouseHandler.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_mouseHandler.py b/share/extensions/jessyInk_mouseHandler.py
new file mode 100755 (executable)
index 0000000..a0c06c1
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+import os
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class  JessyInk_CustomMouseHandler(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--mouseSettings', action = 'store', type = 'string', dest = 'mouseSettings', default = 'default')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+               
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               # Remove old mouse handler
+               for node in self.document.xpath("//jessyink:mousehandler", namespaces=inkex.NSS):
+                       node.getparent().remove(node)
+               
+               if self.options.mouseSettings == "noclick":
+                       # Create new script node.
+                       scriptElm = inkex.etree.Element(inkex.addNS("script", "svg"))
+                       scriptElm.text = open(os.path.join(os.path.dirname(__file__),   "jessyInk_core_mouseHandler_noclick.js")).read()
+                       groupElm = inkex.etree.Element(inkex.addNS("mousehandler", "jessyink"))
+                       groupElm.set("{" + inkex.NSS["jessyink"] + "}subtype", "jessyInk_core_mouseHandler_noclick")
+                       groupElm.append(scriptElm)
+                       self.document.getroot().append(groupElm)
+               elif self.options.mouseSettings == "draggingZoom":
+                       # Create new script node.
+                       scriptElm = inkex.etree.Element(inkex.addNS("script", "svg"))
+                       scriptElm.text = open(os.path.join(os.path.dirname(__file__),   "jessyInk_core_mouseHandler_zoomControl.js")).read()
+                       groupElm = inkex.etree.Element(inkex.addNS("mousehandler", "jessyink"))
+                       groupElm.set("{" + inkex.NSS["jessyink"] + "}subtype", "jessyInk_core_mouseHandler_zoomControl")
+                       groupElm.append(scriptElm)
+                       self.document.getroot().append(groupElm)
+
+# Create effect instance
+effect = JessyInk_CustomMouseHandler()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_summary.inx b/share/extensions/jessyInk_summary.inx
new file mode 100644 (file)
index 0000000..202b6a5
--- /dev/null
@@ -0,0 +1,22 @@
+<inkscape-extension>
+  <_name>summary</_name>
+  <id>jessyink.summary</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_summary.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="help" _gui-text="Help">
+                       <param name="help_text" type="description">This extensions allows you to obtain information about the JessyInk script, effects and transitions contained in this SVG file. Please see code.google.com/p/jessyink for more details.
+                       </param>
+    </page>
+       </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_summary.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_summary.py b/share/extensions/jessyInk_summary.py
new file mode 100755 (executable)
index 0000000..56cec03
--- /dev/null
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+def propStrToList(str):
+       list = []
+       propList = str.split(";")
+       for prop in propList:
+               if not (len(prop) == 0):
+                       list.append(prop.strip())
+       return list
+
+def propListToDict(list):
+       dictio = {}
+
+       for prop in list:
+               keyValue = prop.split(":")
+
+               if len(keyValue) == 2:
+                       dictio[keyValue[0].strip()] = keyValue[1].strip()
+
+       return dictio
+
+class JessyInk_Summary(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               # Find the script node, if present
+               for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS):
+                       sys.stderr.write("JessyInk script ")
+
+                       if node.get("{" + inkex.NSS["jessyink"] + "}version"):
+                               sys.stderr.write("version " + node.get("{" + inkex.NSS["jessyink"] + "}version") + " ")
+
+                       sys.stderr.write("installed.\n")
+       
+               slides = []
+               masterSlide = None 
+
+               for node in self.document.xpath("//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS):
+                       if node.get("{" + inkex.NSS["jessyink"] + "}masterSlide"):
+                               masterSlide = node
+                       else:
+                               slides.append(node)
+
+               if masterSlide is not None:
+                       sys.stderr.write("\nMaster slide:\n")
+                       self.describeNode(masterSlide, "\t", "<the number of the slide>", str(len(slides)), "<the title of the slide>")
+
+               slideCounter = 1
+
+               for slide in slides:
+                       sys.stderr.write("\nSlide " + str(slideCounter) + ":\n")
+                       self.describeNode(slide, "\t", str(slideCounter), str(len(slides)), slide.get("{" + inkex.NSS["inkscape"] + "}label"))
+                       slideCounter += 1
+
+       def describeNode(self, node, prefix, slideNumber, numberOfSlides, slideTitle):
+               sys.stderr.write(prefix + "Layer name: " + node.get("{" + inkex.NSS["inkscape"] + "}label") + "\n")
+               
+               # Display information about transitions.
+               transitionInAttribute = node.get("{" + inkex.NSS["jessyink"] + "}transitionIn")
+               if transitionInAttribute:
+                       transInDict = propListToDict(propStrToList(transitionInAttribute))
+                       sys.stderr.write(prefix + "Transition in: " + transInDict["name"])
+
+                       if (transInDict["name"] != "appear") and transInDict.has_key("length"):
+                               sys.stderr.write(" (" + str(int(transInDict["length"]) / 1000.0) + " s)")
+
+                       sys.stderr.write("\n")
+
+               transitionOutAttribute = node.get("{" + inkex.NSS["jessyink"] + "}transitionOut")
+               if transitionOutAttribute:
+                       transOutDict = propListToDict(propStrToList(transitionOutAttribute))
+                       sys.stderr.write(prefix + "Transition out: " + transOutDict["name"])
+
+                       if (transOutDict["name"] != "appear") and transOutDict.has_key("length"):
+                               sys.stderr.write(" (" + str(int(transOutDict["length"]) / 1000.0) + " s)")
+
+                       sys.stderr.write("\n")
+
+               # Display information about auto-texts.
+               autoTexts = {"slideNumber" : slideNumber, "numberOfSlides" : numberOfSlides, "slideTitle" : slideTitle}
+               autoTextNodes = node.xpath(".//*[@jessyink:autoText]", namespaces=inkex.NSS)
+               
+               if (len(autoTextNodes) > 0):
+                       sys.stderr.write("\n" + prefix + "Auto-texts:\n")
+                       
+                       for atNode in autoTextNodes:
+                               sys.stderr.write(prefix + "\t\"" + atNode.text + "\" (object id \"" + atNode.getparent().get("id") + "\") will be replaced by")
+                               sys.stderr.write(" \"" + autoTexts[atNode.get("{" + inkex.NSS["jessyink"] + "}autoText")] + "\".\n")
+
+               # Collect information about effects.
+               effects = {}
+
+               for effectNode in node.xpath(".//*[@jessyink:effectIn]", namespaces=inkex.NSS):
+                       dictio = propListToDict(propStrToList(effectNode.get("{" + inkex.NSS["jessyink"] + "}effectIn")))
+                       dictio["direction"] = "in"
+                       dictio["id"] = effectNode.get("id")
+                       dictio["type"] = "effect"
+
+                       if not effects.has_key(dictio["order"]):
+                               effects[dictio["order"]] = []
+
+                       effects[dictio["order"]].append(dictio)
+
+               for effectNode in node.xpath(".//*[@jessyink:effectOut]", namespaces=inkex.NSS):
+                       dictio = propListToDict(propStrToList(effectNode.get("{" + inkex.NSS["jessyink"] + "}effectOut")))
+                       dictio["direction"] = "out"
+                       dictio["id"] = effectNode.get("id")
+                       dictio["type"] = "effect"
+
+                       if not effects.has_key(dictio["order"]):
+                               effects[dictio["order"]] = []
+
+                       effects[dictio["order"]].append(dictio)
+
+               for viewNode in node.xpath(".//*[@jessyink:view]", namespaces=inkex.NSS):
+                       dictio = propListToDict(propStrToList(viewNode.get("{" + inkex.NSS["jessyink"] + "}view")))
+                       dictio["id"] = viewNode.get("id")
+                       dictio["type"] = "view"
+
+                       if not effects.has_key(dictio["order"]):
+                               effects[dictio["order"]] = []
+
+                       effects[dictio["order"]].append(dictio)
+
+               order = sorted(effects.keys())
+               orderNumber = 0
+
+               # Display information about effects.
+               for orderItem in order:
+                       if orderNumber == 0:
+                               sys.stderr.write("\n" + prefix + "Initial effect (order number " + effects[orderItem][0]["order"]  + "):\n")
+                       else:
+                               sys.stderr.write("\n" + prefix + "Effect " + str(orderNumber) + " (order number " + effects[orderItem][0]["order"]  + "):\n")
+               
+                       for item in effects[orderItem]:
+                               if item["type"] == "view":
+                                       sys.stderr.write(prefix + "\tView will be set according to object \"" + item["id"] + "\"")
+                               else:
+                                       sys.stderr.write(prefix + "\tObject \"" + item["id"] + "\"")
+       
+                                       if item["direction"] == "in":
+                                               sys.stderr.write(" will appear")
+                                       elif item["direction"] == "out":
+                                               sys.stderr.write(" will disappear")
+       
+                               if item["name"] != "appear":
+                                       sys.stderr.write(" using effect \"" + item["name"] + "\"")
+                                       
+                               if item.has_key("length"):
+                                       sys.stderr.write(" in " + str(int(item["length"]) / 1000.0) + " s")
+
+                               sys.stderr.write(".\n")
+
+                       orderNumber += 1
+
+# Create effect instance
+effect = JessyInk_Summary()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_transitions.inx b/share/extensions/jessyInk_transitions.inx
new file mode 100644 (file)
index 0000000..fc4e059
--- /dev/null
@@ -0,0 +1,40 @@
+<inkscape-extension>
+  <_name>transitions</_name>
+  <id>jessyink.transitions</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_transitions.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="layerName" type="string" _gui-text="Name of Layer"></param>
+                       <param name="effectInLabel" type="description">Transition in effect:</param>
+                       <param name="effectInDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="duration in seconds">0.8</param>
+                       <param name="effectIn" type="optiongroup" _gui-text="type">
+        <_option value="default">default</_option>
+        <_option value="appear">appear</_option>
+        <_option value="fade">fade</_option>
+        <_option value="pop">pop</_option>
+                       </param>
+                       <param name="effectOutLabel" type="description">Transition out effect:</param>
+                       <param name="effectOutDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="duration in seconds">0.8</param>
+                       <param name="effectOut" type="optiongroup" _gui-text="type">
+        <_option value="default">default</_option>
+        <_option value="appear">appear</_option>
+        <_option value="fade">fade</_option>
+        <_option value="pop">pop</_option>
+                       </param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to change the transition JessyInk uses for the selected layer. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>g</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_transitions.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_transitions.py b/share/extensions/jessyInk_transitions.py
new file mode 100755 (executable)
index 0000000..d35477d
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+class JessyInk_Transitions(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--layerName', action = 'store', type = 'string', dest = 'layerName', default = '')
+               self.OptionParser.add_option('--effectIn', action = 'store', type = 'string', dest = 'effectIn', default = 'default')
+               self.OptionParser.add_option('--effectInDuration', action = 'store', type = 'float', dest = 'effectInDuration', default = 0.8)
+               self.OptionParser.add_option('--effectOut', action = 'store', type = 'string', dest = 'effectOut', default = 'default')
+               self.OptionParser.add_option('--effectOutDuration', action = 'store', type = 'float', dest = 'effectOutDuration', default = 0.8)
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               if self.options.layerName != "":
+                       nodes = self.document.xpath("//*[@inkscape:groupmode='layer' and @inkscape:label='" + self.options.layerName + "']", namespaces=inkex.NSS)
+                       if len(nodes) == 0:
+                               sys.stderr.write("Layer not found.\n")
+                       elif len(nodes) > 1:
+                               sys.stderr.write("More than one layer with this name found.\n")
+                       else:
+                               if self.options.effectIn == "default":
+                                       if nodes[0].get("{" + inkex.NSS["jessyink"] + "}transitionIn"):
+                                               del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"]
+                               else:
+                                       nodes[0].set("{" + inkex.NSS["jessyink"] + "}transitionIn","name:" + self.options.effectIn + ";length:" + str(int(self.options.effectInDuration * 1000)))
+                               if self.options.effectOut == "default":
+                                       if nodes[0].get("{" + inkex.NSS["jessyink"] + "}transitionOut"):
+                                               del nodes[0].attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"]
+                               else:
+                                       nodes[0].set("{" + inkex.NSS["jessyink"] + "}transitionOut","name:" + self.options.effectOut + ";length:" + str(int(self.options.effectOutDuration * 1000)))
+               else:
+                       sys.stderr.write("Please enter a layer name.\n")
+
+# Create effect instance
+effect = JessyInk_Transitions()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_uninstall.inx b/share/extensions/jessyInk_uninstall.inx
new file mode 100644 (file)
index 0000000..cc08cad
--- /dev/null
@@ -0,0 +1,31 @@
+<inkscape-extension>
+       <_name>uninstall/remove</_name>
+  <id>jessyink.uninstall</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_uninstall.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="options" _gui-text="Options">
+                       <param name="label" type="description">Please select the parts of JessyInk you want to uninstall/remove.</param>
+                       <param name="remove_script" type="boolean" _gui-text="Remove script">true</param>
+                       <param name="remove_effects" type="boolean" _gui-text="Remove effects">true</param>
+                       <param name="remove_masterSlide" type="boolean" _gui-text="Remove master slide assignment">true</param>
+                       <param name="remove_transitions" type="boolean" _gui-text="Remove transitions">true</param>
+                       <param name="remove_autoTexts" type="boolean" _gui-text="Remove auto-texts">true</param>
+                       <param name="remove_views" type="boolean" _gui-text="Remove views">true</param>
+               </page>
+    <page name="help" _gui-text="Help">
+                       <param name="help_text" type="description">This extensions allows you to uninstall the JessyInk script. Please see code.google.com/p/jessyink for more details.
+                       </param>
+    </page>
+       </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_uninstall.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_uninstall.py b/share/extensions/jessyInk_uninstall.py
new file mode 100755 (executable)
index 0000000..c1ade26
--- /dev/null
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+def propStrToList(str):
+       list = []
+       propList = str.split(";")
+       for prop in propList:
+               if not (len(prop) == 0):
+                       list.append(prop.strip())
+       return list
+
+def listToPropStr(list):
+       str = ""
+       for prop in list:
+               str += " " + prop + ";" 
+       return str[1:]
+
+class JessyInk_Uninstall(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--remove_script', action = 'store', type = 'inkbool', dest = 'remove_script', default = True)
+               self.OptionParser.add_option('--remove_effects', action = 'store', type = 'inkbool', dest = 'remove_effects', default = True)
+               self.OptionParser.add_option('--remove_masterSlide', action = 'store', type = 'inkbool', dest = 'remove_masterSlide', default = True)
+               self.OptionParser.add_option('--remove_transitions', action = 'store', type = 'inkbool', dest = 'remove_transitions', default = True)
+               self.OptionParser.add_option('--remove_autoTexts', action = 'store', type = 'inkbool', dest = 'remove_autoTexts', default = True)
+               self.OptionParser.add_option('--remove_views', action = 'store', type = 'inkbool', dest = 'remove_views', default = True)
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Remove script, if so desired.
+               if self.options.remove_script:
+                       # Find and delete script node.
+                       for node in self.document.xpath("//svg:script[@id='JessyInk']", namespaces=inkex.NSS):
+                               node.getparent().remove(node)
+               
+                       # Remove "jessyInkInit()" in the "onload" attribute, if present.
+                       if self.document.getroot().get("onload"):
+                               propList = propStrToList(self.document.getroot().get("onload"))
+                       else:
+                               propList = []
+               
+                       for prop in propList:
+                               if prop == "jessyInkInit()":
+                                       propList.remove("jessyInkInit()")
+               
+                       if len(propList) > 0:
+                               self.document.getroot().set("onload", listToPropStr(propList))
+                       else:
+                               if self.document.getroot().get("onload"):
+                                       del self.document.getroot().attrib["onload"]
+
+               # Remove effect attributes, if so desired.
+               if self.options.remove_effects:
+                       for node in self.document.xpath("//*[@jessyink:effectIn]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"]
+
+                       for node in self.document.xpath("//*[@jessyink:effectOut]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"]
+
+                       # Remove old style attributes as well.
+                       for node in self.document.xpath("//*[@jessyInk_effectIn]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_effectIn"]
+
+                       for node in self.document.xpath("//*[@jessyInk_effectOut]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_effectOut"]
+
+               # Remove master slide assignment, if so desired.
+               if self.options.remove_masterSlide:
+                       for node in self.document.xpath("//*[@jessyink:masterSlide]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}masterSlide"]
+
+                       # Remove old style attributes as well.
+                       for node in self.document.xpath("//*[@jessyInk_masterSlide]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_masterSlide"]
+
+               # Remove transitions, if so desired.
+               if self.options.remove_transitions:
+                       for node in self.document.xpath("//*[@jessyink:transitionIn]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}transitionIn"]
+
+                       for node in self.document.xpath("//*[@jessyink:transitionOut]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}transitionOut"]
+
+                       # Remove old style attributes as well.
+                       for node in self.document.xpath("//*[@jessyInk_transitionIn]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_transitionIn"]
+
+                       for node in self.document.xpath("//*[@jessyInk_transitionOut]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_transitionOut"]
+
+               # Remove auto texts, if so desired.
+               if self.options.remove_autoTexts:
+                       for node in self.document.xpath("//*[@jessyink:autoText]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}autoText"]
+
+                       # Remove old style attributes as well.
+                       for node in self.document.xpath("//*[@jessyInk_autoText]", namespaces=inkex.NSS):
+                               del node.attrib["jessyInk_autoText"]
+
+               # Remove views, if so desired.
+               if self.options.remove_views:
+                       for node in self.document.xpath("//*[@jessyink:view]", namespaces=inkex.NSS):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}view"]
+
+# Create effect instance.
+effect = JessyInk_Uninstall()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_video.inx b/share/extensions/jessyInk_video.inx
new file mode 100644 (file)
index 0000000..a0c5c10
--- /dev/null
@@ -0,0 +1,21 @@
+<inkscape-extension>
+  <_name>video</_name>
+  <id>jessyink.core.video</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_video.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions puts a JessyInk video element on the current slide (layer). This element allows you to integrate a video into your JessyInk presentation. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>all</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_video.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_video.py b/share/extensions/jessyInk_video.py
new file mode 100755 (executable)
index 0000000..1d4ae0e
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+import os
+import re
+from lxml import etree
+from copy import deepcopy
+
+class JessyInk_Effects(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               baseView = self.document.xpath("//sodipodi:namedview[@id='base']", namespaces=inkex.NSS)
+
+               if len(baseView) != 1:
+                       sys.stderr.write("Could not obtain the selected layer for inclusion of the video element.\n\n")
+
+               layer = self.document.xpath("//svg:g[@id='" + baseView[0].attrib["{" + inkex.NSS["inkscape"] + "}current-layer"] + "']", namespaces=inkex.NSS)
+
+               if (len(layer) != 1):
+                       sys.stderr.write("Could not obtain the selected layer for inclusion of the video element.\n\n")
+
+               # Parse template file.
+               tmplFile = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'jessyInk_video.svg'), 'r')
+               tmplRoot = etree.fromstring(tmplFile.read())
+               tmplFile.close()
+
+               elem = deepcopy(tmplRoot.xpath("//svg:g[@jessyink:element='core.video']", namespaces=inkex.NSS)[0])
+               nodeDict = findInternalLinks(elem, tmplRoot)
+
+               deleteIds(elem)
+
+               idSubst = {}
+
+               for key in nodeDict:
+                       idSubst[key] = getNewId("jessyink.core.video", self.document)
+                       deleteIds(nodeDict[key])
+                       nodeDict[key].attrib['id'] = idSubst[key]
+                       elem.insert(0, nodeDict[key])
+
+               for ndIter in elem.iter():
+                       for attrIter in ndIter.attrib:
+                               for entryIter in idSubst:
+                                       ndIter.attrib[attrIter] = ndIter.attrib[attrIter].replace("#" + entryIter, "#" + idSubst[entryIter])
+
+               # Append element.
+               layer[0].append(elem)
+
+def findInternalLinks(node, docRoot, nodeDict = {}):
+       for entry in re.findall("url\(#.*\)", etree.tostring(node)):
+               linkId = entry[5:len(entry) - 1]
+
+               if not nodeDict.has_key(linkId):
+                       nodeDict[linkId] = deepcopy(docRoot.xpath("//*[@id='" + linkId + "']", namespaces=inkex.NSS)[0])
+                       nodeDict = findInternalLinks(nodeDict[linkId], docRoot, nodeDict)
+
+       for entry in node.iter():
+               if entry.attrib.has_key('{' + inkex.NSS['xlink'] + '}href'):
+                       linkId = entry.attrib['{' + inkex.NSS['xlink'] + '}href'][1:len(entry.attrib['{' + inkex.NSS['xlink'] + '}href'])]
+       
+                       if not nodeDict.has_key(linkId):
+                               nodeDict[linkId] = deepcopy(docRoot.xpath("//*[@id='" + linkId + "']", namespaces=inkex.NSS)[0])
+                               nodeDict = findInternalLinks(nodeDict[linkId], docRoot, nodeDict)
+
+       return nodeDict
+
+def getNewId(prefix, docRoot):
+       import datetime
+
+       number = datetime.datetime.now().microsecond
+
+       while len(docRoot.xpath("//*[@id='" + prefix + str(number) + "']", namespaces=inkex.NSS)) > 0:
+               number += 1
+
+       return prefix + str(number)
+
+def deleteIds(node):
+       for entry in node.iter():
+               if entry.attrib.has_key('id'):
+                       del entry.attrib['id']
+
+# Create effect instance
+effect = JessyInk_Effects()
+effect.affect()
+
diff --git a/share/extensions/jessyInk_video.svg b/share/extensions/jessyInk_video.svg
new file mode 100644 (file)
index 0000000..1d9e755
--- /dev/null
@@ -0,0 +1,596 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:jessyink="https://launchpad.net/jessyink"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="478.3808"
+   height="320.73016"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.47 r22583"
+   version="1.0"
+   sodipodi:docname="jessyInk_video.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3654">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3656" />
+      <stop
+         style="stop-color:#bebebe;stop-opacity:1;"
+         offset="1"
+         id="stop3658" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3398">
+      <stop
+         style="stop-color:#d6d6d6;stop-opacity:1;"
+         offset="0"
+         id="stop3400" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3402" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3388">
+      <stop
+         style="stop-color:#dfdfdf;stop-opacity:1;"
+         offset="0"
+         id="stop3390" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3392" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3327">
+      <stop
+         style="stop-color:#859df1;stop-opacity:1;"
+         offset="0"
+         id="stop3329" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="1"
+         id="stop3331" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2953"
+       id="linearGradient2985"
+       x1="154.24722"
+       y1="110.70377"
+       x2="134.85132"
+       y2="87.365448"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9962198,-0.08686864,0.08686864,0.9962198,-7.0296853,12.597208)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2953"
+       id="linearGradient2977"
+       x1="129.11227"
+       y1="115.96979"
+       x2="120.07692"
+       y2="96.278084"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(0.441989,-0.2209945)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2953"
+       id="linearGradient2968"
+       x1="110.75861"
+       y1="116.03537"
+       x2="106.21297"
+       y2="98.169502"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient2953">
+      <stop
+         style="stop-color:#2502ff;stop-opacity:1;"
+         offset="0"
+         id="stop2955" />
+      <stop
+         style="stop-color:#a8a5ff;stop-opacity:1;"
+         offset="1"
+         id="stop2958" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2953"
+       id="linearGradient2960"
+       x1="108.78014"
+       y1="92.046555"
+       x2="104.73283"
+       y2="88.124817"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2913"
+       id="linearGradient2919"
+       x1="35.085781"
+       y1="55.376659"
+       x2="25.629274"
+       y2="92.503731"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8103484,0,0,0.8087248,2.2289552,16.936481)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2913"
+       id="linearGradient2927"
+       x1="118.00729"
+       y1="86.390717"
+       x2="156.74582"
+       y2="86.390717"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8103484,0,0,0.8087248,14.383651,12.295597)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2913"
+       id="linearGradient2943"
+       x1="81.277565"
+       y1="68.547112"
+       x2="91.105675"
+       y2="91.024132"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7897017,-0.1817575,0.1813933,0.7881194,-4.8653748,24.856814)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2913"
+       id="linearGradient2935"
+       x1="117.19492"
+       y1="96.566895"
+       x2="108.4378"
+       y2="77.16523"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8103484,0,0,0.8087248,11.876496,9.4552995)" />
+    <linearGradient
+       id="linearGradient2913">
+      <stop
+         style="stop-color:#f1ff1d;stop-opacity:1;"
+         offset="0"
+         id="stop2915" />
+      <stop
+         style="stop-color:#fba100;stop-opacity:1"
+         offset="1"
+         id="stop2917" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2913"
+       id="linearGradient2951"
+       x1="34.869572"
+       y1="85.162331"
+       x2="69.447136"
+       y2="85.162331"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8103484,0,0,0.8087248,11.67459,15.220437)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2955"
+       id="linearGradient2970"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0574713,0,0,1.0581729,53.099639,13.989279)"
+       x1="22.693371"
+       y1="28.950274"
+       x2="108.57735"
+       y2="84.930939" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2955"
+       id="linearGradient2966"
+       gradientUnits="userSpaceOnUse"
+       x1="22.693371"
+       y1="28.950274"
+       x2="108.57735"
+       y2="84.930939"
+       gradientTransform="matrix(1.0511003,0,0,1.0517241,27.50652,1.2611923)" />
+    <linearGradient
+       id="linearGradient2955">
+      <stop
+         style="stop-color:#fbfcff;stop-opacity:1;"
+         offset="0"
+         id="stop2957" />
+      <stop
+         style="stop-color:#5258ef;stop-opacity:1;"
+         offset="1"
+         id="stop2959" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2955"
+       id="linearGradient2972"
+       gradientUnits="userSpaceOnUse"
+       x1="22.693371"
+       y1="28.950274"
+       x2="108.57735"
+       y2="84.930939"
+       gradientTransform="translate(1.5469614,-10.38674)" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2955"
+       id="linearGradient3333"
+       x1="347.24408"
+       y1="126.14174"
+       x2="400.7677"
+       y2="267.53937"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(8.5039369,12.755905)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3388"
+       id="radialGradient3433"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4883686,0.6863398,-0.5578719,-0.3969566,490.07776,437.40628)"
+       cx="176.60922"
+       cy="402.68719"
+       fx="176.60922"
+       fy="402.68719"
+       r="30.22246" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3435"
+       gradientUnits="userSpaceOnUse"
+       x1="183.88486"
+       y1="434.88504"
+       x2="177.69821"
+       y2="413.2713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3437"
+       gradientUnits="userSpaceOnUse"
+       x1="211.84167"
+       y1="415.36514"
+       x2="198.24878"
+       y2="405.86191" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3447"
+       x1="323.48425"
+       y1="440.07874"
+       x2="308.64169"
+       y2="331.98819"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.9709646,0,0,0.7586207,2.3456911,80.73853)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3521"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1652108,0,0,1.3041467,-12.256157,-100.64265)"
+       x1="323.48425"
+       y1="440.07874"
+       x2="308.64169"
+       y2="331.98819" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3388"
+       id="radialGradient3523"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4883686,0.6863398,-0.5578719,-0.3969566,490.07776,437.40628)"
+       cx="176.60922"
+       cy="402.68719"
+       fx="176.60922"
+       fy="402.68719"
+       r="30.22246" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3525"
+       gradientUnits="userSpaceOnUse"
+       x1="183.88486"
+       y1="434.88504"
+       x2="177.69821"
+       y2="413.2713" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3398"
+       id="linearGradient3527"
+       gradientUnits="userSpaceOnUse"
+       x1="211.84167"
+       y1="415.36514"
+       x2="198.24878"
+       y2="405.86191" />
+    <inkscape:perspective
+       id="perspective2914"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2885"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4492"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       id="linearGradient4061">
+      <stop
+         style="stop-color:#cccccc;stop-opacity:1;"
+         offset="0"
+         id="stop4063" />
+      <stop
+         style="stop-color:#686868;stop-opacity:1;"
+         offset="1"
+         id="stop4065" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4501">
+      <stop
+         style="stop-color:#cccccc;stop-opacity:1;"
+         offset="0"
+         id="stop4503" />
+      <stop
+         style="stop-color:#686868;stop-opacity:1;"
+         offset="1"
+         id="stop4505" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient4482"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.0793653,1.1633659,-0.39752037,0.710516,-120.33298,7.3257338)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <linearGradient
+       id="linearGradient4508">
+      <stop
+         style="stop-color:#cccccc;stop-opacity:1;"
+         offset="0"
+         id="stop4510" />
+      <stop
+         style="stop-color:#686868;stop-opacity:1;"
+         offset="1"
+         id="stop4512" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3811"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-210.78387,-103.79557)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3813"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-196.85663,-8.8111487)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3815"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-182.92939,86.173254)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3843"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-210.78387,-103.79557)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3845"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-196.85663,-8.8111487)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4061"
+       id="radialGradient3847"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.2261425,0.84939356,-0.2902365,0.76066958,-182.92939,86.173254)"
+       cx="267.89597"
+       cy="424.69434"
+       fx="267.89597"
+       fy="424.69434"
+       r="50.740894" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3654"
+       id="linearGradient3660"
+       x1="255.21498"
+       y1="420.35464"
+       x2="322.05756"
+       y2="475.00314"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3654"
+       id="linearGradient3668"
+       x1="252.90028"
+       y1="515.85352"
+       x2="319.14331"
+       y2="564.18585"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3654"
+       id="linearGradient3676"
+       x1="254.72079"
+       y1="615.9538"
+       x2="317.50601"
+       y2="661.42786"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.5433534"
+     inkscape:cx="239.1904"
+     inkscape:cy="160.36508"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1268"
+     inkscape:window-height="746"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     showborder="true"
+     inkscape:showpageshadow="false"
+     borderlayer="true"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-255.05484,-196.27865)">
+    <g
+       id="g2879"
+       transform="translate(-434.78725,69.112709)"
+       jessyink:element="core.video">
+      <jessyink:video />
+      <rect
+         style="fill:#b3b3b3;fill-opacity:1;stroke:none"
+         id="rect2961"
+         width="400"
+         height="250"
+         x="767.70032"
+         y="198.79166"
+         rx="0"
+         ry="0"
+         jessyink:video="rect"
+         transform="matrix(0.99999932,-0.00116615,0.00116615,0.99999932,0,0)" />
+      <rect
+         style="font-size:20px;fill:#dfdfdf;fill-opacity:1;stroke:#000000;stroke-width:3.48164916;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="rect2927"
+         width="388.22516"
+         height="75.056519"
+         x="737.79498"
+         y="175.88173"
+         rx="12"
+         ry="11.999999"
+         sodipodi:insensitive="true" />
+      <text
+         xml:space="preserve"
+         style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+         x="788.77039"
+         y="234.86638"
+         id="text2886"
+         sodipodi:linespacing="125%"><tspan
+           sodipodi:role="line"
+           id="tspan2888"
+           x="788.77039"
+           y="234.86638"
+           jessyink:video="url">&lt;replace this text with the url of the movie&gt;</tspan></text>
+      <g
+         transform="matrix(0.35587188,0.26994219,-0.26994219,0.35587188,800.78552,-70.706091)"
+         id="g4309"
+         sodipodi:insensitive="true">
+        <path
+           style="fill:#000000;fill-opacity:1;stroke:none"
+           d="m 209.78125,112.53125 0,290.65625 5.25,0 0,-4.21875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 l 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,4.21875 113,0 0,-4.21875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 l 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,4.21875 5.03125,0 0,-290.65625 -5.03125,0 0,3.34375 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-3.34375 -113,0 0,3.34375 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-3.34375 -5.25,0 z m 8.59375,13.09375 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.3125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.5013 3.3125,3.34375 l 0,6.1875 c 0,1.84245 -1.47005,3.34375 -3.3125,3.34375 l -8.34375,0 c -1.84245,0 -3.34375,-1.5013 -3.34375,-3.34375 l 0,-6.1875 c 0,-1.84245 1.5013,-3.34375 3.34375,-3.34375 z m -128,19.3125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m -128,19.28125 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z m 128,0 8.34375,0 c 1.84245,0 3.3125,1.47005 3.3125,3.3125 l 0,6.21875 c 0,1.84245 -1.47005,3.3125 -3.3125,3.3125 l -8.34375,0 c -1.84245,0 -3.34375,-1.47005 -3.34375,-3.3125 l 0,-6.21875 c 0,-1.84245 1.5013,-3.3125 3.34375,-3.3125 z"
+           id="path4311"
+           transform="translate(0,284.36221)" />
+        <rect
+           style="fill:url(#linearGradient3660);fill-opacity:1;stroke:none"
+           id="rect4313"
+           width="101.48179"
+           height="82.622345"
+           x="235.6795"
+           y="405.14673" />
+        <rect
+           style="fill:url(#linearGradient3668);fill-opacity:1;stroke:none"
+           id="rect4315"
+           width="101.48179"
+           height="82.622345"
+           x="235.6795"
+           y="501.14676" />
+        <rect
+           y="597.14679"
+           x="235.6795"
+           height="82.622345"
+           width="101.48179"
+           id="rect4317"
+           style="fill:url(#linearGradient3676);fill-opacity:1;stroke:none" />
+      </g>
+      <text
+         xml:space="preserve"
+         style="font-size:20px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
+         x="811.12927"
+         y="205.86632"
+         id="text3701"
+         sodipodi:linespacing="125%"
+         sodipodi:insensitive="true"><tspan
+           sodipodi:role="line"
+           id="tspan3703"
+           x="811.12927"
+           y="205.86632">JessyInk video element</tspan></text>
+    </g>
+  </g>
+</svg>
diff --git a/share/extensions/jessyInk_view.inx b/share/extensions/jessyInk_view.inx
new file mode 100644 (file)
index 0000000..d063927
--- /dev/null
@@ -0,0 +1,27 @@
+<inkscape-extension>
+  <_name>view</_name>
+  <id>jessyink.view</id>
+  <dependency type="executable" location="extensions">inkex.py</dependency>
+  <dependency type="executable" location="extensions">jessyInk_view.py</dependency>
+  <param name="tab" type="notebook">
+    <page name="settings" _gui-text="Settings">
+                       <param name="viewOrder" type="int" min="0" max="100" _gui-text="order">1</param>
+                       <param name="viewDuration" precision="1" type="float" min="0.1" max="10.0" _gui-text="duration in seconds">0.8</param>
+                       <param name="removeView" type="boolean" _gui-text="Remove view">false</param>
+      <param name="help_text" type="description">Choose order number 0 to set the initial view of a slide.</param>
+               </page>
+    <page name="help" _gui-text="Help">
+      <param name="help_text" type="description">This extensions allows you to set, update and remove views for a JessyInk presentation. Please see code.google.com/p/jessyink for more details.</param>
+    </page>
+  </param>
+  <effect>
+    <object-type>rect</object-type>
+      <effects-menu>
+        <submenu _name="JessyInk"/>
+      </effects-menu>
+  </effect>
+  <script>
+    <command reldir="extensions" interpreter="python">jessyInk_view.py</command>
+  </script>
+</inkscape-extension>
+
diff --git a/share/extensions/jessyInk_view.py b/share/extensions/jessyInk_view.py
new file mode 100755 (executable)
index 0000000..639cb6b
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# Copyright 2008, 2009 Hannes Hochreiner
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/.
+
+# These lines are only needed if you don't put the script directly into
+# the installation directory
+import sys
+# Unix
+sys.path.append('/usr/share/inkscape/extensions')
+# OS X
+sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
+# Windows
+sys.path.append('C:\Program Files\Inkscape\share\extensions')
+
+# We will use the inkex module with the predefined Effect base class.
+import inkex
+
+def propStrToList(str):
+       list = []
+       propList = str.split(";")
+       for prop in propList:
+               if not (len(prop) == 0):
+                       list.append(prop.strip())
+       return list
+
+def propListToDict(list):
+       dictio = {}
+
+       for prop in list:
+               keyValue = prop.split(":")
+
+               if len(keyValue) == 2:
+                       dictio[keyValue[0].strip()] = keyValue[1].strip()
+
+       return dictio
+
+class JessyInk_Effects(inkex.Effect):
+       def __init__(self):
+               # Call the base class constructor.
+               inkex.Effect.__init__(self)
+
+               self.OptionParser.add_option('--tab', action = 'store', type = 'string', dest = 'what')
+               self.OptionParser.add_option('--viewOrder', action = 'store', type = 'string', dest = 'viewOrder', default = 1)
+               self.OptionParser.add_option('--viewDuration', action = 'store', type = 'float', dest = 'viewDuration', default = 0.8)
+               self.OptionParser.add_option('--removeView', action = 'store', type = 'inkbool', dest = 'removeView', default = False)
+
+               inkex.NSS[u"jessyink"] = u"https://launchpad.net/jessyink"
+
+       def effect(self):
+               # Check version.
+               scriptNodes = self.document.xpath("//svg:script[@jessyink:version='1.5.1']", namespaces=inkex.NSS)
+
+               if len(scriptNodes) != 1:
+                       sys.stderr.write("The JessyInk script is not installed in this SVG file or has a different version than the JessyInk extensions. Please select \"install/update...\" from the \"JessyInk\" sub-menu of the \"Effects\" menu to install or update the JessyInk script.\n\n")
+
+               rect = None
+
+               for id, node in self.selected.items():
+                       if rect == None:
+                               rect = node
+                       else:
+                               sys.stderr.write("More than one object selected. Please select only one object.\n")
+                               exit()
+
+               if rect == None:
+                       sys.stderr.write("No object selected. Please select the object you want to assign a view to and then press apply.\n")
+                       exit()
+
+               if not self.options.removeView:
+                       # Remove the view that currently has the requested order number.
+                       for node in rect.xpath("ancestor::svg:g[@inkscape:groupmode='layer']/descendant::*[@jessyink:view]", namespaces=inkex.NSS):
+                               propDict = propListToDict(propStrToList(node.attrib["{" + inkex.NSS["jessyink"] + "}view"]))
+       
+                               if propDict["order"] == self.options.viewOrder:
+                                       del node.attrib["{" + inkex.NSS["jessyink"] + "}view"]
+                       
+                       # Set the new view.
+                       rect.set("{" + inkex.NSS["jessyink"] + "}view","name:view;order:" + self.options.viewOrder + ";length:" + str(int(self.options.viewDuration * 1000)))
+
+                       # Remove possible effect arguments.
+                       if rect.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectIn"):
+                               del rect.attrib["{" + inkex.NSS["jessyink"] + "}effectIn"]
+
+                       if rect.attrib.has_key("{" + inkex.NSS["jessyink"] + "}effectOut"):
+                               del rect.attrib["{" + inkex.NSS["jessyink"] + "}effectOut"]
+               else:
+                       if node.attrib.has_key("{" + inkex.NSS["jessyink"] + "}view"):
+                               del node.attrib["{" + inkex.NSS["jessyink"] + "}view"]
+               
+# Create effect instance
+effect = JessyInk_Effects()
+effect.affect()
+
index dd8fb7b519e5040edaf38129d9ffc86922e4656b..fb20237372cbdd162e1687d2fa66b4eab6b39fab 100644 (file)
@@ -287,6 +287,7 @@ void AboutBox::initStrings() {
     "AurĂ©lio A. Heckert\n"
     "Carl Hetherington\n"
     "Jos Hirth\n"
+    "Hannes Hochreiner\n"
     "Thomas Holder\n"
     "Joel Holdsworth\n"
     "Alan Horkan\n"