Skip to content

Commit

Permalink
Merge pull request #370 from mikevoets/master
Browse files Browse the repository at this point in the history
Add support for counters and multiple values in pseudo-elements
  • Loading branch information
jrit authored Jan 11, 2021
2 parents d23b44d + 83b0167 commit 94a39ce
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 6 deletions.
121 changes: 116 additions & 5 deletions lib/inline.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var utils = require('./utils');
var numbers = require('./numbers');

module.exports = function makeJuiceClient(juiceClient) {

Expand All @@ -26,6 +27,7 @@ function inlineDocument($, css, options) {
var rules = utils.parseCSS(css);
var editedElements = [];
var styleAttributeName = 'style';
var counters = {};

if (options.styleAttributeName) {
styleAttributeName = options.styleAttributeName;
Expand Down Expand Up @@ -129,6 +131,7 @@ function inlineDocument($, css, options) {
pseudoEl = el[pseudoElPropName] = $('<span />').get(0);
pseudoEl.pseudoElementType = pseudoElementType;
pseudoEl.pseudoElementParent = el;
pseudoEl.counterProps = el.counterProps;
el[pseudoElPropName] = pseudoEl;
}
el = pseudoEl;
Expand All @@ -147,13 +150,59 @@ function inlineDocument($, css, options) {
editedElements.push(el);
}

if (!el.counterProps) {
el.counterProps = el.parent && el.parent.counterProps
? Object.create(el.parent.counterProps)
: {};
}

function resetCounter(el, value) {
var tokens = value.split(/\s+/);

for (var j = 0; j < tokens.length; j++) {
var counter = tokens[j];
var resetval = parseInt(tokens[j+1], 10);

isNaN(resetval)
? el.counterProps[counter] = counters[counter] = 0
: el.counterProps[counter] = counters[tokens[j++]] = resetval;
}
}

function incrementCounter(el, value) {
var tokens = value.split(/\s+/);

for (var j = 0; j < tokens.length; j++) {
var counter = tokens[j];

if (el.counterProps[counter] === undefined) {
continue;
}

var incrval = parseInt(tokens[j+1], 10);

isNaN(incrval)
? el.counterProps[counter] = counters[counter] += 1
: el.counterProps[counter] = counters[tokens[j++]] += incrval;
}
}

// go through the properties
function addProps(style, selector) {
for (var i = 0, l = style.length; i < l; i++) {
if (style[i].type == 'property') {
var name = style[i].name;
var value = style[i].value;
var important = style[i].value.match(/!important$/) !== null;

if (name === 'counter-reset') {
resetCounter(el, value);
}

if (name === 'counter-increment') {
incrementCounter(el, value);
}

var important = value.match(/!important$/) !== null;
if (important && !options.preserveImportant) value = value.replace(/\s*!important$/, '');
// adds line number and column number for the properties as "additionalPriority" to the
// properties because in CSS the position directly affect the priority.
Expand Down Expand Up @@ -218,7 +267,7 @@ function inlineDocument($, css, options) {

function inlinePseudoElements(el) {
if (el.pseudoElementType && el.styleProps.content) {
var parsed = parseContent(el.styleProps.content.value);
var parsed = parseContent(el);
if (parsed.img) {
el.name = 'img';
$(el).attr('src', parsed.img);
Expand Down Expand Up @@ -283,7 +332,37 @@ function inlineDocument($, css, options) {
}
}

function parseContent(content) {
function findVariableValue(el, variable) {
while (el) {
if (variable in el.styleProps) {
return el.styleProps[variable].value;
}

var el = el.parent || el.pseudoElementParent;
}
}

function applyCounterStyle(counter, style) {
switch (style) {
case 'lower-roman':
return numbers.romanize(counter).toLowerCase();
case 'upper-roman':
return numbers.romanize(counter);
case 'lower-latin':
case 'lower-alpha':
return numbers.alphanumeric(counter).toLowerCase();
case 'upper-latin':
case 'upper-alpha':
return numbers.alphanumeric(counter);
// TODO support more counter styles
default:
return counter.toString();
}
}

function parseContent(el) {
var content = el.styleProps.content.value;

if (content === 'none' || content === 'normal') {
return '';
}
Expand All @@ -294,8 +373,40 @@ function parseContent(content) {
return { img: url };
}

// Naive parsing, assume well-formed value
content = content.slice(1, content.length - 1);
var parsed = [];

var tokens = content.split(/['"]/);
for (var i = 0; i < tokens.length; i++) {
if (tokens[i] === '') continue;

var varMatch = tokens[i].match(/var\s*\(\s*(.*?)\s*(,\s*(.*?)\s*)?\s*\)/i);
if (varMatch) {
var variable = findVariableValue(el, varMatch[1]) || varMatch[2];
parsed.push(variable.replace(/^['"]|['"]$/g, ''));
continue;
}

var counterMatch = tokens[i].match(/counter\s*\(\s*(.*?)\s*(,\s*(.*?)\s*)?\s*\)/i);
if (counterMatch && counterMatch[1] in el.counterProps) {
var counter = el.counterProps[counterMatch[1]];
parsed.push(applyCounterStyle(counter, counterMatch[3]));
continue;
}

var attrMatch = tokens[i].match(/attr\s*\(\s*(.*?)\s*\)/i);
if (attrMatch) {
var attr = attrMatch[1];
parsed.push(el.pseudoElementParent
? el.pseudoElementParent.attribs[attr]
: el.attribs[attr]
);
continue;
}

parsed.push(tokens[i]);
}

content = parsed.join('');
// Naive unescape, assume no unicode char codes
content = content.replace(/\\/g, '');
return content;
Expand Down
40 changes: 40 additions & 0 deletions lib/numbers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

/**
* Converts a decimal number to roman numeral.
* https://stackoverflow.com/questions/9083037/convert-a-number-into-a-roman-numeral-in-javascript
*
* @param {Number} number
* @api private.
*/
exports.romanize = function(num) {
if (isNaN(num))
return NaN;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}

/**
* Converts a decimal number to alphanumeric numeral.
* https://stackoverflow.com/questions/45787459/convert-number-to-alphabet-string-javascript
*
* @param {Number} number
* @api private.
*/
exports.alphanumeric = function(num) {
var s = '', t;

while (num > 0) {
t = (num - 1) % 26;
s = String.fromCharCode(65 + t) + s;
num = (num - t)/26 | 0;
}
return s || undefined;
}
30 changes: 30 additions & 0 deletions test/cases/juice-content/pseudo-elements.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<html><body>
<style>
body {
--var: 'Element';
counter-reset: element 20 other;
}

a {
text-decoration: underline;
}
Expand Down Expand Up @@ -47,11 +52,36 @@
content: url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=');
width: 20%;
}

e {
counter-increment: element other -2;
}

e:before {
content: var(--var) " / " counter(element) '. ' counter(other);
}

e[length]:after {
content: ' | Length: ' attr(length) ' [' counter(element, lower-roman) ':' counter(element, upper-latin) ']';
}

f {
counter-increment: other;
}

f:before {
content: counter(other);
}

</style>
<a href="#">Test</a>
<b></b>
<b><span></span></b>
<c></c>
<c class="not"></c>
<d><span>test</span></d>
<e><span></span></e>
<e length="100"><span>Test</span></e>
<f><span></span></f>
<f><span></span></f>
</body></html>
6 changes: 5 additions & 1 deletion test/cases/juice-content/pseudo-elements.out
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<html><body>
<html><body style="--var: 'Element'; counter-reset: element 20 other;">

<a href="#" style="text-decoration: underline; color: blue;"><span>®+</span>Test<span style="font-weight: bold;">a</span></a>
<b><span style="color: green;">b</span></b>
<b><span style="color: red;"></span><span style="color: green;">b</span></b>
<c style="color: red;"></c>
<c class="not"></c>
<d><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="><span>test</span><img style="width: 20%;" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></d>
<e style="counter-increment: element other -2;"><span>Element / 21. -2</span><span></span></e>
<e length="100" style="counter-increment: element other -2;"><span>Element / 22. -4</span><span>Test</span><span> | Length: 100 [xxii:V]</span></e>
<f style="counter-increment: other;"><span>-3</span><span></span></f>
<f style="counter-increment: other;"><span>-2</span><span></span></f>
</body></html>

0 comments on commit 94a39ce

Please sign in to comment.