Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9033070
Showing
36 changed files
with
790 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
test/ | ||
support/ | ||
README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
test: | ||
@./node_modules/.bin/expresso \ | ||
-t 3000 \ | ||
--serial \ | ||
test/juice.test.js | ||
|
||
.PHONY: test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
# Juice | ||
|
||
Given HTML and CSS, juice will inline your properties into the `style` | ||
attribute. | ||
|
||
## How to use | ||
|
||
```js | ||
juice('<p>Test</p>', 'p { color: red; }') | ||
// '<p style="color: red;">Test</p>' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
module.exports = require('./lib/juice'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
|
||
/** | ||
* juice | ||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com> | ||
* MIT Licensed | ||
*/ | ||
|
||
module.exports = exports = juice; | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
|
||
var soupselect = require('soupselect') | ||
, utils = require('./utils') | ||
, Selector = require('./selector') | ||
, Property = require('./property') | ||
|
||
/** | ||
* Export Selector. | ||
*/ | ||
|
||
exports.Selector = Selector; | ||
|
||
/** | ||
* Export Property. | ||
*/ | ||
|
||
exports.Property = Property; | ||
|
||
/** | ||
* Export utils. | ||
*/ | ||
|
||
exports.utils = require('./utils'); | ||
|
||
/** | ||
* Inlines the CSS specified by `css` into the `html` | ||
* | ||
* @param {String} html | ||
* @param {String} css | ||
* @api public | ||
*/ | ||
|
||
function juice (html, css, options) { | ||
var rules = utils.parseCSS(css) | ||
, dom = utils.parseHTML(html) | ||
, editedElements = [] | ||
, topmost = new Selector('<style attribute>', [1, 0, 0, 0]) | ||
|
||
function select (sel) { | ||
return soupselect.select(dom, sel); | ||
} | ||
|
||
rules.forEach(function (rule) { | ||
var sel = rule[0] | ||
, style = rule[1] | ||
, matches = select(sel) | ||
, selector | ||
|
||
console.log('\ngot selector "%s" - matches: %s ', sel, matches.length); | ||
matches.forEach(function (el) { | ||
// we initialize the Selector lazily to avoid needless parsing | ||
if (!selector) { | ||
selector = new Selector(sel) | ||
console.log( | ||
'selector "%s" has specificity "%s"' | ||
, sel | ||
, JSON.stringify(selector.specificity()) | ||
); | ||
} | ||
|
||
if (!el.styleProps) { | ||
el.styleProps = {} | ||
|
||
// if the element has inline styles, fake selector with topmost specificity | ||
if (el.attribs && el.attribs.style) { | ||
console.log('element has inline style - caching properties'); | ||
addProps( | ||
utils.parseCSS(el.attribs.style) | ||
, topmost | ||
); | ||
} | ||
|
||
// store reference to an element we need to compile style="" attr for | ||
editedElements.push(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] | ||
? new Selector('!important', [2,0,0,0]) | ||
: selector | ||
, prop = new Property(name, value, sel) | ||
, existing = el.styleProps[name] | ||
|
||
if (existing) { | ||
var winner = existing.compare(prop) | ||
, loser = prop == winner ? existing : prop | ||
|
||
console.log( | ||
' - already existing from selector %s, selector %s beats %s' | ||
, name | ||
, existing.selector.text | ||
, winner.text | ||
, loser.text | ||
); | ||
|
||
if (winner == prop) { | ||
console.log(' + new value "%s"', value); | ||
} | ||
} else { | ||
el.styleProps[name] = prop; | ||
console.log(' - property "%s" added with value "%s"', name, value); | ||
} | ||
} | ||
} | ||
|
||
addProps(style, selector); | ||
}); | ||
}); | ||
|
||
console.log('elements affected "%s"', editedElements.length); | ||
editedElements.forEach(function (el) { | ||
var style = ''; | ||
|
||
for (var i in el.styleProps) { | ||
style += el.styleProps[i].toString(); | ||
} | ||
|
||
if (!el.attribs) el.attribs = {}; | ||
|
||
el.attribs.style = style; | ||
}); | ||
|
||
return utils.domToHTML(dom); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
|
||
/** | ||
* juice | ||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com> | ||
* MIT Licensed | ||
*/ | ||
|
||
module.exports = exports = Property; | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
|
||
var compare = require('./utils').compare | ||
|
||
/** | ||
* CSS property constructor. | ||
* | ||
* @param {String} property | ||
* @param {String} value | ||
* @param {Selector} selector the property originates from | ||
* @api public | ||
*/ | ||
|
||
function Property (prop, value, selector) { | ||
this.prop = prop; | ||
this.value = value; | ||
this.selector = selector | ||
} | ||
|
||
/** | ||
* Compares with another Property based on Selector#specificity. | ||
* | ||
* @api public | ||
*/ | ||
|
||
Property.prototype.compare = function (property) { | ||
var a = this.selector.specificity() | ||
, b = property.selector.specificity() | ||
, winner = compare(a, b) | ||
|
||
if (winner == a) return this; | ||
return property; | ||
}; | ||
|
||
/** | ||
* Returns CSS property | ||
* | ||
* @api public | ||
*/ | ||
|
||
Property.prototype.toString = function () { | ||
return this.prop + ': ' + this.value + ';'; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
|
||
var parse = require('mootools-slick-parser').Slick.parse | ||
|
||
/** | ||
* Module exports. | ||
*/ | ||
|
||
module.exports = exports = Selector; | ||
|
||
/** | ||
* CSS selector constructor. | ||
* | ||
* @param {String} selector text | ||
* @param {Array} optionally, precalculated specificity | ||
* @api public | ||
*/ | ||
|
||
function Selector (text, spec) { | ||
this.text = text; | ||
this.spec = spec; | ||
} | ||
|
||
/** | ||
* Lazy specificity getter | ||
* | ||
* @api public | ||
*/ | ||
|
||
Selector.prototype.specificity = function () { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
rauchg
Author
Contributor
|
||
if (this.spec) return this.spec; | ||
|
||
this.parsed = parse(this.text).expressions[0]; | ||
this.spec = [0, 0, 0, 0]; | ||
|
||
for (var i = 0, l = this.parsed.length; i < l; i++) { | ||
var token = this.parsed[i]; | ||
|
||
// id awards a point in the second column | ||
if (undefined != token.id) this.spec[1]++; | ||
|
||
// classes award a point each in the third column | ||
if (token.classes) this.spec[2] += token.classes.length; | ||
|
||
// attributes award a point each in the third column | ||
if (token.attributes) this.spec[2] += token.attributes.length; | ||
|
||
// pseudos award a point each in the third column | ||
if (token.pseudos) this.spec[2] += token.pseudos.length; | ||
|
||
// tag awards a point in the fourth column | ||
if ('*' != token.tag) this.spec[3]++; | ||
} | ||
|
||
return this.spec; | ||
} |
Oops, something went wrong.
Hey, for MooTools/Slick I made a bit more complete implementation (also using Slick.parse): https://github.com/arian/DOM/blob/matcher-specificity/Source/specificity.js#L8-33