Skip to content

Instantly share code, notes, and snippets.

@twalker
Last active February 26, 2020 09:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save twalker/8e349a4423ae284a396e to your computer and use it in GitHub Desktop.
Save twalker/8e349a4423ae284a396e to your computer and use it in GitHub Desktop.
inline-styles using phantomjs
{
"ignoreSslErrors": true,
"localToRemoteUrlAccessEnabled": true,
"webSecurityEnabled": false
}
/**
* 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