inline-css
Advanced tools
Comparing version 2.1.0 to 2.1.1
51
index.js
'use strict'; | ||
var extractCss = require('extract-css'), | ||
inlineCss = require('./lib/inline-css'), | ||
Promise = require('bluebird'); | ||
var Promise = require('bluebird'), | ||
inlineContent = require('./lib/inlineContent'); | ||
function extend(obj, src) { | ||
var own = {}.hasOwnProperty; | ||
var key, | ||
own = {}.hasOwnProperty; | ||
for (var key in src) { | ||
for (key in src) { | ||
if (own.call(src, key)) { | ||
@@ -18,36 +18,15 @@ obj[key] = src[key]; | ||
function inlineContent(src, options) { | ||
return new Promise(function (resolve, reject) { | ||
var content; | ||
if (!options.url) { | ||
reject('options.url is required'); | ||
} | ||
extractCss(src, options, function (err, html, css) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
css += '\n' + options.extraCss; | ||
content = inlineCss(html, css, options); | ||
resolve(content); | ||
}); | ||
}); | ||
} | ||
module.exports = function (html, options) { | ||
return new Promise(function (resolve, reject) { | ||
var opt = extend({ | ||
extraCss: '', | ||
applyStyleTags: true, | ||
removeStyleTags: true, | ||
applyLinkTags: true, | ||
removeLinkTags: true, | ||
preserveMediaQueries: false, | ||
removeHtmlSelectors: false, | ||
applyWidthAttributes: false, | ||
applyTableAttributes: false | ||
}, options); | ||
extraCss: '', | ||
applyStyleTags: true, | ||
removeStyleTags: true, | ||
applyLinkTags: true, | ||
removeLinkTags: true, | ||
preserveMediaQueries: false, | ||
removeHtmlSelectors: false, | ||
applyWidthAttributes: false, | ||
applyTableAttributes: false | ||
}, options); | ||
@@ -54,0 +33,0 @@ inlineContent(html, opt) |
@@ -5,37 +5,13 @@ 'use strict'; | ||
cheerio = require('cheerio'), | ||
Selector = require('style-selector'), | ||
Property = require('css-property'), | ||
styleSelector = new Selector('<style attribute>', [1, 0, 0, 0]), | ||
importantSelector = new Selector('<!important>', [2, 0, 0, 0]), | ||
ignoredPseudos = ['hover', 'active', 'focus', 'visited', 'link'], | ||
widthElements = ['table', 'td', 'img']; | ||
pseudoCheck = require('./pseudoCheck'), | ||
handleRule = require('./handleRule'), | ||
flatten = require('flatten'), | ||
setStyleAttrs = require('./setStyleAttrs'), | ||
setWidthAttrs = require('./setWidthAttrs'), | ||
removeClassId = require('./removeClassId'), | ||
setTableAttrs = require('./setTableAttrs'); | ||
var tableStyleAttrMap = { | ||
'table': { | ||
'float': 'align', | ||
'background-color': 'bgcolor', | ||
'width': 'width', | ||
'height': 'height' | ||
}, | ||
'tr': { | ||
'background-color': 'bgcolor', | ||
'vertical-align': 'valign', | ||
'text-align': 'align' | ||
}, | ||
'td,th': { | ||
'background-color': 'bgcolor', | ||
'width': 'width', | ||
'height': 'height', | ||
'vertical-align': 'valign', | ||
'text-align': 'align', | ||
'white-space': 'nowrap' | ||
}, | ||
'tbody,thead,tfoot': { | ||
'vertical-align': 'valign', | ||
'text-align': 'align' | ||
} | ||
}; | ||
module.exports = function (html, css, options) { | ||
var rules = parseCSS(css), | ||
var opts = options || {}, | ||
rules = parseCSS(css), | ||
editedElements = [], | ||
@@ -46,177 +22,44 @@ $ = cheerio.load(html, { | ||
function handleRule(rule) { | ||
var sel = rule[0], | ||
style = rule[1], | ||
selector = new Selector(sel); | ||
rules.forEach(function (rule) { | ||
var el, | ||
ignoredPseudos; | ||
// skip rule if the selector has any pseudos which are ignored | ||
var parsedSelector = selector.parsed(); | ||
ignoredPseudos = pseudoCheck(rule); | ||
for (var i = 0; i < parsedSelector.length; ++i) { | ||
var subSel = parsedSelector[i]; | ||
if (subSel.pseudos) { | ||
for (var j = 0; j < subSel.pseudos.length; ++j) { | ||
var subSelPseudo = subSel.pseudos[j]; | ||
if (ignoredPseudos.indexOf(subSelPseudo.name) >= 0) { | ||
return; | ||
} | ||
} | ||
} | ||
if (ignoredPseudos) { | ||
return false; | ||
} | ||
var $els; | ||
try { | ||
el = handleRule(rule, $); | ||
try { | ||
$els = $(sel); | ||
editedElements.push(el); | ||
} catch (err) { | ||
// skip invalid selector | ||
return; | ||
return false; | ||
} | ||
$els.each(function (index, el) { | ||
// go through the properties | ||
function addProps(style, selector) { | ||
for (var i = 0, l = style.length; i < l; i++) { | ||
var name = style[i], | ||
value = style[name], | ||
sel = style._importants[name] ? importantSelector : selector, | ||
prop = new Property(name, value, sel), | ||
existing = el.styleProps[name], | ||
winner, | ||
loser; | ||
}); | ||
if (existing) { | ||
winner = existing.compare(prop); | ||
loser = prop === winner ? existing : prop; | ||
// flatten array if nested | ||
editedElements = flatten(editedElements); | ||
if (winner === prop) { | ||
el.styleProps[name] = prop; | ||
} | ||
} else { | ||
el.styleProps[name] = prop; | ||
} | ||
} | ||
} | ||
editedElements.forEach(function (el) { | ||
setStyleAttrs(el, $); | ||
if (!el.styleProps) { | ||
el.styleProps = {}; | ||
// if the element has inline styles, fake selector with topmost specificity | ||
if ($(el).attr('style')) { | ||
var cssText = '* { ' + $(el).attr('style') + ' } '; | ||
addProps(parseCSS(cssText)[0][1], styleSelector); | ||
} | ||
// store reference to an element we need to compile style="" attr for | ||
editedElements.push(el); | ||
} | ||
addProps(style, selector); | ||
}); | ||
} | ||
function setStyleAttrs(el) { | ||
var style = []; | ||
for (var i in el.styleProps) { | ||
// add !important | ||
if (typeof el.styleProps[i].selector.spec !== 'undefined') { | ||
if (el.styleProps[i].selector.spec[0] === 2) { | ||
el.styleProps[i].value = el.styleProps[i].value + ' !important'; | ||
} | ||
} | ||
style.push(el.styleProps[i].prop + ': ' + el.styleProps[i].value.replace(/["]/g, '\'') + ';'); | ||
if (opts.applyWidthAttributes) { | ||
setWidthAttrs(el, $); | ||
} | ||
// sorting will arrange styles like padding: before padding-bottom: which will preserve the expected styling | ||
style = style.sort(function (a, b) { | ||
var aProp = a.split(':')[0]; | ||
var bProp = b.split(':')[0]; | ||
return (aProp > bProp ? 1 : aProp < bProp ? -1 : 0); | ||
}); | ||
$(el).attr('style', style.join(' ')); | ||
} | ||
function setWidthAttrs(el) { | ||
if (widthElements.indexOf(el.name) > -1) { | ||
for (var i in el.styleProps) { | ||
if (el.styleProps[i].prop === 'width' && el.styleProps[i].value.match(/px/)) { | ||
var pxWidth = el.styleProps[i].value.replace('px', ''); | ||
$(el).attr('width', pxWidth); | ||
return; | ||
} | ||
} | ||
if (opts.removeHtmlSelectors) { | ||
removeClassId(el, $); | ||
} | ||
} | ||
}); | ||
function removeHtmlSelectors(el) { | ||
var selectors = ['class', 'id']; | ||
selectors.forEach(function (selector) { | ||
var attribute = $(el).attr(selector); | ||
if (typeof attribute !== 'undefined') { | ||
$(el).removeAttr(selector); | ||
} | ||
if (opts.applyTableAttributes) { | ||
$('table').each(function (index, el) { | ||
setTableAttrs(el, $); | ||
}); | ||
} | ||
function applyStylesAsProps($el, styleToAttrMap) { | ||
for (var style in styleToAttrMap) { | ||
var styleVal = $el.css(style); | ||
if (styleVal != undefined) { | ||
$el.attr(styleToAttrMap[style], styleVal); | ||
$el.css(style, ''); | ||
} | ||
} | ||
} | ||
function setTableAttrs(index, el) { | ||
var $el = $(el); | ||
if (!$el.attr('border')) { | ||
$el.attr('border', 0); | ||
} | ||
if (!$el.attr('cellpadding')) { | ||
$el.attr('cellpadding', 0); | ||
} | ||
if (!$el.attr('cellspacing')) { | ||
$el.attr('cellspacing', 0); | ||
} | ||
for (var selector in tableStyleAttrMap){ | ||
if (selector == 'table'){ | ||
applyStylesAsProps($el, tableStyleAttrMap['table']); | ||
} else { | ||
$el.find(selector).each(function(i, childEl){ | ||
applyStylesAsProps($(childEl), tableStyleAttrMap[selector]); | ||
}); | ||
} | ||
} | ||
} | ||
rules.forEach(handleRule); | ||
editedElements.forEach(setStyleAttrs); | ||
if (options) { | ||
if (options.applyWidthAttributes) { | ||
editedElements.forEach(setWidthAttrs); | ||
} | ||
if (options.applyTableAttributes) { | ||
$('table').each(setTableAttrs); | ||
} | ||
} | ||
if (options && options.removeHtmlSelectors) { | ||
editedElements.forEach(removeHtmlSelectors); | ||
} | ||
return $.html(); | ||
}; |
{ | ||
"name": "inline-css", | ||
"version": "2.1.0", | ||
"version": "2.1.1", | ||
"description": "Inline css into an html file.", | ||
"main": "index.js", | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"dependencies": { | ||
@@ -12,12 +15,29 @@ "bluebird": "^2.9.25", | ||
"extract-css": "^1.0.0", | ||
"flatten": "0.0.1", | ||
"style-selector": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^2.11.6", | ||
"gulp": "^3.9.0", | ||
"gulp-eslint": "^1.1.1", | ||
"gulp-mocha": "^2.0.0", | ||
"gulp-util": "^3.0.2", | ||
"mocha": "^2.1.0", | ||
"nyc": "^5.0.0", | ||
"should": "^5.2.0" | ||
}, | ||
"engines": { | ||
"node": ">=0.10.0" | ||
}, | ||
"scripts": { | ||
"test": "gulp test", | ||
"coverage": "istanbul cover _mocha --report html -- -R spec && open coverage/index.html" | ||
"test": "mocha", | ||
"lint": "gulp lint", | ||
"coverage": "nyc npm test && nyc report", | ||
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/jonkemp/inline-css" | ||
}, | ||
"files": [ | ||
"index.js", | ||
"lib" | ||
], | ||
"repository": "jonkemp/inline-css", | ||
"keywords": [ | ||
@@ -30,17 +50,3 @@ "inline", | ||
"author": "Jonathan Kemp <kempdogg@gmail.com> (http://jonkemp.com/)", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/jonkemp/inline-css/issues" | ||
}, | ||
"homepage": "https://github.com/jonkemp/inline-css", | ||
"devDependencies": { | ||
"gulp": "^3.9.0", | ||
"gulp-jscs": "^3.0.0", | ||
"gulp-jshint": "^1.9.0", | ||
"gulp-mocha": "^2.0.0", | ||
"gulp-util": "^3.0.2", | ||
"istanbul": "0.3.x", | ||
"mocha": "^2.1.0", | ||
"should": "^5.2.0" | ||
} | ||
"license": "MIT" | ||
} |
@@ -1,2 +0,2 @@ | ||
# inline-css [![npm](http://img.shields.io/npm/v/inline-css.svg?style=flat)](https://badge.fury.io/js/inline-css) [![Build Status](https://travis-ci.org/jonkemp/inline-css.svg?branch=master)](https://travis-ci.org/jonkemp/inline-css) | ||
# inline-css [![npm](http://img.shields.io/npm/v/inline-css.svg?style=flat)](https://badge.fury.io/js/inline-css) [![Build Status](https://travis-ci.org/jonkemp/inline-css.svg?branch=master)](https://travis-ci.org/jonkemp/inline-css) [![Coverage Status](https://coveralls.io/repos/jonkemp/inline-css/badge.svg?branch=master&service=github)](https://coveralls.io/github/jonkemp/inline-css?branch=master) | ||
@@ -152,4 +152,15 @@ [![NPM](https://nodei.co/npm/inline-css.png?downloads=true)](https://nodei.co/npm/inline-css/) | ||
#### options.removeHtmlSelectors | ||
Type: `Boolean` | ||
Default: `false` | ||
Whether to remove the `class` and `id` attributes from the markup. | ||
## Contributing | ||
See the [CONTRIBUTING Guidelines](https://github.com/jonkemp/inline-css/blob/master/CONTRIBUTING.md) | ||
## License | ||
MIT © [Jonathan Kemp](http://jonkemp.com) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13
297
166
14234
7
1
1
1
1
+ Addedflatten@0.0.1
+ Addedflatten@0.0.1(transitive)