Created
December 21, 2017 18:20
-
-
Save davo/14c74ef3319a30a2f9348dd2cc1aee15 to your computer and use it in GitHub Desktop.
SVG Crowbar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var doctype = | |
'<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; | |
window.URL = window.URL || window.webkitURL; | |
var body = document.body; | |
var prefix = { | |
xmlns: 'http://www.w3.org/2000/xmlns/', | |
xlink: 'http://www.w3.org/1999/xlink', | |
svg: 'http://www.w3.org/2000/svg' | |
}; | |
initialize(); | |
function initialize() { | |
var documents = [window.document], | |
SVGSources = []; | |
(iframes = document.querySelectorAll('iframe')), | |
(objects = document.querySelectorAll('object')); | |
[].forEach.call(iframes, function(el) { | |
try { | |
if (el.contentDocument) { | |
documents.push(el.contentDocument); | |
} | |
} catch (err) { | |
console.log(err); | |
} | |
}); | |
[].forEach.call(objects, function(el) { | |
try { | |
if (el.contentDocument) { | |
documents.push(el.contentDocument); | |
} | |
} catch (err) { | |
console.log(err); | |
} | |
}); | |
documents.forEach(function(doc) { | |
var styles = getStyles(doc); | |
var newSources = getSources(doc, styles); | |
// because of prototype on NYT pages | |
for (var i = 0; i < newSources.length; i++) { | |
SVGSources.push(newSources[i]); | |
} | |
}); | |
if (SVGSources.length > 1) { | |
createPopover(SVGSources); | |
} else if (SVGSources.length > 0) { | |
download(SVGSources[0]); | |
} else { | |
alert('The Crowbar couldn’t find any SVG nodes.'); | |
} | |
} | |
function createPopover(sources) { | |
cleanup(); | |
sources.forEach(function(s1) { | |
sources.forEach(function(s2) { | |
if (s1 !== s2) { | |
if ( | |
Math.abs(s1.top - s2.top) < 38 && | |
Math.abs(s1.left - s2.left) < 38 | |
) { | |
s2.top += 38; | |
s2.left += 38; | |
} | |
} | |
}); | |
}); | |
var buttonsContainer = document.createElement('div'); | |
body.appendChild(buttonsContainer); | |
buttonsContainer.setAttribute('class', 'svg-crowbar'); | |
buttonsContainer.style['z-index'] = 1e7; | |
buttonsContainer.style['position'] = 'absolute'; | |
buttonsContainer.style['top'] = 0; | |
buttonsContainer.style['left'] = 0; | |
var background = document.createElement('div'); | |
body.appendChild(background); | |
background.setAttribute('class', 'svg-crowbar'); | |
background.style['background'] = 'rgba(255, 255, 255, 0.7)'; | |
background.style['position'] = 'fixed'; | |
background.style['left'] = 0; | |
background.style['top'] = 0; | |
background.style['width'] = '100%'; | |
background.style['height'] = '100%'; | |
sources.forEach(function(d, i) { | |
var buttonWrapper = document.createElement('div'); | |
buttonsContainer.appendChild(buttonWrapper); | |
buttonWrapper.setAttribute('class', 'svg-crowbar'); | |
buttonWrapper.style['position'] = 'absolute'; | |
buttonWrapper.style['top'] = d.top + document.body.scrollTop + 'px'; | |
buttonWrapper.style['left'] = document.body.scrollLeft + d.left + 'px'; | |
buttonWrapper.style['padding'] = '4px'; | |
buttonWrapper.style['border-radius'] = '3px'; | |
buttonWrapper.style['color'] = 'white'; | |
buttonWrapper.style['text-align'] = 'center'; | |
buttonWrapper.style['font-family'] = "'Helvetica Neue'"; | |
buttonWrapper.style['background'] = 'rgba(0, 0, 0, 0.8)'; | |
buttonWrapper.style['box-shadow'] = '0px 4px 18px rgba(0, 0, 0, 0.4)'; | |
buttonWrapper.style['cursor'] = 'move'; | |
buttonWrapper.textContent = | |
'SVG #' + | |
i + | |
': ' + | |
(d.id ? '#' + d.id : '') + | |
(d.class ? '.' + d.class : ''); | |
var button = document.createElement('button'); | |
buttonWrapper.appendChild(button); | |
button.setAttribute('data-source-id', i); | |
button.style['width'] = '150px'; | |
button.style['font-size'] = '12px'; | |
button.style['line-height'] = '1.4em'; | |
button.style['margin'] = '5px 0 0 0'; | |
button.textContent = 'Download'; | |
button.onclick = function(el) { | |
// console.log(el, d, i, sources) | |
download(d); | |
}; | |
}); | |
} | |
function cleanup() { | |
var crowbarElements = document.querySelectorAll('.svg-crowbar'); | |
[].forEach.call(crowbarElements, function(el) { | |
el.parentNode.removeChild(el); | |
}); | |
} | |
function getSources(doc, styles) { | |
var svgInfo = [], | |
svgs = doc.querySelectorAll('svg'); | |
styles = styles === undefined ? '' : styles; | |
[].forEach.call(svgs, function(svg) { | |
svg.setAttribute('version', '1.1'); | |
var defsEl = document.createElement('defs'); | |
svg.insertBefore(defsEl, svg.firstChild); //TODO .insert("defs", ":first-child") | |
// defsEl.setAttribute("class", "svg-crowbar"); | |
var styleEl = document.createElement('style'); | |
defsEl.appendChild(styleEl); | |
styleEl.setAttribute('type', 'text/css'); | |
// removing attributes so they aren't doubled up | |
svg.removeAttribute('xmlns'); | |
svg.removeAttribute('xlink'); | |
// These are needed for the svg | |
if (!svg.hasAttributeNS(prefix.xmlns, 'xmlns')) { | |
svg.setAttributeNS(prefix.xmlns, 'xmlns', prefix.svg); | |
} | |
if (!svg.hasAttributeNS(prefix.xmlns, 'xmlns:xlink')) { | |
svg.setAttributeNS(prefix.xmlns, 'xmlns:xlink', prefix.xlink); | |
} | |
var source = new XMLSerializer() | |
.serializeToString(svg) | |
.replace('</style>', '<![CDATA[' + styles + ']]></style>'); | |
var rect = svg.getBoundingClientRect(); | |
svgInfo.push({ | |
top: rect.top, | |
left: rect.left, | |
width: rect.width, | |
height: rect.height, | |
class: svg.getAttribute('class'), | |
id: svg.getAttribute('id'), | |
childElementCount: svg.childElementCount, | |
source: [doctype + source] | |
}); | |
}); | |
return svgInfo; | |
} | |
function download(source) { | |
var filename = 'untitled'; | |
if (source.id) { | |
filename = source.id; | |
} else if (source.class) { | |
filename = source.class; | |
} else if (window.document.title) { | |
filename = window.document.title | |
.replace(/[^a-z0-9]/gi, '-') | |
.toLowerCase(); | |
} | |
var url = window.URL.createObjectURL( | |
new Blob(source.source, { type: 'text/xml' }) | |
); | |
var a = document.createElement('a'); | |
body.appendChild(a); | |
a.setAttribute('class', 'svg-crowbar'); | |
a.setAttribute('download', filename + '.svg'); | |
a.setAttribute('href', url); | |
a.style['display'] = 'none'; | |
a.click(); | |
setTimeout(function() { | |
window.URL.revokeObjectURL(url); | |
}, 10); | |
} | |
function getStyles(doc) { | |
var styles = '', | |
styleSheets = doc.styleSheets; | |
if (styleSheets) { | |
for (var i = 0; i < styleSheets.length; i++) { | |
processStyleSheet(styleSheets[i]); | |
} | |
} | |
function processStyleSheet(ss) { | |
if (ss.cssRules) { | |
for (var i = 0; i < ss.cssRules.length; i++) { | |
var rule = ss.cssRules[i]; | |
if (rule.type === 3) { | |
// Import Rule | |
processStyleSheet(rule.styleSheet); | |
} else { | |
// hack for illustrator crashing on descendent selectors | |
if (rule.selectorText) { | |
if (rule.selectorText.indexOf('>') === -1) { | |
styles += '\n' + rule.cssText; | |
} | |
} | |
} | |
} | |
} | |
} | |
return styles; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment