Skip to content

Commit

Permalink
Selective support for multiple property values declarations
Browse files Browse the repository at this point in the history
This complicate a bit the logic of the inliner but this is the only way
to support multiple properties. This fixes #221, fixes
#205, fixes #27.
  • Loading branch information
bago committed Jun 24, 2016
1 parent 6166b1a commit fadb57e
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 12 deletions.
26 changes: 22 additions & 4 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,23 @@ function inlineDocument($, css, options) {
var value = style[i].value;
var important = style[i].value.match(/!important$/) !== null;
if (important && !options.preserveImportant) value = value.replace(/\s*!important$/, '');
var prop = new utils.Property(name, value, selector, important ? 2 : 0);
// adds line number and column number for the properties as "additionalPriority" to the
// properties because in CSS the position directly affect the priority.
var additionalPriority = [style[i].position.start.line, style[i].position.start.col];
var prop = new utils.Property(name, value, selector, important ? 2 : 0, additionalPriority);
var existing = el.styleProps[name];

// if property name is not in the excluded properties array
if (juiceClient.excludedProperties.indexOf(name) < 0) {
if (existing && existing.compare(prop) === prop || !existing) {
// deleting a property let us change the order (move it to the end in the setStyleAttrs loop)
if (existing) delete el.styleProps[name];
if (existing && existing.selector !== selector) {
delete el.styleProps[name];
} else if (existing) {
// make "prop" a special composed property.
prop.nextProp = existing;
}

el.styleProps[name] = prop;
}
}
Expand All @@ -175,8 +184,17 @@ function inlineDocument($, css, options) {
}

function setStyleAttrs(el) {
var props = Object.keys(el.styleProps).map(function(key) {
return el.styleProps[key];
var l = Object.keys(el.styleProps).length;
var props = [];
// Here we loop each property and make sure to "expand"
// linked "nextProp" properties happening when the same property
// is declared multiple times in the same selector.
Object.keys(el.styleProps).forEach(function(key) {
var np = el.styleProps[key];
while (typeof np != 'undefined') {
props.push(np);
np = np.nextProp;
}
});
// sort properties by their originating selector's specificity so that
// props like "padding" and "padding-bottom" are resolved as expected.
Expand Down
11 changes: 10 additions & 1 deletion lib/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ var utils = require('./utils');
* @param {String} property
* @param {String} value
* @param {Selector} selector the property originates from
* @param {Integer} priority 0 for normal properties, 2 for !important properties.
* @param {Array} additional array of integers representing more detailed priorities (sorting)
* @api public
*/

function Property(prop, value, selector, priority) {
function Property(prop, value, selector, priority, additionalPriority) {
this.prop = prop;
this.value = value;
this.selector = selector;
this.priority = priority || 0;
this.additionalPriority = additionalPriority;
}

/**
Expand All @@ -33,9 +36,15 @@ function Property(prop, value, selector, priority) {
Property.prototype.compareFunc = function(property) {
var a = [];
a.push.apply(a, this.selector.specificity());
if (this.additionalPriority) {
a.push.apply(a, this.additionalPriority);
}
a[0] += this.priority;
var b = [];
b.push.apply(b, property.selector.specificity());
if (property.additionalPriority) {
b.push.apply(b, property.additionalPriority);
}
b[0] += property.priority;
return utils.compareFunc(a, b);
};
Expand Down
7 changes: 4 additions & 3 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ exports.extract = function extract(selectorText) {
*/

exports.parseCSS = function(css) {
var parsed = mensch.parse(css);
var parsed = mensch.parse(css, {position: true, comments: true});
var rules = typeof parsed.stylesheet != 'undefined' && parsed.stylesheet.rules ? parsed.stylesheet.rules : [];
var ret = [];

Expand Down Expand Up @@ -196,13 +196,14 @@ exports.decodeEntities = function(html) {
*/

exports.compareFunc = function(a, b) {
for (var i = 0; i < 4; i++) {
var min = Math.min(a.length, b.length);
for (var i = 0; i < min; i++) {
if (a[i] === b[i]) { continue; }
if (a[i] > b[i]) { return 1; }
return -1;
}

return 0;
return a.length-b.length;
};

exports.compare = function(a, b) {
Expand Down
2 changes: 1 addition & 1 deletion test/cases/integration.out
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<table style="margin: 0; padding: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 100%; line-height: 1.6; width: 100%;">
<tr style="margin: 0; padding: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 100%; line-height: 1.6;">
<td class="padding" style="margin: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 100%; line-height: 1.6; padding: 10px 0;">
<p style="margin: 0; padding: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.6; margin-bottom: 10px; font-weight: normal; font-size: 14px;"><a href="https://github.com/leemunroe/html-email-template" class="btn-primary" style="margin: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 100%; padding: 0; text-decoration: none; color: #FFF; background-color: #348eda; border: solid #348eda; border-radius: 25px; line-height: 2; font-weight: bold; margin-right: 10px; text-align: center; cursor: pointer; display: inline-block; border-width: 10px 20px;">View the source and instructions on GitHub</a></p>
<p style="margin: 0; padding: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; line-height: 1.6; margin-bottom: 10px; font-weight: normal; font-size: 14px;"><a href="https://github.com/leemunroe/html-email-template" class="btn-primary" style="margin: 0; padding: 0; font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 100%; text-decoration: none; color: #FFF; background-color: #348eda; border: solid #348eda; border-width: 10px 20px; line-height: 2; font-weight: bold; margin-right: 10px; text-align: center; cursor: pointer; display: inline-block; border-radius: 25px;">View the source and instructions on GitHub</a></p>
</td>
</tr>
</table>
Expand Down
25 changes: 25 additions & 0 deletions test/cases/multiple-properties-occourences.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.a {
color: #800000;
color: red;
color: rgba(255,0,0,.5);
color: -prefix-unknownvalue;
}
.b {
color: blue;
}
.b {
color: rgba(0,0,255,.5);
}
.c {
color: red !important;
color: rgba(255,0,0,.5) !important;
color: blue;
}
.d {
padding: 5px;
padding-top: 20px;
padding: 10px;
padding-top: unexpected value;
padding: unknown;
padding-top: again something unexpected;
}
4 changes: 4 additions & 0 deletions test/cases/multiple-properties-occourences.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
<div class="d"></div>
4 changes: 4 additions & 0 deletions test/cases/multiple-properties-occourences.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="a" style="color: #800000; color: red; color: rgba(255,0,0,.5); color: -prefix-unknownvalue;"></div>
<div class="b" style="color: rgba(0,0,255,.5);"></div>
<div class="c" style="color: red; color: rgba(255,0,0,.5);"></div>
<div class="d" style="padding: 5px; padding-top: 20px; padding: 10px; padding-top: unexpected value; padding: unknown; padding-top: again something unexpected;"></div>
2 changes: 1 addition & 1 deletion test/cases/propertyorder.out
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="a b" style="color: red; padding-top: 0; padding: 10px;"></div>
<div class="a b" style="color: red; padding-top: 0; padding: 5px; padding: 10px;"></div>
7 changes: 5 additions & 2 deletions test/juice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ it('parse simple css into a object structure', function() {
var b = actual[1];

assert.equal(a[0],'a');
assert.deepEqual(a[1]['0'],{ type: 'property', name: 'c', value: 'e' });
assert.deepEqual(a[1]['0'],{ type: 'property', name: 'c', value: 'e', position: { start: { line: 1, col: 8 }, end: { line: 1, col: 12 } }});
assert.equal(a[1].length,1);
assert.deepEqual(a[1],b[1]);
});
Expand All @@ -125,13 +125,16 @@ it('parse complex css into a object structure', function() {
var bed = actual[2];
var cab = actual[3];

/*
delete bed[1].parentRule;
delete cab[1].parentRule;
delete bed[1].__starts;
delete cab[1].__starts;
*/

assert.deepEqual(a[1],b[1]);
assert.deepEqual(bed[1],cab[1]);
assert.equal(bed[1].name,cab[1].name);
assert.equal(bed[1].value,cab[1].value);
});

it('test excludedProperties setting', function() {
Expand Down

0 comments on commit fadb57e

Please sign in to comment.