<header>
<h1>dompeg.js</h1>
</header>
<main>
<p>The bookmarklet: <a href="javascript:(async%20function%20()%7B%20%0A%20const%20width%20%3D%20document.scrollingElement.scrollWidth%3B%0A%20const%20height%20%3D%20document.scrollingElement.scrollHeight%3B%0A%20const%20doc%20%3D%20document.implementation.createHTMLDocument('')%3B%0A%20doc.write(document.documentElement.outerHTML)%3B%0A%20doc.documentElement.setAttribute('xmlns'%2C%20doc.documentElement.namespaceURI)%3B%0A%20%0A%20const%20styles%20%3D%20%5B%5D%3B%0A%20for(%20let%20i%20%3D%200%3B%20i%20%3C%20document.styleSheets.length%3B%20i%2B%2B%20)%20%7B%0A%20const%20ss%20%3D%20document.styleSheets%5Bi%5D%3B%0A%20if%20(%20ss.cssRules%20)%20%7B%0A%20for(%20let%20j%20%3D%200%3B%20j%20%3C%20ss.cssRules.length%3B%20j%2B%2B%20)%20%7B%0A%20styles.push(%20ss.cssRules%5Bj%5D.cssText%20)%3B%0A%20%7D%0A%20%7D%20else%20%7B%0A%20try%20%7B%0A%20const%20res%20%3D%20await%20fetch(ss.href)%3B%0A%20const%20cssText%20%3D%20await%20res.text()%3B%0A%20styles.push(cssText)%3B%0A%20%7D%20catch(e)%20%7B%0A%20try%20%7B%0A%20const%20res%20%3D%20await%20fetch(%60https%3A%2F%2Fdompeg-proxy-90jseygqo65r.runkit.sh%2F%3Furl%3D%24%7Bbtoa(ss.href)%7D%60)%3B%0A%20const%20cssText%20%3D%20await%20res.text()%3B%0A%20styles.push(cssText)%3B%0A%20%7D%20catch(e)%20%7B%20%0A%20console.warn(%60Exception%20adding%20styles%20from%20%24%7Bss.href%7D%60%2C%20e%2C%20e.stack)%3B%0A%20%7D%0A%20%7D%0A%20%7D%0A%20%7D%0A%20%0A%20%0A%20Array.from(%20doc.querySelectorAll('noscript%2C%20link%2C%20script')).forEach(%20el%20%3D%3E%20el.remove()%20)%3B%0A%20stripComments(doc)%3B%0A%20Array.from(%20doc.querySelectorAll('*%5Bstyle%5D')).forEach(%20el%20%3D%3E%20%7B%0A%20const%20styleText%20%3D%20el.getAttribute('style')%3B%0A%20const%20uniq%20%3D%20(Math.random()%2B''%2Bperformance.now()).replace(%2F%5C.%2Fg%2C'x')%3B%0A%20const%20className%20%3D%20%60class%24%7Buniq%7D%60%3B%0A%20const%20cssText%20%3D%20%60.%24%7BclassName%7D%20%7B%24%7B%20styleText%20%7D%7D%60%3B%0A%20styles.push(%20cssText%20)%3B%0A%20el.classList.add(%20className%20)%3B%0A%20%7D)%3B%0A%20%0A%20%0A%20const%20styleElement%20%3D%20doc.createElement('style')%3B%0A%20styleElement.innerText%20%3D%20styles.join('%5Cn')%3B%0A%20doc.documentElement.appendChild(styleElement)%3B%0A%0A%20%0A%20const%20canvas%20%3D%20document.createElement('canvas')%3B%0A%20Object.assign(%20canvas%2C%20%7Bwidth%2Cheight%7D)%3B%0A%20const%20ctx%20%3D%20canvas.getContext('2d')%3B%0A%0A%20const%20data%20%3D%20%60%0A%20%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22%24%7Bwidth%7D%22%20height%3D%22%24%7Bheight%7D%22%3E%0A%20%3CforeignObject%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%0A%20%24%7B(new%20XMLSerializer).serializeToString(doc).slice(15)%7D%0A%20%3C%2FforeignObject%3E%0A%20%3C%2Fsvg%3E%60%3B%0A%20const%20DOMURL%20%3D%20window.URL%20%7C%7C%20window.webkitURL%20%7C%7C%20window%3B%0A%0A%20const%20img%20%3D%20new%20Image()%3B%0A%20const%20svg%20%3D%20new%20Blob(%5Bdata%5D%2C%20%7Btype%3A%20'image%2Fsvg%2Bxml'%7D)%3B%0A%20%0A%20Object.assign(%20img%2C%20%7Bwidth%2Cheight%7D)%3B%20%0A%20img.crossOrigin%20%3D%20%22Anonymous%22%3B%0A%20img.onload%20%3D%20function()%20%7B%0A%20ctx.fillStyle%20%3D%20'white'%3B%0A%20ctx.fillRect(%200%2C%200%2C%20canvas.width%2C%20canvas.height%20)%3B%0A%20ctx.drawImage(img%2C%200%2C%200)%3B%0A%20const%20datauri%20%3D%20canvas.toDataURL('image%2Fjpeg')%3B%0A%20const%20anchor%20%3D%20document.createElement('a')%3B%0A%20anchor.download%20%3D%20'screen.jpg'%3B%0A%20anchor.href%20%3D%20datauri%3B%0A%20anchor.target%20%3D%20%22_new%22%3B%0A%20anchor.innerText%20%3D%20'download%20screen.jpg'%3B%0A%20anchor.addEventListener('click'%2C%20e%20%3D%3E%20%7Be.stopPropagation()%3Banchor.remove()%3B%7D%2C%20%7B%20capture%3A%20true%20%7D)%3B%0A%20document.body.appendChild(anchor)%3B%0A%20Object.assign(%20anchor.style%2C%20%7B%0A%20position%3A%20'fixed'%2C%0A%20background%3A'white'%2C%0A%20fontSize%3A%20'18px'%2C%0A%20fontFamily%3A%20'monospace'%2C%0A%20color%3A%20'blue'%2C%0A%20top%3A%200%2C%0A%20left%3A%200%2C%0A%20zIndex%3A%20Number.MAX_SAFE_INTEGER%0A%20%7D)%3B%0A%20%7D%0A%20img.src%20%3D%20buildSvgImageUrl(data)%3B%20%0A%20img.style.position%20%3D%20%22absolute%22%3B%0A%20img.style.zIndex%20%3D%20%2210000000%22%3B%0A%20img.style.backgroundColor%20%3D%20%22white%22%3B%0A%20%2F%2Fdocument.body.appendChild(img)%3B%0A%20%0A%20function%20buildSvgImageUrl(svg)%20%7B%0A%20const%20b64%20%3D%20btoa(unescape(encodeURIComponent(svg)))%3B%0A%20return%20%22data%3Aimage%2Fsvg%2Bxml%3Bbase64%2C%22%20%2B%20b64%3B%0A%20%7D%0A%20%0A%20function%20stripComments(docNode)%7B%0A%20const%20commentWalker%20%3D%20docNode.evaluate('%2F%2Fcomment()'%2C%20docNode%2C%20null%2C%20XPathResult.ANY_TYPE%2C%20null)%3B%0A%20let%20comment%20%3D%20commentWalker.iterateNext()%3B%0A%20const%20cuts%20%3D%20%5B%5D%3B%0A%0A%20while%20(comment)%20%7B%0A%20cuts.push(comment)%3B%0A%20comment%20%3D%20commentWalker.iterateNext()%3B%0A%20%7D%0A%20cuts.forEach(%20node%20%3D%3E%20node.remove())%3B%0A%20%7D%0A%7D())%3B">📷DOMPEG</a>
<p><em>save your google search result pages, and other sites, as jpgs.</em>
<h1>The code</h1>
<code><pre>
(async function (){
const width = document.scrollingElement.scrollWidth;
const height = document.scrollingElement.scrollHeight;
const doc = document.implementation.createHTMLDocument('');
doc.write(document.documentElement.outerHTML);
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
const styles = [];
for( let i = 0; i < document.styleSheets.length; i++ ) {
const ss = document.styleSheets[i];
if ( ss.cssRules ) {
for( let j = 0; j < ss.cssRules.length; j++ ) {
styles.push( ss.cssRules[j].cssText );
}
} else {
try {
const res = await fetch(ss.href);
const cssText = await res.text();
styles.push(cssText);
} catch(e) {
try {
const res = await fetch(`https://dompeg-proxy-90jseygqo65r.runkit.sh/?url=${btoa(ss.href)}`);
const cssText = await res.text();
styles.push(cssText);
} catch(e) {
console.warn(`Exception adding styles from ${ss.href}`, e, e.stack);
}
}
}
}
Array.from( doc.querySelectorAll('noscript, link, script')).forEach( el => el.remove() );
stripComments(doc);
Array.from( doc.querySelectorAll('*[style]')).forEach( el => {
const styleText = el.getAttribute('style');
const uniq = (Math.random()+''+performance.now()).replace(/\./g,'x');
const className = `class${uniq}`;
const cssText = `.${className} {${ styleText }}`;
styles.push( cssText );
el.classList.add( className );
});
const styleElement = doc.createElement('style');
styleElement.innerText = styles.join('\n');
doc.documentElement.appendChild(styleElement);
const canvas = document.createElement('canvas');
Object.assign( canvas, {width,height});
const ctx = canvas.getContext('2d');
const data = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject width="100%" height="100%">
${(new XMLSerializer).serializeToString(doc).slice(15)}
</foreignObject>
</svg>`;
const DOMURL = window.URL || window.webkitURL || window;
const img = new Image();
const svg = new Blob([data], {type: 'image/svg+xml'});
Object.assign( img, {width,height});
img.crossOrigin = "Anonymous";
img.onload = function() {
ctx.fillStyle = 'white';
ctx.fillRect( 0, 0, canvas.width, canvas.height );
ctx.drawImage(img, 0, 0);
const datauri = canvas.toDataURL('image/jpeg');
const anchor = document.createElement('a');
anchor.download = 'screen.jpg';
anchor.href = datauri;
anchor.target = "_new";
anchor.innerText = 'download screen.jpg';
anchor.addEventListener('click', e => {e.stopPropagation();anchor.remove();}, { capture: true });
document.body.appendChild(anchor);
Object.assign( anchor.style, {
position: 'fixed',
background:'white',
fontSize: '18px',
fontFamily: 'monospace',
color: 'blue',
top: 0,
left: 0,
zIndex: Number.MAX_SAFE_INTEGER
});
}
img.src = buildSvgImageUrl(data);
img.style.position = "absolute";
img.style.zIndex = "10000000";
img.style.backgroundColor = "white";
//document.body.appendChild(img);
function buildSvgImageUrl(svg) {
const b64 = btoa(unescape(encodeURIComponent(svg)));
return "data:image/svg+xml;base64," + b64;
}
function stripComments(docNode){
const commentWalker = docNode.evaluate('//comment()', docNode, null, XPathResult.ANY_TYPE, null);
let comment = commentWalker.iterateNext();
const cuts = [];
while (comment) {
cuts.push(comment);
comment = commentWalker.iterateNext();
}
cuts.forEach( node => node.remove());
}
}());
</code></pre>
!