summaryrefslogtreecommitdiffstats
path: root/share/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions')
-rw-r--r--share/extensions/Inkscape.pm8
-rw-r--r--share/extensions/Makefile.am28
-rw-r--r--share/extensions/generate_voronoi.inx4
-rw-r--r--share/extensions/inkscape_help_keys.inx2
-rw-r--r--share/extensions/inkscape_help_relnotes.inx2
-rwxr-xr-xshare/extensions/interp_att_g.py2
-rwxr-xr-xshare/extensions/jessyInk.js2722
-rw-r--r--share/extensions/jessyInk_autoTexts.inx30
-rwxr-xr-xshare/extensions/jessyInk_autoTexts.py68
-rw-r--r--share/extensions/jessyInk_core_mouseHandler_noclick.js53
-rw-r--r--share/extensions/jessyInk_core_mouseHandler_zoomControl.js443
-rw-r--r--share/extensions/jessyInk_effects.inx42
-rwxr-xr-xshare/extensions/jessyInk_effects.py76
-rw-r--r--share/extensions/jessyInk_export.inx31
-rwxr-xr-xshare/extensions/jessyInk_export.py173
-rw-r--r--share/extensions/jessyInk_install.inx23
-rwxr-xr-xshare/extensions/jessyInk_install.py131
-rw-r--r--share/extensions/jessyInk_keyBindings.inx69
-rwxr-xr-xshare/extensions/jessyInk_keyBindings.py250
-rw-r--r--share/extensions/jessyInk_masterSlide.inx26
-rwxr-xr-xshare/extensions/jessyInk_masterSlide.py63
-rw-r--r--share/extensions/jessyInk_mouseHandler.inx29
-rwxr-xr-xshare/extensions/jessyInk_mouseHandler.py72
-rw-r--r--share/extensions/jessyInk_summary.inx22
-rwxr-xr-xshare/extensions/jessyInk_summary.py197
-rw-r--r--share/extensions/jessyInk_transitions.inx41
-rwxr-xr-xshare/extensions/jessyInk_transitions.py73
-rw-r--r--share/extensions/jessyInk_uninstall.inx31
-rwxr-xr-xshare/extensions/jessyInk_uninstall.py137
-rw-r--r--share/extensions/jessyInk_video.inx22
-rwxr-xr-xshare/extensions/jessyInk_video.py121
-rw-r--r--share/extensions/jessyInk_video.svg596
-rw-r--r--share/extensions/jessyInk_view.inx28
-rwxr-xr-xshare/extensions/jessyInk_view.py104
-rw-r--r--share/extensions/scour.inx2
-rwxr-xr-xshare/extensions/scour.py58
-rwxr-xr-xshare/extensions/svg2xaml.xsl848
-rw-r--r--share/extensions/test/inkweb-debug.js2
-rw-r--r--share/extensions/uniconv-ext.py6
-rw-r--r--share/extensions/uniconv_output.py2
-rw-r--r--share/extensions/webslicer_create_group.inx4
-rw-r--r--share/extensions/webslicer_create_rect.inx7
-rw-r--r--share/extensions/webslicer_export.inx4
-rwxr-xr-xshare/extensions/webslicer_export.py321
44 files changed, 6561 insertions, 412 deletions
diff --git a/share/extensions/Inkscape.pm b/share/extensions/Inkscape.pm
index b87762c0d..3e9027baa 100644
--- a/share/extensions/Inkscape.pm
+++ b/share/extensions/Inkscape.pm
@@ -166,14 +166,14 @@ sub generateSearchRegExp {
=head3 setElAttribute
- # Setting an atribute in a tag localizated it's id:
+ # Setting an attribute in a tag localizated it's id:
$svg->setElAttribute {id=>'myDrawing'}, 'width', $w * 2;
- # Setting an atribute in a tag localizated it's position:
+ # Setting an attribute in a tag localizated it's position:
$svg->setElAttribute {tag=>'svg',pos=>0}, 'width', $w * 2;
- # Setting an atribute in some tags localizated by atributes:
+ # Setting an attribute in some tags localizated by attributes:
$svg->setElAttribute {tag=>'circle',r=>10}, 'r', 50;
-This method will set an atribute in a tag or in a colection of tags, selected
+This method will set an attribute in a tag or in a colection of tags, selected
by the localizator hash.
=cut
diff --git a/share/extensions/Makefile.am b/share/extensions/Makefile.am
index 7327ffbd5..78532f939 100644
--- a/share/extensions/Makefile.am
+++ b/share/extensions/Makefile.am
@@ -76,6 +76,21 @@ extensions = \
inkweb.js \
interp.py \
interp_att_g.py \
+ jessyInk.js \
+ jessyInk_autoTexts.py \
+ jessyInk_core_mouseHandler_noclick.js \
+ jessyInk_core_mouseHandler_zoomControl.js \
+ jessyInk_effects.py \
+ jessyInk_export.py \
+ jessyInk_install.py \
+ jessyInk_keyBindings.py \
+ jessyInk_masterSlide.py \
+ jessyInk_mouseHandler.py \
+ jessyInk_summary.py \
+ jessyInk_transitions.py \
+ jessyInk_uninstall.py \
+ jessyInk_video.py \
+ jessyInk_view.py \
launch_webbrowser.py \
lindenmayer.py \
lorem_ipsum.py \
@@ -148,6 +163,7 @@ extensions = \
otherstuff = \
aisvg.xslt \
colors.xml \
+ jessyInk_video.svg \
svg2xaml.xsl \
xaml2svg.xsl
@@ -217,6 +233,18 @@ modules = \
inkscape_help_keys.inx \
interp.inx \
interp_att_g.inx \
+ jessyInk_autoTexts.inx \
+ jessyInk_effects.inx \
+ jessyInk_export.inx \
+ jessyInk_install.inx \
+ jessyInk_keyBindings.inx \
+ jessyInk_masterSlide.inx \
+ jessyInk_mouseHandler.inx \
+ jessyInk_summary.inx \
+ jessyInk_transitions.inx \
+ jessyInk_uninstall.inx \
+ jessyInk_video.inx \
+ jessyInk_view.inx \
lindenmayer.inx \
lorem_ipsum.inx \
markers_strokepaint.inx \
diff --git a/share/extensions/generate_voronoi.inx b/share/extensions/generate_voronoi.inx
index 4a04ffe1d..dc0dde07d 100644
--- a/share/extensions/generate_voronoi.inx
+++ b/share/extensions/generate_voronoi.inx
@@ -7,8 +7,8 @@
<dependency type="executable" location="extensions">inkex.py</dependency>
<_param name="title1" type="description">Generate a random pattern of Voronoi cells. The pattern will be accessible in the Fill and Stroke dialog. You must select an object or a group.</_param>
<_param name="title2" type="description">If border is zero, the pattern will be discontinuous at the edges. Use a positive border, preferably greater than the cell size, to produce a smooth join of the pattern at the edges. Use a negative border to reduce the size of the pattern and get an empty border.</_param>
- <param name="size" type="int" min="2" max="200" _gui-text=" Average size of cell (px) ">10</param>
- <param name="border" type="int" min="-200" max="200" _gui-text=" Size of Border (px) ">0</param>
+ <param name="size" type="int" min="2" max="200" _gui-text="Average size of cell (px)">10</param>
+ <param name="border" type="int" min="-200" max="200" _gui-text="Size of Border (px)">0</param>
<effect>
<object-type>all</object-type>
<effects-menu>
diff --git a/share/extensions/inkscape_help_keys.inx b/share/extensions/inkscape_help_keys.inx
index c7894a81a..938a11077 100644
--- a/share/extensions/inkscape_help_keys.inx
+++ b/share/extensions/inkscape_help_keys.inx
@@ -3,7 +3,7 @@
<_name>Keys and Mouse Reference</_name>
<id>org.inkscape.help.keys</id>
<dependency type="executable" location="extensions">launch_webbrowser.py</dependency>
- <param name="url" gui-hidden="true" type="string">http://inkscape.org/doc/keys047.html</param>
+ <param name="url" gui-hidden="true" type="string">http://inkscape.org/doc/keys048.html</param>
<effect needs-document="false">
<object-type>all</object-type>
<effects-menu hidden="true"/>
diff --git a/share/extensions/inkscape_help_relnotes.inx b/share/extensions/inkscape_help_relnotes.inx
index ab790bd9f..881eada0c 100644
--- a/share/extensions/inkscape_help_relnotes.inx
+++ b/share/extensions/inkscape_help_relnotes.inx
@@ -3,7 +3,7 @@
<_name>New in This Version</_name>
<id>org.inkscape.help.relnotes</id>
<dependency type="executable" location="extensions">launch_webbrowser.py</dependency>
- <param name="url" gui-hidden="true" type="string">http://wiki.inkscape.org/wiki/index.php/ReleaseNotes047</param>
+ <param name="url" gui-hidden="true" type="string">http://wiki.inkscape.org/wiki/index.php/Release_notes/0.48</param>
<effect needs-document="false">
<object-type>all</object-type>
<effects-menu hidden="true"/>
diff --git a/share/extensions/interp_att_g.py b/share/extensions/interp_att_g.py
index 503e4ec60..f23edaf36 100755
--- a/share/extensions/interp_att_g.py
+++ b/share/extensions/interp_att_g.py
@@ -29,7 +29,7 @@ class InterpAttG(inkex.Effect):
self.OptionParser.add_option("-o", "--att-other",
action="store", type="string",
dest="att_other",
- help="Other atribute (for a limited UI).")
+ help="Other attribute (for a limited UI).")
self.OptionParser.add_option("-t", "--att-other-type",
action="store", type="string",
dest="att_other_type",
diff --git a/share/extensions/jessyInk.js b/share/extensions/jessyInk.js
new file mode 100755
index 000000000..51736ad4e
--- /dev/null
+++ b/share/extensions/jessyInk.js
@@ -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
index 000000000..93e19b301
--- /dev/null
+++ b/share/extensions/jessyInk_autoTexts.inx
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..a9873e2fc
--- /dev/null
+++ b/share/extensions/jessyInk_autoTexts.py
@@ -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
index 000000000..88579754a
--- /dev/null
+++ b/share/extensions/jessyInk_core_mouseHandler_noclick.js
@@ -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
index 000000000..7944ae82f
--- /dev/null
+++ b/share/extensions/jessyInk_core_mouseHandler_zoomControl.js
@@ -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
index 000000000..3a8a03df8
--- /dev/null
+++ b/share/extensions/jessyInk_effects.inx
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..862e55001
--- /dev/null
+++ b/share/extensions/jessyInk_effects.py
@@ -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
index 000000000..42ab811b9
--- /dev/null
+++ b/share/extensions/jessyInk_export.inx
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..6f9bf9ecc
--- /dev/null
+++ b/share/extensions/jessyInk_export.py
@@ -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
index 000000000..0e89b60b4
--- /dev/null
+++ b/share/extensions/jessyInk_install.inx
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..f96615289
--- /dev/null
+++ b/share/extensions/jessyInk_install.py
@@ -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
index 000000000..965079127
--- /dev/null
+++ b/share/extensions/jessyInk_keyBindings.inx
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 width to default:">0</param>
+ <param name="drawing_pathWidth1" type="string" _gui-text="Set path width to 1:">1</param>
+ <param name="drawing_pathWidth3" type="string" _gui-text="Set path width to 3:">3</param>
+ <param name="drawing_pathWidth5" type="string" _gui-text="Set path width to 5:">5</param>
+ <param name="drawing_pathWidth7" type="string" _gui-text="Set path width to 7:">7</param>
+ <param name="drawing_pathWidth9" type="string" _gui-text="Set path width to 9:">9</param>
+ <param name="drawing_pathColourBlue" type="string" _gui-text="Set path color to blue:">b</param>
+ <param name="drawing_pathColourCyan" type="string" _gui-text="Set path color to cyan:">c</param>
+ <param name="drawing_pathColourGreen" type="string" _gui-text="Set path color to green:">g</param>
+ <param name="drawing_pathColourBlack" type="string" _gui-text="Set path color to black:">k</param>
+ <param name="drawing_pathColourMagenta" type="string" _gui-text="Set path color to magenta:">m</param>
+ <param name="drawing_pathColourOrange" type="string" _gui-text="Set path color to orange:">o</param>
+ <param name="drawing_pathColourRed" type="string" _gui-text="Set path color to red:">r</param>
+ <param name="drawing_pathColourWhite" type="string" _gui-text="Set path color to white:">w</param>
+ <param name="drawing_pathColourYellow" type="string" _gui-text="Set path color 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 extension 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
index 000000000..45c0fe2ef
--- /dev/null
+++ b/share/extensions/jessyInk_keyBindings.py
@@ -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
index 000000000..e9518ba11
--- /dev/null
+++ b/share/extensions/jessyInk_masterSlide.inx
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..9089215d9
--- /dev/null
+++ b/share/extensions/jessyInk_masterSlide.py
@@ -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
index 000000000..528360781
--- /dev/null
+++ b/share/extensions/jessyInk_mouseHandler.inx
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..a0c06c196
--- /dev/null
+++ b/share/extensions/jessyInk_mouseHandler.py
@@ -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
index 000000000..37bc206fc
--- /dev/null
+++ b/share/extensions/jessyInk_summary.inx
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..56cec0308
--- /dev/null
+++ b/share/extensions/jessyInk_summary.py
@@ -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
index 000000000..1599d2c20
--- /dev/null
+++ b/share/extensions/jessyInk_transitions.inx
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..d35477d20
--- /dev/null
+++ b/share/extensions/jessyInk_transitions.py
@@ -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
index 000000000..802b3c394
--- /dev/null
+++ b/share/extensions/jessyInk_uninstall.inx
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..c1ade2693
--- /dev/null
+++ b/share/extensions/jessyInk_uninstall.py
@@ -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
index 000000000..59c68114d
--- /dev/null
+++ b/share/extensions/jessyInk_video.inx
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..1d4ae0ee3
--- /dev/null
+++ b/share/extensions/jessyInk_video.py
@@ -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
index 000000000..1d9e75506
--- /dev/null
+++ b/share/extensions/jessyInk_video.svg
@@ -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
index 000000000..06fc08bc5
--- /dev/null
+++ b/share/extensions/jessyInk_view.inx
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/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 extension 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
index 000000000..639cb6b47
--- /dev/null
+++ b/share/extensions/jessyInk_view.py
@@ -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()
+
diff --git a/share/extensions/scour.inx b/share/extensions/scour.inx
index 472643dce..e5b6e90f5 100644
--- a/share/extensions/scour.inx
+++ b/share/extensions/scour.inx
@@ -26,7 +26,7 @@
<_param name="instructions" type="description" xml:space="preserve">This extension optimizes the SVG file according to the following options:
* Simplify colors: convert all colors to #RRGGBB format.
* Style to xml: convert styles into XML attributes.
- * Group collapsing: collapse &lt;g&gt; elements.
+ * Group collapsing: collapse group elements.
* Enable id stripping: remove all un-referenced ID attributes.
* Embed rasters: embed rasters as base64-encoded data.
* Keep editor data: don't remove Inkscape, Sodipodi or Adobe Illustrator elements and attributes.
diff --git a/share/extensions/scour.py b/share/extensions/scour.py
index b851e48c3..3a5392ad2 100755
--- a/share/extensions/scour.py
+++ b/share/extensions/scour.py
@@ -34,7 +34,6 @@
# at rounded corners)
# Next Up:
-# - Bug 511186: option to keep XML comments between prolog and root element
# - only remove unreferenced elements if they are not children of a referenced element
# - add an option to remove ids if they match the Inkscape-style of IDs
# - investigate point-reducing algorithms
@@ -72,7 +71,7 @@ except ImportError:
pass
APP = 'scour'
-VER = '0.24'
+VER = '0.25r171'
COPYRIGHT = 'Copyright Jeff Schiller, 2010'
NS = { 'SVG': 'http://www.w3.org/2000/svg',
@@ -116,6 +115,7 @@ svgAttributes = [
'stop-color',
'stop-opacity',
'stroke',
+ 'stroke-dasharray',
'stroke-dashoffset',
'stroke-linecap',
'stroke-linejoin',
@@ -1355,7 +1355,7 @@ def cleanPath(element) :
else:
# we have a move and then 1 or more coords for lines
N = len(path[0][1])
- if path[0] == 'M':
+ if path[0][0] == 'M':
# take the last pair of coordinates for the starting point
x = path[0][1][N-2]
y = path[0][1][N-1]
@@ -1624,13 +1624,14 @@ def cleanPath(element) :
for (cmd,data) in path[1:]:
# flush the previous command if it is not the same type as the current command
if prevCmd != '':
- if cmd != prevCmd:
+ if cmd != prevCmd or cmd == 'm':
newPath.append( (prevCmd, prevData) )
prevCmd = ''
prevData = []
# if the previous and current commands are the same type, collapse
- if cmd == prevCmd:
+ # but only if they are not move commands (since move can contain implicit lineto commands)
+ if cmd == prevCmd and cmd != 'm':
for coord in data:
prevData.append(coord)
@@ -1757,13 +1758,13 @@ def cleanPath(element) :
for (cmd,data) in path[1:]:
# flush the previous command if it is not the same type as the current command
if prevCmd != '':
- if cmd != prevCmd:
+ if cmd != prevCmd or cmd == 'm':
newPath.append( (prevCmd, prevData) )
prevCmd = ''
prevData = []
# if the previous and current commands are the same type, collapse
- if cmd == prevCmd:
+ if cmd == prevCmd and cmd != 'm':
for coord in data:
prevData.append(coord)
@@ -1786,12 +1787,43 @@ def parseListOfPoints(s):
Returns a list of containing an even number of coordinate strings
"""
+ i = 0
+ points = []
+
# (wsp)? comma-or-wsp-separated coordinate pairs (wsp)?
# coordinate-pair = coordinate comma-or-wsp coordinate
# coordinate = sign? integer
- nums = re.split("\\s*\\,?\\s*", s.strip())
+ # comma-wsp: (wsp+ comma? wsp*) | (comma wsp*)
+ ws_nums = re.split("\\s*\\,?\\s*", s.strip())
+ nums = []
+
+ # also, if 100-100 is found, split it into two also
+ # <polygon points="100,-100,100-100,100-100-100,-100-100" />
+ for i in range(len(ws_nums)):
+ negcoords = re.split("\\-", ws_nums[i]);
+
+ # this string didn't have any negative coordinates
+ if len(negcoords) == 1:
+ nums.append(negcoords[0])
+ # we got negative coords
+ else:
+ for j in range(len(negcoords)):
+ # first number could be positive
+ if j == 0:
+ if negcoords[0] != '':
+ nums.append(negcoords[0])
+ # otherwise all other strings will be negative
+ else:
+ # unless we accidentally split a number that was in scientific notation
+ # and had a negative exponent (500.00e-1)
+ prev = nums[len(nums)-1]
+ if prev[len(prev)-1] == 'e' or prev[len(prev)-1] == 'E':
+ nums[len(nums)-1] = prev + '-' + negcoords[j]
+ else:
+ nums.append( '-'+negcoords[j] )
+
+ # now resolve into SVGLength values
i = 0
- points = []
while i < len(nums):
x = SVGLength(nums[i])
# if we had an odd number of points, return empty
@@ -1803,7 +1835,7 @@ def parseListOfPoints(s):
points.append( str(x.value) )
points.append( str(y.value) )
i += 2
-
+
return points
def cleanPolygon(elem):
@@ -1820,14 +1852,14 @@ def cleanPolygon(elem):
if startx == endx and starty == endy:
pts = pts[:-2]
numPointsRemovedFromPolygon += 1
- elem.setAttribute('points', scourCoordinates(pts))
+ elem.setAttribute('points', scourCoordinates(pts,True))
def cleanPolyline(elem):
"""
Scour the polyline points attribute
"""
pts = parseListOfPoints(elem.getAttribute('points'))
- elem.setAttribute('points', scourCoordinates(pts))
+ elem.setAttribute('points', scourCoordinates(pts,True))
def serializePath(pathObj):
"""
@@ -2449,5 +2481,3 @@ if __name__ == '__main__':
sizediff = (newsize / oldsize) * 100
print >>sys.stderr, ' Original file size:', oldsize, 'bytes;', \
'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)'
-
-
diff --git a/share/extensions/svg2xaml.xsl b/share/extensions/svg2xaml.xsl
index ef01e2023..cda6c7a97 100755
--- a/share/extensions/svg2xaml.xsl
+++ b/share/extensions/svg2xaml.xsl
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-Copyright (c) 2005-2007 Toine de Greef (a.degreef@chello.nl)
+Copyright (c) 2005-2007 authors:
+Original version: Toine de Greef (a.degreef@chello.nl)
+Modified (2010) by Nicolas Dufour (nicoduf@yahoo.fr) (blur support, units
+convertion, comments, and some other fixes)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -38,10 +41,352 @@ exclude-result-prefixes="rdf xlink xs exsl libxslt">
<xsl:param name="silverlight_compatible" select="1" />
+<!-- Root template.
+Everything starts here! -->
+<xsl:template match="/">
+ <xsl:choose>
+ <xsl:when test="$silverlight_compatible = 1">
+ <xsl:apply-templates mode="forward" />
+ </xsl:when>
+ <xsl:otherwise>
+ <Viewbox Stretch="Uniform">
+ <xsl:apply-templates mode="forward" />
+ </Viewbox>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- SVG and groups
+(including layers) -->
+<xsl:template mode="forward" match="*[name(.) = 'svg' or name(.) = 'g']">
+ <xsl:choose>
+ <xsl:when test="name(.) = 'svg' or @transform or @viewBox or @id or @clip-path or (@style and contains(@style, 'clip-path:url(#')) or (@width and not(contains(@width, '%'))) or @x or @y or (@height and not(contains(@height, '%'))) or *[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
+ <Canvas>
+ <xsl:apply-templates mode="id" select="." />
+ <!--
+ <xsl:apply-templates mode="clip" select="." />
+ -->
+ <xsl:if test="@style and contains(@style, 'display:none')"><xsl:attribute name="Visibility">Collapsed</xsl:attribute></xsl:if>
+ <xsl:if test="@style and contains(@style, 'opacity:')">
+ <xsl:attribute name="Opacity">
+ <xsl:choose>
+ <xsl:when test="contains(substring-after(@style, 'opacity:'), ';')"><xsl:value-of select="substring-before(substring-after(@style, 'opacity:'), ';')" /></xsl:when>
+ <xsl:otherwise><xsl:value-of select="substring-after(@style, 'opacity:')" /></xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@width and not(contains(@width, '%'))">
+ <xsl:attribute name="Width">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@width" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@height and not(contains(@height, '%'))">
+ <xsl:attribute name="Height">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@height" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@x">
+ <xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y">
+ <xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@viewBox">
+ <xsl:variable name="viewBox"><xsl:value-of select="normalize-space(translate(@viewBox, ',', ' '))" /></xsl:variable>
+ <xsl:attribute name="Width"><xsl:value-of select="substring-before(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
+ <xsl:attribute name="Height"><xsl:value-of select="substring-after(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
+ <Canvas.RenderTransform>
+ <TranslateTransform>
+ <xsl:attribute name="X"><xsl:value-of select="-number(substring-before($viewBox, ' '))" /></xsl:attribute>
+ <xsl:attribute name="Y"><xsl:value-of select="-number(substring-before(substring-after($viewBox, ' '), ' '))" /></xsl:attribute>
+ </TranslateTransform>
+ </Canvas.RenderTransform>
+ </xsl:if>
+ <xsl:if test="@transform">
+ <Canvas>
+ <Canvas.RenderTransform>
+ <TransformGroup><xsl:apply-templates mode="transform" select="." /></TransformGroup>
+ </Canvas.RenderTransform>
+ <xsl:apply-templates mode="forward" select="*" />
+ </Canvas>
+ </xsl:if>
+
+ <xsl:if test="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
+ <Canvas.Resources>
+ <xsl:apply-templates mode="forward" select="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']" />
+ </Canvas.Resources>
+ </xsl:if>
+ <xsl:if test="not(@transform)">
+ <xsl:apply-templates mode="forward" select="*[name(.) != 'linearGradient' and name(.) != 'radialGradient' and name(.) != 'defs' and name(.) != 'clipPath']" />
+ </xsl:if>
+ </Canvas>
+ </xsl:when>
+ <xsl:when test="not(@transform)">
+ <xsl:apply-templates mode="forward" select="*" />
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
<!--
-Values with units (except %) are converted to pixels.
-Unknown units are kept.
+// Resources (defs) //
+
+* Resources ids
+* Generic defs template
+* Generic filters template
+* Filter effects
+* Linked filter effects
+* Linear gradients
+* Radial gradients
+* Generic gradient stops
+* Clipping
-->
+
+<!-- Resources ids -->
+<xsl:template mode="resources" match="*">
+ <!-- should be in-depth -->
+ <xsl:if test="ancestor::*[name(.) = 'defs']"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+</xsl:template>
+
+<!-- Generic defs template -->
+<xsl:template mode="forward" match="defs">
+ <xsl:apply-templates mode="forward" />
+</xsl:template>
+
+<!-- Generic filters template
+Limited to one filter (can be improved) -->
+<xsl:template mode="forward" match="*[name(.) = 'filter']">
+ <xsl:if test="count(*) = 1">
+ <xsl:apply-templates mode="forward" />
+ </xsl:if>
+</xsl:template>
+
+<!-- Filter effects -->
+<xsl:template mode="forward" match="*[name(.) = 'feGaussianBlur']">
+ <BlurEffect>
+ <xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
+ <xsl:if test="@stdDeviation"><xsl:attribute name="Radius"><xsl:value-of select="round(@stdDeviation * 3)" /></xsl:attribute></xsl:if>
+ </BlurEffect>
+</xsl:template>
+
+<!-- Linked filter effect -->
+<xsl:template mode="filter_effect" match="*">
+<xsl:choose>
+ <xsl:when test="@filter and starts-with(@filter, 'url(#')">
+ <xsl:attribute name="Effect">
+ <xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@filter, 'url(#'), ')'), '}')" />
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="@style and contains(@style, 'filter:url(#')">
+ <xsl:attribute name="Effect">
+ <xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'filter:url(#'), ')'), '}')" />
+ </xsl:attribute>
+ </xsl:when>
+</xsl:choose>
+</xsl:template>
+
+<!-- Linear gradient -->
+<xsl:template mode="forward" match="*[name(.) = 'linearGradient']">
+ <LinearGradientBrush>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <xsl:attribute name="MappingMode">
+ <xsl:choose>
+ <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
+ <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:if test="@spreadMethod">
+ <xsl:attribute name="SpreadMethod">
+ <xsl:choose>
+ <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
+ <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
+ <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@x1 and @y1 and @x2 and @y2">
+ <xsl:choose>
+ <xsl:when test="contains(@x1, '%') and contains(@y1, '%')">
+ <xsl:attribute name="StartPoint"><xsl:value-of select="concat(substring-before(@x1, '%') div 100, ',', substring-before(@y1,'%') div 100)" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="StartPoint"><xsl:value-of select="concat(@x1, ',', @y1)" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:choose>
+ <xsl:when test="contains(@x2, '%') and contains(@y2, '%')">
+ <xsl:attribute name="EndPoint"><xsl:value-of select="concat(substring-before(@x2, '%') div 100, ',', substring-before(@y2,'%') div 100)" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="EndPoint"><xsl:value-of select="concat(@x2, ',', @y2)" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="StartPoint"><xsl:value-of select="'0,0'" /></xsl:attribute>
+ <xsl:attribute name="EndPoint"><xsl:value-of select="'1,1'" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <LinearGradientBrush.GradientStops>
+ <GradientStopCollection>
+ <xsl:choose>
+ <xsl:when test="@xlink:href">
+ <xsl:variable name="reference_id" select="@xlink:href" />
+ <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
+ </xsl:when>
+ <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
+ </xsl:choose>
+ </GradientStopCollection>
+ </LinearGradientBrush.GradientStops>
+ <xsl:if test="@gradientTransform">
+ <LinearGradientBrush.Transform>
+ <xsl:apply-templates mode="transform" select="." />
+ </LinearGradientBrush.Transform>
+ </xsl:if>
+ </LinearGradientBrush>
+</xsl:template>
+
+<!-- Radial gradient -->
+<xsl:template mode="forward" match="*[name(.) = 'radialGradient']">
+ <RadialGradientBrush>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <xsl:attribute name="MappingMode">
+ <xsl:choose>
+ <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
+ <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:if test="@spreadMethod">
+ <xsl:attribute name="SpreadMethod">
+ <xsl:choose>
+ <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
+ <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
+ <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@cx and @cy">
+ <xsl:attribute name="Center">
+ <xsl:choose>
+ <xsl:when test="contains(@cx, '%') and contains(@cy, '%')">
+ <xsl:value-of select="concat(number(substring-before(@cx, '%')) div 100, ',', number(substring-before(@cy, '%')) div 100)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(@cx, ',', @cy)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@fx and @fy">
+ <xsl:attribute name="GradientOrigin">
+ <xsl:choose>
+ <xsl:when test="contains(@fx, '%') and contains(@fy, '%')">
+ <xsl:value-of select="concat(number(substring-before(@fx, '%')) div 100, ',', number(substring-before(@fy, '%')) div 100)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(@fx, ',', @fy)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@r">
+ <xsl:choose>
+ <xsl:when test="contains(@r, '%')">
+ <xsl:attribute name="RadiusX"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
+ <xsl:attribute name="RadiusY"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="RadiusX"><xsl:value-of select="@r" /></xsl:attribute>
+ <xsl:attribute name="RadiusY"><xsl:value-of select="@r" /></xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <RadialGradientBrush.GradientStops>
+ <GradientStopCollection>
+ <xsl:choose>
+ <xsl:when test="@xlink:href">
+ <xsl:variable name="reference_id" select="@xlink:href" />
+ <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
+ </xsl:when>
+ <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
+ </xsl:choose>
+ </GradientStopCollection>
+ </RadialGradientBrush.GradientStops>
+ <xsl:if test="@gradientTransform">
+ <RadialGradientBrush.Transform>
+ <xsl:apply-templates mode="transform" select="." />
+ </RadialGradientBrush.Transform>
+ </xsl:if>
+ </RadialGradientBrush>
+</xsl:template>
+
+<!-- Generic gradient stops -->
+<xsl:template mode="forward" match="*[name(.) = 'stop']">
+ <GradientStop>
+ <!--xsl:apply-templates mode="stop_opacity" select="." /-->
+ <xsl:apply-templates mode="stop_color" select="." />
+ <xsl:apply-templates mode="offset" select="." />
+ <xsl:apply-templates mode="forward" />
+ </GradientStop>
+</xsl:template>
+
+<!-- Clipping -->
+<xsl:template mode="clip" match="*">
+ <xsl:choose>
+ <xsl:when test="@clip-path and defs/clipPath/path/@d"><xsl:attribute name="Clip"><xsl:value-of select="defs/clipPath/path/@d" /></xsl:attribute></xsl:when>
+ <xsl:when test="@clip-path and starts-with(@clip-path, 'url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@clip-path, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
+ <xsl:when test="@style and contains(@style, 'clip-path:url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
+ <xsl:when test="clipPath"><xsl:apply-templates mode="forward" /></xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!--
+// Misc templates //
+
+* Object description
+* Id converter
+* Decimal to hexadecimal converter
+* Unit to pixel converter
+* Title and description
+* Misc ignored stuff (markers, patterns, styles)
+* Symbols
+* Use
+* RDF and foreign objects
+* Unknows tags
+-->
+
+<!-- Object description -->
+<xsl:template mode="desc" match="*">
+ <xsl:if test="*[name(.) = 'desc']/text()"><xsl:attribute name="Tag"><xsl:value-of select="*[name(.) = 'desc']/text()" /></xsl:attribute></xsl:if>
+</xsl:template>
+
+<!-- Id converter. Removes "-" from the original id. -->
+<xsl:template mode="id" match="*">
+<xsl:if test="@id">
+ <xsl:attribute name="Name"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
+ <!--
+ <xsl:attribute name="x:Key"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
+ -->
+</xsl:if>
+</xsl:template>
+
+<!-- Decimal to hexadecimal converter -->
+<xsl:template name="to_hex">
+ <xsl:param name="convert" />
+ <xsl:value-of select="concat(substring('0123456789ABCDEF', 1 + floor(round($convert) div 16), 1), substring('0123456789ABCDEF', 1 + round($convert) mod 16, 1))" />
+</xsl:template>
+
+<!-- Unit to pixel converter
+Values with units (except %) are converted to pixels and rounded.
+Unknown units are kept. -->
<xsl:template name="convert_unit">
<xsl:param name="convert_value" />
<xsl:choose>
@@ -66,12 +411,82 @@ Unknown units are kept.
<xsl:when test="contains($convert_value, 'ft')">
<xsl:value-of select="round(translate($convert_value, 'ft', '') * 1080)" />
</xsl:when>
+ <xsl:when test="not(string(number($convert_value))='NaN')">
+ <xsl:value-of select="round($convert_value)" />
+ </xsl:when>
<xsl:otherwise>
<xsl:value-of select="$convert_value" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
+<!-- Title and description
+Blank template. Title is ignored and desc is converted to Tag in the mode="desc" template
+-->
+<xsl:template mode="forward" match="*[name(.) = 'title' or name(.) = 'desc']">
+ <!-- -->
+</xsl:template>
+
+<!-- Misc ignored stuff (markers, patterns, styles) -->
+<xsl:template mode="forward" match="*[name(.) = 'marker' or name(.) = 'pattern' or name(.) = 'style']">
+ <!-- -->
+</xsl:template>
+
+<!-- Symbols -->
+<xsl:template mode="forward" match="*[name(.) = 'symbol']">
+ <Style>
+ <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
+ <Canvas>
+ <xsl:apply-templates mode="forward" />
+ </Canvas>
+ </Style>
+</xsl:template>
+
+<!-- Use -->
+<xsl:template mode="forward" match="*[name(.) = 'use']">
+ <Canvas>
+ <xsl:if test="@xlink:href"><xsl:attribute name="Style"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
+ <!--xsl:apply-templates mode="transform" select="." /-->
+ <xsl:apply-templates mode="forward" />
+ </Canvas>
+</xsl:template>
+
+<!-- RDF and foreign objects -->
+<xsl:template mode="forward" match="rdf:RDF | *[name(.) = 'foreignObject']">
+ <!-- -->
+</xsl:template>
+
+<!-- Unknown tags -->
+<xsl:template match="*">
+<xsl:comment><xsl:value-of select="concat('Unknown tag: ', name(.))" /></xsl:comment>
+</xsl:template>
+
+
+<!--
+// Colors and patterns //
+
+* Generic color template
+* Fill
+* Fill opacity
+* Fill rule
+* Generic fill template
+* Stroke
+* Stroke opacity
+* Generic stroke template
+* Stroke width
+* Stroke mitterlimit
+* Stroke dasharray
+* Stroke dashoffset
+* Linejoin SVG to XAML converter
+* Stroke linejoin
+* Linecap SVG to XAML converter
+* Stroke linecap
+* Gradient stop
+* Gradient stop opacity
+* Gradient stop offset
+-->
+
+<!-- Generic color template -->
<xsl:template name="template_color">
<xsl:param name="colorspec" />
<xsl:param name="opacityspec" />
@@ -117,6 +532,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Fill -->
<xsl:template mode="fill" match="*">
<xsl:choose>
<xsl:when test="@fill and starts-with(@fill, 'url(#')"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@fill, 'url(#'), ')'), '}')" /></xsl:when>
@@ -135,6 +551,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Fill opacity -->
<xsl:template mode="fill_opacity" match="*">
<xsl:choose>
<xsl:when test="@fill-opacity"><xsl:value-of select="@fill-opacity" /></xsl:when>
@@ -149,6 +566,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Fill rule -->
<xsl:template mode="fill_rule" match="*">
<xsl:choose>
<xsl:when test="@fill-rule and (@fill-rule = 'nonzero' or @fill-rule = 'evenodd')"><xsl:attribute name="FillRule"><xsl:value-of select="@fill-rule" /></xsl:attribute></xsl:when>
@@ -166,6 +584,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Generic fill template -->
<xsl:template mode="template_fill" match="*">
<xsl:variable name="fill"><xsl:apply-templates mode="fill" select="." /></xsl:variable>
<xsl:variable name="fill_opacity"><xsl:apply-templates mode="fill_opacity" select="." /></xsl:variable>
@@ -186,6 +605,7 @@ Unknown units are kept.
</xsl:if>
</xsl:template>
+<!-- Stroke -->
<xsl:template mode="stroke" match="*">
<xsl:choose>
<xsl:when test="@stroke and starts-with(@stroke, 'url(#')"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@stroke, 'url(#'), ')'), '}')" /></xsl:when>
@@ -204,6 +624,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke opacity -->
<xsl:template mode="stroke_opacity" match="*">
<xsl:choose>
<xsl:when test="@stroke-opacity"><xsl:value-of select="@stroke-opacity" /></xsl:when>
@@ -218,6 +639,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Generic stroke template -->
<xsl:template mode="template_stroke" match="*">
<xsl:variable name="stroke"><xsl:apply-templates mode="stroke" select="." /></xsl:variable>
<xsl:variable name="stroke_opacity"><xsl:apply-templates mode="stroke_opacity" select="." /></xsl:variable>
@@ -231,6 +653,7 @@ Unknown units are kept.
</xsl:if>
</xsl:template>
+<!-- Stroke width -->
<xsl:template mode="stroke_width" match="*">
<xsl:choose>
<xsl:when test="@stroke-width">
@@ -252,6 +675,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke miterlimit -->
<xsl:template mode="stroke_miterlimit" match="*">
<xsl:choose>
<xsl:when test="@stroke-miterlimit"><xsl:attribute name="StrokeMiterLimit"><xsl:value-of select="@stroke-miterlimit" /></xsl:attribute></xsl:when>
@@ -268,6 +692,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke dasharray -->
<xsl:template mode="stroke_dasharray" match="*">
<!-- stroke-dasharray="10,30,20,30" becomes StrokeDashArray="1 3 2 3" ?? -->
<xsl:choose>
@@ -285,6 +710,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke dashoffset -->
<xsl:template mode="stroke_dashoffset" match="*">
<xsl:choose>
<xsl:when test="@stroke-dashoffset"><xsl:attribute name="StrokeDashOffset"><xsl:value-of select="@stroke-dashoffset" /></xsl:attribute></xsl:when>
@@ -301,6 +727,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Linejoin SVG to XAML converter -->
<xsl:template name="linejoin_svg_to_xaml">
<xsl:param name="linejoin" />
<xsl:choose>
@@ -310,6 +737,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke linejoin -->
<xsl:template mode="stroke_linejoin" match="*">
<xsl:choose>
<xsl:when test="@stroke-miterlimit">
@@ -333,6 +761,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Linecap SVG to XAML converter -->
<xsl:template name="linecap_svg_to_xaml">
<xsl:param name="linecap" />
<xsl:choose>
@@ -342,6 +771,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Stroke linecap -->
<xsl:template mode="stroke_linecap" match="*">
<xsl:choose>
<xsl:when test="@stroke-linecap">
@@ -379,20 +809,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
-<xsl:template mode="resources" match="*">
- <!-- should be in-depth -->
- <xsl:if test="ancestor::*[name(.) = 'defs']"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
-</xsl:template>
-
-<xsl:template mode="desc" match="*">
- <xsl:if test="*[name(.) = 'desc']/text()"><xsl:attribute name="Tag"><xsl:value-of select="*[name(.) = 'desc']/text()" /></xsl:attribute></xsl:if>
-</xsl:template>
-
-<xsl:template name="to_hex">
- <xsl:param name="convert" />
- <xsl:value-of select="concat(substring('0123456789ABCDEF', 1 + floor(round($convert) div 16), 1), substring('0123456789ABCDEF', 1 + round($convert) mod 16, 1))" />
-</xsl:template>
-
+<!-- Gradient stops -->
<xsl:template mode="stop_color" match="*">
<xsl:variable name="Opacity">
<xsl:choose>
@@ -441,6 +858,7 @@ Unknown units are kept.
</xsl:attribute>
</xsl:template>
+<!-- Gradient stop opacity -->
<xsl:template mode="stop_opacity" match="*">
<xsl:choose>
<xsl:when test="@stop-opacity"><xsl:attribute name="Opacity"><xsl:value-of select="@stop-opacity" /></xsl:attribute></xsl:when>
@@ -457,6 +875,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Gradient stop offset -->
<xsl:template mode="offset" match="*">
<xsl:choose>
<xsl:when test="@offset">
@@ -481,6 +900,17 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!--
+// Transforms //
+All the matrix, translate, rotate... stuff.
+Fixme: XAML transforms don't show the same result as SVG ones with the same values.
+
+* Parse transform
+* Apply transform
+* Apply transform v2
+-->
+
+<!-- Parse transform -->
<xsl:template name="parse_transform">
<xsl:param name="input" />
<xsl:choose>
@@ -544,6 +974,7 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!-- Apply transform -->
<xsl:template mode="transform" match="*">
<xsl:param name="mapped_type" />
<xsl:if test="@transform or @gradientTransform">
@@ -583,108 +1014,8 @@ Unknown units are kept.
</xsl:if>
</xsl:template>
-<xsl:template mode="clip" match="*">
- <xsl:choose>
- <xsl:when test="@clip-path and defs/clipPath/path/@d"><xsl:attribute name="Clip"><xsl:value-of select="defs/clipPath/path/@d" /></xsl:attribute></xsl:when>
- <xsl:when test="@clip-path and starts-with(@clip-path, 'url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@clip-path, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
- <xsl:when test="@style and contains(@style, 'clip-path:url(#')"><xsl:attribute name="Clip"><xsl:value-of select="concat('{StaticResource ', substring-before(substring-after(@style, 'url(#'), ')'), '}')" /></xsl:attribute></xsl:when>
- <xsl:when test="clipPath"><xsl:apply-templates mode="forward" /></xsl:when>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template mode="id" match="*">
-<xsl:if test="@id">
- <xsl:attribute name="Name"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
- <!--
- <xsl:attribute name="x:Key"><xsl:value-of select="translate(@id, '- ', '')" /></xsl:attribute>
- -->
-</xsl:if>
-</xsl:template>
-
-<xsl:template match="/">
- <xsl:choose>
- <xsl:when test="$silverlight_compatible = 1">
- <xsl:apply-templates mode="forward" />
- </xsl:when>
- <xsl:otherwise>
- <Viewbox Stretch="Uniform">
- <xsl:apply-templates mode="forward" />
- </Viewbox>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template mode="forward" match="defs">
- <xsl:apply-templates mode="forward" />
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'svg' or name(.) = 'g']">
- <xsl:choose>
- <xsl:when test="name(.) = 'svg' or @transform or @viewBox or @id or @clip-path or (@style and contains(@style, 'clip-path:url(#')) or (@width and not(contains(@width, '%'))) or @x or @y or (@height and not(contains(@height, '%'))) or *[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
- <Canvas>
- <xsl:apply-templates mode="id" select="." />
- <!--
- <xsl:apply-templates mode="clip" select="." />
- -->
- <xsl:if test="@style and contains(@style, 'display:none')"><xsl:attribute name="Visibility">Collapsed</xsl:attribute></xsl:if>
- <xsl:if test="@style and contains(@style, 'opacity:')">
- <xsl:attribute name="Opacity">
- <xsl:choose>
- <xsl:when test="contains(substring-after(@style, 'opacity:'), ';')"><xsl:value-of select="substring-before(substring-after(@style, 'opacity:'), ';')" /></xsl:when>
- <xsl:otherwise><xsl:value-of select="substring-after(@style, 'opacity:')" /></xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@width and not(contains(@width, '%'))">
- <xsl:attribute name="Width">
- <xsl:call-template name="convert_unit">
- <xsl:with-param name="convert_value" select="@width" />
- </xsl:call-template>
- </xsl:attribute></xsl:if>
- <xsl:if test="@height and not(contains(@height, '%'))">
- <xsl:attribute name="Height">
- <xsl:call-template name="convert_unit">
- <xsl:with-param name="convert_value" select="@height" />
- </xsl:call-template>
- </xsl:attribute></xsl:if>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
- <xsl:if test="@viewBox">
- <xsl:variable name="viewBox"><xsl:value-of select="normalize-space(translate(@viewBox, ',', ' '))" /></xsl:variable>
- <xsl:attribute name="Width"><xsl:value-of select="substring-before(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
- <xsl:attribute name="Height"><xsl:value-of select="substring-after(substring-after(substring-after($viewBox, ' '), ' '), ' ')" /></xsl:attribute>
- <Canvas.RenderTransform>
- <TranslateTransform>
- <xsl:attribute name="X"><xsl:value-of select="-number(substring-before($viewBox, ' '))" /></xsl:attribute>
- <xsl:attribute name="Y"><xsl:value-of select="-number(substring-before(substring-after($viewBox, ' '), ' '))" /></xsl:attribute>
- </TranslateTransform>
- </Canvas.RenderTransform>
- </xsl:if>
- <xsl:if test="@transform">
- <Canvas>
- <Canvas.RenderTransform>
- <TransformGroup><xsl:apply-templates mode="transform" select="." /></TransformGroup>
- </Canvas.RenderTransform>
- <xsl:apply-templates mode="forward" select="*" />
- </Canvas>
- </xsl:if>
-
- <xsl:if test="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']">
- <Canvas.Resources>
- <xsl:apply-templates mode="forward" select="*[name(.) = 'linearGradient' or name(.) = 'radialGradient' or name(.) = 'defs' or name(.) = 'clipPath']" />
- </Canvas.Resources>
- </xsl:if>
- <xsl:if test="not(@transform)">
- <xsl:apply-templates mode="forward" select="*[name(.) != 'linearGradient' and name(.) != 'radialGradient' and name(.) != 'defs' and name(.) != 'clipPath']" />
- </xsl:if>
- </Canvas>
- </xsl:when>
- <xsl:when test="not(@transform)">
- <xsl:apply-templates mode="forward" select="*" />
- </xsl:when>
- </xsl:choose>
-</xsl:template>
-
+<!-- Apply transform v2
+Fixme: is this template still in use? -->
<xsl:template mode="transform2" match="*">
<xsl:choose>
<xsl:when test="@transform">
@@ -701,13 +1032,35 @@ Unknown units are kept.
</xsl:choose>
</xsl:template>
+<!--
+// Objects //
+
+* Image
+* Text
+* Lines
+* Rectangle
+* Polygon
+* Polyline
+* Path
+* Ellipse
+* Circle
+-->
+
+<!-- Image -->
<xsl:template mode="forward" match="*[name(.) = 'image']">
<Image>
<xsl:apply-templates mode="id" select="." />
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:if test="@xlink:href"><xsl:attribute name="Source"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
<xsl:if test="@width"><xsl:attribute name="Width">
@@ -728,6 +1081,7 @@ Unknown units are kept.
</Image>
</xsl:template>
+<!-- Text -->
<xsl:template mode="forward" match="*[name(.) = 'text']">
<TextBlock>
<xsl:if test="@font-size"><xsl:attribute name="FontSize"><xsl:value-of select="@font-size" /></xsl:attribute></xsl:if>
@@ -809,12 +1163,19 @@ Unknown units are kept.
<xsl:with-param name="convert_value" select="@height" />
</xsl:call-template>
</xsl:attribute></xsl:if>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:apply-templates mode="id" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<!--xsl:apply-templates mode="transform" select="." /-->
<!--xsl:apply-templates mode="forward" /-->
@@ -822,187 +1183,8 @@ Unknown units are kept.
<xsl:if test="*[name(.) = 'tspan']/text()"><xsl:value-of select="*[name(.) = 'tspan']/text()" /></xsl:if>
</TextBlock>
</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'title' or name(.) = 'desc']">
- <!-- -->
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'marker' or name(.) = 'pattern' or name(.) = 'style']">
- <!-- -->
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'symbol']">
- <Style>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <Canvas>
- <xsl:apply-templates mode="forward" />
- </Canvas>
- </Style>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'use']">
- <Canvas>
- <xsl:if test="@xlink:href"><xsl:attribute name="Style"><xsl:value-of select="@xlink:href" /></xsl:attribute></xsl:if>
- <!--xsl:apply-templates mode="transform" select="." /-->
- <xsl:apply-templates mode="forward" />
- </Canvas>
-</xsl:template>
-
-<xsl:template mode="forward" match="rdf:RDF | *[name(.) = 'foreignObject']">
- <!-- -->
-</xsl:template>
-
-<xsl:template match="*">
-<xsl:comment><xsl:value-of select="concat('Unknown tag: ', name(.))" /></xsl:comment>
-</xsl:template>
-
-<!-- BRUSHES -->
-
-<xsl:template mode="forward" match="*[name(.) = 'linearGradient']">
- <LinearGradientBrush>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <xsl:attribute name="MappingMode">
- <xsl:choose>
- <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
- <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <xsl:if test="@spreadMethod">
- <xsl:attribute name="SpreadMethod">
- <xsl:choose>
- <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
- <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
- <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:choose>
- <xsl:when test="@x1 and @y1 and @x2 and @y2">
- <xsl:choose>
- <xsl:when test="contains(@x1, '%') and contains(@y1, '%')">
- <xsl:attribute name="StartPoint"><xsl:value-of select="concat(substring-before(@x1, '%') div 100, ',', substring-before(@y1,'%') div 100)" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="StartPoint"><xsl:value-of select="concat(@x1, ',', @y1)" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- <xsl:choose>
- <xsl:when test="contains(@x2, '%') and contains(@y2, '%')">
- <xsl:attribute name="EndPoint"><xsl:value-of select="concat(substring-before(@x2, '%') div 100, ',', substring-before(@y2,'%') div 100)" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="EndPoint"><xsl:value-of select="concat(@x2, ',', @y2)" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="StartPoint"><xsl:value-of select="'0,0'" /></xsl:attribute>
- <xsl:attribute name="EndPoint"><xsl:value-of select="'1,1'" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- <LinearGradientBrush.GradientStops>
- <GradientStopCollection>
- <xsl:choose>
- <xsl:when test="@xlink:href">
- <xsl:variable name="reference_id" select="@xlink:href" />
- <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
- </xsl:when>
- <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
- </xsl:choose>
- </GradientStopCollection>
- </LinearGradientBrush.GradientStops>
- <xsl:if test="@gradientTransform">
- <LinearGradientBrush.Transform>
- <xsl:apply-templates mode="transform" select="." />
- </LinearGradientBrush.Transform>
- </xsl:if>
- </LinearGradientBrush>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'radialGradient']">
- <RadialGradientBrush>
- <xsl:if test="@id"><xsl:attribute name="x:Key"><xsl:value-of select="@id" /></xsl:attribute></xsl:if>
- <xsl:attribute name="MappingMode">
- <xsl:choose>
- <xsl:when test="@gradientUnits = 'userSpaceOnUse' ">Absolute</xsl:when>
- <xsl:otherwise>RelativeToBoundingBox</xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- <xsl:if test="@spreadMethod">
- <xsl:attribute name="SpreadMethod">
- <xsl:choose>
- <xsl:when test="@spreadMethod = 'pad'">Pad</xsl:when>
- <xsl:when test="@spreadMethod = 'reflect'">Reflect</xsl:when>
- <xsl:when test="@spreadMethod = 'repeat'">Repeat</xsl:when>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@cx and @cy">
- <xsl:attribute name="Center">
- <xsl:choose>
- <xsl:when test="contains(@cx, '%') and contains(@cy, '%')">
- <xsl:value-of select="concat(number(substring-before(@cx, '%')) div 100, ',', number(substring-before(@cy, '%')) div 100)" />
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="concat(@cx, ',', @cy)" />
- </xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@fx and @fy">
- <xsl:attribute name="GradientOrigin">
- <xsl:choose>
- <xsl:when test="contains(@fx, '%') and contains(@fy, '%')">
- <xsl:value-of select="concat(number(substring-before(@fx, '%')) div 100, ',', number(substring-before(@fy, '%')) div 100)" />
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="concat(@fx, ',', @fy)" />
- </xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:if>
- <xsl:if test="@r">
- <xsl:choose>
- <xsl:when test="contains(@r, '%')">
- <xsl:attribute name="RadiusX"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
- <xsl:attribute name="RadiusY"><xsl:value-of select="number(substring-before(@r, '%')) div 100" /></xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="RadiusX"><xsl:value-of select="@r" /></xsl:attribute>
- <xsl:attribute name="RadiusY"><xsl:value-of select="@r" /></xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:if>
- <RadialGradientBrush.GradientStops>
- <GradientStopCollection>
- <xsl:choose>
- <xsl:when test="@xlink:href">
- <xsl:variable name="reference_id" select="@xlink:href" />
- <xsl:apply-templates mode="forward" select="//*[name(.) = 'linearGradient' and $reference_id = concat('#', @id)]/*" />
- </xsl:when>
- <xsl:otherwise><xsl:apply-templates mode="forward" /></xsl:otherwise>
- </xsl:choose>
- </GradientStopCollection>
- </RadialGradientBrush.GradientStops>
- <xsl:if test="@gradientTransform">
- <RadialGradientBrush.Transform>
- <xsl:apply-templates mode="transform" select="." />
- </RadialGradientBrush.Transform>
- </xsl:if>
- </RadialGradientBrush>
-</xsl:template>
-
-<xsl:template mode="forward" match="*[name(.) = 'stop']">
- <GradientStop>
- <!--xsl:apply-templates mode="stop_opacity" select="." /-->
- <xsl:apply-templates mode="stop_color" select="." />
- <xsl:apply-templates mode="offset" select="." />
- <xsl:apply-templates mode="forward" />
- </GradientStop>
-</xsl:template>
-
-<!-- SHAPES -->
-
+
+<!-- Lines -->
<xsl:template mode="forward" match="*[name(.) = 'line']">
<Line>
<xsl:if test="@x1"><xsl:attribute name="X1"><xsl:value-of select="@x1" /></xsl:attribute></xsl:if>
@@ -1018,7 +1200,7 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1029,10 +1211,19 @@ Unknown units are kept.
</Line>
</xsl:template>
+<!-- Rectangle -->
<xsl:template mode="forward" match="*[name(.) = 'rect']">
<Rectangle>
- <xsl:if test="@x"><xsl:attribute name="Canvas.Left"><xsl:value-of select="@x" /></xsl:attribute></xsl:if>
- <xsl:if test="@y"><xsl:attribute name="Canvas.Top"><xsl:value-of select="@y" /></xsl:attribute></xsl:if>
+ <xsl:if test="@x"><xsl:attribute name="Canvas.Left">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@x" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
+ <xsl:if test="@y"><xsl:attribute name="Canvas.Top">
+ <xsl:call-template name="convert_unit">
+ <xsl:with-param name="convert_value" select="@y" />
+ </xsl:call-template>
+ </xsl:attribute></xsl:if>
<xsl:if test="@width"><xsl:attribute name="Width">
<xsl:call-template name="convert_unit">
<xsl:with-param name="convert_value" select="@width" />
@@ -1057,11 +1248,9 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="resources" select="." />
-
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1072,6 +1261,7 @@ Unknown units are kept.
</Rectangle>
</xsl:template>
+<!-- Polygon -->
<xsl:template mode="forward" match="*[name(.) = 'polygon']">
<Polygon>
<xsl:if test="@points"><xsl:attribute name="Points"><xsl:value-of select="@points" /></xsl:attribute></xsl:if>
@@ -1085,7 +1275,7 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1096,6 +1286,7 @@ Unknown units are kept.
</Polygon>
</xsl:template>
+<!-- Polyline -->
<xsl:template mode="forward" match="*[name(.) = 'polyline']">
<Polyline>
<xsl:if test="@points"><xsl:attribute name="Points"><xsl:value-of select="@points" /></xsl:attribute></xsl:if>
@@ -1109,7 +1300,7 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1120,6 +1311,7 @@ Unknown units are kept.
</Polyline>
</xsl:template>
+<!-- Path -->
<xsl:template mode="forward" match="*[name(.) = 'path']">
<Path>
<xsl:apply-templates mode="id" select="." />
@@ -1131,7 +1323,7 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
<xsl:if test="@d">
@@ -1162,6 +1354,7 @@ Unknown units are kept.
</Path>
</xsl:template>
+<!-- Ellipse -->
<xsl:template mode="forward" match="*[name(.) = 'ellipse']">
<Ellipse>
<xsl:variable name="cx">
@@ -1193,9 +1386,8 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1206,6 +1398,7 @@ Unknown units are kept.
</Ellipse>
</xsl:template>
+<!-- Circle -->
<xsl:template mode="forward" match="*[name(.) = 'circle']">
<Ellipse>
<xsl:variable name="cx">
@@ -1221,9 +1414,9 @@ Unknown units are kept.
</xsl:choose>
</xsl:variable>
<xsl:if test="@r">
- <xsl:attribute name="Canvas.Left"><xsl:value-of select='format-number($cx - @r, "#.#")' /></xsl:attribute>
- <xsl:attribute name="Canvas.Top"><xsl:value-of select='format-number($cy - @r, "#.#")' /></xsl:attribute>
- <xsl:attribute name="Width"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute>
+ <xsl:attribute name="Canvas.Left"><xsl:value-of select='format-number($cx - @r, "#.#")' /></xsl:attribute>
+ <xsl:attribute name="Canvas.Top"><xsl:value-of select='format-number($cy - @r, "#.#")' /></xsl:attribute>
+ <xsl:attribute name="Width"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute>
<xsl:attribute name="Height"><xsl:value-of select='format-number(2 * @r, "#.#")' /></xsl:attribute>
</xsl:if>
<xsl:apply-templates mode="id" select="." />
@@ -1235,9 +1428,8 @@ Unknown units are kept.
<xsl:apply-templates mode="stroke_dashoffset" select="." />
<xsl:apply-templates mode="stroke_linejoin" select="." />
<xsl:apply-templates mode="stroke_linecap" select="." />
-
+ <xsl:apply-templates mode="filter_effect" select="." />
<xsl:apply-templates mode="desc" select="." />
-
<xsl:apply-templates mode="clip" select="." />
<xsl:apply-templates mode="transform" select=".">
@@ -1248,10 +1440,19 @@ Unknown units are kept.
</Ellipse>
</xsl:template>
+<!--
+// Geometry //
+* Generic clip path template
+* Geometry for circle
+* Geometry for rectangle
+-->
+
+<!-- Generic clip path template -->
<xsl:template mode="forward" match="*[name(.) = 'clipPath']">
<xsl:apply-templates mode="geometry" />
</xsl:template>
+<!-- Geometry for circle -->
<xsl:template mode="geometry" match="*[name(.) = 'circle']">
<EllipseGeometry>
<xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
@@ -1263,6 +1464,7 @@ Unknown units are kept.
</EllipseGeometry>
</xsl:template>
+<!-- Geometry for rectangle -->
<xsl:template mode="geometry" match="*[name(.) = 'rect']">
<RectangleGeometry>
<xsl:if test="../@id"><xsl:attribute name="x:Key"><xsl:value-of select="../@id" /></xsl:attribute></xsl:if>
diff --git a/share/extensions/test/inkweb-debug.js b/share/extensions/test/inkweb-debug.js
index 3dd3a4541..a4035503a 100644
--- a/share/extensions/test/inkweb-debug.js
+++ b/share/extensions/test/inkweb-debug.js
@@ -253,7 +253,7 @@ InkWeb.__logSequence = function ( msg ) {
InkWeb.logToString = function (conf) {
/* Show the log in a formated string.
- ** conf atributes:
+ ** conf attributes:
** format: a string to format the log itens.
** formatError: to format the error log itens.
** sep: the log itens separator string.
diff --git a/share/extensions/uniconv-ext.py b/share/extensions/uniconv-ext.py
index 3b248d8b8..d3d69546c 100644
--- a/share/extensions/uniconv-ext.py
+++ b/share/extensions/uniconv-ext.py
@@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import sys
from run_command import run
+import gettext
+_ = gettext.gettext
cmd = None
@@ -49,11 +51,11 @@ if cmd == None:
import imp
imp.find_module("uniconvertor")
except ImportError:
- sys.stderr.write('You need to install the UniConvertor software.\n'+\
+ sys.stderr.write(_('You need to install the UniConvertor software.\n'+\
'For GNU/Linux: install the package python-uniconvertor.\n'+\
'For Windows: download it from\n'+\
'http://sk1project.org/modules.php?name=Products&product=uniconvertor\n'+\
- 'and install into your Inkscape\'s Python location\n')
+ 'and install into your Inkscape\'s Python location\n'))
sys.exit(1)
cmd = 'python -c "import uniconvertor"'
diff --git a/share/extensions/uniconv_output.py b/share/extensions/uniconv_output.py
index 9cdac7fc2..f7746c2f4 100644
--- a/share/extensions/uniconv_output.py
+++ b/share/extensions/uniconv_output.py
@@ -31,6 +31,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import os
import sys
import tempfile
+import gettext
+_ = gettext.gettext
def run(command_format, prog_name, uniconv_format):
outfile = tempfile.mktemp(uniconv_format)
diff --git a/share/extensions/webslicer_create_group.inx b/share/extensions/webslicer_create_group.inx
index 6352c2066..2238a0901 100644
--- a/share/extensions/webslicer_create_group.inx
+++ b/share/extensions/webslicer_create_group.inx
@@ -6,8 +6,8 @@
<dependency type="executable" location="extensions">webslicer_create_group.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<_param name="about" type="description">Layout Group is only about to help a better code generation (if you need it). To use this, you must to select some "Slicer rectangles" first.</_param>
- <param name="html-id" type="string" _gui-text="HTML id atribute:"></param>
- <param name="html-class" type="string" _gui-text="HTML class atribute:"></param>
+ <param name="html-id" type="string" _gui-text="HTML id attribute:"></param>
+ <param name="html-class" type="string" _gui-text="HTML class attribute:"></param>
<param name="width-unity" type="enum" _gui-text="Width unit:">
<_item value="px">Pixel (fixed)</_item>
<_item value="percent">Percent (relative to parent size)</_item>
diff --git a/share/extensions/webslicer_create_rect.inx b/share/extensions/webslicer_create_rect.inx
index 651dda668..acf440cca 100644
--- a/share/extensions/webslicer_create_rect.inx
+++ b/share/extensions/webslicer_create_rect.inx
@@ -13,7 +13,8 @@
</param>
<param name="dpi" type="float" min="1" max="9999" _gui-text="DPI:">90</param>
<param name="dimension" type="string" _gui-text="Force Dimension:"></param>
- <_param name="help-dimension1" type="description">Force Dimension must be set as "&lt;width&gt;x&lt;height&gt;"</_param>
+<!-- i18n. Description duplicated in a fake value attribute in order to make it translatable -->
+ <param name="help-dimension1" type="description" _value="Force Dimension must be set as &lt;width&gt;x&lt;height&gt;">Force Dimension must be set as &lt;width&gt;x&lt;height&gt;</param>
<_param name="help-dimension2" type="description">If set, this will replace DPI.</_param>
<param name="bg-color" type="string" _gui-text="Background color:"></param>
<param name="tab" type="notebook">
@@ -31,8 +32,8 @@
<param name="palette-size" type="int" min="2" max="256" _gui-text="Palette size:">256</param>
</page>
<page name="tabHTML" gui-text="HTML">
- <param name="html-id" type="string" _gui-text="HTML id atribute:"></param>
- <param name="html-class" type="string" _gui-text="HTML class atribute:"></param>
+ <param name="html-id" type="string" _gui-text="HTML id attribute:"></param>
+ <param name="html-class" type="string" _gui-text="HTML class attribute:"></param>
<_param name="help-gif" type="description">Options for HTML export</_param>
<param name="layout-disposition" type="enum" _gui-text="Layout disposition:">
<_item value="bg-el-norepeat">Positioned html block element with the image as Background</_item>
diff --git a/share/extensions/webslicer_export.inx b/share/extensions/webslicer_export.inx
index eeb753add..3a7324370 100644
--- a/share/extensions/webslicer_export.inx
+++ b/share/extensions/webslicer_export.inx
@@ -5,8 +5,8 @@
<dependency type="executable" location="extensions">webslicer_effect.py</dependency>
<dependency type="executable" location="extensions">webslicer_export.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
- <_param name="about" type="description">All sliced images, and optionally code, will be generated as you had configured and saved to one directory.</_param>
- <param name="dir" type="string" _gui-text="Directory path to export:"></param>
+ <_param name="about" type="description">All sliced images, and optionaly code, will be generated as you had configured and saved to one directory.</_param>
+ <param name="dir" type="string" _gui-text="Directory path to export"></param>
<param name="create-dir" type="boolean" _gui-text="Create directory, if it does not exists">false</param>
<param name="with-code" type="boolean" _gui-text="With HTML and CSS">true</param>
<effect needs-live-preview="false">
diff --git a/share/extensions/webslicer_export.py b/share/extensions/webslicer_export.py
index 6b4ef75d8..78de70a37 100755
--- a/share/extensions/webslicer_export.py
+++ b/share/extensions/webslicer_export.py
@@ -21,6 +21,7 @@ from webslicer_effect import *
import inkex
import gettext
import os.path
+import subprocess
import tempfile
import commands
@@ -45,7 +46,11 @@ class WebSlicer_Export(WebSlicer_Effect):
dest="with_code",
help="")
- def effect(self):
+
+ svgNS = '{http://www.w3.org/2000/svg}'
+
+
+ def validate_inputs(self):
# The user must supply a directory to export:
if is_empty( self.options.dir ):
inkex.errormsg(_('You must to give a directory to export the slices.'))
@@ -66,32 +71,78 @@ class WebSlicer_Export(WebSlicer_Effect):
else:
inkex.errormsg(_('The directory "%s" does not exists.') % self.options.dir)
return
- # Create HTML and CSS files, if the user wants:
- if self.options.with_code:
- try:
- self.html = open(os.path.join(self.options.dir,'layout.html'), 'w')
- self.css = open(os.path.join(self.options.dir,'style.css'), 'w')
- self.html.write('Only a test yet\n\n')
- self.css.write('/* Only a test yet */\n\n')
- except Exception as e:
- inkex.errormsg( _('Can\'t create code files.') )
- inkex.errormsg( _('Error: %s') % e )
- return {'error':'Can\'t create code files.'}
+ self.unique_html_id( self.get_slicer_layer() )
+ return None
+
+
+ _html_ids = []
+ def unique_html_id(self, el):
+ for child in el.getchildren():
+ if child.tag in [ self.svgNS+'rect', self.svgNS+'path',
+ self.svgNS+'circle', self.svgNS+'g' ]:
+ conf = self.get_el_conf(child)
+ if conf['html-id'] in self._html_ids:
+ inkex.errormsg(
+ _('You have more than one element with "%s" html-id.') %
+ conf['html-id'] )
+ n = 2
+ while (conf['html-id']+"-"+str(n)) in self._html_ids: n += 1
+ conf['html-id'] += "-"+str(n)
+ self._html_ids.append( conf['html-id'] )
+ self.save_conf( conf, child )
+ self.unique_html_id( child )
+
+
+ def test_if_has_imagemagick(self):
+ (status, output) = commands.getstatusoutput('convert --version')
+ self.has_magick = ( status == 0 and 'ImageMagick' in output )
+
+
+ def effect(self):
+ self.test_if_has_imagemagick()
+ error = self.validate_inputs()
+ if error: return error
+ # Register the basic CSS code:
+ self.reg_css( 'body', 'text-align', 'center' )
# Create the temporary SVG with invisible Slicer layer to export image pieces
self.create_the_temporary_svg()
# Start what we really want!
self.export_chids_of( self.get_slicer_layer() )
- # Close the HTML and CSS files:
+ # Write the HTML and CSS files if asked for:
if self.options.with_code:
- self.html.close()
- self.css.write( self.css_code() )
- self.css.close()
+ self.make_html_file()
+ self.make_css_file()
# Delete the temporary SVG with invisible Slicer layer
self.delete_the_temporary_svg()
#TODO: prevent inkex to return svg code to update Inkscape
- svgNS = '{http://www.w3.org/2000/svg}'
+ def make_html_file(self):
+ f = open(os.path.join(self.options.dir,'layout.html'), 'w')
+ f.write(
+ '<html>\n<head>\n' +\
+ ' <title>Web Layout Testing</title>\n' +\
+ ' <style type="text/css" media="screen">\n' +\
+ ' @import url("style.css")\n' +\
+ ' </style>\n' +\
+ '</head>\n<body>\n' +\
+ self.html_code() +\
+ ' <p style="position:absolute; bottom:10px">\n' +\
+ ' This HTML code is not done to the web. <br/>\n' +\
+ ' The automatic HTML and CSS code are only a helper.' +\
+ '</p>\n</body>\n</html>' )
+ f.close()
+
+
+ def make_css_file(self):
+ f = open(os.path.join(self.options.dir,'style.css'), 'w')
+ f.write(
+ '/*\n' +\
+ '** This CSS code is not done to the web.\n' +\
+ '** The automatic HTML and CSS code are only a helper.\n' +\
+ '*/\n' +\
+ self.css_code() )
+ f.close()
def create_the_temporary_svg(self):
@@ -109,7 +160,7 @@ class WebSlicer_Export(WebSlicer_Effect):
noid_element_count = 0
def get_el_conf(self, el):
- desc = el.find('{http://www.w3.org/2000/svg}desc')
+ desc = el.find(self.svgNS+'desc')
conf = {}
if desc is None:
desc = inkex.etree.SubElement(el, 'desc')
@@ -129,6 +180,15 @@ class WebSlicer_Export(WebSlicer_Effect):
return conf
+ def save_conf(self, conf, el):
+ desc = el.find('{http://www.w3.org/2000/svg}desc')
+ if desc is not None:
+ conf_a = []
+ for k in conf:
+ conf_a.append( k+' : '+conf[k] )
+ desc.text = "\n".join(conf_a)
+
+
def export_chids_of(self, parent):
parent_id = self.get_el_conf( parent )['html-id']
for el in parent.getchildren():
@@ -145,66 +205,217 @@ class WebSlicer_Export(WebSlicer_Effect):
def register_group_code(self, group, conf):
- #inkex.errormsg( 'group CSS and HTML' )
- self.html.write( '<div id="G">\n' )
- for att in conf:
- self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) )
+ self.reg_html('div', group)
+ selec = '#'+conf['html-id']
+ self.reg_css( selec, 'position', 'absolute' )
+ geometry = self.get_relative_el_geometry(group)
+ self.reg_css( selec, 'top', str(int(geometry['y']))+'px' )
+ self.reg_css( selec, 'left', str(int(geometry['x']))+'px' )
+ self.reg_css( selec, 'width', str(int(geometry['w']))+'px' )
+ self.reg_css( selec, 'height', str(int(geometry['h']))+'px' )
self.export_chids_of( group )
- self.html.write( '</div><!-- end id="G" -->\n' )
- def register_unity_code(self, el, conf, parent_id):
- #inkex.errormsg( 'unity CSS and HTML' )
- css_selector = '#'+conf['html-id']
+ def __validate_slice_conf(self, conf):
if not 'layout-disposition' in conf:
conf['layout-disposition'] = 'bg-el-norepeat'
- if conf['layout-disposition'][0:9] == 'bg-parent':
- if parent_id == '#body#':
- css_selector = 'body'
- else:
- css_selector = '#'+parent_id
- for att in conf:
- self.html.write( '<!-- bg {att} : {val} -->\n'.format(att=att, val=conf[att]) )
- else:
- self.html.write( '<div id="image">\n' )
- for att in conf:
- self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) )
- self.html.write( '</div><!-- end id="image" -->\n' )
- self.reg_css( css_selector, 'background',
- 'url("%s")' % self.img_name(el, conf) )
+ if not 'layout-position-anchor' in conf:
+ conf['layout-position-anchor'] = 'mc'
+ return conf
+
+
+ def register_unity_code(self, el, conf, parent_id):
+ conf = self.__validate_slice_conf(conf)
+ css_selector = '#'+conf['html-id']
+ bg_repeat = 'no-repeat'
+ img_name = self.img_name(el, conf)
+ if conf['layout-disposition'][0:2] == 'bg':
+ if conf['layout-disposition'][0:9] == 'bg-parent':
+ if parent_id == '#body#':
+ css_selector = 'body'
+ else:
+ css_selector = '#'+parent_id
+ if conf['layout-disposition'] == 'bg-parent-repeat':
+ bg_repeat = 'repeat'
+ if conf['layout-disposition'] == 'bg-parent-repeat-x':
+ bg_repeat = 'repeat-x'
+ if conf['layout-disposition'] == 'bg-parent-repeat-y':
+ bg_repeat = 'repeat-y'
+ lay_anchor = conf['layout-position-anchor']
+ if lay_anchor == 'tl': lay_anchor = 'top left'
+ if lay_anchor == 'tc': lay_anchor = 'top center'
+ if lay_anchor == 'tr': lay_anchor = 'top right'
+ if lay_anchor == 'ml': lay_anchor = 'middle left'
+ if lay_anchor == 'mc': lay_anchor = 'middle center'
+ if lay_anchor == 'mr': lay_anchor = 'middle right'
+ if lay_anchor == 'bl': lay_anchor = 'bottom left'
+ if lay_anchor == 'bc': lay_anchor = 'bottom center'
+ if lay_anchor == 'br': lay_anchor = 'bottom right'
+ self.reg_css( css_selector, 'background',
+ 'url("%s") %s %s' % (img_name, bg_repeat, lay_anchor) )
+ else: # conf['layout-disposition'][0:9] == 'bg-el...'
+ self.reg_html('div', el)
+ self.reg_css( css_selector, 'background',
+ 'url("%s") %s' % (img_name, 'no-repeat') )
+ self.reg_css( css_selector, 'position', 'absolute' )
+ geo = self.get_relative_el_geometry(el,True)
+ self.reg_css( css_selector, 'top', geo['y'] )
+ self.reg_css( css_selector, 'left', geo['x'] )
+ self.reg_css( css_selector, 'width', geo['w'] )
+ self.reg_css( css_selector, 'height', geo['h'] )
+ else: # conf['layout-disposition'] == 'img...'
+ self.reg_html('img', el)
+ if conf['layout-disposition'] == 'img-pos':
+ self.reg_css( css_selector, 'position', 'absolute' )
+ geo = self.get_relative_el_geometry(el)
+ self.reg_css( css_selector, 'left', str(geo['x'])+'px' )
+ self.reg_css( css_selector, 'top', str(geo['y'])+'px' )
+ if conf['layout-disposition'] == 'img-float-left':
+ self.reg_css( css_selector, 'float', 'right' )
+ if conf['layout-disposition'] == 'img-float-right':
+ self.reg_css( css_selector, 'float', 'right' )
+
+
+ el_geo = { }
+ def register_all_els_geometry(self):
+ ink_cmm = 'inkscape --query-all '+self.tmp_svg
+ (status, output) = commands.getstatusoutput( ink_cmm )
+ self.el_geo = { }
+ if status == 0:
+ for el in output.split('\n'):
+ el = el.split(',')
+ if len(el) == 5:
+ self.el_geo[el[0]] = { 'x':float(el[1]), 'y':float(el[2]),
+ 'w':float(el[3]), 'h':float(el[4]) }
+ doc_w = inkex.unittouu( self.document.getroot().get('width') )
+ doc_h = inkex.unittouu( self.document.getroot().get('height') )
+ self.el_geo['webslicer-layer'] = { 'x':0, 'y':0, 'w':doc_w, 'h':doc_h }
+
+
+ def get_relative_el_geometry(self, el, value_to_css=False):
+ # This method return a dictionary with x, y, w and h keys.
+ # All values are float, if value_to_css is False, otherwise
+ # that is a string ended with "px". The x and y values are
+ # relative to parent position.
+ if not self.el_geo: self.register_all_els_geometry()
+ parent = self.getParentNode(el)
+ geometry = self.el_geo[el.attrib['id']]
+ geometry['x'] -= self.el_geo[parent.attrib['id']]['x']
+ geometry['y'] -= self.el_geo[parent.attrib['id']]['y']
+ if value_to_css:
+ for k in geometry: geometry[k] = str(int(geometry[k]))+'px'
+ return geometry
def img_name(self, el, conf):
- return el.attrib['id']+'.png'
+ return el.attrib['id']+'.'+conf['format']
+
def export_img(self, el, conf):
+ if not self.has_magick:
+ inkex.errormsg(_('You must install the ImageMagick to get JPG and GIF.'))
+ conf['format'] = 'png'
+ img_name = os.path.join( self.options.dir, self.img_name(el, conf) )
+ img_name_png = img_name
+ if conf['format'] != 'png':
+ img_name_png = img_name+'.png'
+ opts = ''
+ if 'bg-color' in conf: opts += ' -b "'+conf['bg-color']+'" -y 1'
+ if 'dpi' in conf: opts += ' -d '+conf['dpi']
+ if 'dimension' in conf:
+ dim = conf['dimension'].split('x')
+ opts += ' -w '+dim[0]+' -h '+dim[1]
(status, output) = commands.getstatusoutput(
- "inkscape -i '%s' -e '%s' '%s'" % (
- el.attrib['id'],
- os.path.join( self.options.dir, self.img_name(el, conf) ),
- self.tmp_svg
+ 'inkscape %s -i "%s" -e "%s" "%s"' % (
+ opts, el.attrib['id'], img_name_png, self.tmp_svg
)
)
- #inkex.errormsg( status )
- #inkex.errormsg( output )
+ if conf['format'] != 'png':
+ opts = ''
+ if conf['format'] == 'jpg':
+ opts += ' -quality '+str(conf['quality'])
+ if conf['format'] == 'gif':
+ if conf['gif-type'] == 'grayscale':
+ opts += ' -type Grayscale'
+ else:
+ opts += ' -type Palette'
+ if conf['palette-size'] < 256:
+ opts += ' -colors '+str(conf['palette-size'])
+ (status, output) = commands.getstatusoutput(
+ 'convert "%s" %s "%s"' % ( img_name_png, opts, img_name )
+ )
+ if status != 0:
+ inkex.errormsg('Upss... ImageMagick error: '+output)
+ os.remove(img_name_png)
- _css = {}
+ _html = {}
+ def reg_html(self, el_tag, el):
+ parent = self.getParentNode( el )
+ parent_id = self.get_el_conf( parent )['html-id']
+ if parent == self.get_slicer_layer(): parent_id = 'body'
+ conf = self.get_el_conf( el )
+ el_id = conf['html-id']
+ if 'html-class' in conf:
+ el_class = conf['html-class']
+ else:
+ el_class = ''
+ if not parent_id in self._html: self._html[parent_id] = []
+ self._html[parent_id].append({ 'tag':el_tag, 'id':el_id, 'class':el_class })
+
+
+ def html_code(self, parent='body', ident=' '):
+ #inkex.errormsg( self._html )
+ if not parent in self._html: return ''
+ code = ''
+ for el in self._html[parent]:
+ child_code = self.html_code(el['id'], ident+' ')
+ tag_class = ''
+ if el['class'] != '': tag_class = ' class="'+el['class']+'"'
+ if el['tag'] == 'img':
+ code += ident+'<img id="'+el['id']+'"'+tag_class+\
+ ' src="'+self.img_name(el, self.get_el_conf(el))+'"/>\n'
+ else:
+ code += ident+'<'+el['tag']+' id="'+el['id']+'"'+tag_class+'>\n'
+ if child_code:
+ code += child_code
+ else:
+ code += ident+' Element '+el['id']+'\n'
+ code += ident+'</'+el['tag']+'><!-- id="'+el['id']+'" -->\n'
+ return code
+
+
+ _css = []
def reg_css(self, selector, att, val):
- if not selector in self._css: self._css[selector] = {}
- if not att in self._css[selector]: self._css[selector][att] = []
- self._css[selector][att].append( val )
+ pos = i = -1
+ for s in self._css:
+ i += 1
+ if s['selector'] == selector: pos = i
+ if pos == -1:
+ pos = i + 1
+ self._css.append({ 'selector':selector, 'atts':{} })
+ if not att in self._css[pos]['atts']: self._css[pos]['atts'][att] = []
+ self._css[pos]['atts'][att].append( val )
+
def css_code(self):
code = ''
- for selector in self._css:
- code += '\n'+selector+' {\n'
- for att in self._css[selector]:
- code += ' '+ att +': '+ (', '.join(self._css[selector][att])) +';\n'
+ for s in self._css:
+ code += '\n'+s['selector']+' {\n'
+ for att in s['atts']:
+ val = s['atts'][att]
+ if att == 'background' and len(val) > 1:
+ code += ' /* the next attribute needs a CSS3 enabled browser */\n'
+ code += ' '+ att +': '+ (', '.join(val)) +';\n'
code += '}\n'
return code
+ def output(self):
+ # Cancel document serialization to stdout
+ pass
+
+
if __name__ == '__main__':
e = WebSlicer_Export()
e.affect()