-
-
Save SamMousa/beec717b105b8665a515788e8c629574 to your computer and use it in GitHub Desktop.
inline-styles using phantomjs
This file contains 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
{ | |
"ignoreSslErrors": true, | |
"localToRemoteUrlAccessEnabled": true, | |
"webSecurityEnabled": false | |
} |
This file contains 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
/** | |
* phantomjs --config=prep4email-config.json prep4email.js | |
* prep4email URL | |
* | |
*/ | |
var system = require('system'); | |
var fs = require('fs'); | |
var page = require('webpage').create(); | |
var url = system.args[1]; | |
if (phantom.args.length !== 1) { | |
system.stdout.writeLine('Usage: prep4email URL'); | |
phantom.exit(0) | |
} | |
//page.viewportSize = { width: 600 }; | |
page.onConsoleMessage = function(msg) { | |
//console.log(msg); | |
}; | |
page.open(url, function (status) { | |
if (status !== 'success') { | |
system.stderr.writeLine('Unable to access the network'); | |
phantom.exit(1); | |
} else { | |
var out = page.evaluate(function onEvaluate(){ | |
//////////////////////////// | |
// begin browser context // | |
////////////////////////// | |
// create an array from arrayish lists | |
function arrayFrom(nodelist){return [].slice.call(nodelist);} | |
// parses the value of `inline-options` attribute | |
function parseOptions(el){ | |
var s = el.getAttribute('data-inline-options') || ''; | |
return { ignore: /ignore/.test(s), preserve: /preserve/.test(s) }; | |
} | |
// remove element from the dom | |
function removeElement(el){ return el.parentNode.removeChild(el); } | |
// ensures elements matching a rule | |
// have their associated properties set inline. | |
function inlineRule(el, rule){ | |
var computed = window.getComputedStyle(el); | |
for(var i = 0; i < rule.style.length; i++){ | |
var prop = rule.style.item(i); | |
var computedVal = computed.getPropertyValue(prop); | |
// if element's style attribute doesn't have this property, | |
// then set it to the computed value | |
if(!el.style.getPropertyValue(prop)){ | |
el.style.setProperty(prop, computedVal); | |
} | |
} | |
} | |
// Loops through a stylesheet's rules, | |
// selects each rule's targeted elements, and | |
// ensures matching properties have been inlined. | |
function inlineStyle(elStyle){ | |
//console.log('elStyle', elStyle) | |
var doc = elStyle.ownerDocument; | |
var rules = elStyle.sheet.cssRules; | |
if(!rules){ | |
console.log('stylesheet rules cannot be read from ', elStyle.href); | |
return; | |
} | |
arrayFrom(rules) | |
// only STYLE_RULEs, and no pseudo selectors or starting stars | |
// see: https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants | |
.filter(function(r){ | |
return r.type == 1 && | |
// no pseudo selectors | |
!(/:/.test(r.selectorText)) && | |
// no starting stars (universal selector) | |
!(/^\*/.test(r.selectorText)); | |
}) | |
.forEach(function(rule){ | |
arrayFrom(doc.querySelectorAll(rule.selectorText)) | |
.forEach(function(el){ | |
inlineRule(el, rule); | |
}) | |
}) | |
} | |
// takes a rendered document and inlines its stylesheets. | |
function inlineStyles(doc){ | |
var tags = arrayFrom(doc.querySelectorAll('style, link[rel="stylesheet"]')), | |
tags2inline = [], | |
tags2remove = [], | |
tags2ignore = []; | |
tags.forEach(function processTag(el){ | |
var opts = parseOptions(el); | |
// ignore and inline tags are mutually exclusive | |
(opts.ignore ? tags2ignore : tags2inline).push(el); | |
if(!opts.preserve) tags2remove.push(el); | |
}); | |
// disable ignored tags so they're not a part of the computedStyles | |
tags2ignore.forEach(function(el){el._prevDisabled = el.disabled; el.disabled = true; }); | |
// inline link/style rules | |
tags2inline.forEach(inlineStyle); | |
// re-enable ignored tags, restoring their original disabled state | |
tags2ignore.forEach(function(el){ el.disabled = el._prevDisabled;}); | |
// cleanup | |
tags2remove.forEach(removeElement); | |
// move any remaining tags in the head to the body | |
arrayFrom(doc.head.querySelectorAll('style, link[rel="stylesheet"]')) | |
.reverse() | |
.forEach(function(el){ | |
el.removeAttribute('data-inline-options') | |
doc.body.insertBefore(el, doc.body.firstChild); | |
}) | |
return doc; | |
} | |
// run inliner on phantom document | |
inlineStyles(document); | |
return document.head.outerHTML + '\n' + document.body.outerHTML; | |
////////////////////////// | |
// end browser context // | |
//////////////////////// | |
}); | |
system.stdout.writeLine(out) | |
//fs.write(outdir + '/inlined.html', out, 'w'); | |
//page.render('./inlined.png') | |
phantom.exit(0); | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment