Gridmapper is a web application because it runs in a web browser. It consists of an SVG document and some Javascript which adds and removes elements to the SVG document in your browser’s memory. Sadly, where as SVG has a way to get key press events and mouse events and touch events – all the stuff Javascript knows about – it doesn’t have text fields and text areas like HTML does. So, what now? In theory, it’s easy: include XHTML in the SVG document – this is possible because both are XML dialects and SVG has an element that reserves space for non-SVG elements: foreignObject. So if I don’t want to re-implement all of the things that text fields and text areas do, all the cursor movements, selection, copy, cut, paste, and all that, then this is what I’ll have to do.
I currently have a way to do that, but I didn’t like it. It looks a bit off on the iPad Pro. It doesn’t look exactly the way I want it to on different operating systems. And so I decided to change it. Replace a lot of the XHTML other than the text area with pure SVG.
And then, when it was nearly done, I ran into weird problems with Chrome.
This was the first problem: When you click on the *Download* link, it turns invisible and some text that was invisible instead shows up. With the rewrite, making the Download link invisible turned all of the text invisible. Huh? After many iterations, I turned out that if I rearranged the links so that it wasn’t the *first* link, it worked. Huh?
The second problem was that the text area suddenly started jumping to the left if more than a few lines of code were inserted by the *Text Export* link. Huh?
I decided to kill it all. And now I’m sad.
// PNG download hides user interface. We must actually *remove* all // the foreignObject elements because it causes a security exception // in Safari. // http://stackoverflow.com/questions/33972254/svgpng-from-canvas-todataurl-throws-dom-exception-18-security-error-in-safari-9 var clone = document.createElementNS("http://www.w3.org/2000/svg", "svg"); var background = svg.getElementById('background'); clone.setAttribute('width', svg.getAttribute('width')); clone.setAttribute('height', svg.getAttribute('width')); clone.appendChild(svg.querySelector('defs').cloneNode(true)); clone.appendChild(svg.querySelector('style').cloneNode(true)); clone.appendChild(svg.getElementById('background').cloneNode(true)); clone.appendChild(svg.getElementById('levels').cloneNode(true)); source = btoa(unescape(encodeURIComponent(new XMLSerializer().serializeToString(clone)))); link = document.getElementById('download_png'); // SVG to PNG using canvas https://gist.github.com/gustavohenke/9073132 var canvas = document.createElementNS(xhtmlNs, 'canvas'); var rect = Map.ui.getBoundingClientRect(); canvas.width = (Map.width + 2) * Map.tileWidth; // see resizeSVG canvas.height = (Map.width + 2) * Map.tileWidth; var ctx = canvas.getContext('2d'); var img = document.createElementNS(xhtmlNs, 'img'); img.setAttribute('src', 'data:image/svg+xml;base64,' + source); // when the image arrived in the canvas, set the link img.onload = function() { ctx.drawImage(img, 0, 0); link.setAttributeNS(xlinkNs, 'href', canvas.toDataURL('image/png')); };
1. hide other levels (the old functionality)
2. use a black background
3. back to the default: light gray background and transparent images of the levels above and below
Not wasted after all. 😄
#Gridmapper #SVG