/* # SVG fragment builds for reveal.js ## LIMITATIONS - won't work in Chrome against `file://` - workarounds: - dropbox - sharepoint - confluence - gist - `python -m SimpleHTTPServer` - probably won't work in IE - wontfix ## Basic use case - build an SVG (maybe in inkscape) - save it someplace reveal.js can find it (maybe next to your presentation) - figure out how to identify them (maybe use named layers) - in reveal.js/index.html - add `svg-fragment.js` as a dependency - in a `
` of reveal.js markup - add `data-svg-fragment=""` to something, e.g. a `div` - add some things with `class="fragment"` inside that thing - add `title=""` to those things - `[*|label=]` is good - for more about selectors, check out the [w3c](http://www.w3.org/TR/css3-selectors/) ## Example Let's assume I made an SVG in Inkscape, and saved it next to my `index.html`. It has three layers: `base`, `fragment1` and `fragment2` ...
... ... */ ;Reveal.SvgFragment = (function(Reveal){ "use strict"; var window = this, document = window.document, proto = window.location.protocol, local = proto === "file:", defaults = { d3: (local ? "http:" : proto) + "//cdn.jsdelivr.net/d3js/latest/d3.min.js", selector: "title" }; // the main function, to be called when d3 is available function api(){ var d3 = window.d3, container = d3.selectAll("[data-svg-fragment]"); container.data(function(){ return container[0].map(function(d){ var $ = d3.select(d); return { container: $, url: $.attr("data-svg-fragment") }; }); }); container.append("iframe") .attr({ src: "about:blank", // TODO: make this an option? scrolling: "no" }) .on("load", api.iframed); Reveal.addEventListener("fragmentshown", api.listen(container, true)); Reveal.addEventListener("fragmenthidden", api.listen(container)); return api; }; // generate listeners for reveal events api.listen = function(container, show){ return function(event){ var fragment = d3.select(event.fragment); container.filter(function(){ return this === event.fragment.parentNode; }).each(function(item){ api.toggle(fragment, item, show); }); return api; }; }; // toggle a fragment // TODO: add hide api.toggle = function(fragment, item, show){ var selector = fragment.attr(api.cfg("selector")); item.svg.selectAll(selector) .transition() .style({opacity: show ? 1 : 0}); return api; }; // the iframe was created for this item api.iframed = function(item){ item.iframe = d3.select(this); item.idoc = d3.select(this.contentDocument); d3.xml(item.url, "image/svg+xml", function(xml){ item.idoc.node().body.appendChild(xml.documentElement); item.svg = item.idoc.select("svg"); api.svged(item); }); return api; }; // the svg was loaded for this item api.svged = function(item){ item.iframe.attr({ width: item.svg.attr("width"), height: item.svg.attr("height") }); return api.clean(item); }; // prepare // TODO: smarter initialization? api.clean = function(item){ var base; item.container.selectAll(".fragment").each(function(){ item.svg.selectAll(d3.select(this).attr(api.cfg("selector"))) .style({opacity: 0}); }); if(base = item.url.match(/(?:#)(.*)$/)){ item.svg.selectAll(base[1]) .style({opacity: 1}); } return api; }; // preflight, call immediately it d3 is available, otherwise load the script api.init = function(){ var options = Reveal.getConfig().svgFragment || {}; return window.api ? api() : api.load(api.cfg("d3"), api); }; // get configuration values (or defaults) api.cfg = function(opt){ var cfg = Reveal.getConfig().svgFragment || {}; return cfg.hasOwnProperty(opt) ? cfg[opt] : defaults.hasOwnProperty(opt) ? defaults[opt] : function(){ throw new Error("Unknown property: "+ opt); }; }; // load a script, jacked from search, i think api.load = function(url, callback){ var head = document.querySelector('head'), script = document.createElement('script'); // Wrapper for callback to make sure it only fires once var finish = function(){ if(typeof callback === 'function') { callback.call(); callback = null; } }; // IE script.onreadystatechange = function() { if (this.readyState === 'loaded') { finish(); } }; script.type = 'text/javascript'; script.src = url; script.onload = finish; // Normal browsers head.appendChild(script); return api; }; return api.init(); }).call(this, Reveal);