Comparing version 1.0.2 to 1.1.0
@@ -0,1 +1,7 @@ | ||
1.1.0 / 2015-05-04 | ||
================== | ||
* Fix order of inlined style properties. Now sorted by selector specificity, resulting in the same computed styles that the original CSS would have had. | ||
* Add option to inline pseudo elements as <span> elements | ||
1.0.2 / 2015-04-27 | ||
@@ -2,0 +8,0 @@ ================== |
117
lib/juice.js
@@ -78,2 +78,6 @@ | ||
if (options && options.inlinePseudoElements) { | ||
editedElements.forEach(inlinePseudoElements); | ||
} | ||
if (options && options.applyWidthAttributes) { | ||
@@ -90,6 +94,7 @@ editedElements.forEach(setWidthAttrs); | ||
, style = rule[1] | ||
, selector = new Selector(sel); | ||
, selector = new Selector(sel) | ||
, parsedSelector = selector.parsed() | ||
, pseudoElementType = getPseudoElementType(parsedSelector); | ||
// skip rule if the selector has any pseudos which are ignored | ||
var parsedSelector = selector.parsed(); | ||
for (var i = 0; i < parsedSelector.length; ++i) { | ||
@@ -105,2 +110,10 @@ var subSel = parsedSelector[i]; | ||
if (pseudoElementType) { | ||
var last = parsedSelector[parsedSelector.length - 1]; | ||
var pseudos = last.pseudos; | ||
last.pseudos = filterElementPseudos(last.pseudos), | ||
sel = parsedSelector.toString(); | ||
last.pseudos = pseudos; | ||
} | ||
var els; | ||
@@ -113,4 +126,18 @@ try { | ||
} | ||
els.each(function () { | ||
var el = this; | ||
if (pseudoElementType) { | ||
var pseudoElPropName = "pseudo" + pseudoElementType; | ||
var pseudoEl = el[pseudoElPropName]; | ||
if (!pseudoEl) { | ||
pseudoEl = el[pseudoElPropName] = $("<span />"); | ||
pseudoEl.pseudoElementType = pseudoElementType; | ||
pseudoEl.pseudoElementParent = el; | ||
el[pseudoElPropName] = pseudoEl; | ||
} | ||
el = pseudoEl; | ||
} | ||
if (!el.styleProps) { | ||
@@ -154,16 +181,39 @@ el.styleProps = {} | ||
function setStyleAttrs(el) { | ||
var style = []; | ||
for (var i in el.styleProps) { | ||
style.push(el.styleProps[i].prop + ": " + el.styleProps[i].value.replace(/["]/g, "'") + ";"); | ||
var props = Object.keys(el.styleProps).map(function(key) { | ||
return el.styleProps[key]; | ||
}); | ||
// sort properties by their originating selector's specificity so that | ||
// props like "padding" and "padding-bottom" are resolved as expected. | ||
props.sort(function(a, b) { | ||
return a.selector.specificity().join("").localeCompare( | ||
b.selector.specificity().join("")); | ||
}); | ||
var string = props | ||
.filter(function(prop) { | ||
// Content becomes the innerHTML of pseudo elements, not used as a | ||
// style property | ||
return prop.prop !== "content"; | ||
}) | ||
.map(function(prop) { | ||
return prop.prop + ": " + prop.value.replace(/["]/g, "'") + ";"; | ||
}) | ||
.join(" "); | ||
if (string) { | ||
$(el).attr('style', string); | ||
} | ||
// 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 inlinePseudoElements(el) { | ||
if (el.pseudoElementType && el.styleProps.content) { | ||
el.html(parseContent(el.styleProps.content.value)); | ||
var parent = el.pseudoElementParent; | ||
if (el.pseudoElementType === "before") { | ||
$(parent).prepend(el); | ||
} | ||
else { | ||
$(parent).append(el); | ||
} | ||
} | ||
} | ||
function setWidthAttrs(el) { | ||
@@ -196,2 +246,43 @@ var elName = el.name.toUpperCase(); | ||
function parseContent(content) { | ||
if (content === "none" || content === "normal") { | ||
return ""; | ||
} | ||
// Naive parsing, assume well-formed value | ||
content = content.slice(1, content.length - 1); | ||
// Naive unescape, assume no unicode char codes | ||
content = content.replace(/\\/g, ""); | ||
return content; | ||
} | ||
// Return "before" or "after" if the given selector is a pseudo element (e.g., | ||
// a::after). | ||
function getPseudoElementType(selector) { | ||
if (selector.length === 0) { | ||
return; | ||
} | ||
var pseudos = selector[selector.length - 1].pseudos; | ||
if (!pseudos) { | ||
return; | ||
} | ||
for (var i = 0; i < pseudos.length; i++) { | ||
if (isPseudoElementName(pseudos[i])) { | ||
return pseudos[i].name; | ||
} | ||
} | ||
} | ||
function isPseudoElementName(pseudo) { | ||
return pseudo.name === "before" || pseudo.name === "after"; | ||
} | ||
function filterElementPseudos(pseudos) { | ||
return pseudos.filter(function(pseudo) { | ||
return !isPseudoElementName(pseudo); | ||
}); | ||
} | ||
function juiceDocument($, options) { | ||
@@ -198,0 +289,0 @@ options = getDefaultOptions(options); |
{ | ||
"name": "juice", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"description": "Inlines css into html source", | ||
@@ -5,0 +5,0 @@ "bin": "./bin/juice", |
@@ -53,3 +53,4 @@ [![Build Status](https://travis-ci.org/Automattic/juice.png?branch=master)](https://travis-ci.org/Automattic/juice) | ||
* `applyAttributesTableElements` - whether to create attributes for styles in `juice.styleToAttribute` on elements set in `juice.tableElements`. Defaults to `false`. | ||
* `webResources` - An options object that will be passed through to web-resource-inliner for juice functions that will get remote resources (`juiceResources` and `juiceFile`). Defaults to `{}`. | ||
* `webResources` - An options object that will be passed to [web-resource-inliner](https://www.npmjs.com/package/web-resource-inliner) for juice functions that will get remote resources (`juiceResources` and `juiceFile`). Defaults to `{}`. | ||
* `inlinePseudoElements` - Whether to insert pseudo elements (`::before` and `::after`) as `<span>` into the DOM. *Note*: Inserting pseudo elements will modify the DOM and may conflict with CSS selectors elsewhere on the page (e.g., `:last-child`). | ||
@@ -56,0 +57,0 @@ ### Methods |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
238068
663
159
0