Comparing version 0.11.0 to 1.0.0-0
define(function () { 'use strict'; | ||
var emptyArray = []; | ||
var emptyObject = {}; | ||
var emptyArray = []; | ||
var type = emptyObject.toString; | ||
var own = emptyObject.hasOwnProperty; | ||
var ARRAY = type.call(emptyArray); | ||
var OBJECT = type.call(emptyObject); | ||
var ARRAY = type.call(emptyArray); | ||
var STRING = type.call(''); | ||
/*/-inline-/*/ | ||
// function cartesian(a, b, res, i, j) { | ||
// res = []; | ||
// for (j in b) if (own.call(b, j)) | ||
// for (i in a) if (own.call(a, i)) | ||
// res.push(a[i] + b[j]); | ||
// return res; | ||
// } | ||
/*/-inline-/*/ | ||
/* /-statements-/*/ | ||
function cartesian(a,b, selectorP, res, i, j) { | ||
res = [] | ||
var FUNCTION = type.call(type); | ||
var own = emptyObject.hasOwnProperty; | ||
function cartesian(a,b) { | ||
var res = [], i, j | ||
for (j in b) if(own.call(b, j)) | ||
for (i in a) if(own.call(a, i)) | ||
res.push(concat(a[i], b[j], selectorP)) | ||
res.push(a[i] + b[j]) | ||
return res | ||
} | ||
function concat(a, b, selectorP) { | ||
// `b.replace(/&/g, a)` is never falsy, since the | ||
// 'a' of cartesian can't be the empty string | ||
// in selector mode. | ||
return selectorP && ( | ||
/^[-\w$]+$/.test(b) && ':-error-bad-sub-selector-' + b || | ||
/&/.test(b) && /* never falsy */ b.replace(/&/g, a) | ||
) || a + b | ||
// "Tokenizes" the selectors into parts relevant for the next function. | ||
// Strings and comments are matched, but ignored afterwards. | ||
// This is not a full tokenizers. It only recognizes comas, parentheses, | ||
// strings and comments. | ||
// regexp generated by scripts/regexps.js then trimmed by hand | ||
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
/** | ||
* This will split a coma-separated selector list into individual selectors, | ||
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists). | ||
* @param {string} selector | ||
* @return {string[]} | ||
*/ | ||
function splitSelector(selector) { | ||
var indices = [], res = [], inParen = 0, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = selectorTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
switch(o[0]){ | ||
case '(': inParen++; break | ||
case ')': inParen--; break | ||
case ',': if (inParen) break; indices.push(o.index) | ||
} | ||
} | ||
for (o = indices.length; o--;){ | ||
res.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
res.unshift(selector) | ||
return res | ||
} | ||
// This is like the `selectorTokenizer`, but for the `&` operator | ||
var ampersandTokenizer = /&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
function ampersand (selector, parents) { | ||
var indices = [], split = [], res, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = ampersandTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
if (o[0] == '&') indices.push(o.index) | ||
} | ||
for (o = indices.length; o--;){ | ||
split.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
split.unshift(selector) | ||
res = [split[0]] | ||
for (o = 1; o < split.length; o++) { | ||
res = cartesian(res, cartesian(parents, [split[o]])) | ||
} | ||
return res.join(',') | ||
} | ||
function flatIter (f) { | ||
return function iter(arg) { | ||
if (type.call(arg) === ARRAY) for (var i= 0 ; i < arg.length; i ++) iter(arg[i]) | ||
else f(arg) | ||
} | ||
} | ||
function decamelize(match) { | ||
@@ -46,26 +90,20 @@ return '-' + match.toLowerCase() | ||
* | ||
* @param {array|object|string} o - the declarations. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built. | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current property or a prefix in case of nested | ||
* sub-properties. | ||
* @param {string} vendors - a list of vendor prefixes. | ||
* @Param {boolean} local - are we in @local or in @global scope. | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes. | ||
* @param {function} ns.e - @extend helper. | ||
* @param {function} ns.l - @local helper. | ||
* @param {array|object|string} o - the declarations. | ||
* @param {boolean} local - are we in @local or in @global scope. | ||
*/ | ||
function declarations(o, buf, prefix, vendors, local, ns, /*var*/ k, v, kk) { | ||
function declarations(parser, emit, prefix, o, local) { | ||
var k, v, kk | ||
if (o==null) return | ||
if (/\$/.test(prefix)) { | ||
for (kk in (prefix = prefix.split('$'))) if (own.call(prefix, kk)) { | ||
declarations(o, buf, prefix[kk], vendors, local, ns) | ||
} | ||
return | ||
} | ||
switch ( type.call(o = o.valueOf()) ) { | ||
case ARRAY: | ||
for (k = 0; k < o.length; k++) | ||
declarations(o[k], buf, prefix, vendors, local, ns) | ||
declarations(parser, emit, prefix, o[k], local) | ||
break | ||
@@ -79,6 +117,11 @@ case OBJECT: | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) | ||
declarations(v, buf, prefix + k[kk], vendors, local, ns) | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, prefix + k[kk], v, local) | ||
} | ||
} else { | ||
declarations(v, buf, prefix + k, vendors, local, ns) | ||
declarations(parser, emit, prefix + k, v, local) | ||
} | ||
@@ -90,115 +133,136 @@ } | ||
// at the top level. | ||
// `o` is then treated as a `property:value` pair. | ||
// otherwise, `prefix` is the property name, and | ||
// `o` is then treated as a `property:value` pair, or a | ||
// semi-colon-separated list thereof. | ||
// Otherwise, `prefix` is the property name, and | ||
// `o` is the value. | ||
// restore the dashes | ||
k = prefix.replace(/_/g, '-').replace(/[A-Z]/g, decamelize) | ||
if (local && (k == 'animation-name' || k == 'animation')) { | ||
if (local && (k == 'animation-name' || k == 'animation' || k == 'list-style')) { | ||
// no need to tokenize here a plain `.split(',')` has all bases covered. | ||
// We may 'parser' a comment, but it's not a big deal. | ||
o = o.split(',').map(function (o) { | ||
return o.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, ns.l) | ||
return o.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, parser.L) | ||
}).join(',') | ||
} | ||
if (/^animation|^transition/.test(k)) vendors = ['webkit'] | ||
// '@' in properties also triggers the *ielte7 hack | ||
// Since plugins dispatch on the /^@/ for at-rules | ||
// we swap the at for an asterisk | ||
// http://browserhacks.com/#hack-6d49e92634f26ae6d6e46b3ebc10019a | ||
k = k.replace(/^@/, '*') | ||
emit.d(k, o) | ||
/*/-statements-/*/ | ||
// vendorify | ||
for (kk = 0; kk < vendors.length; kk++) | ||
buf.push('-', vendors[kk], '-', k, k ? ':': '', o, ';\n') | ||
/*/-statements-/*/ | ||
buf.push(k, k ? ':': '', o, ';\n') | ||
} | ||
} | ||
var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g | ||
/** | ||
* Hanldes at-rules | ||
* | ||
* @param {string} k - The at-rule name, and, if takes both parameters and a | ||
* block, the parameters. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {string[]} v - Either parameters for block-less rules or their block | ||
* for the others. | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {array} k - The parsed at-rule, including the parameters, | ||
* if takes both parameters and a block. | ||
* @param {string} prefix - the current selector or the selector prefix | ||
* in case of nested rules | ||
* @param {string|string[]|object|object[]} v - Either parameters for | ||
* block-less rules or | ||
* their block | ||
* for the others. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){ | ||
var kk | ||
if (/^@(?:namespace|import|charset)$/.test(k)) { | ||
if(type.call(v) == ARRAY){ | ||
for (kk = 0; kk < v.length; kk++) { | ||
buf.push(k, ' ', v[kk], ';\n') | ||
} | ||
} else { | ||
buf.push(k, ' ', v, ';\n') | ||
function atRules(parser, emit, k, v, prefix, local, inAtRule) { | ||
for (var i = 0; i < parser.$a.length; i++) { | ||
if (parser.$a[i](parser, emit, k, v, prefix, local, inAtRule)) return | ||
} | ||
if (!k[3] && /^global$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 0, inAtRule) | ||
} else if (!k[3] && /^local$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 1, inAtRule) | ||
} else if (k[3] && /^adopt$/.test(k[2])) { | ||
if (!local || inAtRule) return emit.a('@-error-bad-at-adopt-placement' , JSON.stringify(k[0]), 0) | ||
if (!/^\.?[_A-Za-z][-\w]*$/.test(k[3])) return emit.a('@-error-bad-at-adopter', k[3], 0) | ||
i = [] | ||
flatIter(function(c, s){ | ||
s = c.toString() | ||
if(!/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(s)) emit.a('@-error-bad-at-adoptee', JSON.stringify(c), 0) | ||
else i.push(s.replace(/\./g, '')) | ||
})(v) | ||
// we may end up with duplicate classes but AFAIK it has no consequences on specificity. | ||
if (i.length) { | ||
// console.log("========== I ===========\n", i) | ||
parser.l(k[3] = k[3].replace(/\./g, '')) | ||
parser.n[k[3]] += (' ' + i.join(' ')) | ||
} | ||
} else if (/^@keyframes /.test(k)) { | ||
k = local ? k.replace( | ||
// generated by script/regexps.js | ||
/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, | ||
ns.l | ||
) : k | ||
// add a @-webkit-keyframes block too. | ||
buf.push('@-webkit-', k.slice(1), ' {\n') | ||
sheet(v, buf, '', '', ['webkit']) | ||
buf.push('}\n') | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, '', '', vendors, local, ns) | ||
buf.push('}\n') | ||
} else if (!k[3] && /^(?:namespace|import|charset)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
} else if (/^@extends?$/.test(k)) { | ||
emit.a(k[0], v) | ||
/*eslint-disable no-cond-assign*/ | ||
// pick the last class to be extended | ||
while (kk = findClass.exec(rawPrefix)) k = kk[4] | ||
/*eslint-enable no-cond-assign*/ | ||
if (k == null || !local) { | ||
// we're in a @global{} block | ||
buf.push('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
} else if (/^@extends?$/.test(k)) { | ||
// no class in the selector | ||
buf.push('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
})(v) | ||
} else if (!k[3] && /^(?:font-face|viewport)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
emit.a(k[1], '', 1) | ||
declarations(parser, emit, '', v, local) | ||
emit.A(k[1], '') | ||
})(v) | ||
} else if (k[3] && /^(?:media|supports|page|keyframes)$/.test(k[2])) { | ||
if (local && 'keyframes' == k[2]) { | ||
k[3] = k[3].replace( | ||
// generated by script/regexps.js | ||
/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, | ||
parser.L | ||
) | ||
} | ||
ns.e( | ||
type.call(v) == ARRAY ? v.map(function (parent) { | ||
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l) | ||
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l), | ||
k | ||
) | ||
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) { | ||
sheet(v, buf, k, k, emptyArray) | ||
} else if (/^@global$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 0, ns) | ||
emit.a(k[1], k[3], 1) | ||
} else if (/^@local$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 1, ns) | ||
if ('page' == k[2]) { | ||
} else if (/^@(?:media |supports |document )./.test(k)) { | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, prefix, rawPrefix, vendors, local, ns) | ||
buf.push('}\n') | ||
declarations(parser, emit, '', v, local) | ||
} else { | ||
sheet( | ||
parser, emit, | ||
'keyframes' == k[2] ? '' : prefix, | ||
v, local, 1 | ||
) | ||
} | ||
emit.A(k[1], k[3]) | ||
} else { | ||
buf.push('@-error-unsupported-at-rule ', JSON.stringify(k), ';\n') | ||
emit.a('@-error-unsupported-at-rule', JSON.stringify(k[0])) | ||
} | ||
@@ -208,172 +272,160 @@ } | ||
/** | ||
* Add rulesets and other CSS statements to the sheet. | ||
* Add rulesets and other CSS tree to the sheet. | ||
* | ||
* @param {array|string|object} statements - a source object or sub-object. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {array|string|object} tree - a source object or sub-object. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function sheet(statements, buf, prefix, rawPrefix, vendors, local, ns) { | ||
var k, kk, v, inDeclaration | ||
function sheet(parser, emit, prefix, tree, local, inAtRule) { | ||
var k, v, inDeclaration, kk | ||
switch (type.call(statements)) { | ||
switch (type.call(tree)) { | ||
case ARRAY: | ||
for (k = 0; k < statements.length; k++) | ||
sheet(statements[k], buf, prefix, rawPrefix, vendors, local, ns) | ||
break | ||
case OBJECT: | ||
for (k in tree) if (own.call(tree, k)) { | ||
v = tree[k] | ||
case OBJECT: | ||
for (k in statements) { | ||
v = statements[k] | ||
if (prefix && /^[-\w$]+$/.test(k)) { | ||
if (!inDeclaration) { | ||
inDeclaration = 1 | ||
buf.push(( prefix || '*' ), ' {\n') | ||
emit.s(prefix) | ||
} | ||
declarations(v, buf, k, vendors, local, ns) | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, k[kk], v, local) | ||
} | ||
} else { | ||
declarations(parser, emit, k, v, local) | ||
} | ||
} else if (/^@/.test(k)) { | ||
// Handle At-rules | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
inDeclaration = 0 | ||
at(k, v, buf, prefix, rawPrefix, vendors, local, ns) | ||
atRules(parser, emit, | ||
/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(k) || [k,'@','',''], | ||
v, prefix, local, inAtRule | ||
) | ||
} else { | ||
// selector or nested sub-selectors | ||
inDeclaration = 0 | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
sheet( | ||
parser, emit, | ||
// `prefix` ... Hefty. Ugly. Sadly necessary. | ||
// | ||
(prefix && (/,/.test(prefix) || /,/.test(k))) ? | ||
sheet(v, buf, | ||
(kk = /,/.test(prefix) || prefix && /,/.test(k)) ? | ||
cartesian(prefix.split(','), ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
).split(','), prefix).join(',') : | ||
concat(prefix, ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
), prefix), | ||
kk ? | ||
cartesian(rawPrefix.split(','), k.split(','), rawPrefix).join(',') : | ||
concat(rawPrefix, k, rawPrefix), | ||
vendors, | ||
local, ns | ||
/*0*/ (kk = splitSelector(prefix), splitSelector( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
).map(function (k) { | ||
return /&/.test(k) ? ampersand(k, kk) : kk.map(function(kk) { | ||
return kk + k | ||
}).join(',') | ||
}).join(',')) : | ||
/*0*/ /&/.test(k) ? | ||
/*1*/ ampersand( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k, | ||
[prefix] | ||
) : | ||
/*1*/ prefix + ( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
), | ||
v, local, inAtRule | ||
) | ||
} | ||
} | ||
if (inDeclaration) buf.push('}\n') | ||
break | ||
case ARRAY: | ||
for (k = 0; k < tree.length; k++){ | ||
sheet(parser, emit, prefix, tree[k], local, inAtRule) | ||
} | ||
break | ||
case STRING: | ||
buf.push( | ||
( prefix || ':-error-no-selector' ) , ' {\n' | ||
) | ||
declarations(statements, buf, '', vendors, local, ns) | ||
buf.push('}\n') | ||
} | ||
} | ||
// CSS hacks or ouptut of `j2c.inline`. | ||
var scope_root = '_j2c_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_'; | ||
var counter = 0; | ||
function j2c(res) { | ||
res = res || {} | ||
var extensions = [] | ||
emit.s(prefix || ':-error-no-selector') | ||
function finalize(buf, i) { | ||
for (i = 0; i< extensions.length; i++) buf = extensions[i](buf) || buf | ||
return buf.join('') | ||
declarations(parser, emit, '', tree, local) | ||
} | ||
} | ||
res.use = function() { | ||
var args = arguments | ||
for (var i = 0; i < args.length; i++){ | ||
extensions.push(args[i]) | ||
} | ||
return res | ||
} | ||
/*/-statements-/*/ | ||
res.sheet = function(ns, statements) { | ||
if (arguments.length === 1) { | ||
statements = ns; ns = {} | ||
} | ||
var | ||
suffix = scope_root + counter++, | ||
locals = {}, | ||
k, buf = [] | ||
// pick only non-numeric keys since `(NaN != NaN) === true` | ||
for (k in ns) if (k-0 != k-0 && own.call(ns, k)) { | ||
locals[k] = ns[k] | ||
} | ||
sheet( | ||
statements, buf, '', '', emptyArray /*vendors*/, | ||
1, // local | ||
{ | ||
e: function extend(parent, child) { | ||
var nameList = locals[child] | ||
locals[child] = | ||
nameList.slice(0, nameList.lastIndexOf(' ') + 1) + | ||
parent + ' ' + | ||
nameList.slice(nameList.lastIndexOf(' ') + 1) | ||
}, | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) { | ||
return space + global | ||
} | ||
if (!locals[name]) locals[name] = name + suffix | ||
return space + dot + locals[name].match(/\S+$/) | ||
} | ||
// This is the first entry in the filters array, which is | ||
// actually the last step of the compiler. It inserts | ||
// closing braces to close normal (non at-) rules (those | ||
// that start with a selector). Doing it earlier is | ||
// impossible without passing state around in unrelated code | ||
// or ending up with duplicated selectors when the source tree | ||
// contains arrays. | ||
// There's no `S` handler, because the core compiler never | ||
// calls it. | ||
function closeSelectors(next, inline) { | ||
var lastSelector | ||
return inline ? next : { | ||
i: function(){lastSelector = 0; next.i()}, | ||
x: function (raw) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
return next.x(raw) | ||
}, | ||
a: function (rule, param, takesBlock) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.a(rule, param, takesBlock) | ||
}, | ||
A: function (rule) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.A(rule) | ||
}, | ||
s: function (selector) { | ||
if (selector !== lastSelector){ | ||
if (lastSelector) next.S() | ||
next.s(selector) | ||
lastSelector = selector | ||
} | ||
) | ||
/*jshint -W053 */ | ||
buf = new String(finalize(buf)) | ||
/*jshint +W053 */ | ||
for (k in locals) if (own.call(locals, k)) buf[k] = locals[k] | ||
return buf | ||
}, | ||
d: next.d | ||
} | ||
/*/-statements-/*/ | ||
res.inline = function (locals, decl, buf) { | ||
if (arguments.length === 1) { | ||
decl = locals; locals = {} | ||
} | ||
declarations( | ||
decl, | ||
buf = [], | ||
'', // prefix | ||
emptyArray, // vendors | ||
1, | ||
{ | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) return space + global | ||
if (!locals[name]) return name | ||
return space + dot + locals[name] | ||
} | ||
}) | ||
return finalize(buf) | ||
} | ||
res.prefix = function(val, vendors) { | ||
return cartesian( | ||
vendors.map(function(p){return '-' + p + '-'}).concat(['']), | ||
[val] | ||
) | ||
} | ||
return res | ||
} | ||
j2c.global = function(x) { | ||
function global(x) { | ||
return ':global(' + x + ')' | ||
} | ||
j2c.kv = kv | ||
function kv (k, v, o) { | ||
@@ -385,18 +437,157 @@ o = {} | ||
j2c.at = function at (rule, params, block) { | ||
function at (rule, params, block) { | ||
if ( | ||
arguments.length < 3 | ||
) { | ||
// inner curry! | ||
var _at = at.bind.apply(at, [null].concat([].slice.call(arguments,0))) | ||
// So that it can be used as a key in an ES6 object literal. | ||
_at.toString = function(){return '@' + rule + ' ' + params} | ||
return _at | ||
} | ||
else return kv('@' + rule + ' ' + params, block) | ||
else return kv('@' + rule +' ' + params, block) | ||
} | ||
j2c(j2c) | ||
delete j2c.use | ||
function j2c() { | ||
var $filters = [closeSelectors] | ||
var $atHandlers = [] | ||
var instance = { | ||
at: at, | ||
global: global, | ||
kv: kv, | ||
names: {}, | ||
suffix: '__j2c-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36), | ||
use: function() { | ||
_use(emptyArray.slice.call(arguments)) | ||
return instance | ||
}, | ||
$plugins: [] | ||
} | ||
function _default(target, source) { | ||
for (var k in source) if (own.call(source, k) && k.indexOf('$')) { | ||
if (OBJECT == type.call(source[k]) && OBJECT == type.call(target[k])) _default(target[k], source[k]) | ||
else if (!(k in target)) target[k] = source[k] | ||
} | ||
} | ||
var buf | ||
var $sink = { | ||
i: function(){buf=[]}, | ||
x: function (raw) {return raw ? buf : buf.join('')}, | ||
a: function (rule, argument, takesBlock) { | ||
buf.push(rule, argument && ' ',argument, takesBlock ? ' {\n' : ';\n') | ||
}, | ||
A: function () {buf.push('}\n')}, | ||
s: function (selector) {buf.push(selector, ' {\n')}, | ||
S: function () {buf.push('}\n')}, | ||
d: function (prop, value) {buf.push(prop, prop && ':', value, ';\n')} | ||
} | ||
var streams = [] | ||
var parsers = [ | ||
{ | ||
$a: $atHandlers, | ||
a: atRules, | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names, | ||
s: sheet | ||
}, { | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names | ||
} | ||
] | ||
var _use = flatIter(function(plugin) { | ||
// `~n` is falsy for `n === -1` and truthy otherwise. | ||
// Works well to turn the result of `a.indexOf(x)` | ||
// into a value that reflects the presence of `x` in | ||
// `a`. | ||
if (~instance.$plugins.indexOf(plugin)) return | ||
instance.$plugins.push(plugin) | ||
if (type.call(plugin) === FUNCTION) plugin = plugin(instance) | ||
if (!plugin) return | ||
flatIter(function(filter) { | ||
$filters.push(filter) | ||
})(plugin.$filter || emptyArray) | ||
flatIter(function(handler) { | ||
$atHandlers.push(handler) | ||
})(plugin.$at || emptyArray) | ||
$sink = plugin.$sink || $sink | ||
_default(instance, plugin) | ||
}) | ||
function getStream(inline) { | ||
if (!streams.length) { | ||
for(var i = 0; i < 2; i++){ | ||
$filters[$filters.length - i] = function(_, inline) {return inline ? {i:$sink.i, d:$sink.d, x:$sink.x} : $sink} | ||
for (var j = $filters.length; j--;) streams[i] = $filters[j](streams[i], !!i, parsers[i]) | ||
} | ||
} | ||
var res = streams[inline] | ||
return res | ||
} | ||
function localize(name) { | ||
if (!instance.names[name]) instance.names[name] = name + instance.suffix | ||
return instance.names[name].match(/^\S+/) | ||
} | ||
function localizeReplacer(match, string, global, dot, name) { | ||
if (string || global) return string || global | ||
return dot + localize(name) | ||
} | ||
/*/-statements-/*/ | ||
instance.sheet = function(tree) { | ||
var emit = getStream(0) | ||
emit.i() | ||
sheet( | ||
parsers[0], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1, // local, by default | ||
0 // inAtRule | ||
) | ||
return emit.x() | ||
} | ||
/*/-statements-/*/ | ||
instance.inline = function (tree) { | ||
var emit = getStream(1) | ||
emit.i() | ||
declarations( | ||
parsers[1], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1 //local | ||
) | ||
return emit.x() | ||
} | ||
return instance | ||
} | ||
var _j2c = j2c() | ||
'sheet|inline|names|at|global|kv|suffix'.split('|').map(function(m){j2c[m] = _j2c[m]}) | ||
return j2c; | ||
}); |
@@ -1,1 +0,1 @@ | ||
define(function(){"use strict";function n(n,e,r,l,s,a){l=[];for(a in e)if(f.call(e,a))for(s in n)f.call(n,s)&&l.push(t(n[s],e[a],r));return l}function t(n,t,e){return e&&(/^[-\w$]+$/.test(t)&&":-error-bad-sub-selector-"+t||/&/.test(t)&&t.replace(/&/g,n))||n+t}function e(n){return"-"+n.toLowerCase()}function r(n,t,l,s,a,o,i,u,g){if(null!=n)if(/\$/.test(l))for(g in l=l.split("$"))f.call(l,g)&&r(n,t,l[g],s,a,o);else switch(c.call(n=n.valueOf())){case h:for(i=0;i<n.length;i++)r(n[i],t,l,s,a,o);break;case p:l=l&&l+"-";for(i in n)if(f.call(n,i))if(u=n[i],/\$/.test(i))for(g in i=i.split("$"))f.call(i,g)&&r(u,t,l+i[g],s,a,o);else r(u,t,l+i,s,a,o);break;default:for(i=l.replace(/_/g,"-").replace(/[A-Z]/g,e),!a||"animation-name"!=i&&"animation"!=i||(n=n.split(",").map(function(n){return n.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,o.l)}).join(",")),/^animation|^transition/.test(i)&&(s=["webkit"]),i=i.replace(/^@/,"*"),g=0;g<s.length;g++)t.push("-",s[g],"-",i,i?":":"",n,";\n");t.push(i,i?":":"",n,";\n")}}function l(n,t,e,r,l,a,o,i){var f;if(/^@(?:namespace|import|charset)$/.test(n))if(c.call(t)==h)for(f=0;f<t.length;f++)e.push(n," ",t[f],";\n");else e.push(n," ",t,";\n");else if(/^@keyframes /.test(n))n=o?n.replace(/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,i.l):n,e.push("@-webkit-",n.slice(1)," {\n"),s(t,e,"","",["webkit"]),e.push("}\n"),e.push(n," {\n"),s(t,e,"","",a,o,i),e.push("}\n");else if(/^@extends?$/.test(n)){for(;f=w.exec(l);)n=f[4];if(null==n||!o)return void e.push("@-error-cannot-extend-in-global-context ",JSON.stringify(l),";\n");if(/^@extends?$/.test(n))return void e.push("@-error-no-class-to-extend-in ",JSON.stringify(l),";\n");i.e(c.call(t)==h?t.map(function(n){return n.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,i.l)}).join(" "):t.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,i.l),n)}else/^@(?:font-face$|viewport$|page )/.test(n)?s(t,e,n,n,u):/^@global$/.test(n)?s(t,e,r,l,a,0,i):/^@local$/.test(n)?s(t,e,r,l,a,1,i):/^@(?:media |supports |document )./.test(n)?(e.push(n," {\n"),s(t,e,r,l,a,o,i),e.push("}\n")):e.push("@-error-unsupported-at-rule ",JSON.stringify(n),";\n")}function s(e,a,o,i,u,f,w){var m,b,d,$;switch(c.call(e)){case h:for(m=0;m<e.length;m++)s(e[m],a,o,i,u,f,w);break;case p:for(m in e)d=e[m],o&&/^[-\w$]+$/.test(m)?($||($=1,a.push(o||"*"," {\n")),r(d,a,m,u,f,w)):/^@/.test(m)?($=$&&a.push("}\n")&&0,l(m,d,a,o,i,u,f,w)):($=$&&a.push("}\n")&&0,s(d,a,(b=/,/.test(o)||o&&/,/.test(m))?n(o.split(","),(f?m.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,w.l):m).split(","),o).join(","):t(o,f?m.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,w.l):m,o),b?n(i.split(","),m.split(","),i).join(","):t(i,m,i),u,f,w));$&&a.push("}\n");break;case g:a.push(o||":-error-no-selector"," {\n"),r(e,a,"",u,f,w),a.push("}\n")}}function a(t){function e(n,t){for(t=0;t<l.length;t++)n=l[t](n)||n;return n.join("")}t=t||{};var l=[];return t.use=function(){for(var n=arguments,e=0;e<n.length;e++)l.push(n[e]);return t},t.sheet=function(n,t){1===arguments.length&&(t=n,n={});var r,l=m+b++,a={},o=[];for(r in n)r-0!=r-0&&f.call(n,r)&&(a[r]=n[r]);s(t,o,"","",u,1,{e:function(n,t){var e=a[t];a[t]=e.slice(0,e.lastIndexOf(" ")+1)+n+" "+e.slice(e.lastIndexOf(" ")+1)},l:function(n,t,e,r,s){return e?t+e:(a[s]||(a[s]=s+l),t+r+a[s].match(/\S+$/))}}),o=new String(e(o));for(r in a)f.call(a,r)&&(o[r]=a[r]);return o},t.inline=function(n,t,l){return 1===arguments.length&&(t=n,n={}),r(t,l=[],"",u,1,{l:function(t,e,r,l,s){return r?e+r:n[s]?e+l+n[s]:s}}),e(l)},t.prefix=function(t,e){return n(e.map(function(n){return"-"+n+"-"}).concat([""]),[t])},t}function o(n,t,e){return e={},e[n]=t,e}var i={},u=[],c=i.toString,f=i.hasOwnProperty,p=c.call(i),h=c.call(u),g=c.call(""),w=/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,m="_j2c_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_",b=0;return a.global=function(n){return":global("+n+")"},a.kv=o,a.at=function d(n,t,e){if(arguments.length<3){var r=d.bind.apply(d,[null].concat([].slice.call(arguments,0)));return r.toString=function(){return"@"+n+" "+t},r}return o("@"+n+" "+t,e)},a(a),delete a.use,a}); | ||
define(function(){"use strict";function n(n,t){var e,a,r=[];for(a in t)if(v.call(t,a))for(e in n)v.call(n,e)&&r.push(n[e]+t[a]);return r}function t(n){for(var t,e=[],a=[],r=0;t=S.exec(n);)switch(t[0]){case"(":r++;break;case")":r--;break;case",":if(r)break;e.push(t.index)}for(t=e.length;t--;)a.unshift(n.slice(e[t]+1)),n=n.slice(0,e[t]);return a.unshift(n),a}function e(t,e){for(var a,r,i=[],s=[];r=A.exec(t);)"&"==r[0]&&i.push(r.index);for(r=i.length;r--;)s.unshift(t.slice(i[r]+1)),t=t.slice(0,i[r]);for(s.unshift(t),a=[s[0]],r=1;r<s.length;r++)a=n(a,n(e,[s[r]]));return a.join(",")}function a(n){return function t(e){if(m.call(e)===d)for(var a=0;a<e.length;a++)t(e[a]);else n(e)}}function r(n){return"-"+n.toLowerCase()}function i(n,t,e,a,s){var l,o,f;if(null!=a)switch(m.call(a=a.valueOf())){case d:for(l=0;l<a.length;l++)i(n,t,e,a[l],s);break;case $:e=e&&e+"-";for(l in a)if(v.call(a,l))if(o=a[l],/\$/.test(l))for(f in l=l.split("$"))v.call(l,f)&&i(n,t,e+l[f],o,s);else i(n,t,e+l,o,s);break;default:l=e.replace(/_/g,"-").replace(/[A-Z]/g,r),!s||"animation-name"!=l&&"animation"!=l&&"list-style"!=l||(a=a.split(",").map(function(t){return t.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,n.L)}).join(",")),t.d(l,a)}}function s(n,t,e,r,s,o,f){for(var u=0;u<n.$a.length;u++)if(n.$a[u](n,t,e,r,s,o,f))return;if(!e[3]&&/^global$/.test(e[2]))l(n,t,s,r,0,f);else if(!e[3]&&/^local$/.test(e[2]))l(n,t,s,r,1,f);else if(e[3]&&/^adopt$/.test(e[2])){if(!o||f)return t.a("@-error-bad-at-adopt-placement",JSON.stringify(e[0]),0);if(!/^\.?[_A-Za-z][-\w]*$/.test(e[3]))return t.a("@-error-bad-at-adopter",e[3],0);u=[],a(function(n,e){e=n.toString(),/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(e)?u.push(e.replace(/\./g,"")):t.a("@-error-bad-at-adoptee",JSON.stringify(n),0)})(r),u.length&&(n.l(e[3]=e[3].replace(/\./g,"")),n.n[e[3]]+=" "+u.join(" "))}else!e[3]&&/^(?:namespace|import|charset)$/.test(e[2])?a(function(n){t.a(e[0],n)})(r):!e[3]&&/^(?:font-face|viewport)$/.test(e[2])?a(function(a){t.a(e[1],"",1),i(n,t,"",a,o),t.A(e[1],"")})(r):e[3]&&/^(?:media|supports|page|keyframes)$/.test(e[2])?(o&&"keyframes"==e[2]&&(e[3]=e[3].replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,n.L)),t.a(e[1],e[3],1),"page"==e[2]?i(n,t,"",r,o):l(n,t,"keyframes"==e[2]?"":s,r,o,1),t.A(e[1],e[3])):t.a("@-error-unsupported-at-rule",JSON.stringify(e[0]))}function l(n,a,r,o,f,u){var c,p,g,h;switch(m.call(o)){case $:for(c in o)if(v.call(o,c))if(p=o[c],r&&/^[-\w$]+$/.test(c))if(g||(g=1,a.s(r)),/\$/.test(c))for(h in c=c.split("$"))v.call(c,h)&&i(n,a,c[h],p,f);else i(n,a,c,p,f);else/^@/.test(c)?(g=0,s(n,a,/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(c)||[c,"@","",""],p,r,f,u)):(g=0,l(n,a,r&&(/,/.test(r)||/,/.test(c))?(h=t(r),t(f?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c).map(function(n){return/&/.test(n)?e(n,h):h.map(function(t){return t+n}).join(",")}).join(",")):/&/.test(c)?e(f?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c,[r]):r+(f?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c),p,f,u));break;case d:for(c=0;c<o.length;c++)l(n,a,r,o[c],f,u);break;case w:a.s(r||":-error-no-selector"),i(n,a,"",o,f)}}function o(n,t){var e;return t?n:{i:function(){e=0,n.i()},x:function(t){return e&&(n.S(),e=0),n.x(t)},a:function(t,a,r){e&&(n.S(),e=0),n.a(t,a,r)},A:function(t){e&&(n.S(),e=0),n.A(t)},s:function(t){t!==e&&(e&&n.S(),n.s(t),e=t)},d:n.d}}function f(n){return":global("+n+")"}function u(n,t,e){return e={},e[n]=t,e}function c(n,t,e){if(arguments.length<3){var a=c.bind.apply(c,[null].concat([].slice.call(arguments,0)));return a.toString=function(){return"@"+n+" "+t},a}return u("@"+n+" "+t,e)}function p(){function n(t,e){for(var a in e)v.call(e,a)&&a.indexOf("$")&&($==m.call(e[a])&&$==m.call(t[a])?n(t[a],e[a]):a in t||(t[a]=e[a]))}function t(n){if(!A.length)for(var t=0;2>t;t++){h[h.length-t]=function(n,t){return t?{i:S.i,d:S.d,x:S.x}:S};for(var e=h.length;e--;)A[t]=h[e](A[t],!!t,x[t])}var a=A[n];return a}function e(n){return w.names[n]||(w.names[n]=n+w.suffix),w.names[n].match(/^\S+/)}function r(n,t,a,r,i){return t||a?t||a:r+e(i)}var p,h=[o],d=[],w={at:c,global:f,kv:u,names:{},suffix:"__j2c-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36),use:function(){return _(g.slice.call(arguments)),w},$plugins:[]},S={i:function(){p=[]},x:function(n){return n?p:p.join("")},a:function(n,t,e){p.push(n,t&&" ",t,e?" {\n":";\n")},A:function(){p.push("}\n")},s:function(n){p.push(n," {\n")},S:function(){p.push("}\n")},d:function(n,t){p.push(n,n&&":",t,";\n")}},A=[],x=[{$a:d,a:s,d:i,L:r,l:e,n:w.names,s:l},{d:i,L:r,l:e,n:w.names}],_=a(function(t){~w.$plugins.indexOf(t)||(w.$plugins.push(t),m.call(t)===b&&(t=t(w)),t&&(a(function(n){h.push(n)})(t.$filter||g),a(function(n){d.push(n)})(t.$at||g),S=t.$sink||S,n(w,t)))});return w.sheet=function(n){var e=t(0);return e.i(),l(x[0],e,"",n,1,0),e.x()},w.inline=function(n){var e=t(1);return e.i(),i(x[1],e,"",n,1),e.x()},w}var g=[],h={},m=h.toString,d=m.call(g),$=m.call(h),w=m.call(""),b=m.call(m),v=h.hasOwnProperty,S=/[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,A=/&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,x=p();return"sheet|inline|names|at|global|kv|suffix".split("|").map(function(n){p[n]=x[n]}),p}); |
'use strict'; | ||
var emptyArray = []; | ||
var emptyObject = {}; | ||
var emptyArray = []; | ||
var type = emptyObject.toString; | ||
var own = emptyObject.hasOwnProperty; | ||
var ARRAY = type.call(emptyArray); | ||
var OBJECT = type.call(emptyObject); | ||
var ARRAY = type.call(emptyArray); | ||
var STRING = type.call(''); | ||
/*/-inline-/*/ | ||
// function cartesian(a, b, res, i, j) { | ||
// res = []; | ||
// for (j in b) if (own.call(b, j)) | ||
// for (i in a) if (own.call(a, i)) | ||
// res.push(a[i] + b[j]); | ||
// return res; | ||
// } | ||
/*/-inline-/*/ | ||
/* /-statements-/*/ | ||
function cartesian(a,b, selectorP, res, i, j) { | ||
res = [] | ||
var FUNCTION = type.call(type); | ||
var own = emptyObject.hasOwnProperty; | ||
function cartesian(a,b) { | ||
var res = [], i, j | ||
for (j in b) if(own.call(b, j)) | ||
for (i in a) if(own.call(a, i)) | ||
res.push(concat(a[i], b[j], selectorP)) | ||
res.push(a[i] + b[j]) | ||
return res | ||
} | ||
function concat(a, b, selectorP) { | ||
// `b.replace(/&/g, a)` is never falsy, since the | ||
// 'a' of cartesian can't be the empty string | ||
// in selector mode. | ||
return selectorP && ( | ||
/^[-\w$]+$/.test(b) && ':-error-bad-sub-selector-' + b || | ||
/&/.test(b) && /* never falsy */ b.replace(/&/g, a) | ||
) || a + b | ||
// "Tokenizes" the selectors into parts relevant for the next function. | ||
// Strings and comments are matched, but ignored afterwards. | ||
// This is not a full tokenizers. It only recognizes comas, parentheses, | ||
// strings and comments. | ||
// regexp generated by scripts/regexps.js then trimmed by hand | ||
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
/** | ||
* This will split a coma-separated selector list into individual selectors, | ||
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists). | ||
* @param {string} selector | ||
* @return {string[]} | ||
*/ | ||
function splitSelector(selector) { | ||
var indices = [], res = [], inParen = 0, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = selectorTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
switch(o[0]){ | ||
case '(': inParen++; break | ||
case ')': inParen--; break | ||
case ',': if (inParen) break; indices.push(o.index) | ||
} | ||
} | ||
for (o = indices.length; o--;){ | ||
res.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
res.unshift(selector) | ||
return res | ||
} | ||
// This is like the `selectorTokenizer`, but for the `&` operator | ||
var ampersandTokenizer = /&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
function ampersand (selector, parents) { | ||
var indices = [], split = [], res, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = ampersandTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
if (o[0] == '&') indices.push(o.index) | ||
} | ||
for (o = indices.length; o--;){ | ||
split.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
split.unshift(selector) | ||
res = [split[0]] | ||
for (o = 1; o < split.length; o++) { | ||
res = cartesian(res, cartesian(parents, [split[o]])) | ||
} | ||
return res.join(',') | ||
} | ||
function flatIter (f) { | ||
return function iter(arg) { | ||
if (type.call(arg) === ARRAY) for (var i= 0 ; i < arg.length; i ++) iter(arg[i]) | ||
else f(arg) | ||
} | ||
} | ||
function decamelize(match) { | ||
@@ -46,26 +90,20 @@ return '-' + match.toLowerCase() | ||
* | ||
* @param {array|object|string} o - the declarations. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built. | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current property or a prefix in case of nested | ||
* sub-properties. | ||
* @param {string} vendors - a list of vendor prefixes. | ||
* @Param {boolean} local - are we in @local or in @global scope. | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes. | ||
* @param {function} ns.e - @extend helper. | ||
* @param {function} ns.l - @local helper. | ||
* @param {array|object|string} o - the declarations. | ||
* @param {boolean} local - are we in @local or in @global scope. | ||
*/ | ||
function declarations(o, buf, prefix, vendors, local, ns, /*var*/ k, v, kk) { | ||
function declarations(parser, emit, prefix, o, local) { | ||
var k, v, kk | ||
if (o==null) return | ||
if (/\$/.test(prefix)) { | ||
for (kk in (prefix = prefix.split('$'))) if (own.call(prefix, kk)) { | ||
declarations(o, buf, prefix[kk], vendors, local, ns) | ||
} | ||
return | ||
} | ||
switch ( type.call(o = o.valueOf()) ) { | ||
case ARRAY: | ||
for (k = 0; k < o.length; k++) | ||
declarations(o[k], buf, prefix, vendors, local, ns) | ||
declarations(parser, emit, prefix, o[k], local) | ||
break | ||
@@ -79,6 +117,11 @@ case OBJECT: | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) | ||
declarations(v, buf, prefix + k[kk], vendors, local, ns) | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, prefix + k[kk], v, local) | ||
} | ||
} else { | ||
declarations(v, buf, prefix + k, vendors, local, ns) | ||
declarations(parser, emit, prefix + k, v, local) | ||
} | ||
@@ -90,115 +133,136 @@ } | ||
// at the top level. | ||
// `o` is then treated as a `property:value` pair. | ||
// otherwise, `prefix` is the property name, and | ||
// `o` is then treated as a `property:value` pair, or a | ||
// semi-colon-separated list thereof. | ||
// Otherwise, `prefix` is the property name, and | ||
// `o` is the value. | ||
// restore the dashes | ||
k = prefix.replace(/_/g, '-').replace(/[A-Z]/g, decamelize) | ||
if (local && (k == 'animation-name' || k == 'animation')) { | ||
if (local && (k == 'animation-name' || k == 'animation' || k == 'list-style')) { | ||
// no need to tokenize here a plain `.split(',')` has all bases covered. | ||
// We may 'parser' a comment, but it's not a big deal. | ||
o = o.split(',').map(function (o) { | ||
return o.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, ns.l) | ||
return o.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, parser.L) | ||
}).join(',') | ||
} | ||
if (/^animation|^transition/.test(k)) vendors = ['webkit'] | ||
// '@' in properties also triggers the *ielte7 hack | ||
// Since plugins dispatch on the /^@/ for at-rules | ||
// we swap the at for an asterisk | ||
// http://browserhacks.com/#hack-6d49e92634f26ae6d6e46b3ebc10019a | ||
k = k.replace(/^@/, '*') | ||
emit.d(k, o) | ||
/*/-statements-/*/ | ||
// vendorify | ||
for (kk = 0; kk < vendors.length; kk++) | ||
buf.push('-', vendors[kk], '-', k, k ? ':': '', o, ';\n') | ||
/*/-statements-/*/ | ||
buf.push(k, k ? ':': '', o, ';\n') | ||
} | ||
} | ||
var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g | ||
/** | ||
* Hanldes at-rules | ||
* | ||
* @param {string} k - The at-rule name, and, if takes both parameters and a | ||
* block, the parameters. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {string[]} v - Either parameters for block-less rules or their block | ||
* for the others. | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {array} k - The parsed at-rule, including the parameters, | ||
* if takes both parameters and a block. | ||
* @param {string} prefix - the current selector or the selector prefix | ||
* in case of nested rules | ||
* @param {string|string[]|object|object[]} v - Either parameters for | ||
* block-less rules or | ||
* their block | ||
* for the others. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){ | ||
var kk | ||
if (/^@(?:namespace|import|charset)$/.test(k)) { | ||
if(type.call(v) == ARRAY){ | ||
for (kk = 0; kk < v.length; kk++) { | ||
buf.push(k, ' ', v[kk], ';\n') | ||
} | ||
} else { | ||
buf.push(k, ' ', v, ';\n') | ||
function atRules(parser, emit, k, v, prefix, local, inAtRule) { | ||
for (var i = 0; i < parser.$a.length; i++) { | ||
if (parser.$a[i](parser, emit, k, v, prefix, local, inAtRule)) return | ||
} | ||
if (!k[3] && /^global$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 0, inAtRule) | ||
} else if (!k[3] && /^local$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 1, inAtRule) | ||
} else if (k[3] && /^adopt$/.test(k[2])) { | ||
if (!local || inAtRule) return emit.a('@-error-bad-at-adopt-placement' , JSON.stringify(k[0]), 0) | ||
if (!/^\.?[_A-Za-z][-\w]*$/.test(k[3])) return emit.a('@-error-bad-at-adopter', k[3], 0) | ||
i = [] | ||
flatIter(function(c, s){ | ||
s = c.toString() | ||
if(!/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(s)) emit.a('@-error-bad-at-adoptee', JSON.stringify(c), 0) | ||
else i.push(s.replace(/\./g, '')) | ||
})(v) | ||
// we may end up with duplicate classes but AFAIK it has no consequences on specificity. | ||
if (i.length) { | ||
// console.log("========== I ===========\n", i) | ||
parser.l(k[3] = k[3].replace(/\./g, '')) | ||
parser.n[k[3]] += (' ' + i.join(' ')) | ||
} | ||
} else if (/^@keyframes /.test(k)) { | ||
k = local ? k.replace( | ||
// generated by script/regexps.js | ||
/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, | ||
ns.l | ||
) : k | ||
// add a @-webkit-keyframes block too. | ||
buf.push('@-webkit-', k.slice(1), ' {\n') | ||
sheet(v, buf, '', '', ['webkit']) | ||
buf.push('}\n') | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, '', '', vendors, local, ns) | ||
buf.push('}\n') | ||
} else if (!k[3] && /^(?:namespace|import|charset)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
} else if (/^@extends?$/.test(k)) { | ||
emit.a(k[0], v) | ||
/*eslint-disable no-cond-assign*/ | ||
// pick the last class to be extended | ||
while (kk = findClass.exec(rawPrefix)) k = kk[4] | ||
/*eslint-enable no-cond-assign*/ | ||
if (k == null || !local) { | ||
// we're in a @global{} block | ||
buf.push('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
} else if (/^@extends?$/.test(k)) { | ||
// no class in the selector | ||
buf.push('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
})(v) | ||
} else if (!k[3] && /^(?:font-face|viewport)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
emit.a(k[1], '', 1) | ||
declarations(parser, emit, '', v, local) | ||
emit.A(k[1], '') | ||
})(v) | ||
} else if (k[3] && /^(?:media|supports|page|keyframes)$/.test(k[2])) { | ||
if (local && 'keyframes' == k[2]) { | ||
k[3] = k[3].replace( | ||
// generated by script/regexps.js | ||
/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, | ||
parser.L | ||
) | ||
} | ||
ns.e( | ||
type.call(v) == ARRAY ? v.map(function (parent) { | ||
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l) | ||
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l), | ||
k | ||
) | ||
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) { | ||
sheet(v, buf, k, k, emptyArray) | ||
} else if (/^@global$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 0, ns) | ||
emit.a(k[1], k[3], 1) | ||
} else if (/^@local$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 1, ns) | ||
if ('page' == k[2]) { | ||
} else if (/^@(?:media |supports |document )./.test(k)) { | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, prefix, rawPrefix, vendors, local, ns) | ||
buf.push('}\n') | ||
declarations(parser, emit, '', v, local) | ||
} else { | ||
sheet( | ||
parser, emit, | ||
'keyframes' == k[2] ? '' : prefix, | ||
v, local, 1 | ||
) | ||
} | ||
emit.A(k[1], k[3]) | ||
} else { | ||
buf.push('@-error-unsupported-at-rule ', JSON.stringify(k), ';\n') | ||
emit.a('@-error-unsupported-at-rule', JSON.stringify(k[0])) | ||
} | ||
@@ -208,172 +272,160 @@ } | ||
/** | ||
* Add rulesets and other CSS statements to the sheet. | ||
* Add rulesets and other CSS tree to the sheet. | ||
* | ||
* @param {array|string|object} statements - a source object or sub-object. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {array|string|object} tree - a source object or sub-object. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function sheet(statements, buf, prefix, rawPrefix, vendors, local, ns) { | ||
var k, kk, v, inDeclaration | ||
function sheet(parser, emit, prefix, tree, local, inAtRule) { | ||
var k, v, inDeclaration, kk | ||
switch (type.call(statements)) { | ||
switch (type.call(tree)) { | ||
case ARRAY: | ||
for (k = 0; k < statements.length; k++) | ||
sheet(statements[k], buf, prefix, rawPrefix, vendors, local, ns) | ||
break | ||
case OBJECT: | ||
for (k in tree) if (own.call(tree, k)) { | ||
v = tree[k] | ||
case OBJECT: | ||
for (k in statements) { | ||
v = statements[k] | ||
if (prefix && /^[-\w$]+$/.test(k)) { | ||
if (!inDeclaration) { | ||
inDeclaration = 1 | ||
buf.push(( prefix || '*' ), ' {\n') | ||
emit.s(prefix) | ||
} | ||
declarations(v, buf, k, vendors, local, ns) | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, k[kk], v, local) | ||
} | ||
} else { | ||
declarations(parser, emit, k, v, local) | ||
} | ||
} else if (/^@/.test(k)) { | ||
// Handle At-rules | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
inDeclaration = 0 | ||
at(k, v, buf, prefix, rawPrefix, vendors, local, ns) | ||
atRules(parser, emit, | ||
/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(k) || [k,'@','',''], | ||
v, prefix, local, inAtRule | ||
) | ||
} else { | ||
// selector or nested sub-selectors | ||
inDeclaration = 0 | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
sheet( | ||
parser, emit, | ||
// `prefix` ... Hefty. Ugly. Sadly necessary. | ||
// | ||
(prefix && (/,/.test(prefix) || /,/.test(k))) ? | ||
sheet(v, buf, | ||
(kk = /,/.test(prefix) || prefix && /,/.test(k)) ? | ||
cartesian(prefix.split(','), ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
).split(','), prefix).join(',') : | ||
concat(prefix, ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
), prefix), | ||
kk ? | ||
cartesian(rawPrefix.split(','), k.split(','), rawPrefix).join(',') : | ||
concat(rawPrefix, k, rawPrefix), | ||
vendors, | ||
local, ns | ||
/*0*/ (kk = splitSelector(prefix), splitSelector( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
).map(function (k) { | ||
return /&/.test(k) ? ampersand(k, kk) : kk.map(function(kk) { | ||
return kk + k | ||
}).join(',') | ||
}).join(',')) : | ||
/*0*/ /&/.test(k) ? | ||
/*1*/ ampersand( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k, | ||
[prefix] | ||
) : | ||
/*1*/ prefix + ( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
), | ||
v, local, inAtRule | ||
) | ||
} | ||
} | ||
if (inDeclaration) buf.push('}\n') | ||
break | ||
case ARRAY: | ||
for (k = 0; k < tree.length; k++){ | ||
sheet(parser, emit, prefix, tree[k], local, inAtRule) | ||
} | ||
break | ||
case STRING: | ||
buf.push( | ||
( prefix || ':-error-no-selector' ) , ' {\n' | ||
) | ||
declarations(statements, buf, '', vendors, local, ns) | ||
buf.push('}\n') | ||
} | ||
} | ||
// CSS hacks or ouptut of `j2c.inline`. | ||
var scope_root = '_j2c_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_'; | ||
var counter = 0; | ||
function j2c(res) { | ||
res = res || {} | ||
var extensions = [] | ||
emit.s(prefix || ':-error-no-selector') | ||
function finalize(buf, i) { | ||
for (i = 0; i< extensions.length; i++) buf = extensions[i](buf) || buf | ||
return buf.join('') | ||
declarations(parser, emit, '', tree, local) | ||
} | ||
} | ||
res.use = function() { | ||
var args = arguments | ||
for (var i = 0; i < args.length; i++){ | ||
extensions.push(args[i]) | ||
} | ||
return res | ||
} | ||
/*/-statements-/*/ | ||
res.sheet = function(ns, statements) { | ||
if (arguments.length === 1) { | ||
statements = ns; ns = {} | ||
} | ||
var | ||
suffix = scope_root + counter++, | ||
locals = {}, | ||
k, buf = [] | ||
// pick only non-numeric keys since `(NaN != NaN) === true` | ||
for (k in ns) if (k-0 != k-0 && own.call(ns, k)) { | ||
locals[k] = ns[k] | ||
} | ||
sheet( | ||
statements, buf, '', '', emptyArray /*vendors*/, | ||
1, // local | ||
{ | ||
e: function extend(parent, child) { | ||
var nameList = locals[child] | ||
locals[child] = | ||
nameList.slice(0, nameList.lastIndexOf(' ') + 1) + | ||
parent + ' ' + | ||
nameList.slice(nameList.lastIndexOf(' ') + 1) | ||
}, | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) { | ||
return space + global | ||
} | ||
if (!locals[name]) locals[name] = name + suffix | ||
return space + dot + locals[name].match(/\S+$/) | ||
} | ||
// This is the first entry in the filters array, which is | ||
// actually the last step of the compiler. It inserts | ||
// closing braces to close normal (non at-) rules (those | ||
// that start with a selector). Doing it earlier is | ||
// impossible without passing state around in unrelated code | ||
// or ending up with duplicated selectors when the source tree | ||
// contains arrays. | ||
// There's no `S` handler, because the core compiler never | ||
// calls it. | ||
function closeSelectors(next, inline) { | ||
var lastSelector | ||
return inline ? next : { | ||
i: function(){lastSelector = 0; next.i()}, | ||
x: function (raw) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
return next.x(raw) | ||
}, | ||
a: function (rule, param, takesBlock) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.a(rule, param, takesBlock) | ||
}, | ||
A: function (rule) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.A(rule) | ||
}, | ||
s: function (selector) { | ||
if (selector !== lastSelector){ | ||
if (lastSelector) next.S() | ||
next.s(selector) | ||
lastSelector = selector | ||
} | ||
) | ||
/*jshint -W053 */ | ||
buf = new String(finalize(buf)) | ||
/*jshint +W053 */ | ||
for (k in locals) if (own.call(locals, k)) buf[k] = locals[k] | ||
return buf | ||
}, | ||
d: next.d | ||
} | ||
/*/-statements-/*/ | ||
res.inline = function (locals, decl, buf) { | ||
if (arguments.length === 1) { | ||
decl = locals; locals = {} | ||
} | ||
declarations( | ||
decl, | ||
buf = [], | ||
'', // prefix | ||
emptyArray, // vendors | ||
1, | ||
{ | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) return space + global | ||
if (!locals[name]) return name | ||
return space + dot + locals[name] | ||
} | ||
}) | ||
return finalize(buf) | ||
} | ||
res.prefix = function(val, vendors) { | ||
return cartesian( | ||
vendors.map(function(p){return '-' + p + '-'}).concat(['']), | ||
[val] | ||
) | ||
} | ||
return res | ||
} | ||
j2c.global = function(x) { | ||
function global(x) { | ||
return ':global(' + x + ')' | ||
} | ||
j2c.kv = kv | ||
function kv (k, v, o) { | ||
@@ -385,16 +437,155 @@ o = {} | ||
j2c.at = function at (rule, params, block) { | ||
function at (rule, params, block) { | ||
if ( | ||
arguments.length < 3 | ||
) { | ||
// inner curry! | ||
var _at = at.bind.apply(at, [null].concat([].slice.call(arguments,0))) | ||
// So that it can be used as a key in an ES6 object literal. | ||
_at.toString = function(){return '@' + rule + ' ' + params} | ||
return _at | ||
} | ||
else return kv('@' + rule + ' ' + params, block) | ||
else return kv('@' + rule +' ' + params, block) | ||
} | ||
j2c(j2c) | ||
delete j2c.use | ||
function j2c() { | ||
var $filters = [closeSelectors] | ||
var $atHandlers = [] | ||
var instance = { | ||
at: at, | ||
global: global, | ||
kv: kv, | ||
names: {}, | ||
suffix: '__j2c-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36), | ||
use: function() { | ||
_use(emptyArray.slice.call(arguments)) | ||
return instance | ||
}, | ||
$plugins: [] | ||
} | ||
function _default(target, source) { | ||
for (var k in source) if (own.call(source, k) && k.indexOf('$')) { | ||
if (OBJECT == type.call(source[k]) && OBJECT == type.call(target[k])) _default(target[k], source[k]) | ||
else if (!(k in target)) target[k] = source[k] | ||
} | ||
} | ||
var buf | ||
var $sink = { | ||
i: function(){buf=[]}, | ||
x: function (raw) {return raw ? buf : buf.join('')}, | ||
a: function (rule, argument, takesBlock) { | ||
buf.push(rule, argument && ' ',argument, takesBlock ? ' {\n' : ';\n') | ||
}, | ||
A: function () {buf.push('}\n')}, | ||
s: function (selector) {buf.push(selector, ' {\n')}, | ||
S: function () {buf.push('}\n')}, | ||
d: function (prop, value) {buf.push(prop, prop && ':', value, ';\n')} | ||
} | ||
var streams = [] | ||
var parsers = [ | ||
{ | ||
$a: $atHandlers, | ||
a: atRules, | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names, | ||
s: sheet | ||
}, { | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names | ||
} | ||
] | ||
var _use = flatIter(function(plugin) { | ||
// `~n` is falsy for `n === -1` and truthy otherwise. | ||
// Works well to turn the result of `a.indexOf(x)` | ||
// into a value that reflects the presence of `x` in | ||
// `a`. | ||
if (~instance.$plugins.indexOf(plugin)) return | ||
instance.$plugins.push(plugin) | ||
if (type.call(plugin) === FUNCTION) plugin = plugin(instance) | ||
if (!plugin) return | ||
flatIter(function(filter) { | ||
$filters.push(filter) | ||
})(plugin.$filter || emptyArray) | ||
flatIter(function(handler) { | ||
$atHandlers.push(handler) | ||
})(plugin.$at || emptyArray) | ||
$sink = plugin.$sink || $sink | ||
_default(instance, plugin) | ||
}) | ||
function getStream(inline) { | ||
if (!streams.length) { | ||
for(var i = 0; i < 2; i++){ | ||
$filters[$filters.length - i] = function(_, inline) {return inline ? {i:$sink.i, d:$sink.d, x:$sink.x} : $sink} | ||
for (var j = $filters.length; j--;) streams[i] = $filters[j](streams[i], !!i, parsers[i]) | ||
} | ||
} | ||
var res = streams[inline] | ||
return res | ||
} | ||
function localize(name) { | ||
if (!instance.names[name]) instance.names[name] = name + instance.suffix | ||
return instance.names[name].match(/^\S+/) | ||
} | ||
function localizeReplacer(match, string, global, dot, name) { | ||
if (string || global) return string || global | ||
return dot + localize(name) | ||
} | ||
/*/-statements-/*/ | ||
instance.sheet = function(tree) { | ||
var emit = getStream(0) | ||
emit.i() | ||
sheet( | ||
parsers[0], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1, // local, by default | ||
0 // inAtRule | ||
) | ||
return emit.x() | ||
} | ||
/*/-statements-/*/ | ||
instance.inline = function (tree) { | ||
var emit = getStream(1) | ||
emit.i() | ||
declarations( | ||
parsers[1], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1 //local | ||
) | ||
return emit.x() | ||
} | ||
return instance | ||
} | ||
var _j2c = j2c() | ||
'sheet|inline|names|at|global|kv|suffix'.split('|').map(function(m){j2c[m] = _j2c[m]}) | ||
module.exports = j2c; |
@@ -1,1 +0,1 @@ | ||
"use strict";function cartesian(e,t,n,r,a,s){r=[];for(s in t)if(own.call(t,s))for(a in e)own.call(e,a)&&r.push(concat(e[a],t[s],n));return r}function concat(e,t,n){return n&&(/^[-\w$]+$/.test(t)&&":-error-bad-sub-selector-"+t||/&/.test(t)&&t.replace(/&/g,e))||e+t}function decamelize(e){return"-"+e.toLowerCase()}function declarations(e,t,n,r,a,s,l,o,c){if(null!=e)if(/\$/.test(n))for(c in n=n.split("$"))own.call(n,c)&&declarations(e,t,n[c],r,a,s);else switch(type.call(e=e.valueOf())){case ARRAY:for(l=0;l<e.length;l++)declarations(e[l],t,n,r,a,s);break;case OBJECT:n=n&&n+"-";for(l in e)if(own.call(e,l))if(o=e[l],/\$/.test(l))for(c in l=l.split("$"))own.call(l,c)&&declarations(o,t,n+l[c],r,a,s);else declarations(o,t,n+l,r,a,s);break;default:for(l=n.replace(/_/g,"-").replace(/[A-Z]/g,decamelize),!a||"animation-name"!=l&&"animation"!=l||(e=e.split(",").map(function(e){return e.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,s.l)}).join(",")),/^animation|^transition/.test(l)&&(r=["webkit"]),l=l.replace(/^@/,"*"),c=0;c<r.length;c++)t.push("-",r[c],"-",l,l?":":"",e,";\n");t.push(l,l?":":"",e,";\n")}}function at(e,t,n,r,a,s,l,o){var c;if(/^@(?:namespace|import|charset)$/.test(e))if(type.call(t)==ARRAY)for(c=0;c<t.length;c++)n.push(e," ",t[c],";\n");else n.push(e," ",t,";\n");else if(/^@keyframes /.test(e))e=l?e.replace(/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,o.l):e,n.push("@-webkit-",e.slice(1)," {\n"),sheet(t,n,"","",["webkit"]),n.push("}\n"),n.push(e," {\n"),sheet(t,n,"","",s,l,o),n.push("}\n");else if(/^@extends?$/.test(e)){for(;c=findClass.exec(a);)e=c[4];if(null==e||!l)return void n.push("@-error-cannot-extend-in-global-context ",JSON.stringify(a),";\n");if(/^@extends?$/.test(e))return void n.push("@-error-no-class-to-extend-in ",JSON.stringify(a),";\n");o.e(type.call(t)==ARRAY?t.map(function(e){return e.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,o.l)}).join(" "):t.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,o.l),e)}else/^@(?:font-face$|viewport$|page )/.test(e)?sheet(t,n,e,e,emptyArray):/^@global$/.test(e)?sheet(t,n,r,a,s,0,o):/^@local$/.test(e)?sheet(t,n,r,a,s,1,o):/^@(?:media |supports |document )./.test(e)?(n.push(e," {\n"),sheet(t,n,r,a,s,l,o),n.push("}\n")):n.push("@-error-unsupported-at-rule ",JSON.stringify(e),";\n")}function sheet(e,t,n,r,a,s,l){var o,c,i,u;switch(type.call(e)){case ARRAY:for(o=0;o<e.length;o++)sheet(e[o],t,n,r,a,s,l);break;case OBJECT:for(o in e)i=e[o],n&&/^[-\w$]+$/.test(o)?(u||(u=1,t.push(n||"*"," {\n")),declarations(i,t,o,a,s,l)):/^@/.test(o)?(u=u&&t.push("}\n")&&0,at(o,i,t,n,r,a,s,l)):(u=u&&t.push("}\n")&&0,sheet(i,t,(c=/,/.test(n)||n&&/,/.test(o))?cartesian(n.split(","),(s?o.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,l.l):o).split(","),n).join(","):concat(n,s?o.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,l.l):o,n),c?cartesian(r.split(","),o.split(","),r).join(","):concat(r,o,r),a,s,l));u&&t.push("}\n");break;case STRING:t.push(n||":-error-no-selector"," {\n"),declarations(e,t,"",a,s,l),t.push("}\n")}}function j2c(e){function t(e,t){for(t=0;t<n.length;t++)e=n[t](e)||e;return e.join("")}e=e||{};var n=[];return e.use=function(){for(var t=arguments,r=0;r<t.length;r++)n.push(t[r]);return e},e.sheet=function(e,n){1===arguments.length&&(n=e,e={});var r,a=scope_root+counter++,s={},l=[];for(r in e)r-0!=r-0&&own.call(e,r)&&(s[r]=e[r]);sheet(n,l,"","",emptyArray,1,{e:function(e,t){var n=s[t];s[t]=n.slice(0,n.lastIndexOf(" ")+1)+e+" "+n.slice(n.lastIndexOf(" ")+1)},l:function(e,t,n,r,l){return n?t+n:(s[l]||(s[l]=l+a),t+r+s[l].match(/\S+$/))}}),l=new String(t(l));for(r in s)own.call(s,r)&&(l[r]=s[r]);return l},e.inline=function(e,n,r){return 1===arguments.length&&(n=e,e={}),declarations(n,r=[],"",emptyArray,1,{l:function(t,n,r,a,s){return r?n+r:e[s]?n+a+e[s]:s}}),t(r)},e.prefix=function(e,t){return cartesian(t.map(function(e){return"-"+e+"-"}).concat([""]),[e])},e}function kv(e,t,n){return n={},n[e]=t,n}var emptyObject={},emptyArray=[],type=emptyObject.toString,own=emptyObject.hasOwnProperty,OBJECT=type.call(emptyObject),ARRAY=type.call(emptyArray),STRING=type.call(""),findClass=/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,scope_root="_j2c_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_",counter=0;j2c.global=function(e){return":global("+e+")"},j2c.kv=kv,j2c.at=function e(t,n,r){if(arguments.length<3){var a=e.bind.apply(e,[null].concat([].slice.call(arguments,0)));return a.toString=function(){return"@"+t+" "+n},a}return kv("@"+t+" "+n,r)},j2c(j2c),delete j2c.use,module.exports=j2c; | ||
"use strict";function cartesian(e,t){var n,a,r=[];for(a in t)if(own.call(t,a))for(n in e)own.call(e,n)&&r.push(e[n]+t[a]);return r}function splitSelector(e){for(var t,n=[],a=[],r=0;t=selectorTokenizer.exec(e);)switch(t[0]){case"(":r++;break;case")":r--;break;case",":if(r)break;n.push(t.index)}for(t=n.length;t--;)a.unshift(e.slice(n[t]+1)),e=e.slice(0,n[t]);return a.unshift(e),a}function ampersand(e,t){for(var n,a,r=[],i=[];a=ampersandTokenizer.exec(e);)"&"==a[0]&&r.push(a.index);for(a=r.length;a--;)i.unshift(e.slice(r[a]+1)),e=e.slice(0,r[a]);for(i.unshift(e),n=[i[0]],a=1;a<i.length;a++)n=cartesian(n,cartesian(t,[i[a]]));return n.join(",")}function flatIter(e){return function t(n){if(type.call(n)===ARRAY)for(var a=0;a<n.length;a++)t(n[a]);else e(n)}}function decamelize(e){return"-"+e.toLowerCase()}function declarations(e,t,n,a,r){var i,s,l;if(null!=a)switch(type.call(a=a.valueOf())){case ARRAY:for(i=0;i<a.length;i++)declarations(e,t,n,a[i],r);break;case OBJECT:n=n&&n+"-";for(i in a)if(own.call(a,i))if(s=a[i],/\$/.test(i))for(l in i=i.split("$"))own.call(i,l)&&declarations(e,t,n+i[l],s,r);else declarations(e,t,n+i,s,r);break;default:i=n.replace(/_/g,"-").replace(/[A-Z]/g,decamelize),!r||"animation-name"!=i&&"animation"!=i&&"list-style"!=i||(a=a.split(",").map(function(t){return t.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,e.L)}).join(",")),t.d(i,a)}}function atRules(e,t,n,a,r,i,s){for(var l=0;l<e.$a.length;l++)if(e.$a[l](e,t,n,a,r,i,s))return;if(!n[3]&&/^global$/.test(n[2]))sheet(e,t,r,a,0,s);else if(!n[3]&&/^local$/.test(n[2]))sheet(e,t,r,a,1,s);else if(n[3]&&/^adopt$/.test(n[2])){if(!i||s)return t.a("@-error-bad-at-adopt-placement",JSON.stringify(n[0]),0);if(!/^\.?[_A-Za-z][-\w]*$/.test(n[3]))return t.a("@-error-bad-at-adopter",n[3],0);l=[],flatIter(function(e,n){n=e.toString(),/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(n)?l.push(n.replace(/\./g,"")):t.a("@-error-bad-at-adoptee",JSON.stringify(e),0)})(a),l.length&&(e.l(n[3]=n[3].replace(/\./g,"")),e.n[n[3]]+=" "+l.join(" "))}else!n[3]&&/^(?:namespace|import|charset)$/.test(n[2])?flatIter(function(e){t.a(n[0],e)})(a):!n[3]&&/^(?:font-face|viewport)$/.test(n[2])?flatIter(function(a){t.a(n[1],"",1),declarations(e,t,"",a,i),t.A(n[1],"")})(a):n[3]&&/^(?:media|supports|page|keyframes)$/.test(n[2])?(i&&"keyframes"==n[2]&&(n[3]=n[3].replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,e.L)),t.a(n[1],n[3],1),"page"==n[2]?declarations(e,t,"",a,i):sheet(e,t,"keyframes"==n[2]?"":r,a,i,1),t.A(n[1],n[3])):t.a("@-error-unsupported-at-rule",JSON.stringify(n[0]))}function sheet(e,t,n,a,r,i){var s,l,o,c;switch(type.call(a)){case OBJECT:for(s in a)if(own.call(a,s))if(l=a[s],n&&/^[-\w$]+$/.test(s))if(o||(o=1,t.s(n)),/\$/.test(s))for(c in s=s.split("$"))own.call(s,c)&&declarations(e,t,s[c],l,r);else declarations(e,t,s,l,r);else/^@/.test(s)?(o=0,atRules(e,t,/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(s)||[s,"@","",""],l,n,r,i)):(o=0,sheet(e,t,n&&(/,/.test(n)||/,/.test(s))?(c=splitSelector(n),splitSelector(r?s.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,e.L):s).map(function(e){return/&/.test(e)?ampersand(e,c):c.map(function(t){return t+e}).join(",")}).join(",")):/&/.test(s)?ampersand(r?s.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,e.L):s,[n]):n+(r?s.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,e.L):s),l,r,i));break;case ARRAY:for(s=0;s<a.length;s++)sheet(e,t,n,a[s],r,i);break;case STRING:t.s(n||":-error-no-selector"),declarations(e,t,"",a,r)}}function closeSelectors(e,t){var n;return t?e:{i:function(){n=0,e.i()},x:function(t){return n&&(e.S(),n=0),e.x(t)},a:function(t,a,r){n&&(e.S(),n=0),e.a(t,a,r)},A:function(t){n&&(e.S(),n=0),e.A(t)},s:function(t){t!==n&&(n&&e.S(),e.s(t),n=t)},d:e.d}}function global(e){return":global("+e+")"}function kv(e,t,n){return n={},n[e]=t,n}function at(e,t,n){if(arguments.length<3){var a=at.bind.apply(at,[null].concat([].slice.call(arguments,0)));return a.toString=function(){return"@"+e+" "+t},a}return kv("@"+e+" "+t,n)}function j2c(){function e(t,n){for(var a in n)own.call(n,a)&&a.indexOf("$")&&(OBJECT==type.call(n[a])&&OBJECT==type.call(t[a])?e(t[a],n[a]):a in t||(t[a]=n[a]))}function t(e){if(!c.length)for(var t=0;2>t;t++){i[i.length-t]=function(e,t){return t?{i:o.i,d:o.d,x:o.x}:o};for(var n=i.length;n--;)c[t]=i[n](c[t],!!t,f[t])}var a=c[e];return a}function n(e){return l.names[e]||(l.names[e]=e+l.suffix),l.names[e].match(/^\S+/)}function a(e,t,a,r,i){return t||a?t||a:r+n(i)}var r,i=[closeSelectors],s=[],l={at:at,global:global,kv:kv,names:{},suffix:"__j2c-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36),use:function(){return u(emptyArray.slice.call(arguments)),l},$plugins:[]},o={i:function(){r=[]},x:function(e){return e?r:r.join("")},a:function(e,t,n){r.push(e,t&&" ",t,n?" {\n":";\n")},A:function(){r.push("}\n")},s:function(e){r.push(e," {\n")},S:function(){r.push("}\n")},d:function(e,t){r.push(e,e&&":",t,";\n")}},c=[],f=[{$a:s,a:atRules,d:declarations,L:a,l:n,n:l.names,s:sheet},{d:declarations,L:a,l:n,n:l.names}],u=flatIter(function(t){~l.$plugins.indexOf(t)||(l.$plugins.push(t),type.call(t)===FUNCTION&&(t=t(l)),t&&(flatIter(function(e){i.push(e)})(t.$filter||emptyArray),flatIter(function(e){s.push(e)})(t.$at||emptyArray),o=t.$sink||o,e(l,t)))});return l.sheet=function(e){var n=t(0);return n.i(),sheet(f[0],n,"",e,1,0),n.x()},l.inline=function(e){var n=t(1);return n.i(),declarations(f[1],n,"",e,1),n.x()},l}var emptyArray=[],emptyObject={},type=emptyObject.toString,ARRAY=type.call(emptyArray),OBJECT=type.call(emptyObject),STRING=type.call(""),FUNCTION=type.call(type),own=emptyObject.hasOwnProperty,selectorTokenizer=/[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,ampersandTokenizer=/&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,_j2c=j2c();"sheet|inline|names|at|global|kv|suffix".split("|").map(function(e){j2c[e]=_j2c[e]}),module.exports=j2c; |
@@ -0,37 +1,81 @@ | ||
var emptyArray = []; | ||
var emptyObject = {}; | ||
var emptyArray = []; | ||
var type = emptyObject.toString; | ||
var own = emptyObject.hasOwnProperty; | ||
var ARRAY = type.call(emptyArray); | ||
var OBJECT = type.call(emptyObject); | ||
var ARRAY = type.call(emptyArray); | ||
var STRING = type.call(''); | ||
/*/-inline-/*/ | ||
// function cartesian(a, b, res, i, j) { | ||
// res = []; | ||
// for (j in b) if (own.call(b, j)) | ||
// for (i in a) if (own.call(a, i)) | ||
// res.push(a[i] + b[j]); | ||
// return res; | ||
// } | ||
/*/-inline-/*/ | ||
/* /-statements-/*/ | ||
function cartesian(a,b, selectorP, res, i, j) { | ||
res = [] | ||
var FUNCTION = type.call(type); | ||
var own = emptyObject.hasOwnProperty; | ||
function cartesian(a,b) { | ||
var res = [], i, j | ||
for (j in b) if(own.call(b, j)) | ||
for (i in a) if(own.call(a, i)) | ||
res.push(concat(a[i], b[j], selectorP)) | ||
res.push(a[i] + b[j]) | ||
return res | ||
} | ||
function concat(a, b, selectorP) { | ||
// `b.replace(/&/g, a)` is never falsy, since the | ||
// 'a' of cartesian can't be the empty string | ||
// in selector mode. | ||
return selectorP && ( | ||
/^[-\w$]+$/.test(b) && ':-error-bad-sub-selector-' + b || | ||
/&/.test(b) && /* never falsy */ b.replace(/&/g, a) | ||
) || a + b | ||
// "Tokenizes" the selectors into parts relevant for the next function. | ||
// Strings and comments are matched, but ignored afterwards. | ||
// This is not a full tokenizers. It only recognizes comas, parentheses, | ||
// strings and comments. | ||
// regexp generated by scripts/regexps.js then trimmed by hand | ||
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
/** | ||
* This will split a coma-separated selector list into individual selectors, | ||
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists). | ||
* @param {string} selector | ||
* @return {string[]} | ||
*/ | ||
function splitSelector(selector) { | ||
var indices = [], res = [], inParen = 0, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = selectorTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
switch(o[0]){ | ||
case '(': inParen++; break | ||
case ')': inParen--; break | ||
case ',': if (inParen) break; indices.push(o.index) | ||
} | ||
} | ||
for (o = indices.length; o--;){ | ||
res.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
res.unshift(selector) | ||
return res | ||
} | ||
// This is like the `selectorTokenizer`, but for the `&` operator | ||
var ampersandTokenizer = /&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
function ampersand (selector, parents) { | ||
var indices = [], split = [], res, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = ampersandTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
if (o[0] == '&') indices.push(o.index) | ||
} | ||
for (o = indices.length; o--;){ | ||
split.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
split.unshift(selector) | ||
res = [split[0]] | ||
for (o = 1; o < split.length; o++) { | ||
res = cartesian(res, cartesian(parents, [split[o]])) | ||
} | ||
return res.join(',') | ||
} | ||
function flatIter (f) { | ||
return function iter(arg) { | ||
if (type.call(arg) === ARRAY) for (var i= 0 ; i < arg.length; i ++) iter(arg[i]) | ||
else f(arg) | ||
} | ||
} | ||
function decamelize(match) { | ||
@@ -44,26 +88,20 @@ return '-' + match.toLowerCase() | ||
* | ||
* @param {array|object|string} o - the declarations. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built. | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current property or a prefix in case of nested | ||
* sub-properties. | ||
* @param {string} vendors - a list of vendor prefixes. | ||
* @Param {boolean} local - are we in @local or in @global scope. | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes. | ||
* @param {function} ns.e - @extend helper. | ||
* @param {function} ns.l - @local helper. | ||
* @param {array|object|string} o - the declarations. | ||
* @param {boolean} local - are we in @local or in @global scope. | ||
*/ | ||
function declarations(o, buf, prefix, vendors, local, ns, /*var*/ k, v, kk) { | ||
function declarations(parser, emit, prefix, o, local) { | ||
var k, v, kk | ||
if (o==null) return | ||
if (/\$/.test(prefix)) { | ||
for (kk in (prefix = prefix.split('$'))) if (own.call(prefix, kk)) { | ||
declarations(o, buf, prefix[kk], vendors, local, ns) | ||
} | ||
return | ||
} | ||
switch ( type.call(o = o.valueOf()) ) { | ||
case ARRAY: | ||
for (k = 0; k < o.length; k++) | ||
declarations(o[k], buf, prefix, vendors, local, ns) | ||
declarations(parser, emit, prefix, o[k], local) | ||
break | ||
@@ -77,6 +115,11 @@ case OBJECT: | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) | ||
declarations(v, buf, prefix + k[kk], vendors, local, ns) | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, prefix + k[kk], v, local) | ||
} | ||
} else { | ||
declarations(v, buf, prefix + k, vendors, local, ns) | ||
declarations(parser, emit, prefix + k, v, local) | ||
} | ||
@@ -88,115 +131,136 @@ } | ||
// at the top level. | ||
// `o` is then treated as a `property:value` pair. | ||
// otherwise, `prefix` is the property name, and | ||
// `o` is then treated as a `property:value` pair, or a | ||
// semi-colon-separated list thereof. | ||
// Otherwise, `prefix` is the property name, and | ||
// `o` is the value. | ||
// restore the dashes | ||
k = prefix.replace(/_/g, '-').replace(/[A-Z]/g, decamelize) | ||
if (local && (k == 'animation-name' || k == 'animation')) { | ||
if (local && (k == 'animation-name' || k == 'animation' || k == 'list-style')) { | ||
// no need to tokenize here a plain `.split(',')` has all bases covered. | ||
// We may 'parser' a comment, but it's not a big deal. | ||
o = o.split(',').map(function (o) { | ||
return o.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, ns.l) | ||
return o.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, parser.L) | ||
}).join(',') | ||
} | ||
if (/^animation|^transition/.test(k)) vendors = ['webkit'] | ||
// '@' in properties also triggers the *ielte7 hack | ||
// Since plugins dispatch on the /^@/ for at-rules | ||
// we swap the at for an asterisk | ||
// http://browserhacks.com/#hack-6d49e92634f26ae6d6e46b3ebc10019a | ||
k = k.replace(/^@/, '*') | ||
emit.d(k, o) | ||
/*/-statements-/*/ | ||
// vendorify | ||
for (kk = 0; kk < vendors.length; kk++) | ||
buf.push('-', vendors[kk], '-', k, k ? ':': '', o, ';\n') | ||
/*/-statements-/*/ | ||
buf.push(k, k ? ':': '', o, ';\n') | ||
} | ||
} | ||
var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g | ||
/** | ||
* Hanldes at-rules | ||
* | ||
* @param {string} k - The at-rule name, and, if takes both parameters and a | ||
* block, the parameters. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {string[]} v - Either parameters for block-less rules or their block | ||
* for the others. | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {array} k - The parsed at-rule, including the parameters, | ||
* if takes both parameters and a block. | ||
* @param {string} prefix - the current selector or the selector prefix | ||
* in case of nested rules | ||
* @param {string|string[]|object|object[]} v - Either parameters for | ||
* block-less rules or | ||
* their block | ||
* for the others. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){ | ||
var kk | ||
if (/^@(?:namespace|import|charset)$/.test(k)) { | ||
if(type.call(v) == ARRAY){ | ||
for (kk = 0; kk < v.length; kk++) { | ||
buf.push(k, ' ', v[kk], ';\n') | ||
} | ||
} else { | ||
buf.push(k, ' ', v, ';\n') | ||
function atRules(parser, emit, k, v, prefix, local, inAtRule) { | ||
for (var i = 0; i < parser.$a.length; i++) { | ||
if (parser.$a[i](parser, emit, k, v, prefix, local, inAtRule)) return | ||
} | ||
if (!k[3] && /^global$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 0, inAtRule) | ||
} else if (!k[3] && /^local$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 1, inAtRule) | ||
} else if (k[3] && /^adopt$/.test(k[2])) { | ||
if (!local || inAtRule) return emit.a('@-error-bad-at-adopt-placement' , JSON.stringify(k[0]), 0) | ||
if (!/^\.?[_A-Za-z][-\w]*$/.test(k[3])) return emit.a('@-error-bad-at-adopter', k[3], 0) | ||
i = [] | ||
flatIter(function(c, s){ | ||
s = c.toString() | ||
if(!/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(s)) emit.a('@-error-bad-at-adoptee', JSON.stringify(c), 0) | ||
else i.push(s.replace(/\./g, '')) | ||
})(v) | ||
// we may end up with duplicate classes but AFAIK it has no consequences on specificity. | ||
if (i.length) { | ||
// console.log("========== I ===========\n", i) | ||
parser.l(k[3] = k[3].replace(/\./g, '')) | ||
parser.n[k[3]] += (' ' + i.join(' ')) | ||
} | ||
} else if (/^@keyframes /.test(k)) { | ||
k = local ? k.replace( | ||
// generated by script/regexps.js | ||
/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, | ||
ns.l | ||
) : k | ||
// add a @-webkit-keyframes block too. | ||
buf.push('@-webkit-', k.slice(1), ' {\n') | ||
sheet(v, buf, '', '', ['webkit']) | ||
buf.push('}\n') | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, '', '', vendors, local, ns) | ||
buf.push('}\n') | ||
} else if (!k[3] && /^(?:namespace|import|charset)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
} else if (/^@extends?$/.test(k)) { | ||
emit.a(k[0], v) | ||
/*eslint-disable no-cond-assign*/ | ||
// pick the last class to be extended | ||
while (kk = findClass.exec(rawPrefix)) k = kk[4] | ||
/*eslint-enable no-cond-assign*/ | ||
if (k == null || !local) { | ||
// we're in a @global{} block | ||
buf.push('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
} else if (/^@extends?$/.test(k)) { | ||
// no class in the selector | ||
buf.push('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
})(v) | ||
} else if (!k[3] && /^(?:font-face|viewport)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
emit.a(k[1], '', 1) | ||
declarations(parser, emit, '', v, local) | ||
emit.A(k[1], '') | ||
})(v) | ||
} else if (k[3] && /^(?:media|supports|page|keyframes)$/.test(k[2])) { | ||
if (local && 'keyframes' == k[2]) { | ||
k[3] = k[3].replace( | ||
// generated by script/regexps.js | ||
/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, | ||
parser.L | ||
) | ||
} | ||
ns.e( | ||
type.call(v) == ARRAY ? v.map(function (parent) { | ||
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l) | ||
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l), | ||
k | ||
) | ||
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) { | ||
sheet(v, buf, k, k, emptyArray) | ||
} else if (/^@global$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 0, ns) | ||
emit.a(k[1], k[3], 1) | ||
} else if (/^@local$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 1, ns) | ||
if ('page' == k[2]) { | ||
} else if (/^@(?:media |supports |document )./.test(k)) { | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, prefix, rawPrefix, vendors, local, ns) | ||
buf.push('}\n') | ||
declarations(parser, emit, '', v, local) | ||
} else { | ||
sheet( | ||
parser, emit, | ||
'keyframes' == k[2] ? '' : prefix, | ||
v, local, 1 | ||
) | ||
} | ||
emit.A(k[1], k[3]) | ||
} else { | ||
buf.push('@-error-unsupported-at-rule ', JSON.stringify(k), ';\n') | ||
emit.a('@-error-unsupported-at-rule', JSON.stringify(k[0])) | ||
} | ||
@@ -206,172 +270,160 @@ } | ||
/** | ||
* Add rulesets and other CSS statements to the sheet. | ||
* Add rulesets and other CSS tree to the sheet. | ||
* | ||
* @param {array|string|object} statements - a source object or sub-object. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {array|string|object} tree - a source object or sub-object. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function sheet(statements, buf, prefix, rawPrefix, vendors, local, ns) { | ||
var k, kk, v, inDeclaration | ||
function sheet(parser, emit, prefix, tree, local, inAtRule) { | ||
var k, v, inDeclaration, kk | ||
switch (type.call(statements)) { | ||
switch (type.call(tree)) { | ||
case ARRAY: | ||
for (k = 0; k < statements.length; k++) | ||
sheet(statements[k], buf, prefix, rawPrefix, vendors, local, ns) | ||
break | ||
case OBJECT: | ||
for (k in tree) if (own.call(tree, k)) { | ||
v = tree[k] | ||
case OBJECT: | ||
for (k in statements) { | ||
v = statements[k] | ||
if (prefix && /^[-\w$]+$/.test(k)) { | ||
if (!inDeclaration) { | ||
inDeclaration = 1 | ||
buf.push(( prefix || '*' ), ' {\n') | ||
emit.s(prefix) | ||
} | ||
declarations(v, buf, k, vendors, local, ns) | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, k[kk], v, local) | ||
} | ||
} else { | ||
declarations(parser, emit, k, v, local) | ||
} | ||
} else if (/^@/.test(k)) { | ||
// Handle At-rules | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
inDeclaration = 0 | ||
at(k, v, buf, prefix, rawPrefix, vendors, local, ns) | ||
atRules(parser, emit, | ||
/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(k) || [k,'@','',''], | ||
v, prefix, local, inAtRule | ||
) | ||
} else { | ||
// selector or nested sub-selectors | ||
inDeclaration = 0 | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
sheet( | ||
parser, emit, | ||
// `prefix` ... Hefty. Ugly. Sadly necessary. | ||
// | ||
(prefix && (/,/.test(prefix) || /,/.test(k))) ? | ||
sheet(v, buf, | ||
(kk = /,/.test(prefix) || prefix && /,/.test(k)) ? | ||
cartesian(prefix.split(','), ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
).split(','), prefix).join(',') : | ||
concat(prefix, ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
), prefix), | ||
kk ? | ||
cartesian(rawPrefix.split(','), k.split(','), rawPrefix).join(',') : | ||
concat(rawPrefix, k, rawPrefix), | ||
vendors, | ||
local, ns | ||
/*0*/ (kk = splitSelector(prefix), splitSelector( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
).map(function (k) { | ||
return /&/.test(k) ? ampersand(k, kk) : kk.map(function(kk) { | ||
return kk + k | ||
}).join(',') | ||
}).join(',')) : | ||
/*0*/ /&/.test(k) ? | ||
/*1*/ ampersand( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k, | ||
[prefix] | ||
) : | ||
/*1*/ prefix + ( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
), | ||
v, local, inAtRule | ||
) | ||
} | ||
} | ||
if (inDeclaration) buf.push('}\n') | ||
break | ||
case ARRAY: | ||
for (k = 0; k < tree.length; k++){ | ||
sheet(parser, emit, prefix, tree[k], local, inAtRule) | ||
} | ||
break | ||
case STRING: | ||
buf.push( | ||
( prefix || ':-error-no-selector' ) , ' {\n' | ||
) | ||
declarations(statements, buf, '', vendors, local, ns) | ||
buf.push('}\n') | ||
} | ||
} | ||
// CSS hacks or ouptut of `j2c.inline`. | ||
var scope_root = '_j2c_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_'; | ||
var counter = 0; | ||
function j2c(res) { | ||
res = res || {} | ||
var extensions = [] | ||
emit.s(prefix || ':-error-no-selector') | ||
function finalize(buf, i) { | ||
for (i = 0; i< extensions.length; i++) buf = extensions[i](buf) || buf | ||
return buf.join('') | ||
declarations(parser, emit, '', tree, local) | ||
} | ||
} | ||
res.use = function() { | ||
var args = arguments | ||
for (var i = 0; i < args.length; i++){ | ||
extensions.push(args[i]) | ||
} | ||
return res | ||
} | ||
/*/-statements-/*/ | ||
res.sheet = function(ns, statements) { | ||
if (arguments.length === 1) { | ||
statements = ns; ns = {} | ||
} | ||
var | ||
suffix = scope_root + counter++, | ||
locals = {}, | ||
k, buf = [] | ||
// pick only non-numeric keys since `(NaN != NaN) === true` | ||
for (k in ns) if (k-0 != k-0 && own.call(ns, k)) { | ||
locals[k] = ns[k] | ||
} | ||
sheet( | ||
statements, buf, '', '', emptyArray /*vendors*/, | ||
1, // local | ||
{ | ||
e: function extend(parent, child) { | ||
var nameList = locals[child] | ||
locals[child] = | ||
nameList.slice(0, nameList.lastIndexOf(' ') + 1) + | ||
parent + ' ' + | ||
nameList.slice(nameList.lastIndexOf(' ') + 1) | ||
}, | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) { | ||
return space + global | ||
} | ||
if (!locals[name]) locals[name] = name + suffix | ||
return space + dot + locals[name].match(/\S+$/) | ||
} | ||
// This is the first entry in the filters array, which is | ||
// actually the last step of the compiler. It inserts | ||
// closing braces to close normal (non at-) rules (those | ||
// that start with a selector). Doing it earlier is | ||
// impossible without passing state around in unrelated code | ||
// or ending up with duplicated selectors when the source tree | ||
// contains arrays. | ||
// There's no `S` handler, because the core compiler never | ||
// calls it. | ||
function closeSelectors(next, inline) { | ||
var lastSelector | ||
return inline ? next : { | ||
i: function(){lastSelector = 0; next.i()}, | ||
x: function (raw) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
return next.x(raw) | ||
}, | ||
a: function (rule, param, takesBlock) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.a(rule, param, takesBlock) | ||
}, | ||
A: function (rule) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.A(rule) | ||
}, | ||
s: function (selector) { | ||
if (selector !== lastSelector){ | ||
if (lastSelector) next.S() | ||
next.s(selector) | ||
lastSelector = selector | ||
} | ||
) | ||
/*jshint -W053 */ | ||
buf = new String(finalize(buf)) | ||
/*jshint +W053 */ | ||
for (k in locals) if (own.call(locals, k)) buf[k] = locals[k] | ||
return buf | ||
}, | ||
d: next.d | ||
} | ||
/*/-statements-/*/ | ||
res.inline = function (locals, decl, buf) { | ||
if (arguments.length === 1) { | ||
decl = locals; locals = {} | ||
} | ||
declarations( | ||
decl, | ||
buf = [], | ||
'', // prefix | ||
emptyArray, // vendors | ||
1, | ||
{ | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) return space + global | ||
if (!locals[name]) return name | ||
return space + dot + locals[name] | ||
} | ||
}) | ||
return finalize(buf) | ||
} | ||
res.prefix = function(val, vendors) { | ||
return cartesian( | ||
vendors.map(function(p){return '-' + p + '-'}).concat(['']), | ||
[val] | ||
) | ||
} | ||
return res | ||
} | ||
j2c.global = function(x) { | ||
function global(x) { | ||
return ':global(' + x + ')' | ||
} | ||
j2c.kv = kv | ||
function kv (k, v, o) { | ||
@@ -383,16 +435,155 @@ o = {} | ||
j2c.at = function at (rule, params, block) { | ||
function at (rule, params, block) { | ||
if ( | ||
arguments.length < 3 | ||
) { | ||
// inner curry! | ||
var _at = at.bind.apply(at, [null].concat([].slice.call(arguments,0))) | ||
// So that it can be used as a key in an ES6 object literal. | ||
_at.toString = function(){return '@' + rule + ' ' + params} | ||
return _at | ||
} | ||
else return kv('@' + rule + ' ' + params, block) | ||
else return kv('@' + rule +' ' + params, block) | ||
} | ||
j2c(j2c) | ||
delete j2c.use | ||
function j2c() { | ||
var $filters = [closeSelectors] | ||
var $atHandlers = [] | ||
var instance = { | ||
at: at, | ||
global: global, | ||
kv: kv, | ||
names: {}, | ||
suffix: '__j2c-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36), | ||
use: function() { | ||
_use(emptyArray.slice.call(arguments)) | ||
return instance | ||
}, | ||
$plugins: [] | ||
} | ||
function _default(target, source) { | ||
for (var k in source) if (own.call(source, k) && k.indexOf('$')) { | ||
if (OBJECT == type.call(source[k]) && OBJECT == type.call(target[k])) _default(target[k], source[k]) | ||
else if (!(k in target)) target[k] = source[k] | ||
} | ||
} | ||
var buf | ||
var $sink = { | ||
i: function(){buf=[]}, | ||
x: function (raw) {return raw ? buf : buf.join('')}, | ||
a: function (rule, argument, takesBlock) { | ||
buf.push(rule, argument && ' ',argument, takesBlock ? ' {\n' : ';\n') | ||
}, | ||
A: function () {buf.push('}\n')}, | ||
s: function (selector) {buf.push(selector, ' {\n')}, | ||
S: function () {buf.push('}\n')}, | ||
d: function (prop, value) {buf.push(prop, prop && ':', value, ';\n')} | ||
} | ||
var streams = [] | ||
var parsers = [ | ||
{ | ||
$a: $atHandlers, | ||
a: atRules, | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names, | ||
s: sheet | ||
}, { | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names | ||
} | ||
] | ||
var _use = flatIter(function(plugin) { | ||
// `~n` is falsy for `n === -1` and truthy otherwise. | ||
// Works well to turn the result of `a.indexOf(x)` | ||
// into a value that reflects the presence of `x` in | ||
// `a`. | ||
if (~instance.$plugins.indexOf(plugin)) return | ||
instance.$plugins.push(plugin) | ||
if (type.call(plugin) === FUNCTION) plugin = plugin(instance) | ||
if (!plugin) return | ||
flatIter(function(filter) { | ||
$filters.push(filter) | ||
})(plugin.$filter || emptyArray) | ||
flatIter(function(handler) { | ||
$atHandlers.push(handler) | ||
})(plugin.$at || emptyArray) | ||
$sink = plugin.$sink || $sink | ||
_default(instance, plugin) | ||
}) | ||
function getStream(inline) { | ||
if (!streams.length) { | ||
for(var i = 0; i < 2; i++){ | ||
$filters[$filters.length - i] = function(_, inline) {return inline ? {i:$sink.i, d:$sink.d, x:$sink.x} : $sink} | ||
for (var j = $filters.length; j--;) streams[i] = $filters[j](streams[i], !!i, parsers[i]) | ||
} | ||
} | ||
var res = streams[inline] | ||
return res | ||
} | ||
function localize(name) { | ||
if (!instance.names[name]) instance.names[name] = name + instance.suffix | ||
return instance.names[name].match(/^\S+/) | ||
} | ||
function localizeReplacer(match, string, global, dot, name) { | ||
if (string || global) return string || global | ||
return dot + localize(name) | ||
} | ||
/*/-statements-/*/ | ||
instance.sheet = function(tree) { | ||
var emit = getStream(0) | ||
emit.i() | ||
sheet( | ||
parsers[0], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1, // local, by default | ||
0 // inAtRule | ||
) | ||
return emit.x() | ||
} | ||
/*/-statements-/*/ | ||
instance.inline = function (tree) { | ||
var emit = getStream(1) | ||
emit.i() | ||
declarations( | ||
parsers[1], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1 //local | ||
) | ||
return emit.x() | ||
} | ||
return instance | ||
} | ||
var _j2c = j2c() | ||
'sheet|inline|names|at|global|kv|suffix'.split('|').map(function(m){j2c[m] = _j2c[m]}) | ||
export default j2c; |
@@ -1,39 +0,84 @@ | ||
var j2c = (function () { 'use strict'; | ||
var j2c = (function () { | ||
'use strict'; | ||
var emptyArray = []; | ||
var emptyObject = {}; | ||
var emptyArray = []; | ||
var type = emptyObject.toString; | ||
var own = emptyObject.hasOwnProperty; | ||
var ARRAY = type.call(emptyArray); | ||
var OBJECT = type.call(emptyObject); | ||
var ARRAY = type.call(emptyArray); | ||
var STRING = type.call(''); | ||
/*/-inline-/*/ | ||
// function cartesian(a, b, res, i, j) { | ||
// res = []; | ||
// for (j in b) if (own.call(b, j)) | ||
// for (i in a) if (own.call(a, i)) | ||
// res.push(a[i] + b[j]); | ||
// return res; | ||
// } | ||
/*/-inline-/*/ | ||
/* /-statements-/*/ | ||
function cartesian(a,b, selectorP, res, i, j) { | ||
res = [] | ||
var FUNCTION = type.call(type); | ||
var own = emptyObject.hasOwnProperty; | ||
function cartesian(a,b) { | ||
var res = [], i, j | ||
for (j in b) if(own.call(b, j)) | ||
for (i in a) if(own.call(a, i)) | ||
res.push(concat(a[i], b[j], selectorP)) | ||
res.push(a[i] + b[j]) | ||
return res | ||
} | ||
function concat(a, b, selectorP) { | ||
// `b.replace(/&/g, a)` is never falsy, since the | ||
// 'a' of cartesian can't be the empty string | ||
// in selector mode. | ||
return selectorP && ( | ||
/^[-\w$]+$/.test(b) && ':-error-bad-sub-selector-' + b || | ||
/&/.test(b) && /* never falsy */ b.replace(/&/g, a) | ||
) || a + b | ||
// "Tokenizes" the selectors into parts relevant for the next function. | ||
// Strings and comments are matched, but ignored afterwards. | ||
// This is not a full tokenizers. It only recognizes comas, parentheses, | ||
// strings and comments. | ||
// regexp generated by scripts/regexps.js then trimmed by hand | ||
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
/** | ||
* This will split a coma-separated selector list into individual selectors, | ||
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists). | ||
* @param {string} selector | ||
* @return {string[]} | ||
*/ | ||
function splitSelector(selector) { | ||
var indices = [], res = [], inParen = 0, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = selectorTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
switch(o[0]){ | ||
case '(': inParen++; break | ||
case ')': inParen--; break | ||
case ',': if (inParen) break; indices.push(o.index) | ||
} | ||
} | ||
for (o = indices.length; o--;){ | ||
res.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
res.unshift(selector) | ||
return res | ||
} | ||
// This is like the `selectorTokenizer`, but for the `&` operator | ||
var ampersandTokenizer = /&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
function ampersand (selector, parents) { | ||
var indices = [], split = [], res, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = ampersandTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
if (o[0] == '&') indices.push(o.index) | ||
} | ||
for (o = indices.length; o--;){ | ||
split.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
split.unshift(selector) | ||
res = [split[0]] | ||
for (o = 1; o < split.length; o++) { | ||
res = cartesian(res, cartesian(parents, [split[o]])) | ||
} | ||
return res.join(',') | ||
} | ||
function flatIter (f) { | ||
return function iter(arg) { | ||
if (type.call(arg) === ARRAY) for (var i= 0 ; i < arg.length; i ++) iter(arg[i]) | ||
else f(arg) | ||
} | ||
} | ||
function decamelize(match) { | ||
@@ -46,26 +91,20 @@ return '-' + match.toLowerCase() | ||
* | ||
* @param {array|object|string} o - the declarations. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built. | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current property or a prefix in case of nested | ||
* sub-properties. | ||
* @param {string} vendors - a list of vendor prefixes. | ||
* @Param {boolean} local - are we in @local or in @global scope. | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes. | ||
* @param {function} ns.e - @extend helper. | ||
* @param {function} ns.l - @local helper. | ||
* @param {array|object|string} o - the declarations. | ||
* @param {boolean} local - are we in @local or in @global scope. | ||
*/ | ||
function declarations(o, buf, prefix, vendors, local, ns, /*var*/ k, v, kk) { | ||
function declarations(parser, emit, prefix, o, local) { | ||
var k, v, kk | ||
if (o==null) return | ||
if (/\$/.test(prefix)) { | ||
for (kk in (prefix = prefix.split('$'))) if (own.call(prefix, kk)) { | ||
declarations(o, buf, prefix[kk], vendors, local, ns) | ||
} | ||
return | ||
} | ||
switch ( type.call(o = o.valueOf()) ) { | ||
case ARRAY: | ||
for (k = 0; k < o.length; k++) | ||
declarations(o[k], buf, prefix, vendors, local, ns) | ||
declarations(parser, emit, prefix, o[k], local) | ||
break | ||
@@ -79,6 +118,11 @@ case OBJECT: | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) | ||
declarations(v, buf, prefix + k[kk], vendors, local, ns) | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, prefix + k[kk], v, local) | ||
} | ||
} else { | ||
declarations(v, buf, prefix + k, vendors, local, ns) | ||
declarations(parser, emit, prefix + k, v, local) | ||
} | ||
@@ -90,115 +134,136 @@ } | ||
// at the top level. | ||
// `o` is then treated as a `property:value` pair. | ||
// otherwise, `prefix` is the property name, and | ||
// `o` is then treated as a `property:value` pair, or a | ||
// semi-colon-separated list thereof. | ||
// Otherwise, `prefix` is the property name, and | ||
// `o` is the value. | ||
// restore the dashes | ||
k = prefix.replace(/_/g, '-').replace(/[A-Z]/g, decamelize) | ||
if (local && (k == 'animation-name' || k == 'animation')) { | ||
if (local && (k == 'animation-name' || k == 'animation' || k == 'list-style')) { | ||
// no need to tokenize here a plain `.split(',')` has all bases covered. | ||
// We may 'parser' a comment, but it's not a big deal. | ||
o = o.split(',').map(function (o) { | ||
return o.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, ns.l) | ||
return o.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, parser.L) | ||
}).join(',') | ||
} | ||
if (/^animation|^transition/.test(k)) vendors = ['webkit'] | ||
// '@' in properties also triggers the *ielte7 hack | ||
// Since plugins dispatch on the /^@/ for at-rules | ||
// we swap the at for an asterisk | ||
// http://browserhacks.com/#hack-6d49e92634f26ae6d6e46b3ebc10019a | ||
k = k.replace(/^@/, '*') | ||
emit.d(k, o) | ||
/*/-statements-/*/ | ||
// vendorify | ||
for (kk = 0; kk < vendors.length; kk++) | ||
buf.push('-', vendors[kk], '-', k, k ? ':': '', o, ';\n') | ||
/*/-statements-/*/ | ||
buf.push(k, k ? ':': '', o, ';\n') | ||
} | ||
} | ||
var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g | ||
/** | ||
* Hanldes at-rules | ||
* | ||
* @param {string} k - The at-rule name, and, if takes both parameters and a | ||
* block, the parameters. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {string[]} v - Either parameters for block-less rules or their block | ||
* for the others. | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {array} k - The parsed at-rule, including the parameters, | ||
* if takes both parameters and a block. | ||
* @param {string} prefix - the current selector or the selector prefix | ||
* in case of nested rules | ||
* @param {string|string[]|object|object[]} v - Either parameters for | ||
* block-less rules or | ||
* their block | ||
* for the others. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){ | ||
var kk | ||
if (/^@(?:namespace|import|charset)$/.test(k)) { | ||
if(type.call(v) == ARRAY){ | ||
for (kk = 0; kk < v.length; kk++) { | ||
buf.push(k, ' ', v[kk], ';\n') | ||
} | ||
} else { | ||
buf.push(k, ' ', v, ';\n') | ||
function atRules(parser, emit, k, v, prefix, local, inAtRule) { | ||
for (var i = 0; i < parser.$a.length; i++) { | ||
if (parser.$a[i](parser, emit, k, v, prefix, local, inAtRule)) return | ||
} | ||
if (!k[3] && /^global$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 0, inAtRule) | ||
} else if (!k[3] && /^local$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 1, inAtRule) | ||
} else if (k[3] && /^adopt$/.test(k[2])) { | ||
if (!local || inAtRule) return emit.a('@-error-bad-at-adopt-placement' , JSON.stringify(k[0]), 0) | ||
if (!/^\.?[_A-Za-z][-\w]*$/.test(k[3])) return emit.a('@-error-bad-at-adopter', k[3], 0) | ||
i = [] | ||
flatIter(function(c, s){ | ||
s = c.toString() | ||
if(!/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(s)) emit.a('@-error-bad-at-adoptee', JSON.stringify(c), 0) | ||
else i.push(s.replace(/\./g, '')) | ||
})(v) | ||
// we may end up with duplicate classes but AFAIK it has no consequences on specificity. | ||
if (i.length) { | ||
// console.log("========== I ===========\n", i) | ||
parser.l(k[3] = k[3].replace(/\./g, '')) | ||
parser.n[k[3]] += (' ' + i.join(' ')) | ||
} | ||
} else if (/^@keyframes /.test(k)) { | ||
k = local ? k.replace( | ||
// generated by script/regexps.js | ||
/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, | ||
ns.l | ||
) : k | ||
// add a @-webkit-keyframes block too. | ||
buf.push('@-webkit-', k.slice(1), ' {\n') | ||
sheet(v, buf, '', '', ['webkit']) | ||
buf.push('}\n') | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, '', '', vendors, local, ns) | ||
buf.push('}\n') | ||
} else if (!k[3] && /^(?:namespace|import|charset)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
} else if (/^@extends?$/.test(k)) { | ||
emit.a(k[0], v) | ||
/*eslint-disable no-cond-assign*/ | ||
// pick the last class to be extended | ||
while (kk = findClass.exec(rawPrefix)) k = kk[4] | ||
/*eslint-enable no-cond-assign*/ | ||
if (k == null || !local) { | ||
// we're in a @global{} block | ||
buf.push('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
} else if (/^@extends?$/.test(k)) { | ||
// no class in the selector | ||
buf.push('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
})(v) | ||
} else if (!k[3] && /^(?:font-face|viewport)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
emit.a(k[1], '', 1) | ||
declarations(parser, emit, '', v, local) | ||
emit.A(k[1], '') | ||
})(v) | ||
} else if (k[3] && /^(?:media|supports|page|keyframes)$/.test(k[2])) { | ||
if (local && 'keyframes' == k[2]) { | ||
k[3] = k[3].replace( | ||
// generated by script/regexps.js | ||
/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, | ||
parser.L | ||
) | ||
} | ||
ns.e( | ||
type.call(v) == ARRAY ? v.map(function (parent) { | ||
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l) | ||
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l), | ||
k | ||
) | ||
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) { | ||
sheet(v, buf, k, k, emptyArray) | ||
} else if (/^@global$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 0, ns) | ||
emit.a(k[1], k[3], 1) | ||
} else if (/^@local$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 1, ns) | ||
if ('page' == k[2]) { | ||
} else if (/^@(?:media |supports |document )./.test(k)) { | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, prefix, rawPrefix, vendors, local, ns) | ||
buf.push('}\n') | ||
declarations(parser, emit, '', v, local) | ||
} else { | ||
sheet( | ||
parser, emit, | ||
'keyframes' == k[2] ? '' : prefix, | ||
v, local, 1 | ||
) | ||
} | ||
emit.A(k[1], k[3]) | ||
} else { | ||
buf.push('@-error-unsupported-at-rule ', JSON.stringify(k), ';\n') | ||
emit.a('@-error-unsupported-at-rule', JSON.stringify(k[0])) | ||
} | ||
@@ -208,172 +273,160 @@ } | ||
/** | ||
* Add rulesets and other CSS statements to the sheet. | ||
* Add rulesets and other CSS tree to the sheet. | ||
* | ||
* @param {array|string|object} statements - a source object or sub-object. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {array|string|object} tree - a source object or sub-object. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
function sheet(statements, buf, prefix, rawPrefix, vendors, local, ns) { | ||
var k, kk, v, inDeclaration | ||
function sheet(parser, emit, prefix, tree, local, inAtRule) { | ||
var k, v, inDeclaration, kk | ||
switch (type.call(statements)) { | ||
switch (type.call(tree)) { | ||
case ARRAY: | ||
for (k = 0; k < statements.length; k++) | ||
sheet(statements[k], buf, prefix, rawPrefix, vendors, local, ns) | ||
break | ||
case OBJECT: | ||
for (k in tree) if (own.call(tree, k)) { | ||
v = tree[k] | ||
case OBJECT: | ||
for (k in statements) { | ||
v = statements[k] | ||
if (prefix && /^[-\w$]+$/.test(k)) { | ||
if (!inDeclaration) { | ||
inDeclaration = 1 | ||
buf.push(( prefix || '*' ), ' {\n') | ||
emit.s(prefix) | ||
} | ||
declarations(v, buf, k, vendors, local, ns) | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, k[kk], v, local) | ||
} | ||
} else { | ||
declarations(parser, emit, k, v, local) | ||
} | ||
} else if (/^@/.test(k)) { | ||
// Handle At-rules | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
inDeclaration = 0 | ||
at(k, v, buf, prefix, rawPrefix, vendors, local, ns) | ||
atRules(parser, emit, | ||
/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(k) || [k,'@','',''], | ||
v, prefix, local, inAtRule | ||
) | ||
} else { | ||
// selector or nested sub-selectors | ||
inDeclaration = 0 | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
sheet( | ||
parser, emit, | ||
// `prefix` ... Hefty. Ugly. Sadly necessary. | ||
// | ||
(prefix && (/,/.test(prefix) || /,/.test(k))) ? | ||
sheet(v, buf, | ||
(kk = /,/.test(prefix) || prefix && /,/.test(k)) ? | ||
cartesian(prefix.split(','), ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
).split(','), prefix).join(',') : | ||
concat(prefix, ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
), prefix), | ||
kk ? | ||
cartesian(rawPrefix.split(','), k.split(','), rawPrefix).join(',') : | ||
concat(rawPrefix, k, rawPrefix), | ||
vendors, | ||
local, ns | ||
/*0*/ (kk = splitSelector(prefix), splitSelector( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
).map(function (k) { | ||
return /&/.test(k) ? ampersand(k, kk) : kk.map(function(kk) { | ||
return kk + k | ||
}).join(',') | ||
}).join(',')) : | ||
/*0*/ /&/.test(k) ? | ||
/*1*/ ampersand( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k, | ||
[prefix] | ||
) : | ||
/*1*/ prefix + ( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
), | ||
v, local, inAtRule | ||
) | ||
} | ||
} | ||
if (inDeclaration) buf.push('}\n') | ||
break | ||
case ARRAY: | ||
for (k = 0; k < tree.length; k++){ | ||
sheet(parser, emit, prefix, tree[k], local, inAtRule) | ||
} | ||
break | ||
case STRING: | ||
buf.push( | ||
( prefix || ':-error-no-selector' ) , ' {\n' | ||
) | ||
declarations(statements, buf, '', vendors, local, ns) | ||
buf.push('}\n') | ||
} | ||
} | ||
// CSS hacks or ouptut of `j2c.inline`. | ||
var scope_root = '_j2c_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_'; | ||
var counter = 0; | ||
function j2c(res) { | ||
res = res || {} | ||
var extensions = [] | ||
emit.s(prefix || ':-error-no-selector') | ||
function finalize(buf, i) { | ||
for (i = 0; i< extensions.length; i++) buf = extensions[i](buf) || buf | ||
return buf.join('') | ||
declarations(parser, emit, '', tree, local) | ||
} | ||
} | ||
res.use = function() { | ||
var args = arguments | ||
for (var i = 0; i < args.length; i++){ | ||
extensions.push(args[i]) | ||
} | ||
return res | ||
} | ||
/*/-statements-/*/ | ||
res.sheet = function(ns, statements) { | ||
if (arguments.length === 1) { | ||
statements = ns; ns = {} | ||
} | ||
var | ||
suffix = scope_root + counter++, | ||
locals = {}, | ||
k, buf = [] | ||
// pick only non-numeric keys since `(NaN != NaN) === true` | ||
for (k in ns) if (k-0 != k-0 && own.call(ns, k)) { | ||
locals[k] = ns[k] | ||
} | ||
sheet( | ||
statements, buf, '', '', emptyArray /*vendors*/, | ||
1, // local | ||
{ | ||
e: function extend(parent, child) { | ||
var nameList = locals[child] | ||
locals[child] = | ||
nameList.slice(0, nameList.lastIndexOf(' ') + 1) + | ||
parent + ' ' + | ||
nameList.slice(nameList.lastIndexOf(' ') + 1) | ||
}, | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) { | ||
return space + global | ||
} | ||
if (!locals[name]) locals[name] = name + suffix | ||
return space + dot + locals[name].match(/\S+$/) | ||
} | ||
// This is the first entry in the filters array, which is | ||
// actually the last step of the compiler. It inserts | ||
// closing braces to close normal (non at-) rules (those | ||
// that start with a selector). Doing it earlier is | ||
// impossible without passing state around in unrelated code | ||
// or ending up with duplicated selectors when the source tree | ||
// contains arrays. | ||
// There's no `S` handler, because the core compiler never | ||
// calls it. | ||
function closeSelectors(next, inline) { | ||
var lastSelector | ||
return inline ? next : { | ||
i: function(){lastSelector = 0; next.i()}, | ||
x: function (raw) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
return next.x(raw) | ||
}, | ||
a: function (rule, param, takesBlock) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.a(rule, param, takesBlock) | ||
}, | ||
A: function (rule) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.A(rule) | ||
}, | ||
s: function (selector) { | ||
if (selector !== lastSelector){ | ||
if (lastSelector) next.S() | ||
next.s(selector) | ||
lastSelector = selector | ||
} | ||
) | ||
/*jshint -W053 */ | ||
buf = new String(finalize(buf)) | ||
/*jshint +W053 */ | ||
for (k in locals) if (own.call(locals, k)) buf[k] = locals[k] | ||
return buf | ||
}, | ||
d: next.d | ||
} | ||
/*/-statements-/*/ | ||
res.inline = function (locals, decl, buf) { | ||
if (arguments.length === 1) { | ||
decl = locals; locals = {} | ||
} | ||
declarations( | ||
decl, | ||
buf = [], | ||
'', // prefix | ||
emptyArray, // vendors | ||
1, | ||
{ | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) return space + global | ||
if (!locals[name]) return name | ||
return space + dot + locals[name] | ||
} | ||
}) | ||
return finalize(buf) | ||
} | ||
res.prefix = function(val, vendors) { | ||
return cartesian( | ||
vendors.map(function(p){return '-' + p + '-'}).concat(['']), | ||
[val] | ||
) | ||
} | ||
return res | ||
} | ||
j2c.global = function(x) { | ||
function global(x) { | ||
return ':global(' + x + ')' | ||
} | ||
j2c.kv = kv | ||
function kv (k, v, o) { | ||
@@ -385,18 +438,157 @@ o = {} | ||
j2c.at = function at (rule, params, block) { | ||
function at (rule, params, block) { | ||
if ( | ||
arguments.length < 3 | ||
) { | ||
// inner curry! | ||
var _at = at.bind.apply(at, [null].concat([].slice.call(arguments,0))) | ||
// So that it can be used as a key in an ES6 object literal. | ||
_at.toString = function(){return '@' + rule + ' ' + params} | ||
return _at | ||
} | ||
else return kv('@' + rule + ' ' + params, block) | ||
else return kv('@' + rule +' ' + params, block) | ||
} | ||
j2c(j2c) | ||
delete j2c.use | ||
function j2c() { | ||
var $filters = [closeSelectors] | ||
var $atHandlers = [] | ||
var instance = { | ||
at: at, | ||
global: global, | ||
kv: kv, | ||
names: {}, | ||
suffix: '__j2c-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36), | ||
use: function() { | ||
_use(emptyArray.slice.call(arguments)) | ||
return instance | ||
}, | ||
$plugins: [] | ||
} | ||
function _default(target, source) { | ||
for (var k in source) if (own.call(source, k) && k.indexOf('$')) { | ||
if (OBJECT == type.call(source[k]) && OBJECT == type.call(target[k])) _default(target[k], source[k]) | ||
else if (!(k in target)) target[k] = source[k] | ||
} | ||
} | ||
var buf | ||
var $sink = { | ||
i: function(){buf=[]}, | ||
x: function (raw) {return raw ? buf : buf.join('')}, | ||
a: function (rule, argument, takesBlock) { | ||
buf.push(rule, argument && ' ',argument, takesBlock ? ' {\n' : ';\n') | ||
}, | ||
A: function () {buf.push('}\n')}, | ||
s: function (selector) {buf.push(selector, ' {\n')}, | ||
S: function () {buf.push('}\n')}, | ||
d: function (prop, value) {buf.push(prop, prop && ':', value, ';\n')} | ||
} | ||
var streams = [] | ||
var parsers = [ | ||
{ | ||
$a: $atHandlers, | ||
a: atRules, | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names, | ||
s: sheet | ||
}, { | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names | ||
} | ||
] | ||
var _use = flatIter(function(plugin) { | ||
// `~n` is falsy for `n === -1` and truthy otherwise. | ||
// Works well to turn the result of `a.indexOf(x)` | ||
// into a value that reflects the presence of `x` in | ||
// `a`. | ||
if (~instance.$plugins.indexOf(plugin)) return | ||
instance.$plugins.push(plugin) | ||
if (type.call(plugin) === FUNCTION) plugin = plugin(instance) | ||
if (!plugin) return | ||
flatIter(function(filter) { | ||
$filters.push(filter) | ||
})(plugin.$filter || emptyArray) | ||
flatIter(function(handler) { | ||
$atHandlers.push(handler) | ||
})(plugin.$at || emptyArray) | ||
$sink = plugin.$sink || $sink | ||
_default(instance, plugin) | ||
}) | ||
function getStream(inline) { | ||
if (!streams.length) { | ||
for(var i = 0; i < 2; i++){ | ||
$filters[$filters.length - i] = function(_, inline) {return inline ? {i:$sink.i, d:$sink.d, x:$sink.x} : $sink} | ||
for (var j = $filters.length; j--;) streams[i] = $filters[j](streams[i], !!i, parsers[i]) | ||
} | ||
} | ||
var res = streams[inline] | ||
return res | ||
} | ||
function localize(name) { | ||
if (!instance.names[name]) instance.names[name] = name + instance.suffix | ||
return instance.names[name].match(/^\S+/) | ||
} | ||
function localizeReplacer(match, string, global, dot, name) { | ||
if (string || global) return string || global | ||
return dot + localize(name) | ||
} | ||
/*/-statements-/*/ | ||
instance.sheet = function(tree) { | ||
var emit = getStream(0) | ||
emit.i() | ||
sheet( | ||
parsers[0], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1, // local, by default | ||
0 // inAtRule | ||
) | ||
return emit.x() | ||
} | ||
/*/-statements-/*/ | ||
instance.inline = function (tree) { | ||
var emit = getStream(1) | ||
emit.i() | ||
declarations( | ||
parsers[1], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1 //local | ||
) | ||
return emit.x() | ||
} | ||
return instance | ||
} | ||
var _j2c = j2c() | ||
'sheet|inline|names|at|global|kv|suffix'.split('|').map(function(m){j2c[m] = _j2c[m]}) | ||
return j2c; | ||
})(); | ||
}()); |
@@ -1,1 +0,1 @@ | ||
var j2c=function(){"use strict";function t(t,e,r,l,s,a){l=[];for(a in e)if(f.call(e,a))for(s in t)f.call(t,s)&&l.push(n(t[s],e[a],r));return l}function n(t,n,e){return e&&(/^[-\w$]+$/.test(n)&&":-error-bad-sub-selector-"+n||/&/.test(n)&&n.replace(/&/g,t))||t+n}function e(t){return"-"+t.toLowerCase()}function r(t,n,l,s,a,o,i,u,g){if(null!=t)if(/\$/.test(l))for(g in l=l.split("$"))f.call(l,g)&&r(t,n,l[g],s,a,o);else switch(c.call(t=t.valueOf())){case h:for(i=0;i<t.length;i++)r(t[i],n,l,s,a,o);break;case p:l=l&&l+"-";for(i in t)if(f.call(t,i))if(u=t[i],/\$/.test(i))for(g in i=i.split("$"))f.call(i,g)&&r(u,n,l+i[g],s,a,o);else r(u,n,l+i,s,a,o);break;default:for(i=l.replace(/_/g,"-").replace(/[A-Z]/g,e),!a||"animation-name"!=i&&"animation"!=i||(t=t.split(",").map(function(t){return t.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,o.l)}).join(",")),/^animation|^transition/.test(i)&&(s=["webkit"]),i=i.replace(/^@/,"*"),g=0;g<s.length;g++)n.push("-",s[g],"-",i,i?":":"",t,";\n");n.push(i,i?":":"",t,";\n")}}function l(t,n,e,r,l,a,o,i){var f;if(/^@(?:namespace|import|charset)$/.test(t))if(c.call(n)==h)for(f=0;f<n.length;f++)e.push(t," ",n[f],";\n");else e.push(t," ",n,";\n");else if(/^@keyframes /.test(t))t=o?t.replace(/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/,i.l):t,e.push("@-webkit-",t.slice(1)," {\n"),s(n,e,"","",["webkit"]),e.push("}\n"),e.push(t," {\n"),s(n,e,"","",a,o,i),e.push("}\n");else if(/^@extends?$/.test(t)){for(;f=w.exec(l);)t=f[4];if(null==t||!o)return void e.push("@-error-cannot-extend-in-global-context ",JSON.stringify(l),";\n");if(/^@extends?$/.test(t))return void e.push("@-error-no-class-to-extend-in ",JSON.stringify(l),";\n");i.e(c.call(n)==h?n.map(function(t){return t.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,i.l)}).join(" "):n.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/,i.l),t)}else/^@(?:font-face$|viewport$|page )/.test(t)?s(n,e,t,t,u):/^@global$/.test(t)?s(n,e,r,l,a,0,i):/^@local$/.test(t)?s(n,e,r,l,a,1,i):/^@(?:media |supports |document )./.test(t)?(e.push(t," {\n"),s(n,e,r,l,a,o,i),e.push("}\n")):e.push("@-error-unsupported-at-rule ",JSON.stringify(t),";\n")}function s(e,a,o,i,u,f,w){var m,b,d,$;switch(c.call(e)){case h:for(m=0;m<e.length;m++)s(e[m],a,o,i,u,f,w);break;case p:for(m in e)d=e[m],o&&/^[-\w$]+$/.test(m)?($||($=1,a.push(o||"*"," {\n")),r(d,a,m,u,f,w)):/^@/.test(m)?($=$&&a.push("}\n")&&0,l(m,d,a,o,i,u,f,w)):($=$&&a.push("}\n")&&0,s(d,a,(b=/,/.test(o)||o&&/,/.test(m))?t(o.split(","),(f?m.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,w.l):m).split(","),o).join(","):n(o,f?m.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,w.l):m,o),b?t(i.split(","),m.split(","),i).join(","):n(i,m,i),u,f,w));$&&a.push("}\n");break;case g:a.push(o||":-error-no-selector"," {\n"),r(e,a,"",u,f,w),a.push("}\n")}}function a(n){function e(t,n){for(n=0;n<l.length;n++)t=l[n](t)||t;return t.join("")}n=n||{};var l=[];return n.use=function(){for(var t=arguments,e=0;e<t.length;e++)l.push(t[e]);return n},n.sheet=function(t,n){1===arguments.length&&(n=t,t={});var r,l=m+b++,a={},o=[];for(r in t)r-0!=r-0&&f.call(t,r)&&(a[r]=t[r]);s(n,o,"","",u,1,{e:function(t,n){var e=a[n];a[n]=e.slice(0,e.lastIndexOf(" ")+1)+t+" "+e.slice(e.lastIndexOf(" ")+1)},l:function(t,n,e,r,s){return e?n+e:(a[s]||(a[s]=s+l),n+r+a[s].match(/\S+$/))}}),o=new String(e(o));for(r in a)f.call(a,r)&&(o[r]=a[r]);return o},n.inline=function(t,n,l){return 1===arguments.length&&(n=t,t={}),r(n,l=[],"",u,1,{l:function(n,e,r,l,s){return r?e+r:t[s]?e+l+t[s]:s}}),e(l)},n.prefix=function(n,e){return t(e.map(function(t){return"-"+t+"-"}).concat([""]),[n])},n}function o(t,n,e){return e={},e[t]=n,e}var i={},u=[],c=i.toString,f=i.hasOwnProperty,p=c.call(i),h=c.call(u),g=c.call(""),w=/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g,m="_j2c_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_"+Math.floor(4294967296*Math.random()).toString(36)+"_",b=0;return a.global=function(t){return":global("+t+")"},a.kv=o,a.at=function d(t,n,e){if(arguments.length<3){var r=d.bind.apply(d,[null].concat([].slice.call(arguments,0)));return r.toString=function(){return"@"+t+" "+n},r}return o("@"+t+" "+n,e)},a(a),delete a.use,a}(); | ||
var j2c=function(){"use strict";function n(n,t){var e,a,r=[];for(a in t)if(b.call(t,a))for(e in n)b.call(n,e)&&r.push(n[e]+t[a]);return r}function t(n){for(var t,e=[],a=[],r=0;t=S.exec(n);)switch(t[0]){case"(":r++;break;case")":r--;break;case",":if(r)break;e.push(t.index)}for(t=e.length;t--;)a.unshift(n.slice(e[t]+1)),n=n.slice(0,e[t]);return a.unshift(n),a}function e(t,e){for(var a,r,i=[],s=[];r=A.exec(t);)"&"==r[0]&&i.push(r.index);for(r=i.length;r--;)s.unshift(t.slice(i[r]+1)),t=t.slice(0,i[r]);for(s.unshift(t),a=[s[0]],r=1;r<s.length;r++)a=n(a,n(e,[s[r]]));return a.join(",")}function a(n){return function t(e){if(m.call(e)===d)for(var a=0;a<e.length;a++)t(e[a]);else n(e)}}function r(n){return"-"+n.toLowerCase()}function i(n,t,e,a,s){var l,o,u;if(null!=a)switch(m.call(a=a.valueOf())){case d:for(l=0;l<a.length;l++)i(n,t,e,a[l],s);break;case $:e=e&&e+"-";for(l in a)if(b.call(a,l))if(o=a[l],/\$/.test(l))for(u in l=l.split("$"))b.call(l,u)&&i(n,t,e+l[u],o,s);else i(n,t,e+l,o,s);break;default:l=e.replace(/_/g,"-").replace(/[A-Z]/g,r),!s||"animation-name"!=l&&"animation"!=l&&"list-style"!=l||(a=a.split(",").map(function(t){return t.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,n.L)}).join(",")),t.d(l,a)}}function s(n,t,e,r,s,o,u){for(var f=0;f<n.$a.length;f++)if(n.$a[f](n,t,e,r,s,o,u))return;if(!e[3]&&/^global$/.test(e[2]))l(n,t,s,r,0,u);else if(!e[3]&&/^local$/.test(e[2]))l(n,t,s,r,1,u);else if(e[3]&&/^adopt$/.test(e[2])){if(!o||u)return t.a("@-error-bad-at-adopt-placement",JSON.stringify(e[0]),0);if(!/^\.?[_A-Za-z][-\w]*$/.test(e[3]))return t.a("@-error-bad-at-adopter",e[3],0);f=[],a(function(n,e){e=n.toString(),/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(e)?f.push(e.replace(/\./g,"")):t.a("@-error-bad-at-adoptee",JSON.stringify(n),0)})(r),f.length&&(n.l(e[3]=e[3].replace(/\./g,"")),n.n[e[3]]+=" "+f.join(" "))}else!e[3]&&/^(?:namespace|import|charset)$/.test(e[2])?a(function(n){t.a(e[0],n)})(r):!e[3]&&/^(?:font-face|viewport)$/.test(e[2])?a(function(a){t.a(e[1],"",1),i(n,t,"",a,o),t.A(e[1],"")})(r):e[3]&&/^(?:media|supports|page|keyframes)$/.test(e[2])?(o&&"keyframes"==e[2]&&(e[3]=e[3].replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/,n.L)),t.a(e[1],e[3],1),"page"==e[2]?i(n,t,"",r,o):l(n,t,"keyframes"==e[2]?"":s,r,o,1),t.A(e[1],e[3])):t.a("@-error-unsupported-at-rule",JSON.stringify(e[0]))}function l(n,a,r,o,u,f){var c,p,g,h;switch(m.call(o)){case $:for(c in o)if(b.call(o,c))if(p=o[c],r&&/^[-\w$]+$/.test(c))if(g||(g=1,a.s(r)),/\$/.test(c))for(h in c=c.split("$"))b.call(c,h)&&i(n,a,c[h],p,u);else i(n,a,c,p,u);else/^@/.test(c)?(g=0,s(n,a,/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(c)||[c,"@","",""],p,r,u,f)):(g=0,l(n,a,r&&(/,/.test(r)||/,/.test(c))?(h=t(r),t(u?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c).map(function(n){return/&/.test(n)?e(n,h):h.map(function(t){return t+n}).join(",")}).join(",")):/&/.test(c)?e(u?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c,[r]):r+(u?c.replace(/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g,n.L):c),p,u,f));break;case d:for(c=0;c<o.length;c++)l(n,a,r,o[c],u,f);break;case v:a.s(r||":-error-no-selector"),i(n,a,"",o,u)}}function o(n,t){var e;return t?n:{i:function(){e=0,n.i()},x:function(t){return e&&(n.S(),e=0),n.x(t)},a:function(t,a,r){e&&(n.S(),e=0),n.a(t,a,r)},A:function(t){e&&(n.S(),e=0),n.A(t)},s:function(t){t!==e&&(e&&n.S(),n.s(t),e=t)},d:n.d}}function u(n){return":global("+n+")"}function f(n,t,e){return e={},e[n]=t,e}function c(n,t,e){if(arguments.length<3){var a=c.bind.apply(c,[null].concat([].slice.call(arguments,0)));return a.toString=function(){return"@"+n+" "+t},a}return f("@"+n+" "+t,e)}function p(){function n(t,e){for(var a in e)b.call(e,a)&&a.indexOf("$")&&($==m.call(e[a])&&$==m.call(t[a])?n(t[a],e[a]):a in t||(t[a]=e[a]))}function t(n){if(!A.length)for(var t=0;2>t;t++){h[h.length-t]=function(n,t){return t?{i:S.i,d:S.d,x:S.x}:S};for(var e=h.length;e--;)A[t]=h[e](A[t],!!t,x[t])}var a=A[n];return a}function e(n){return v.names[n]||(v.names[n]=n+v.suffix),v.names[n].match(/^\S+/)}function r(n,t,a,r,i){return t||a?t||a:r+e(i)}var p,h=[o],d=[],v={at:c,global:u,kv:f,names:{},suffix:"__j2c-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36)+"-"+Math.floor(4294967296*Math.random()).toString(36),use:function(){return _(g.slice.call(arguments)),v},$plugins:[]},S={i:function(){p=[]},x:function(n){return n?p:p.join("")},a:function(n,t,e){p.push(n,t&&" ",t,e?" {\n":";\n")},A:function(){p.push("}\n")},s:function(n){p.push(n," {\n")},S:function(){p.push("}\n")},d:function(n,t){p.push(n,n&&":",t,";\n")}},A=[],x=[{$a:d,a:s,d:i,L:r,l:e,n:v.names,s:l},{d:i,L:r,l:e,n:v.names}],_=a(function(t){~v.$plugins.indexOf(t)||(v.$plugins.push(t),m.call(t)===w&&(t=t(v)),t&&(a(function(n){h.push(n)})(t.$filter||g),a(function(n){d.push(n)})(t.$at||g),S=t.$sink||S,n(v,t)))});return v.sheet=function(n){var e=t(0);return e.i(),l(x[0],e,"",n,1,0),e.x()},v.inline=function(n){var e=t(1);return e.i(),i(x[1],e,"",n,1),e.x()},v}var g=[],h={},m=h.toString,d=m.call(g),$=m.call(h),v=m.call(""),w=m.call(m),b=h.hasOwnProperty,S=/[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,A=/&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g,x=p();return"sheet|inline|names|at|global|kv|suffix".split("|").map(function(n){p[n]=x[n]}),p}(); |
{ | ||
"name": "j2c", | ||
"version": "0.11.0", | ||
"version": "1.0.0-0", | ||
"description": "A tiny CSS in JS solution.", | ||
"main": "dist/j2c.commonjs.js", | ||
"scripts": { | ||
"all": "npm run build && npm run eslint && npm run nyan", | ||
"preversion": "npm run -s all", | ||
"preversion": "npm run all", | ||
"prepublish": "(in-publish && git push --follow-tags) || not-in-publish", | ||
"build": "npm run -s clean && node scripts/build.js", | ||
"uglify": "uglifyjs dist/j2c.commonjs.js -c -m --comments > dist/j2c.commonjs.min.js && uglifyjs dist/j2c.amd.js -c -m --comments > dist/j2c.amd.min.js && uglifyjs dist/j2c.global.js -c -m --comments > dist/j2c.global.min.js", | ||
"clean": "rm -f dist/*.js && rm -f dist/inline/*.js", | ||
"eslint": "echo \"linting...\" && eslint --fix src/*.js test/test.js scripts/*.js", | ||
"nyan": "mocha --ui qunit --reporter nyan", | ||
"all": "npm run build && npm run lint && npm run cover", | ||
"dev": "npm run build && npm run test", | ||
"build": "rm -f dist/*.js && node scripts/build.js", | ||
"lint": "eslint --fix src/*.js test/test.js scripts/*.js", | ||
"test": "mocha --ui qunit", | ||
"cover": "mocha --ui qunit --require blanket --reporter mocha-lcov-reporter | coveralls || npm run test" | ||
"cover": "istanbul cover _mocha -- -R nyan --ui qunit && npm run -s check-coverage", | ||
"check-coverage": "istanbul check-coverage --branches 100", | ||
"travis": "npm run build && istanbul cover _mocha --report lcovonly -- -R spec --ui qunit && (codecov || true) && npm run -s check-coverage" | ||
}, | ||
@@ -31,16 +31,12 @@ "repository": { | ||
}, | ||
"config": { | ||
"blanket": { | ||
"pattern": "//j2c\\.commonjs/" | ||
} | ||
}, | ||
"homepage": "http://j2c.py.gy", | ||
"devDependencies": { | ||
"blanket": "^1.1.7", | ||
"codecov": "^1.0.1", | ||
"commonmark": "^0.24.0", | ||
"compose-regexp": "^0.1.8", | ||
"coveralls": "^2.11.2", | ||
"eslint": "^1.10.3", | ||
"eslint": "^2.2.0", | ||
"expect.js": "0.3.1", | ||
"in-publish": "^2.0.0", | ||
"jshint": "^2.8.0", | ||
"istanbul": "^0.4.2", | ||
"jsdom": "^8.1.0", | ||
"mocha": "^2.3.4", | ||
@@ -52,3 +48,3 @@ "mocha-lcov-reporter": "^1.0.0", | ||
"postcss-minify-selectors": "^2.0.2", | ||
"rollup": "^0.24.0", | ||
"rollup": "^0.25.2", | ||
"surgicate": "0.0.3", | ||
@@ -55,0 +51,0 @@ "uglify-js": "^2.6.0" |
117
README.md
@@ -9,21 +9,2 @@ # j2c [![npm][npm_img]][npm_url] ![.min.gz][size_img] | ||
CSS in JS on steroids. | ||
`j2c` follows a [**'local by default'**](https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284) approach to make it easier to write components without having to worry about class and animation names clashes. | ||
Like SASS, LESS and Stylus, `j2c` supports: | ||
- mixins | ||
- `@extend` | ||
- nested selectors | ||
All standard CSS at-rules are available out of the box, most importantly: | ||
- `@media` and `@supports` can be nested anywhere in the sheet | ||
- `@keyframes` (with automatic generation of `@-webkit-keyframes`) | ||
- `@font-face` | ||
The [home page](http://j2c.py.gy) has a few interactive demos. | ||
[trav_img]: https://travis-ci.org/j2css/j2c.svg?branch=master | ||
@@ -43,2 +24,59 @@ [trav_url]: https://travis-ci.org/j2css/j2c | ||
A lean (2.0KB), no hassle CSS in JS solution. | ||
`j2c`: | ||
- supports all CSS features, and then some (most notably, local scope). | ||
- scales from standalone `<script src="j2c.global.js">` to isomorphic apps. | ||
- is compatible with any framework/view library. | ||
- doesn't require any external tooling (`gulp`/`babel`/`browserify`/`webpack`). | ||
- Can be imported as an ES6 or CommonJS module, but also works in ES3 browsers (IE8-) with polyfills. | ||
### Why? | ||
For styling components, mostly. Especially if you plan to publish them standalone (your users won't have to worry about importing the style sheets, and you won't tie your lib to any build system). | ||
### Out of the box | ||
`j2c` supports building either inline styles of full style sheets (arbitrary CSS). | ||
In `sheet` mode, `j2c` follows a [**'local by default'**](https://medium.com/seek-ui-engineering/the-end-of-global-css-90d2a4a06284) approach to make it easier to write components without having to worry about class and animation names clashes. | ||
Like SASS, LESS and Stylus, `j2c` supports: | ||
- mixins | ||
- nested selectors and at-rules | ||
- `@composes`, an `@extends`-like mechanism inspired by @tivac's [Modular CSS]() | ||
All [standard CSS at-rules](https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule) are available out of the box, most importantly: | ||
- `@media` and `@supports`, which can be nested anywhere in the sheet, SASS-style. | ||
- `@keyframes` | ||
- `@font-face` | ||
### With plugins | ||
- Automatic vendor prefix insertion + #%-@$-%$-@$ KB. (Client-side only at the moment) | ||
- color manipulation | ||
- lengths arithmetics | ||
- A bare bones grid system | ||
### Convert CSS to `j2c`... | ||
... with the [`j2c` importer](http://j2c.py.gy/import.html). | ||
The [home page](http://j2c.py.gy) has a few interactive demos. | ||
### `j2c` is mostly done. | ||
At this point, the core (this very repo) is considered feature complete and should not evolve much if at all. We will add new at-rules as they are standardized, and will fix any bugs that are reported. | ||
### Thanks | ||
Thanks to | ||
- @barneycarroll for the inspiration and initial feedback. | ||
- @ArthurClemens, @StephanHoyer, @der-On, @llambda, @dontwork, @futurist and @mithriljs-cn for the support and feedback. | ||
- @tivac for `@compose` which I stole from his [Modular CSS]() project. | ||
## Table of Contents | ||
@@ -56,5 +94,6 @@ | ||
- [At-rules](#at-rules) | ||
- [Mixins and @extend](#mixins-and-extend) | ||
- [Mixins and @composes](#mixins-and-composes) | ||
- [CSS Hacks](#css-hacks) | ||
- [Inserting a stylesheet in a document](#inserting-the-stylesheet-in-the-document) | ||
- [Isomorphic app support](#isomorphic-app-support) | ||
- [Limitations](#limitations) | ||
@@ -381,5 +420,5 @@ - [TODO](#todo) | ||
#### Mixins and `@extend` | ||
#### Mixins and `@coposes` | ||
Mixins and `@extend` make `j2c` sheets composable. Both techniques can be combined. | ||
Mixins and `@composes` make `j2c` sheets composable. Both techniques can be combined. | ||
@@ -390,5 +429,5 @@ ##### Mixins and source objects composition | ||
##### `@extend` | ||
##### `@composes` | ||
`j2c` also supports a SASS-like `@extend`, more powerful in some regards, but more limited in others. | ||
`j2c` also supports `@composes`, which works a bit like the SASS`@extend`, more powerful in some regards, but more limited in others. | ||
@@ -398,12 +437,12 @@ The limitation is that it can only deal with classes. Specifically: | ||
```JS | ||
namespace = j2c.sheet({ | ||
'.red': {color: '#f00'} | ||
}) | ||
sheet = j2c.sheet(namespace, { | ||
sheet = j2c.sheet({ | ||
'.red': { | ||
color: '#f00' | ||
}, | ||
'.great': { | ||
fontSize: '3em' | ||
}, | ||
'.greatRed': { | ||
'@extend': ['.great', '.red'] // you can also pass a single class | ||
// `scarlet` here is the target of the composition, `great` and `red` are the sources. | ||
'.scarlet': { | ||
'@composes': ['.great', '.red'] // you can also pass a single class | ||
} | ||
@@ -413,3 +452,3 @@ }) | ||
`sheet.greatRed` is now defined as `'great_j2c... red_j2c... greatRed_j2c...'` (class names truncated for readability). | ||
`sheet.scarlet` is now defined as `'great__j2c-xxx red__j2c-xxx scarlet__j2c-xxx'` (class names truncated for readability). | ||
@@ -421,3 +460,3 @@ The extra power comes from the fact that you can inherit from arbitrary classes, not just j2c-defined ones: | ||
'.myButton': { | ||
'@extend': ':global(.button)', // coming, say, form Bootstrap | ||
'@composes': ':global(.button)', // coming, say, form Bootstrap | ||
color: theme.highlight | ||
@@ -430,10 +469,6 @@ } | ||
While `@extend` can import from arbitrary classes, it only imports into local ones. | ||
While the `@composes` sources can be arbitrary classes, the target must be a local one. It will not work in global context. | ||
`@extend` works fine with nested selectors. If there are more than one class in a selector, `@extend` applies to the last (right-most) one. | ||
`@composes` doesn't support nested selectors, and doesn't work in conditional at rules. Its target must lie at the first nesting level. | ||
###### Invalid uses | ||
If the last or only selector is a `:global(.klass)`, in `@global` context, or in the absence of a class in the selector, `@extend` is turned into a `at-extend` property and inserted as-is in the sheet. | ||
#### CSS Hacks | ||
@@ -642,2 +677,6 @@ | ||
## Isomorphic app support | ||
Since `j2c` relies on the view library/framework for DOM insertion, it supports the isomorphic scenarios the same way your view solution does (see the previous section). Caveat: local class names will be regenerated on hydration, which means that styles will have to be computed twice by the browser. | ||
## Error handling | ||
@@ -644,0 +683,0 @@ |
@@ -10,59 +10,67 @@ /*eslint-env node*/ | ||
var animation = sequence( | ||
capture(), | ||
either( | ||
sequence( | ||
':global(', | ||
/\s*/, | ||
capture(/[-\w]+/), | ||
/\s*/, | ||
')' | ||
), | ||
sequence( | ||
capture(), | ||
capture(/[-\w]+/) | ||
) | ||
var animation = either( | ||
sequence( | ||
/:?/, | ||
'global(', | ||
/\s*/, | ||
capture(/[_A-Za-z][-\w]*/), | ||
/\s*/, | ||
')' | ||
), | ||
sequence( | ||
capture(), | ||
capture(/-?[_A-Za-z][-\w]*/) | ||
) | ||
) | ||
console.log('anumation / animation-name\n', animation) | ||
console.log('animation / animation-name / @keyframes\n', animation) | ||
var keyframes = sequence( | ||
capture(' '), | ||
either( | ||
sequence( | ||
':global(', | ||
/\s*/, | ||
capture(/[-\w]+/), | ||
/\s*/, | ||
')' | ||
var selector = flags('g', either( | ||
sequence( | ||
':global(', | ||
/\s*/, | ||
capture( | ||
'.', | ||
/-?[_A-Za-z][-\w]*/ | ||
), | ||
sequence( | ||
capture(), | ||
capture(/[-\w]+/) | ||
) | ||
/\s*/, | ||
')' | ||
), | ||
sequence( | ||
capture('.'), | ||
capture(/-?[_A-Za-z][-\w]*/) | ||
) | ||
) | ||
)) | ||
console.log('@keyframes\n', keyframes) | ||
console.log('selector / @global\n', selector) | ||
var selector = flags('g', sequence( | ||
capture(''), | ||
var selectorTokenizer = flags('g', | ||
either( | ||
/[(),]/, | ||
sequence( | ||
':global(', | ||
/\s*/, | ||
capture( | ||
'.', | ||
/[-\w]+/ | ||
'"', | ||
greedy('*', | ||
either( | ||
/\\./, | ||
/[^"\n]/ | ||
) | ||
), | ||
/\s*/, | ||
')' | ||
'"' | ||
), | ||
sequence( | ||
capture('.'), | ||
capture(/[-\w]+/) | ||
"'", | ||
greedy('*', | ||
either( | ||
/\\./, | ||
/[^'\n]/ | ||
) | ||
), | ||
"'" | ||
), | ||
sequence( | ||
'/*', | ||
/[\s\S]*?/, | ||
'*/' | ||
) | ||
) | ||
)) | ||
console.log('selector / @global\n', selector) | ||
) | ||
console.log('selectorTokenizer = ', selectorTokenizer) |
@@ -1,89 +0,117 @@ | ||
import {type, ARRAY, emptyArray} from './helpers' | ||
import {flatIter} from './helpers' | ||
import {sheet} from './sheet' | ||
import {declarations} from './declarations' | ||
var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g | ||
/** | ||
* Hanldes at-rules | ||
* | ||
* @param {string} k - The at-rule name, and, if takes both parameters and a | ||
* block, the parameters. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {string[]} v - Either parameters for block-less rules or their block | ||
* for the others. | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {array} k - The parsed at-rule, including the parameters, | ||
* if takes both parameters and a block. | ||
* @param {string} prefix - the current selector or the selector prefix | ||
* in case of nested rules | ||
* @param {string|string[]|object|object[]} v - Either parameters for | ||
* block-less rules or | ||
* their block | ||
* for the others. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
export function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){ | ||
var kk | ||
if (/^@(?:namespace|import|charset)$/.test(k)) { | ||
if(type.call(v) == ARRAY){ | ||
for (kk = 0; kk < v.length; kk++) { | ||
buf.push(k, ' ', v[kk], ';\n') | ||
} | ||
} else { | ||
buf.push(k, ' ', v, ';\n') | ||
export function atRules(parser, emit, k, v, prefix, local, inAtRule) { | ||
for (var i = 0; i < parser.$a.length; i++) { | ||
if (parser.$a[i](parser, emit, k, v, prefix, local, inAtRule)) return | ||
} | ||
if (!k[3] && /^global$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 0, inAtRule) | ||
} else if (!k[3] && /^local$/.test(k[2])) { | ||
sheet(parser, emit, prefix, v, 1, inAtRule) | ||
} else if (k[3] && /^adopt$/.test(k[2])) { | ||
if (!local || inAtRule) return emit.a('@-error-bad-at-adopt-placement' , JSON.stringify(k[0]), 0) | ||
if (!/^\.?[_A-Za-z][-\w]*$/.test(k[3])) return emit.a('@-error-bad-at-adopter', k[3], 0) | ||
i = [] | ||
flatIter(function(c, s){ | ||
s = c.toString() | ||
if(!/^\.?[_A-Za-z][-\w]*(?:\s+\.?[_A-Za-z][-\w]*)*$/.test(s)) emit.a('@-error-bad-at-adoptee', JSON.stringify(c), 0) | ||
else i.push(s.replace(/\./g, '')) | ||
})(v) | ||
// we may end up with duplicate classes but AFAIK it has no consequences on specificity. | ||
if (i.length) { | ||
// console.log("========== I ===========\n", i) | ||
parser.l(k[3] = k[3].replace(/\./g, '')) | ||
parser.n[k[3]] += (' ' + i.join(' ')) | ||
} | ||
} else if (/^@keyframes /.test(k)) { | ||
k = local ? k.replace( | ||
// generated by script/regexps.js | ||
/( )(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, | ||
ns.l | ||
) : k | ||
// add a @-webkit-keyframes block too. | ||
buf.push('@-webkit-', k.slice(1), ' {\n') | ||
sheet(v, buf, '', '', ['webkit']) | ||
buf.push('}\n') | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, '', '', vendors, local, ns) | ||
buf.push('}\n') | ||
} else if (!k[3] && /^(?:namespace|import|charset)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
} else if (/^@extends?$/.test(k)) { | ||
emit.a(k[0], v) | ||
/*eslint-disable no-cond-assign*/ | ||
// pick the last class to be extended | ||
while (kk = findClass.exec(rawPrefix)) k = kk[4] | ||
/*eslint-enable no-cond-assign*/ | ||
if (k == null || !local) { | ||
// we're in a @global{} block | ||
buf.push('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
} else if (/^@extends?$/.test(k)) { | ||
// no class in the selector | ||
buf.push('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n') | ||
return | ||
})(v) | ||
} else if (!k[3] && /^(?:font-face|viewport)$/.test(k[2])) { | ||
flatIter(function(v) { | ||
emit.a(k[1], '', 1) | ||
declarations(parser, emit, '', v, local) | ||
emit.A(k[1], '') | ||
})(v) | ||
} else if (k[3] && /^(?:media|supports|page|keyframes)$/.test(k[2])) { | ||
if (local && 'keyframes' == k[2]) { | ||
k[3] = k[3].replace( | ||
// generated by script/regexps.js | ||
/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, | ||
parser.L | ||
) | ||
} | ||
ns.e( | ||
type.call(v) == ARRAY ? v.map(function (parent) { | ||
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l) | ||
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l), | ||
k | ||
) | ||
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) { | ||
sheet(v, buf, k, k, emptyArray) | ||
} else if (/^@global$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 0, ns) | ||
emit.a(k[1], k[3], 1) | ||
} else if (/^@local$/.test(k)) { | ||
sheet(v, buf, prefix, rawPrefix, vendors, 1, ns) | ||
if ('page' == k[2]) { | ||
} else if (/^@(?:media |supports |document )./.test(k)) { | ||
buf.push(k, ' {\n') | ||
sheet(v, buf, prefix, rawPrefix, vendors, local, ns) | ||
buf.push('}\n') | ||
declarations(parser, emit, '', v, local) | ||
} else { | ||
sheet( | ||
parser, emit, | ||
'keyframes' == k[2] ? '' : prefix, | ||
v, local, 1 | ||
) | ||
} | ||
emit.A(k[1], k[3]) | ||
} else { | ||
buf.push('@-error-unsupported-at-rule ', JSON.stringify(k), ';\n') | ||
emit.a('@-error-unsupported-at-rule', JSON.stringify(k[0])) | ||
} | ||
} |
@@ -10,26 +10,20 @@ import {own, type, ARRAY, OBJECT} from './helpers' | ||
* | ||
* @param {array|object|string} o - the declarations. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built. | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current property or a prefix in case of nested | ||
* sub-properties. | ||
* @param {string} vendors - a list of vendor prefixes. | ||
* @Param {boolean} local - are we in @local or in @global scope. | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes. | ||
* @param {function} ns.e - @extend helper. | ||
* @param {function} ns.l - @local helper. | ||
* @param {array|object|string} o - the declarations. | ||
* @param {boolean} local - are we in @local or in @global scope. | ||
*/ | ||
export function declarations(o, buf, prefix, vendors, local, ns, /*var*/ k, v, kk) { | ||
export function declarations(parser, emit, prefix, o, local) { | ||
var k, v, kk | ||
if (o==null) return | ||
if (/\$/.test(prefix)) { | ||
for (kk in (prefix = prefix.split('$'))) if (own.call(prefix, kk)) { | ||
declarations(o, buf, prefix[kk], vendors, local, ns) | ||
} | ||
return | ||
} | ||
switch ( type.call(o = o.valueOf()) ) { | ||
case ARRAY: | ||
for (k = 0; k < o.length; k++) | ||
declarations(o[k], buf, prefix, vendors, local, ns) | ||
declarations(parser, emit, prefix, o[k], local) | ||
break | ||
@@ -43,6 +37,11 @@ case OBJECT: | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) | ||
declarations(v, buf, prefix + k[kk], vendors, local, ns) | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, prefix + k[kk], v, local) | ||
} | ||
} else { | ||
declarations(v, buf, prefix + k, vendors, local, ns) | ||
declarations(parser, emit, prefix + k, v, local) | ||
} | ||
@@ -54,29 +53,23 @@ } | ||
// at the top level. | ||
// `o` is then treated as a `property:value` pair. | ||
// otherwise, `prefix` is the property name, and | ||
// `o` is then treated as a `property:value` pair, or a | ||
// semi-colon-separated list thereof. | ||
// Otherwise, `prefix` is the property name, and | ||
// `o` is the value. | ||
// restore the dashes | ||
k = prefix.replace(/_/g, '-').replace(/[A-Z]/g, decamelize) | ||
if (local && (k == 'animation-name' || k == 'animation')) { | ||
if (local && (k == 'animation-name' || k == 'animation' || k == 'list-style')) { | ||
// no need to tokenize here a plain `.split(',')` has all bases covered. | ||
// We may 'parser' a comment, but it's not a big deal. | ||
o = o.split(',').map(function (o) { | ||
return o.replace(/()(?::global\(\s*([-\w]+)\s*\)|()([-\w]+))/, ns.l) | ||
return o.replace(/($^)|:?global\(\s*([_A-Za-z][-\w]*)\s*\)|()(-?[_A-Za-z][-\w]*)/, parser.L) | ||
}).join(',') | ||
} | ||
if (/^animation|^transition/.test(k)) vendors = ['webkit'] | ||
// '@' in properties also triggers the *ielte7 hack | ||
// Since plugins dispatch on the /^@/ for at-rules | ||
// we swap the at for an asterisk | ||
// http://browserhacks.com/#hack-6d49e92634f26ae6d6e46b3ebc10019a | ||
k = k.replace(/^@/, '*') | ||
emit.d(k, o) | ||
/*/-statements-/*/ | ||
// vendorify | ||
for (kk = 0; kk < vendors.length; kk++) | ||
buf.push('-', vendors[kk], '-', k, k ? ':': '', o, ';\n') | ||
/*/-statements-/*/ | ||
buf.push(k, k ? ':': '', o, ';\n') | ||
} | ||
} |
var | ||
emptyArray = [], | ||
emptyObject = {}, | ||
emptyArray = [], | ||
type = emptyObject.toString, | ||
own = emptyObject.hasOwnProperty, | ||
ARRAY = type.call(emptyArray), | ||
OBJECT = type.call(emptyObject), | ||
ARRAY = type.call(emptyArray), | ||
STRING = type.call('') | ||
STRING = type.call(''), | ||
FUNCTION = type.call(type), | ||
own = emptyObject.hasOwnProperty | ||
/*/-inline-/*/ | ||
// function cartesian(a, b, res, i, j) { | ||
// res = []; | ||
// for (j in b) if (own.call(b, j)) | ||
// for (i in a) if (own.call(a, i)) | ||
// res.push(a[i] + b[j]); | ||
// return res; | ||
// } | ||
/*/-inline-/*/ | ||
/* /-statements-/*/ | ||
function cartesian(a,b, selectorP, res, i, j) { | ||
res = [] | ||
function cartesian(a,b) { | ||
var res = [], i, j | ||
for (j in b) if(own.call(b, j)) | ||
for (i in a) if(own.call(a, i)) | ||
res.push(concat(a[i], b[j], selectorP)) | ||
res.push(a[i] + b[j]) | ||
return res | ||
} | ||
function concat(a, b, selectorP) { | ||
// `b.replace(/&/g, a)` is never falsy, since the | ||
// 'a' of cartesian can't be the empty string | ||
// in selector mode. | ||
return selectorP && ( | ||
/^[-\w$]+$/.test(b) && ':-error-bad-sub-selector-' + b || | ||
/&/.test(b) && /* never falsy */ b.replace(/&/g, a) | ||
) || a + b | ||
// "Tokenizes" the selectors into parts relevant for the next function. | ||
// Strings and comments are matched, but ignored afterwards. | ||
// This is not a full tokenizers. It only recognizes comas, parentheses, | ||
// strings and comments. | ||
// regexp generated by scripts/regexps.js then trimmed by hand | ||
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
/** | ||
* This will split a coma-separated selector list into individual selectors, | ||
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists). | ||
* @param {string} selector | ||
* @return {string[]} | ||
*/ | ||
function splitSelector(selector) { | ||
var indices = [], res = [], inParen = 0, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = selectorTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
switch(o[0]){ | ||
case '(': inParen++; break | ||
case ')': inParen--; break | ||
case ',': if (inParen) break; indices.push(o.index) | ||
} | ||
} | ||
for (o = indices.length; o--;){ | ||
res.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
res.unshift(selector) | ||
return res | ||
} | ||
/* /-statements-/*/ | ||
export {emptyObject, emptyArray, type, own, OBJECT, ARRAY, STRING, cartesian, concat} | ||
// This is like the `selectorTokenizer`, but for the `&` operator | ||
var ampersandTokenizer = /&|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g | ||
function ampersand (selector, parents) { | ||
var indices = [], split = [], res, o | ||
/*eslint-disable no-cond-assign*/ | ||
while(o = ampersandTokenizer.exec(selector)) { | ||
/*eslint-enable no-cond-assign*/ | ||
if (o[0] == '&') indices.push(o.index) | ||
} | ||
for (o = indices.length; o--;){ | ||
split.unshift(selector.slice(indices[o] + 1)) | ||
selector = selector.slice(0, indices[o]) | ||
} | ||
split.unshift(selector) | ||
res = [split[0]] | ||
for (o = 1; o < split.length; o++) { | ||
res = cartesian(res, cartesian(parents, [split[o]])) | ||
} | ||
return res.join(',') | ||
} | ||
function flatIter (f) { | ||
return function iter(arg) { | ||
if (type.call(arg) === ARRAY) for (var i= 0 ; i < arg.length; i ++) iter(arg[i]) | ||
else f(arg) | ||
} | ||
} | ||
export { | ||
ARRAY, FUNCTION, OBJECT, STRING, | ||
ampersand, cartesian, | ||
emptyArray, emptyObject, | ||
flatIter, own, | ||
splitSelector, type | ||
} |
226
src/main.js
@@ -1,120 +0,146 @@ | ||
import {own, cartesian, emptyArray} from './helpers' | ||
import {sheet} from './sheet' | ||
import {own, flatIter, emptyArray, type, FUNCTION, OBJECT} from './helpers' | ||
import {sheet, closeSelectors} from './sheet' | ||
import {declarations} from './declarations' | ||
import {atRules} from './at-rules' | ||
import {at, global, kv} from './extras' | ||
var scope_root = '_j2c_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '_', | ||
counter = 0 | ||
export default function j2c(res) { | ||
res = res || {} | ||
var extensions = [] | ||
export default function j2c() { | ||
var $filters = [closeSelectors] | ||
var $atHandlers = [] | ||
var instance = { | ||
at: at, | ||
global: global, | ||
kv: kv, | ||
names: {}, | ||
suffix: '__j2c-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36) + '-' + | ||
Math.floor(Math.random() * 0x100000000).toString(36), | ||
use: function() { | ||
_use(emptyArray.slice.call(arguments)) | ||
return instance | ||
}, | ||
$plugins: [] | ||
} | ||
function finalize(buf, i) { | ||
for (i = 0; i< extensions.length; i++) buf = extensions[i](buf) || buf | ||
return buf.join('') | ||
function _default(target, source) { | ||
for (var k in source) if (own.call(source, k) && k.indexOf('$')) { | ||
if (OBJECT == type.call(source[k]) && OBJECT == type.call(target[k])) _default(target[k], source[k]) | ||
else if (!(k in target)) target[k] = source[k] | ||
} | ||
} | ||
res.use = function() { | ||
var args = arguments | ||
for (var i = 0; i < args.length; i++){ | ||
extensions.push(args[i]) | ||
var buf | ||
var $sink = { | ||
i: function(){buf=[]}, | ||
x: function (raw) {return raw ? buf : buf.join('')}, | ||
a: function (rule, argument, takesBlock) { | ||
buf.push(rule, argument && ' ',argument, takesBlock ? ' {\n' : ';\n') | ||
}, | ||
A: function () {buf.push('}\n')}, | ||
s: function (selector) {buf.push(selector, ' {\n')}, | ||
S: function () {buf.push('}\n')}, | ||
d: function (prop, value) {buf.push(prop, prop && ':', value, ';\n')} | ||
} | ||
var streams = [] | ||
var parsers = [ | ||
{ | ||
$a: $atHandlers, | ||
a: atRules, | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names, | ||
s: sheet | ||
}, { | ||
d: declarations, | ||
L: localizeReplacer, | ||
l: localize, | ||
n: instance.names | ||
} | ||
] | ||
var _use = flatIter(function(plugin) { | ||
// `~n` is falsy for `n === -1` and truthy otherwise. | ||
// Works well to turn the result of `a.indexOf(x)` | ||
// into a value that reflects the presence of `x` in | ||
// `a`. | ||
if (~instance.$plugins.indexOf(plugin)) return | ||
instance.$plugins.push(plugin) | ||
if (type.call(plugin) === FUNCTION) plugin = plugin(instance) | ||
if (!plugin) return | ||
flatIter(function(filter) { | ||
$filters.push(filter) | ||
})(plugin.$filter || emptyArray) | ||
flatIter(function(handler) { | ||
$atHandlers.push(handler) | ||
})(plugin.$at || emptyArray) | ||
$sink = plugin.$sink || $sink | ||
_default(instance, plugin) | ||
}) | ||
function getStream(inline) { | ||
if (!streams.length) { | ||
for(var i = 0; i < 2; i++){ | ||
$filters[$filters.length - i] = function(_, inline) {return inline ? {i:$sink.i, d:$sink.d, x:$sink.x} : $sink} | ||
for (var j = $filters.length; j--;) streams[i] = $filters[j](streams[i], !!i, parsers[i]) | ||
} | ||
} | ||
var res = streams[inline] | ||
return res | ||
} | ||
function localize(name) { | ||
if (!instance.names[name]) instance.names[name] = name + instance.suffix | ||
return instance.names[name].match(/^\S+/) | ||
} | ||
function localizeReplacer(match, string, global, dot, name) { | ||
if (string || global) return string || global | ||
return dot + localize(name) | ||
} | ||
/*/-statements-/*/ | ||
res.sheet = function(ns, statements) { | ||
if (arguments.length === 1) { | ||
statements = ns; ns = {} | ||
} | ||
var | ||
suffix = scope_root + counter++, | ||
locals = {}, | ||
k, buf = [] | ||
// pick only non-numeric keys since `(NaN != NaN) === true` | ||
for (k in ns) if (k-0 != k-0 && own.call(ns, k)) { | ||
locals[k] = ns[k] | ||
} | ||
instance.sheet = function(tree) { | ||
var emit = getStream(0) | ||
emit.i() | ||
sheet( | ||
statements, buf, '', '', emptyArray /*vendors*/, | ||
1, // local | ||
{ | ||
e: function extend(parent, child) { | ||
var nameList = locals[child] | ||
locals[child] = | ||
nameList.slice(0, nameList.lastIndexOf(' ') + 1) + | ||
parent + ' ' + | ||
nameList.slice(nameList.lastIndexOf(' ') + 1) | ||
}, | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) { | ||
return space + global | ||
} | ||
if (!locals[name]) locals[name] = name + suffix | ||
return space + dot + locals[name].match(/\S+$/) | ||
} | ||
} | ||
parsers[0], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1, // local, by default | ||
0 // inAtRule | ||
) | ||
/*jshint -W053 */ | ||
buf = new String(finalize(buf)) | ||
/*jshint +W053 */ | ||
for (k in locals) if (own.call(locals, k)) buf[k] = locals[k] | ||
return buf | ||
return emit.x() | ||
} | ||
/*/-statements-/*/ | ||
res.inline = function (locals, decl, buf) { | ||
if (arguments.length === 1) { | ||
decl = locals; locals = {} | ||
} | ||
instance.inline = function (tree) { | ||
var emit = getStream(1) | ||
emit.i() | ||
declarations( | ||
decl, | ||
buf = [], | ||
'', // prefix | ||
emptyArray, // vendors | ||
1, | ||
{ | ||
l: function localize(match, space, global, dot, name) { | ||
if (global) return space + global | ||
if (!locals[name]) return name | ||
return space + dot + locals[name] | ||
} | ||
}) | ||
return finalize(buf) | ||
} | ||
res.prefix = function(val, vendors) { | ||
return cartesian( | ||
vendors.map(function(p){return '-' + p + '-'}).concat(['']), | ||
[val] | ||
parsers[1], | ||
emit, | ||
'', // prefix | ||
tree, | ||
1 //local | ||
) | ||
return emit.x() | ||
} | ||
return res | ||
} | ||
j2c.global = function(x) { | ||
return ':global(' + x + ')' | ||
return instance | ||
} | ||
j2c.kv = kv | ||
function kv (k, v, o) { | ||
o = {} | ||
o[k] = v | ||
return o | ||
} | ||
j2c.at = function at (rule, params, block) { | ||
if ( | ||
arguments.length < 3 | ||
) { | ||
var _at = at.bind.apply(at, [null].concat([].slice.call(arguments,0))) | ||
_at.toString = function(){return '@' + rule + ' ' + params} | ||
return _at | ||
} | ||
else return kv('@' + rule + ' ' + params, block) | ||
} | ||
j2c(j2c) | ||
delete j2c.use | ||
var _j2c = j2c() | ||
'sheet|inline|names|at|global|kv|suffix'.split('|').map(function(m){j2c[m] = _j2c[m]}) |
@@ -5,16 +5,29 @@ # ./src | ||
As a consequence, the lib is written in shimable ES5 strict mode, with ES6 modules that are eliminated at build time with [rollup](), so that `j2c` works in old IE and is as small as possible. | ||
As a consequence, the lib is written in shimable ES5 strict mode, with ES6 modules that are eliminated at build time with [rollup](), so that `j2c` works in old IE (for those who are forced to develop for it) and is as small as possible. | ||
The code is also written with maximal gzippability in mind. It sometimes means favouring code duplication over abstraction, since exact duplicates compress very well. I also implies sometimes large conditional expressions. | ||
The code is also written with maximal gzippability in mind. It sometimes means favouring code duplication over abstraction, since exact duplicates compress better. I also implies sometimes large conditional expressions, and a heavy use of regexps. | ||
Variables are created only to avoid duplicate computation. | ||
Variables are created only to avoid duplicate computation, never for readability. Variables are recycled in independent parts of the code, and don't always have a descriptive name. | ||
This approach is clearly not scalable and bug prone. | ||
These constrains can lead to code that's difficult to scale and bug prone. | ||
Scalability is not a concern, since I intend `j2c` to remain small. | ||
Regarding bugs, there's at this point a 4/1 test/code ratio, and 100% test coverage, which allows me to catch a lot of possible mistakes. But most of all, behind the raw for loops and repetitive style lies a clean, functional architecture. | ||
Regarding bugs, there's at this point a 3/1 test/code ratio, and 100% test coverage, which allows to catch a lot of possible mistakes. The RegExps are used wisely, mostly for pattern matching or for lexing. The most complex ones are built separately using a library that emulates multi-line regexps. | ||
Not counting helpers, `j2c` is built around three almost pure, pattern matching (conceptually), recursive functions. The only side effects are well defined: populate the buffer (an array of strings to be joined to form the final sheet) and populate or query the local name space (for local class and animation names). Every feature is isolated form the rest, which makes it trivial to track down and pinpoint the source of most bugs when they occur. The harder bugs I've had to track were, unsurprisingly, related to the non-pure parts of `j2c`, and especially the class and animation name localization stuff. However, problems usually become evident with a few well placed `console.*()` calls. | ||
But most of all, behind the raw for loops and repetitions lies a clean, functional architecture. | ||
Including helpers, `j2c` is built around five almost pure, pattern matching (conceptually) functions that recursively walk down JS objects. | ||
The side effects are well defined: | ||
- populate the buffer (an array of strings to be joined to form the final style sheet) | ||
- populate or query the local namespace (for local class and animation names, and `@composes`) | ||
Each of these are implemented through state holding objects (`buf` and `ns`) that are passed as parameters to the pure functions. Side effects are thus isolated and easy to spot. | ||
Feature isolation makes it trivial to track down and pinpoint the source of bugs when they occur. The hardest bugs I've had to track were, unsurprisingly, while refactoring the impure parts of `j2c`, and especially the class and animation name localization. However, problems usually become evident with a few well placed `console.*` calls. | ||
## Navigating the source: | ||
`main.js` contains the public API (`j2c.*` functions) | ||
@@ -24,3 +37,5 @@ | ||
`declarations.js`, `at-rules.js` and `helpers.js` are self-explaining. `declaration` is at the heart of `j2c.inline`. | ||
`declarations.js`, `at-rules.js` and `helpers.js` are self-explaining. `declarations.js` is also at the heart of `j2c.inline`. | ||
--- | ||
180
src/sheet.js
@@ -1,78 +0,158 @@ | ||
import {type, ARRAY, OBJECT, STRING, cartesian, concat} from './helpers' | ||
import {type, ARRAY, OBJECT, STRING, ampersand, own, splitSelector} from './helpers' | ||
import {declarations} from './declarations' | ||
import {at} from './at-rules' | ||
import {atRules} from './at-rules' | ||
/** | ||
* Add rulesets and other CSS statements to the sheet. | ||
* Add rulesets and other CSS tree to the sheet. | ||
* | ||
* @param {array|string|object} statements - a source object or sub-object. | ||
* @param {string[]} buf - the buffer in which the final style sheet is built | ||
* @param {object} parser - holds the parser-related methods and state | ||
* @param {object} emit - the contextual emitters to the final buffer | ||
* @param {string} prefix - the current selector or a prefix in case of nested rules | ||
* @param {string} rawPrefix - as above, but without localization transformations | ||
* @param {string} vendors - a list of vendor prefixes | ||
* @Param {boolean} local - are we in @local or in @global scope? | ||
* @param {object} ns - helper functions to populate or create the @local namespace | ||
* and to @extend classes | ||
* @param {function} ns.e - @extend helper | ||
* @param {function} ns.l - @local helper | ||
* @param {array|string|object} tree - a source object or sub-object. | ||
* @param {string} inAtRule - are we nested in an at-rule? | ||
* @param {boolean} local - are we in @local or in @global scope? | ||
*/ | ||
export function sheet(statements, buf, prefix, rawPrefix, vendors, local, ns) { | ||
var k, kk, v, inDeclaration | ||
export function sheet(parser, emit, prefix, tree, local, inAtRule) { | ||
var k, v, inDeclaration, kk | ||
switch (type.call(statements)) { | ||
switch (type.call(tree)) { | ||
case ARRAY: | ||
for (k = 0; k < statements.length; k++) | ||
sheet(statements[k], buf, prefix, rawPrefix, vendors, local, ns) | ||
break | ||
case OBJECT: | ||
for (k in tree) if (own.call(tree, k)) { | ||
v = tree[k] | ||
case OBJECT: | ||
for (k in statements) { | ||
v = statements[k] | ||
if (prefix && /^[-\w$]+$/.test(k)) { | ||
if (!inDeclaration) { | ||
inDeclaration = 1 | ||
buf.push(( prefix || '*' ), ' {\n') | ||
emit.s(prefix) | ||
} | ||
declarations(v, buf, k, vendors, local, ns) | ||
if (/\$/.test(k)) { | ||
for (kk in (k = k.split('$'))) if (own.call(k, kk)) { | ||
declarations(parser, emit, k[kk], v, local) | ||
} | ||
} else { | ||
declarations(parser, emit, k, v, local) | ||
} | ||
} else if (/^@/.test(k)) { | ||
// Handle At-rules | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
inDeclaration = 0 | ||
at(k, v, buf, prefix, rawPrefix, vendors, local, ns) | ||
atRules(parser, emit, | ||
/^(.(?:-[\w]+-)?([_A-Za-z][-\w]*))\b\s*(.*?)\s*$/.exec(k) || [k,'@','',''], | ||
v, prefix, local, inAtRule | ||
) | ||
} else { | ||
// selector or nested sub-selectors | ||
inDeclaration = 0 | ||
inDeclaration = (inDeclaration && buf.push('}\n') && 0) | ||
sheet( | ||
parser, emit, | ||
// `prefix` ... Hefty. Ugly. Sadly necessary. | ||
// | ||
(prefix && (/,/.test(prefix) || /,/.test(k))) ? | ||
sheet(v, buf, | ||
(kk = /,/.test(prefix) || prefix && /,/.test(k)) ? | ||
cartesian(prefix.split(','), ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
).split(','), prefix).join(',') : | ||
concat(prefix, ( local ? | ||
k.replace( | ||
/()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g, ns.l | ||
) : k | ||
), prefix), | ||
kk ? | ||
cartesian(rawPrefix.split(','), k.split(','), rawPrefix).join(',') : | ||
concat(rawPrefix, k, rawPrefix), | ||
vendors, | ||
local, ns | ||
/*0*/ (kk = splitSelector(prefix), splitSelector( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
).map(function (k) { | ||
return /&/.test(k) ? ampersand(k, kk) : kk.map(function(kk) { | ||
return kk + k | ||
}).join(',') | ||
}).join(',')) : | ||
/*0*/ /&/.test(k) ? | ||
/*1*/ ampersand( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k, | ||
[prefix] | ||
) : | ||
/*1*/ prefix + ( | ||
local ? | ||
k.replace( | ||
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\/)|:global\(\s*(\.-?[_A-Za-z][-\w]*)\s*\)|(\.)(-?[_A-Za-z][-\w]*)/g, parser.L | ||
) : | ||
k | ||
), | ||
v, local, inAtRule | ||
) | ||
} | ||
} | ||
if (inDeclaration) buf.push('}\n') | ||
break | ||
case ARRAY: | ||
for (k = 0; k < tree.length; k++){ | ||
sheet(parser, emit, prefix, tree[k], local, inAtRule) | ||
} | ||
break | ||
case STRING: | ||
buf.push( | ||
( prefix || ':-error-no-selector' ) , ' {\n' | ||
) | ||
declarations(statements, buf, '', vendors, local, ns) | ||
buf.push('}\n') | ||
// CSS hacks or ouptut of `j2c.inline`. | ||
emit.s(prefix || ':-error-no-selector') | ||
declarations(parser, emit, '', tree, local) | ||
} | ||
} | ||
// This is the first entry in the filters array, which is | ||
// actually the last step of the compiler. It inserts | ||
// closing braces to close normal (non at-) rules (those | ||
// that start with a selector). Doing it earlier is | ||
// impossible without passing state around in unrelated code | ||
// or ending up with duplicated selectors when the source tree | ||
// contains arrays. | ||
// There's no `S` handler, because the core compiler never | ||
// calls it. | ||
export function closeSelectors(next, inline) { | ||
var lastSelector | ||
return inline ? next : { | ||
i: function(){lastSelector = 0; next.i()}, | ||
x: function (raw) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
return next.x(raw) | ||
}, | ||
a: function (rule, param, takesBlock) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.a(rule, param, takesBlock) | ||
}, | ||
A: function (rule) { | ||
if (lastSelector) {next.S(); lastSelector = 0} | ||
next.A(rule) | ||
}, | ||
s: function (selector) { | ||
if (selector !== lastSelector){ | ||
if (lastSelector) next.S() | ||
next.s(selector) | ||
lastSelector = selector | ||
} | ||
}, | ||
d: next.d | ||
} | ||
} |
2241
test/test.js
/*eslint-env node, mocha */ | ||
// ensure that we are not sensitive to additions to Object.prototype. | ||
Object.prototype.BADBAD = 5 | ||
// used to normalize styles for reliable comparison. | ||
@@ -10,4 +13,2 @@ var expect = require('expect.js'), | ||
function normalize(s) { return postcss.process(s).css } | ||
@@ -17,9 +18,3 @@ | ||
result = normalize(result) | ||
// since you can't rely on the order of JS object keys, sometimes, several "expected" | ||
// values must be provided. | ||
// expected = (expected instanceof Array ? expected : [expected]).map(function(s){ | ||
// return normalize(s) | ||
// }) | ||
expect(normalize(expected)).to.contain(result) | ||
expect(normalize(expected)).to.be(result) | ||
} | ||
@@ -34,7 +29,5 @@ | ||
function webkitify(decl) {return '-webkit-' + decl + '\n' + decl} | ||
[ | ||
'../dist/j2c.commonjs', | ||
'../dist/j2c.commonjs.min' | ||
'../dist/j2c.commonjs.min', | ||
'../dist/j2c.commonjs' | ||
// , | ||
@@ -44,820 +37,1139 @@ // '../dist/inline/j2c.commonjs', | ||
].forEach(function(lib){ | ||
var j2c = require(lib) | ||
var J2C = require(lib) | ||
;[ | ||
function(){ | ||
Object.keys(J2C.names).forEach(function(k){delete J2C.names[k]}) | ||
return J2C | ||
}, | ||
function(){return J2C()} | ||
].forEach(function(j2c){ | ||
function checkinline(result, expected){ | ||
result = 'p{' + j2c.inline(result) + '}' | ||
expected = (expected instanceof Array ? expected : [expected]).map(function(s){ | ||
return 'p{' + s + '}' | ||
}) | ||
check(result, expected) | ||
} | ||
function checkinline(result, expected){ | ||
result = 'p{' + j2c().inline(result) + '}' | ||
expected = (expected instanceof Array ? expected : [expected]).map(function(s){ | ||
return 'p{' + s + '}' | ||
}) | ||
check(result, expected) | ||
} | ||
///////////////////////////// | ||
/**/ suite('Inline: ') /**/ | ||
///////////////////////////// | ||
///////////////////////////// | ||
/**/ suite('Inline: ') /**/ | ||
///////////////////////////// | ||
test('a single property', function() { | ||
checkinline( | ||
{foo: 'bar'}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('a single property', function() { | ||
checkinline( | ||
{foo: 'bar'}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('two properties', function() { | ||
checkinline( | ||
{foo: 'bar', baz: 'qux'}, | ||
'foo:bar;baz:qux;' | ||
) | ||
}) | ||
test('two properties, ensure order', function() { | ||
checkinline( | ||
{foo: 'bar', baz: 'qux'}, | ||
'foo:bar; baz:qux;' | ||
) | ||
}) | ||
test('two properties, ensure order', function() { | ||
check(j2c.inline({foo: 'bar', baz: 'qux'}), 'foo:bar;\nbaz:qux;') | ||
}) | ||
test('array of values', function() { | ||
checkinline( | ||
{foo:['bar', 'baz']}, | ||
'foo:bar; foo:baz;' | ||
) | ||
}) | ||
test('one sub-property', function(){ | ||
checkinline( | ||
{foo: {bar: 'baz'}}, | ||
'foo-bar: baz;' | ||
) | ||
}) | ||
test('array of values', function() { | ||
checkinline( | ||
{foo:['bar', 'baz']}, | ||
'foo:bar;foo:baz;' | ||
) | ||
}) | ||
test('two declrations at the top level', function() { | ||
checkinline( | ||
{foo: 'qux', baz: 'qux'}, | ||
'foo:qux;baz:qux;' | ||
) | ||
}) | ||
test('sub-properties', function(){ | ||
checkinline( | ||
{foo: {bar: 'baz'}}, | ||
'foo-bar:baz;' | ||
) | ||
}) | ||
test('two sub-properties', function(){ | ||
checkinline( | ||
{foo: {bar: 'baz', qux: 'baz'}}, | ||
'foo-bar:baz; foo-qux:baz;' | ||
) | ||
}) | ||
test('multiple sub-properties', function(){ | ||
checkinline( | ||
{foo: {bar$qux: 'baz'}}, | ||
'foo-bar:baz;foo-qux:baz;' | ||
) | ||
}) | ||
test('two sub-properties with a sub-sub-property', function(){ | ||
checkinline( | ||
{foo: {bar: {qux: 'quux'}, baz: {qix: 'qiix'}}}, | ||
'foo-bar-qux:quux; foo-baz-qix:qiix;' | ||
) | ||
}) | ||
test('multiple sub-properties, ensure order', function() { | ||
check(j2c.inline({foo$baz: 'qux'}), 'foo:qux;\nbaz:qux;') | ||
}) | ||
test('$ operator in sub-property and sub-sub-property', function(){ | ||
checkinline( | ||
{foo: {bar: {qux: 'fred', quux: 'frod'}, baz: {qix: 'frad', qiix: 'frud'}}}, | ||
'foo-bar-qux:fred; foo-bar-quux:frod; foo-baz-qix:frad; foo-baz-qiix:frud;' | ||
) | ||
}) | ||
test('$ operator at the top level', function() { | ||
checkinline( | ||
{foo$baz: 'qux'}, | ||
'foo:qux;baz:qux;' | ||
) | ||
}) | ||
test('$ operator in sub-properties', function(){ | ||
checkinline( | ||
{foo: {bar$qux: 'baz'}}, | ||
'foo-bar:baz; foo-qux:baz;' | ||
) | ||
}) | ||
test('multiple sub-properties with a sub-sub-property', function(){ | ||
checkinline( | ||
{foo: {bar$baz: {qux: 'quux'}}}, | ||
'foo-bar-qux:quux;foo-baz-qux:quux;' | ||
) | ||
}) | ||
test('$ operator in a sub-property with a sub-sub-property', function(){ | ||
checkinline( | ||
{foo: {bar$baz: {qux: 'quux'}}}, | ||
'foo-bar-qux:quux; foo-baz-qux:quux;' | ||
) | ||
}) | ||
test('multiple sub-properties with two sub-sub-properties', function(){ | ||
checkinline( | ||
{foo: {bar$baz: {qux$quux: 'fred'}}}, | ||
'foo-bar-qux:fred;foo-bar-quux:fred;foo-baz-qux:fred;foo-baz-quux:fred;' | ||
) | ||
}) | ||
test('$ operator in sub-property and sub-sub-property', function(){ | ||
checkinline( | ||
{foo: {bar$baz: {qux$quux: 'fred'}}}, | ||
'foo-bar-qux:fred; foo-bar-quux:fred; foo-baz-qux:fred; foo-baz-quux:fred;' | ||
) | ||
}) | ||
test('convert underscores', function() { | ||
checkinline( | ||
{'f_o_o': 'bar'}, | ||
'f-o-o:bar;' | ||
) | ||
}) | ||
test('convert underscores', function() { | ||
checkinline( | ||
{'f_o_o': 'bar'}, | ||
'f-o-o:bar;' | ||
) | ||
}) | ||
test('convert CamelCase', function() { | ||
checkinline( | ||
{'FoO': 'bar'}, | ||
'-fo-o:bar;' | ||
) | ||
}) | ||
test('convert CamelCase', function() { | ||
checkinline( | ||
{'FoO': 'bar'}, | ||
'-fo-o:bar;' | ||
) | ||
}) | ||
test('String value', function() { | ||
checkinline( | ||
'foo:bar', | ||
'foo:bar;' | ||
) | ||
}) | ||
test('String value', function() { | ||
checkinline( | ||
'foo:bar', | ||
'foo:bar;' | ||
) | ||
}) | ||
test('Array of Strings values', function() { | ||
checkinline( | ||
['foo:bar', 'foo:baz'], | ||
'foo:bar;foo:baz' | ||
) | ||
}) | ||
test('Array of Strings values', function() { | ||
checkinline( | ||
['foo:bar', 'foo:baz'], | ||
'foo:bar;foo:baz' | ||
) | ||
}) | ||
test('Array of mixed values at the root', function() { | ||
checkinline( | ||
['foo:bar', {foo: 'baz'}], | ||
'foo:bar;foo:baz' | ||
) | ||
}) | ||
test('Array of mixed values at the root', function() { | ||
checkinline( | ||
['foo:bar', {foo: 'baz'}], | ||
'foo:bar;foo:baz' | ||
) | ||
}) | ||
test('Array of mixed value and sub-property', function() { | ||
checkinline( | ||
{foo:['bar', {baz: 'qux'}]}, | ||
'foo:bar;foo-baz:qux' | ||
) | ||
}) | ||
test('Array of mixed value and sub-property', function() { | ||
checkinline( | ||
{foo:['bar', {baz: 'qux'}]}, | ||
'foo:bar;foo-baz:qux' | ||
) | ||
}) | ||
test('Prefixes by hand', function() { | ||
checkinline( | ||
{_o$_p$: {foo: 'bar'}}, | ||
'-o-foo:bar;-p-foo:bar;foo:bar;' | ||
) | ||
}) | ||
test('Prefixes by hand', function() { | ||
checkinline( | ||
{_o$_p$: {foo: 'bar'}}, | ||
'-o-foo:bar;-p-foo:bar;foo:bar;' | ||
) | ||
}) | ||
test('CSS *Hack', function() { | ||
// tested manually because the crass normalization | ||
// outputs an empty string. | ||
check(j2c.inline({'*foo': 'bar'}), '*foo:bar;') | ||
}) | ||
test('CSS *Hack', function() { | ||
// tested manually because the crass normalization | ||
// outputs an empty string. | ||
checkinline( | ||
{'*foo': 'bar'}, | ||
'*foo:bar;' | ||
) | ||
}) | ||
test('CSS _Hack', function() { | ||
checkinline( | ||
['_foo:bar', {_baz: 'qux'}], | ||
'_foo:bar;-baz:qux;' | ||
) | ||
}) | ||
test('CSS _Hack', function() { | ||
checkinline( | ||
['_foo:bar', {_baz: 'qux'}], | ||
'_foo:bar;-baz:qux;' | ||
) | ||
}) | ||
test('custom obj.valueOf', function() { | ||
var bar = {valueOf:function(){return 'bar'}} | ||
checkinline( | ||
{foo:bar}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('custom obj.valueOf', function() { | ||
var bar = {valueOf:function(){return 'theBAR'}} | ||
checkinline( | ||
{foo:bar}, | ||
'foo:theBAR;' | ||
) | ||
}) | ||
//////////////////////////////////// | ||
/**/ suite('Inline, nulls: ') /**/ | ||
//////////////////////////////////// | ||
//////////////////////////////////// | ||
/**/ suite('Inline, nulls: ') /**/ | ||
//////////////////////////////////// | ||
test('null value', function() { | ||
checkinline( | ||
null, | ||
'' | ||
) | ||
}) | ||
test('null value', function() { | ||
checkinline( | ||
null, | ||
'' | ||
) | ||
}) | ||
test('null leafs', function() { | ||
checkinline( | ||
{foo:null}, | ||
'' | ||
) | ||
}) | ||
test('null leafs', function() { | ||
checkinline( | ||
{foo:null}, | ||
'' | ||
) | ||
}) | ||
test('undefined leafs', function() { | ||
checkinline( | ||
{foo:void 8}, | ||
'' | ||
) | ||
}) | ||
test('undefined leafs', function() { | ||
checkinline( | ||
{foo:void 8}, | ||
'' | ||
) | ||
}) | ||
test('null value', function() { | ||
checkinline( | ||
null, | ||
'' | ||
) | ||
}) | ||
test('null leafs in array', function() { | ||
checkinline( | ||
{foo:[null]}, | ||
'' | ||
) | ||
}) | ||
test('undefined value', function() { | ||
checkinline( | ||
void 8, | ||
'' | ||
) | ||
}) | ||
test('null in Array', function() { | ||
checkinline( | ||
[null], | ||
'' | ||
) | ||
}) | ||
test('undefined leafs in array', function() { | ||
checkinline( | ||
{foo:[void 8]}, | ||
'' | ||
) | ||
}) | ||
test('undefined in Array', function() { | ||
checkinline( | ||
[void 8], | ||
'' | ||
) | ||
}) | ||
test('null leafs in array, preceded by value', function() { | ||
checkinline( | ||
{foo:['bar', null]}, | ||
'foo:bar;' | ||
) | ||
}) | ||
//////////////////////////////////////// | ||
/**/ suite('Inline namespaces: ') /**/ | ||
//////////////////////////////////////// | ||
test('undefined leafs in array, preceded by value', function() { | ||
checkinline( | ||
{foo:['bar', void 8]}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('namespaced animation', function() { | ||
var result = j2c.inline({foo:'theFoo'}, {animation:'foo 1sec'}) | ||
check(result, webkitify('animation:theFoo 1sec;')) | ||
}) | ||
test('null leafs in arry, followed by a value', function() { | ||
checkinline( | ||
{foo:[null, 'bar']}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('namespaced animation-name', function() { | ||
var result = j2c.inline({foo:'theFoo'}, {animation_name:'foo'}) | ||
check(result, webkitify('animation-name:theFoo;')) | ||
}) | ||
test('undefined leafs in arry, followed by a value', function() { | ||
checkinline( | ||
{foo:[void 8, 'bar']}, | ||
'foo:bar;' | ||
) | ||
}) | ||
test('namespaced and non-namespaced animation-name', function() { | ||
var result = j2c.inline({foo:'theFoo'}, {animation_name:'foo, bar'}) | ||
check(result, webkitify('animation-name:theFoo, bar;')) | ||
}) | ||
test('undefined value', function() { | ||
checkinline( | ||
void 8, | ||
'' | ||
) | ||
}) | ||
test('two namespaced animations', function() { | ||
var result = j2c.inline({foo:'theFoo', bar:'theBar'}, {animation:'foo 1sec, bar 2sec'}) | ||
check(result, webkitify('animation:theFoo 1sec, theBar 2sec;')) | ||
}) | ||
test('null in Array', function() { | ||
checkinline( | ||
[null], | ||
'' | ||
) | ||
}) | ||
test('undefined in Array', function() { | ||
checkinline( | ||
[void 8], | ||
'' | ||
) | ||
}) | ||
test('null in Array followed by object', function() { | ||
checkinline( | ||
[null, {foo:'bar'}], | ||
'foo:bar;' | ||
) | ||
}) | ||
///////////////////////////////////// | ||
/**/ suite('Inline plugins: ') /**/ | ||
///////////////////////////////////// | ||
test('undefined in Array followed by object', function() { | ||
checkinline( | ||
[void 8, {foo:'bar'}], | ||
'foo:bar;' | ||
) | ||
}) | ||
test('one plugin that does nothing', function() { | ||
check(''+j2c().use(function(){}).inline( | ||
{foo: 'bar'} | ||
), 'foo:bar;') | ||
}) | ||
test('object followed by null in Array', function() { | ||
checkinline( | ||
[{foo:'bar'}, null], | ||
'foo:bar;' | ||
) | ||
}) | ||
test('one plugin that mutates the buffer', function() { | ||
check(''+j2c().use( | ||
function(buf){ | ||
buf[0] = buf[0].replace('f','k') | ||
} | ||
).inline( | ||
{foo: 'bar'} | ||
), 'koo:bar;') | ||
}) | ||
test('object followed by undefined in Array', function() { | ||
checkinline( | ||
[{foo:'bar'}, void 8], | ||
'foo:bar;' | ||
) | ||
}) | ||
test('one plugin that returns a new buffer', function() { | ||
expect(''+j2c().use( | ||
function(){ | ||
return ['hello:world;'] | ||
} | ||
).inline( | ||
{foo: 'bar'} | ||
)).to.be('hello:world;') | ||
}) | ||
test('two plugins that mutate the buffer', function() { | ||
check(''+j2c().use( | ||
function(buf){ | ||
buf[0]=buf[0].replace('f', 'a') | ||
}, | ||
function(buf){ | ||
buf[0]=buf[0].replace('a', 'm') | ||
} | ||
).inline( | ||
{foo: 'bar'} | ||
), 'moo:bar;') | ||
}) | ||
// ///////////////////////////////// | ||
// /**/ suite('j2c().prefix: ') /**/ | ||
// ///////////////////////////////// | ||
// test('1 x 1', function() { | ||
// var prod = j2c().prefix('foo', ['o']) | ||
// expect(prod[0]).to.be('-o-foo') | ||
// expect(prod[1]).to.be('foo') | ||
// }) | ||
///////////////////////////////// | ||
/**/ suite('j2c.prefix: ') /**/ | ||
///////////////////////////////// | ||
// test('2 x 1', function() { | ||
// var prod = j2c().prefix('foo', ['o', 'p']) | ||
// expect(prod[0]).to.be('-o-foo') | ||
// expect(prod[1]).to.be('-p-foo') | ||
// expect(prod[2]).to.be('foo') | ||
// }) | ||
test('1 x 1', function() { | ||
var prod = j2c.prefix('foo', ['o']) | ||
expect(prod[0]).to.be('-o-foo') | ||
expect(prod[1]).to.be('foo') | ||
}) | ||
//////////////////////////////// | ||
/**/ suite('j2c().sheet: ') /**/ | ||
//////////////////////////////// | ||
test('2 x 1', function() { | ||
var prod = j2c.prefix('foo', ['o', 'p']) | ||
expect(prod[0]).to.be('-o-foo') | ||
expect(prod[1]).to.be('-p-foo') | ||
expect(prod[2]).to.be('foo') | ||
}) | ||
}); | ||
test('direct sheet call', function(){ | ||
check( | ||
j2c().sheet({p: {foo:5}}), | ||
'p{foo:5}' | ||
) | ||
}) | ||
////////////////////////////////// | ||
/**/ suite('Definitions: ') /**/ | ||
////////////////////////////////// | ||
['../dist/j2c.commonjs', '../dist/j2c.commonjs.min'].forEach(function(lib){ | ||
var j2c = require(lib) | ||
test('basic', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo: 'bar' | ||
}}), | ||
'p{foo:bar}' | ||
) | ||
}) | ||
test('convert underscores', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo_foo: 'bar' | ||
}}), | ||
'p{foo-foo:bar}' | ||
) | ||
}) | ||
//////////////////////////////// | ||
/**/ suite('j2c.sheet: ') /**/ | ||
//////////////////////////////// | ||
test('convert CamelCase', function() { | ||
check( | ||
j2c().sheet({p: { | ||
fooFoo: 'bar' | ||
}}), | ||
'p{foo-foo:bar}' | ||
) | ||
}) | ||
test('number values', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo:5 | ||
}}), | ||
'p{foo:5}' | ||
) | ||
}) | ||
test('direct sheet call', function(){ | ||
check( | ||
j2c.sheet({p: {foo:5}}), | ||
'p{foo:5}' | ||
) | ||
}) | ||
test('composed property name', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo: {bar: 'baz'} | ||
}}), | ||
'p{foo-bar:baz}' | ||
) | ||
}) | ||
test('composed selector : child with a given class', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
' .foo': {bar: 'baz'} | ||
}}}), | ||
////////////////////////////////// | ||
/**/ suite('Definitions: ') /**/ | ||
////////////////////////////////// | ||
'p .foo{bar:baz}' | ||
) | ||
}) | ||
test('composed selector: add a class to the root', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
'.foo': {bar: 'baz'} | ||
}}}), | ||
test('basic', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo: 'bar' | ||
}}), | ||
'p{foo:bar}' | ||
) | ||
}) | ||
'p.foo{bar:baz}' | ||
) | ||
}) | ||
test('convert underscores', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo_foo: 'bar' | ||
}}), | ||
'p{foo-foo:bar}' | ||
) | ||
}) | ||
test('manual vendor prefixes', function() { | ||
check( | ||
j2c().sheet({p: { | ||
_o$_ms$_moz$_webkit$: {foo: 'bar'} | ||
}}), | ||
test('number values', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo:5 | ||
}}), | ||
'p{foo:5}' | ||
) | ||
}) | ||
'p {-o-foo:bar;-ms-foo:bar;-moz-foo:bar;-webkit-foo:bar;foo:bar}' | ||
) | ||
}) | ||
test('composed property name', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo: {bar: 'baz'} | ||
}}), | ||
test('mixing definitions and sub-selectors', function() { | ||
check( | ||
j2c().sheet({'@global': { | ||
p: { | ||
foo: 'bar', | ||
' .foo': {bar: 'baz'} | ||
} | ||
}}), | ||
'p {foo:bar} p .foo{bar:baz}' | ||
) | ||
}) | ||
'p{foo-bar:baz}' | ||
) | ||
}) | ||
test('composed selector : child with a given class', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
' .foo': {bar: 'baz'} | ||
}}}), | ||
'p .foo{bar:baz}' | ||
) | ||
}) | ||
////////////////////////////////////////////////// | ||
/**/ suite('Selector Cartesian product: ') /**/ | ||
////////////////////////////////////////////////// | ||
test('composed selector: add a class to the root', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
'.foo': {bar: 'baz'} | ||
}}}), | ||
'p.foo{bar:baz}' | ||
) | ||
}) | ||
test('1 x 2', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
' .foo': { | ||
':before,:after': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}}), | ||
test('manual vendor prefixes', function() { | ||
check( | ||
j2c.sheet({p: { | ||
_o$_ms$_moz$_webkit$: {foo: 'bar'} | ||
}}), | ||
'p .foo:before, p .foo:after {foo:bar}' | ||
) | ||
}) | ||
'p {-o-foo:bar;-ms-foo:bar;-moz-foo:bar;-webkit-foo:bar;foo:bar}' | ||
) | ||
}) | ||
test('2 x 1', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
':before': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}}), | ||
test('mixing definitions and sub-selectors', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
foo: 'bar', | ||
' .foo': {bar: 'baz'} | ||
}}}), | ||
'p .foo:before, p .bar:before {foo:bar}' | ||
) | ||
}) | ||
['p .foo{bar:baz} p {foo:bar}', 'p {foo:bar} p .foo{bar:baz}'] | ||
) | ||
}) | ||
test('2 x 2', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
':before,:after': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}}), | ||
'p .foo:before, p .bar:before, p .foo:after, p .bar:after {foo:bar}' | ||
) | ||
}) | ||
////////////////////////////////////////////////// | ||
/**/ suite('Selector Cartesian product: ') /**/ | ||
////////////////////////////////////////////////// | ||
test('2 x 3 one of which is empty', function() { | ||
check( | ||
j2c().sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
',:before,:after': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}}), | ||
'p .foo, p .bar, p .foo:before, p .bar:before, p .foo:after, p .bar:after {foo:bar}' | ||
) | ||
}) | ||
test("don't split on comas inside double quoted strings ...", function() { | ||
check(j2c().sheet({ | ||
'a[foo="bar,baz"]': { | ||
' p': {qux: 5} | ||
} | ||
}), 'a[foo="bar,baz"] p {qux: 5}') | ||
}) | ||
test('1 x 2', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
' .foo': { | ||
':before,:after': { | ||
foo: 'bar' | ||
} | ||
test('... nor split on comas inside single quoted strings ...', function() { | ||
check(j2c().sheet({ | ||
"a[foo='bar,baz']": { | ||
' p': {qux: 5} | ||
} | ||
}}}), | ||
}), "a[foo='bar,baz'] p {qux: 5}") | ||
}) | ||
'p .foo:before, p .foo:after {foo:bar}' | ||
) | ||
}) | ||
test('... nor split on comas inside comments ...', function() { | ||
check(j2c().sheet({ | ||
'a/*bar,baz*/': { | ||
' p': {qux: 5} | ||
} | ||
}), 'a/*bar,baz*/ p {qux: 5}') | ||
}) | ||
test('2 x 1', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
':before': { | ||
foo: 'bar' | ||
} | ||
test('... nor split on comas inside parentheses ...', function() { | ||
check(j2c().sheet({ | ||
'p:any(a, ul)': { | ||
' li': {qux: 5} | ||
} | ||
}}}), | ||
}), 'p:any(a, ul) li {qux: 5}') | ||
}) | ||
'p .foo:before, p .bar:before {foo:bar}' | ||
) | ||
}) | ||
test('... but split in between', function(){ | ||
check(j2c().sheet({ | ||
'a[foo="bar,baz"], a:any(p, ul), a/*bar,baz*/': { | ||
' p': {qux: 5} | ||
} | ||
}), 'a[foo="bar,baz"] p, a:any(p, ul) p, a/*bar,baz*/ p {qux: 5}') | ||
}) | ||
test('2 x 2', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
':before,:after': { | ||
foo: 'bar' | ||
///////////////////////////////// | ||
/**/ suite('Ampersand: ') /**/ | ||
////////////////////////////////// | ||
test('.foo &', function() { | ||
check( | ||
j2c().sheet({p: { | ||
':global(.foo) &': {bar: 'baz'} | ||
}}), | ||
'.foo p{bar:baz}' | ||
) | ||
}) | ||
test('& &', function() { | ||
check( | ||
j2c().sheet({':global(.foo)': { | ||
'& &': { | ||
bar: 'baz' | ||
} | ||
} | ||
}}}), | ||
}}), | ||
'.foo .foo{bar:baz}' | ||
) | ||
}) | ||
'p .foo:before, p .bar:before, p .foo:after, p .bar:after {foo:bar}' | ||
) | ||
}) | ||
test('& [bar="&"].baz', function() { | ||
check( | ||
j2c().sheet({':global(.foo)': { | ||
'& [bar="&"]:global(.baz)': { | ||
qux: 'quux' | ||
} | ||
}}), | ||
'.foo [bar="&"].baz{qux:quux}' | ||
) | ||
}) | ||
test("& [bar='&'].baz", function() { | ||
check( | ||
j2c().sheet({':global(.foo)': { | ||
'& [bar="&"]:global(.baz)': { | ||
qux: 'quux' | ||
} | ||
}}), | ||
'.foo [bar="&"].baz{qux:quux}' | ||
) | ||
}) | ||
test('2 x 3 one of which is empty', function() { | ||
check( | ||
j2c.sheet({'@global': {p: { | ||
' .foo, .bar': { | ||
',:before,:after': { | ||
foo: 'bar' | ||
test('& /*&*/.baz', function() { | ||
check( | ||
j2c().sheet({':global(.foo)': { | ||
'& /*&*/:global(.baz)': { | ||
qux: 'quux' | ||
} | ||
} | ||
}}}), | ||
'p .foo, p .bar, p .foo:before, p .bar:before, p .foo:after, p .bar:after {foo:bar}' | ||
) | ||
}) | ||
}}), | ||
'.foo /*&*/.baz{qux:quux}' | ||
) | ||
}) | ||
test('& &, cartesian product', function() { | ||
check( | ||
j2c().sheet({'p,a': { | ||
'& &': { | ||
bar: 'baz' | ||
} | ||
}}), | ||
'p p,p a,a p,a a {bar:baz}' | ||
) | ||
}) | ||
test('2 x 2', function() { | ||
check( | ||
j2c().sheet({p: { | ||
' :global(.foo), :global(.bar)': { | ||
' :global(.baz) &, :global(.qux)': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}), | ||
'.baz p .foo,.baz p .bar,p .foo .qux ,p .bar .qux {foo:bar}' | ||
) | ||
}) | ||
///////////////////////////////// | ||
/**/ suite('Ampersand: ') /**/ | ||
////////////////////////////////// | ||
test('& in @global context', function() { | ||
check( | ||
j2c().sheet({'@global': { | ||
'.foo': { | ||
'& &': { | ||
bar: 'baz' | ||
} | ||
} | ||
}}), | ||
'.foo .foo{bar:baz}' | ||
) | ||
}) | ||
////////////////////////////////////////// | ||
/**/ suite('Strings and Arrays: ') /**/ | ||
////////////////////////////////////////// | ||
test('composed selector: add a class to the root', function() { | ||
check( | ||
j2c.sheet({p: { | ||
':global(.foo) &': {bar: 'baz'} | ||
}}), | ||
'.foo p{bar:baz}' | ||
) | ||
}) | ||
test('& &', function() { | ||
check( | ||
j2c.sheet({':global(.foo)': { | ||
'& &': { | ||
bar: 'baz' | ||
} | ||
}}), | ||
'.foo .foo{bar:baz}' | ||
) | ||
}) | ||
test('String literal', function() { | ||
check( | ||
j2c().sheet({p: 'foo:bar'}), | ||
'p{foo:bar}' | ||
) | ||
}) | ||
test('2 x 2', function() { | ||
check( | ||
j2c.sheet({p: { | ||
' :global(.foo), :global(.bar)': { | ||
' :global(.baz) &, :global(.qux)': { | ||
foo: 'bar' | ||
} | ||
} | ||
}}), | ||
'.baz p .foo,.baz p .bar,p .foo .qux ,p .bar .qux {foo:bar}' | ||
) | ||
}) | ||
test('String literal with two declarations', function() { | ||
check( | ||
j2c().sheet({p: 'foo:bar;baz:qux'}), | ||
'p {foo:bar;baz:qux}' | ||
) | ||
}) | ||
////////////////////////////////////////// | ||
/**/ suite('Strings and Arrays: ') /**/ | ||
////////////////////////////////////////// | ||
test('String literal starting with an underscore', function() { | ||
check( | ||
j2c().sheet({p: '_foo:bar'}), | ||
'p {_foo:bar}' | ||
) | ||
}) | ||
test('Array of String literals', function() { | ||
check( | ||
j2c().sheet({p: ['foo:bar', 'foo:baz']}), | ||
'p{foo:bar;foo:baz}' | ||
) | ||
}) | ||
test('String literal', function() { | ||
check( | ||
j2c.sheet({p: 'foo:bar'}), | ||
'p{foo:bar}' | ||
) | ||
}) | ||
test('String literal with two declarations', function() { | ||
check( | ||
j2c.sheet({p: 'foo:bar;baz:qux'}), | ||
'p {foo:bar;baz:qux}' | ||
) | ||
}) | ||
test('overloaded properties', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo:['bar', 'baz'] | ||
}}), | ||
'p{foo:bar;foo:baz}' | ||
) | ||
}) | ||
test('String literal starting with an underscore', function() { | ||
check( | ||
j2c.sheet({p: '_foo:bar'}), | ||
'p {_foo:bar}' | ||
) | ||
}) | ||
test('overloaded sub-properties', function() { | ||
check( | ||
j2c().sheet({p: { | ||
foo:[{bar: 'baz'}, {bar: 'qux'}] | ||
}}), | ||
'p{foo-bar:baz;foo-bar:qux}' | ||
) | ||
}) | ||
test('Array of String literals', function() { | ||
check( | ||
j2c.sheet({p: ['foo:bar', 'foo:baz']}), | ||
'p{foo:bar}p{foo:baz}' | ||
) | ||
}) | ||
test('nested Arrays', function(){ | ||
check( | ||
j2c().sheet({p: [ | ||
[ | ||
{bar: 'baz'}, | ||
{bar: 'qux'} | ||
], | ||
'bar:quux;' | ||
]}), | ||
'p{bar:baz;bar:qux;bar:quux}' | ||
) | ||
}) | ||
test('overloaded properties', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo:['bar', 'baz'] | ||
}}), | ||
'p{foo:bar;foo:baz}' | ||
) | ||
}) | ||
test('overloaded sub-properties', function() { | ||
check( | ||
j2c.sheet({p: { | ||
foo:[{bar: 'baz'}, {bar: 'qux'}] | ||
}}), | ||
'p{foo-bar:baz;foo-bar:qux}' | ||
) | ||
}) | ||
// /////////////////////////////////////////// | ||
// /**/ suite("Sheet auto prefixes: "); /**/ | ||
// /////////////////////////////////////////// | ||
test('nested Arrays', function(){ | ||
check( | ||
j2c.sheet({p: [ | ||
// test("String literal", function() { | ||
// check( | ||
// j2c().sheet({" p": "foo:bar"}, {vendors: ["o", "p"]}), | ||
// "p{-o-foo:bar;-p-foo:bar;foo:bar}" | ||
// ); | ||
// }); | ||
// test("Array of Strings", function() { | ||
// check( | ||
// j2c().sheet({" p": ["foo:bar", "_baz:qux"]}, {vendors: ["o", "p"]}), | ||
// "p{-o-foo:bar;-p-foo:bar;foo:bar;-o-_baz:qux;-p-_baz:qux;_baz:qux}" | ||
// ); | ||
// }); | ||
//////////////////////////////// | ||
/**/ suite('At-rules: ') /**/ | ||
//////////////////////////////// | ||
test('@charset', function() { | ||
check( | ||
j2c().sheet({ | ||
'@charset': '"UTF-8"' | ||
}), | ||
'@charset "UTF-8";' | ||
) | ||
}) | ||
test('@import', function() { | ||
check( | ||
j2c().sheet({ | ||
'@import': 'url("bluish.css") projection, tv' | ||
}), | ||
'@import url("bluish.css") projection, tv;' | ||
) | ||
}) | ||
test('@namespace', function() { | ||
check( | ||
j2c().sheet({ | ||
'@namespace': 'prefix url(XML-namespace-URL)' | ||
}), | ||
'@namespace prefix url(XML-namespace-URL);' | ||
) | ||
}) | ||
test('@media', function() { | ||
check( | ||
j2c().sheet({p: { | ||
'@media foo': {bar: 'baz'} | ||
}}), | ||
'@media foo {p{bar:baz}}' | ||
) | ||
}) | ||
test('@supports', function() { | ||
check( | ||
j2c().sheet({ | ||
'@supports not (text-align-last:justify)': { | ||
'p': { | ||
textAlignLast: 'justify' | ||
} | ||
} | ||
}), | ||
'@supports not (text-align-last:justify) {p {text-align-last:justify}}' | ||
) | ||
}) | ||
test('@page', function() { | ||
check( | ||
j2c().sheet({ | ||
'@page :first': { | ||
margin: '2in 3in' | ||
} | ||
}), | ||
'@page :first {margin: 2in 3in;}' | ||
) | ||
}) | ||
test('several @media with object value', function() { | ||
check( | ||
j2c().sheet({p: { | ||
'@media foo': {bar: 'baz'}, | ||
'@media foo2': {bar2: 'baz2'} | ||
}}), | ||
[ | ||
{bar: 'baz'}, | ||
{bar: 'qux'} | ||
], | ||
'bar:quux;' | ||
]}), | ||
'p{bar:baz}p{bar:qux}p{bar:quux}' | ||
) | ||
}) | ||
'@media foo {p{bar:baz}} @media foo2 {p{bar2:baz2}}' | ||
] | ||
) | ||
}) | ||
test('Array of @import with text values', function() { | ||
check( | ||
j2c().sheet([ | ||
{'@import': "'bar'"}, | ||
{'@import': "'baz'"} | ||
]), | ||
"@import 'bar'; @import 'baz';" | ||
) | ||
}) | ||
test('nested @media', function() { | ||
check( | ||
j2c().sheet({p: {'@media screen': {width:1000, '@media (max-width: 12cm)': {size:5}}}}), | ||
[ | ||
'@media screen{p{width:1000}@media (max-width:12cm){p{size:5}}}' | ||
] | ||
) | ||
}) | ||
// /////////////////////////////////////////// | ||
// /**/ suite("Sheet auto prefixes: "); /**/ | ||
// /////////////////////////////////////////// | ||
test('@font-face', function(){ | ||
check( | ||
j2c().sheet({'@font-face': {foo: 'bar'}}), | ||
'@font-face{foo:bar}' | ||
) | ||
}) | ||
// test("String literal", function() { | ||
// check( | ||
// j2c.sheet({" p": "foo:bar"}, {vendors: ["o", "p"]}), | ||
// "p{-o-foo:bar;-p-foo:bar;foo:bar}" | ||
// ); | ||
// }); | ||
test('@keyframes', function(){ | ||
check( | ||
j2c().sheet({'@keyframes global(qux)': { | ||
from: {foo: 'bar'}, | ||
to: {foo: 'baz'} | ||
}}), | ||
'@keyframes qux{from{foo:bar}to{foo:baz}}' | ||
) | ||
}) | ||
// test("Array of Strings", function() { | ||
// check( | ||
// j2c.sheet({" p": ["foo:bar", "_baz:qux"]}, {vendors: ["o", "p"]}), | ||
// "p{-o-foo:bar;-p-foo:bar;foo:bar;-o-_baz:qux;-p-_baz:qux;_baz:qux}" | ||
// ); | ||
// }); | ||
test('invalid @foo becomes @-error-unsupported-at-rule "@foo"', function(){ | ||
check( | ||
j2c().sheet({'@foo': 'bar'}), | ||
'@-error-unsupported-at-rule "@foo";' | ||
) | ||
}) | ||
test('invalid @ becomes @-error-unsupported-at-rule "@"', function(){ | ||
check( | ||
j2c().sheet({'@': 'bar'}), | ||
'@-error-unsupported-at-rule "@";' | ||
) | ||
//////////////////////////////// | ||
/**/ suite('At-rules: ') /**/ | ||
//////////////////////////////// | ||
}) | ||
///////////////////////////////////////////// | ||
/**/ suite('At-rules with prefixes: ') /**/ | ||
///////////////////////////////////////////// | ||
test('standard at-rule with text value', function() { | ||
check( | ||
j2c.sheet({p: { | ||
'@import': "'bar'" | ||
}}), | ||
"@import 'bar';" | ||
) | ||
}) | ||
test('@-webkit-keyframes', function(){ | ||
check( | ||
j2c().sheet({'@-webkit-keyframes global(qux)': { | ||
from: {foo: 'bar'}, | ||
to: {foo: 'baz'} | ||
}}), | ||
'@-webkit-keyframes qux{from{foo:bar}to{foo:baz}}' | ||
) | ||
}) | ||
test('standard at-rule with object value', function() { | ||
check( | ||
j2c.sheet({p: { | ||
'@media foo': {bar: 'baz'} | ||
}}), | ||
'@media foo {p{bar:baz}}' | ||
) | ||
}) | ||
///////////////////////////////////////////////// | ||
/**/ suite('At-rules with array values: ') /**/ | ||
///////////////////////////////////////////////// | ||
test('several at-rules with object value', function() { | ||
check( | ||
j2c.sheet({p: { | ||
'@media foo': {bar: 'baz'}, | ||
'@media foo2': {bar2: 'baz2'} | ||
}}), | ||
[ | ||
'@media foo {p{bar:baz}} @media foo2 {p{bar2:baz2}}' | ||
] | ||
) | ||
}) | ||
test('Array of at-rules with text values', function() { | ||
check( | ||
j2c.sheet({p: [ | ||
{'@import': "'bar'"}, | ||
{'@import': "'baz'"} | ||
]}), | ||
"@import 'bar'; @import 'baz';" | ||
) | ||
}) | ||
test('@font-face with a 1-element array', function(){ | ||
check( | ||
j2c().sheet({'@font-face':[{foo: 'bar'}]}), | ||
'@font-face{foo:bar}' | ||
) | ||
}) | ||
test('nested at-rules', function() { | ||
check( | ||
j2c.sheet({p: {'@media screen': {width:1000, '@media (max-width: 12cm)': {size:5}}}}), | ||
[ | ||
'@media screen{p{width:1000}@media (max-width:12cm){p{size:5}}}' | ||
] | ||
) | ||
}) | ||
test('@font-face with a 2-elements array', function(){ | ||
check( | ||
j2c().sheet({'@font-face':[{foo: 'bar'}, {foo: 'baz'}]}), | ||
'@font-face{foo:bar}@font-face{foo:baz}' | ||
) | ||
}) | ||
test('@font-face', function(){ | ||
check( | ||
j2c.sheet({p: {'@font-face': {foo: 'bar'}}}), | ||
'@font-face{foo:bar}' | ||
) | ||
}) | ||
test('@namespace with a 1-element array', function(){ | ||
check( | ||
j2c().sheet({'@namespace': ["'http://foo.example.com'"]}), | ||
"@namespace 'http://foo.example.com';" | ||
) | ||
}) | ||
test('@keyframes', function(){ | ||
check( | ||
j2c.sheet({p: {'@keyframes :global(qux)': { | ||
' from': {foo: 'bar'}, | ||
' to': {foo: 'baz'} | ||
}}}), | ||
'@-webkit-keyframes qux{from{-webkit-foo:bar;foo:bar}to{-webkit-foo:baz;foo:baz}}' + | ||
'@keyframes qux{from{foo:bar}to{foo:baz}}' | ||
) | ||
}) | ||
test('@namespace with a 2-elements array', function(){ | ||
check( | ||
j2c().sheet({'@namespace': ["'http://foo.example.com'", "bar 'http://bar.example.com'"]}), | ||
"@namespace 'http://foo.example.com';@namespace bar 'http://bar.example.com';" | ||
) | ||
}) | ||
test('invalid @foo becomes at-foo property', function(){ | ||
check( | ||
j2c.sheet({'@foo': 'bar'}), | ||
'@-error-unsupported-at-rule "@foo";' | ||
) | ||
//////////////////////////// | ||
/**/ suite('@adopt: ') /**/ | ||
//////////////////////////// | ||
}) | ||
test('basic usage', function(){ | ||
var _j2c = j2c() | ||
expect(_j2c.sheet({'@adopt foo': 'bar'})).to.be('') | ||
expect(_j2c.names).to.have.key('foo') | ||
expect(_j2c.names.foo).to.be('foo'+_j2c.suffix+' bar') | ||
}) | ||
////////////////////////////////////////////////// | ||
/**/ suite('At-rules with array values: ') /**/ | ||
////////////////////////////////////////////////// | ||
test('basic usage (with dots)', function(){ | ||
var _j2c = j2c() | ||
expect(_j2c.sheet({'@adopt .foo': '.bar'})).to.be('') | ||
expect(_j2c.names).to.have.key('foo') | ||
expect(_j2c.names.foo).to.be('foo'+_j2c.suffix+' bar') | ||
}) | ||
test('array of adoptees', function(){ | ||
var _j2c = j2c() | ||
expect(_j2c.sheet({'@adopt foo': ['.bar', 'baz']})).to.be('') | ||
expect(_j2c.names).to.have.key('foo') | ||
expect(_j2c.names.foo).to.be('foo'+_j2c.suffix+' bar baz') | ||
}) | ||
test('@font-face with a 1-element array', function(){ | ||
check( | ||
j2c.sheet({p: {'@font-face':[{foo: 'bar'}]}}), | ||
'@font-face{foo:bar}' | ||
) | ||
}) | ||
test('bad target name', function(){ | ||
var _j2c = j2c() | ||
check( | ||
_j2c.sheet({'@adopt /foo': '.bar'}), | ||
'@-error-bad-at-adopter /foo;' | ||
) | ||
expect(_j2c.names).not.to.have.key('/foo') | ||
expect(_j2c.names).not.to.have.key('foo') | ||
test('@font-face with a 2-elements array', function(){ | ||
check( | ||
j2c.sheet({p: {'@font-face':[{foo: 'bar'}, {foo: 'baz'}]}}), | ||
'@font-face{foo:bar}@font-face{foo:baz}' | ||
) | ||
}) | ||
}) | ||
test('@namespace with a 1-element array', function(){ | ||
check( | ||
j2c.sheet({'@namespace': ["'http://foo.example.com'"]}), | ||
"@namespace 'http://foo.example.com';" | ||
) | ||
}) | ||
test('bad parameter name', function(){ | ||
var _j2c = j2c() | ||
check( | ||
_j2c.sheet({'@adopt foo': '/bar'}), | ||
'@-error-bad-at-adoptee "/bar";' | ||
) | ||
expect(_j2c.names).not.to.have.key('foo') | ||
test('@namespace with a 2-elements array', function(){ | ||
check( | ||
j2c.sheet({'@namespace': ["'http://foo.example.com'", "bar 'http://bar.example.com'"]}), | ||
"@namespace 'http://foo.example.com';@namespace bar 'http://bar.example.com';" | ||
) | ||
}) | ||
}) | ||
///////////////////////////////////// | ||
/**/ suite('Locals, Globals ') /**/ | ||
///////////////////////////////////// | ||
test('forbidden in global scope', function(){ | ||
var _j2c = j2c() | ||
check( | ||
_j2c.sheet({'@global':{'@adopt foo': 'bar'}}), | ||
'@-error-bad-at-adopt-placement "@adopt foo";' | ||
) | ||
expect(_j2c.names).not.to.have.key('foo') | ||
}) | ||
test('a local class', function(){ | ||
var css = j2c.sheet({'.bit': {foo:5}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css + '').to.contain('.' + css.bit + ' {\nfoo:5;\n}') | ||
}) | ||
test('forbidden in conditional scope', function(){ | ||
var _j2c = j2c() | ||
check( | ||
_j2c.sheet({'@media screen':{'@adopt foo': 'bar'}}), | ||
'@media screen{@-error-bad-at-adopt-placement "@adopt foo";}' | ||
) | ||
expect(_j2c.names).not.to.have.key('foo') | ||
test('two local classes', function(){ | ||
var css = j2c.sheet({'.bit': {foo:5}, '.bat': {bar:6}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bit.slice(4)).to.be(css.bat.slice(4)) | ||
expect(css + '').to.contain('.' + css.bit + ' {\nfoo:5;\n}') | ||
expect(css + '').to.contain('.' + css.bat + ' {\nbar:6;\n}') | ||
}) | ||
}) | ||
test('a local and a global class', function(){ | ||
var css = j2c.sheet({'.bit': {foo:5}, ':global(.bat)': {bar:6}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bat).to.be(undefined) | ||
expect(css + '').to.contain('.' + css.bit + ' {\nfoo:5;\n}') | ||
expect(css + '').to.contain('.bat {\nbar:6;\n}') | ||
}) | ||
test('a local wrapping a global block', function(){ | ||
var css = j2c.sheet({'.bit': {'@global': {'.bat': {foo:5}}}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bat).to.be(undefined) | ||
expect(css + '').to.contain('.' + css.bit + '.bat {\nfoo:5;\n}') | ||
}) | ||
///////////////////////////////////// | ||
/**/ suite('Locals, Globals ') /**/ | ||
///////////////////////////////////// | ||
test('two local classes, nested', function(){ | ||
var css = j2c.sheet({'.bit': {foo:5, '.bat': {bar:6}}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bit.slice(4)).to.be(css.bat.slice(4)) | ||
expect(css + '').to.contain('.' + css.bit + ' {\nfoo:5;\n}') | ||
expect(css + '').to.contain('.' + css.bit +'.' + css.bat + ' {\nbar:6;\n}') | ||
}) | ||
test('a local class', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {foo:5}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(css).to.contain('.' + names.bit + ' {\nfoo:5;\n}') | ||
}) | ||
test('@keyframes', function(){ | ||
var css = j2c.sheet({'@keyframes bit': {}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css + '').to.contain('@keyframes ' + css.bit +' {') | ||
}) | ||
test("a local class in a doubly quoted string shouldn't be localized", function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'[foo=".bit"]': {foo:5}}) | ||
expect(names.bit).to.be(undefined) | ||
check(css, '[foo=".bit"]{foo:5;}') | ||
}) | ||
test('a global @keyframes', function() { | ||
var css = j2c.sheet({'@keyframes :global(bit)': {}}) | ||
expect(css.bit).to.be(undefined) | ||
expect(css + '').to.contain('@keyframes bit {') | ||
}) | ||
test("a local class in a singly quoted string shouldn't be localized", function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({"[foo='.bit']": {foo:5}}) | ||
expect(names.bit).to.be(undefined) | ||
check(css, "[foo='.bit']{foo:5;}") | ||
}) | ||
test('a @keyframe nested in a @global at-rule', function() { | ||
var css = j2c.sheet({'@global': {'@keyframes bat': {'from':{foo:6}}}}) | ||
expect(css.bat).to.be(undefined) | ||
expect(css + '').to.contain('@keyframes bat {') | ||
}) | ||
test("a local class in a comment shouldn't be localized", function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'p/*.bit*/': {foo:5}}) | ||
expect(names.bit).to.be(undefined) | ||
check(css, 'p/*.bit*/{foo:5;}') | ||
}) | ||
test('one animation', function(){ | ||
var css = j2c.sheet({p: {animation: 'bit 1sec'}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css + '').to.contain('animation:' + css.bit +' ') | ||
}) | ||
test('Mixing strings and comments (regexp validation)', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({"/*'*/.bit/*'*/": {foo:5}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
check(css, "/*'*/." + names.bit + "/*'*/{foo:5;}") | ||
}) | ||
test('a global animation', function() { | ||
var css = j2c.sheet({p: {animation: ':global(bit) 1sec'}}) | ||
expect(css.bit).to.be(undefined) | ||
expect(css + '').to.contain('animation:bit ') | ||
}) | ||
test('two local classes', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {foo:5}, '.bat': {bar:6}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be('bat' + _j2c.suffix) | ||
expect(css).to.contain('.' + names.bit + ' {\nfoo:5;\n}') | ||
expect(css).to.contain('.' + names.bat + ' {\nbar:6;\n}') | ||
}) | ||
test('an animation nested in a @global at-rule', function() { | ||
var css = j2c.sheet({'@global': {p: {animation: 'bit 1sec'}}}) | ||
expect(css.bit).to.be(undefined) | ||
expect(css + '').to.contain('animation:bit ') | ||
}) | ||
test('a local and a global class', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {foo:5}, ':global(.bat)': {bar:6}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be(undefined) | ||
expect(css).to.contain('.' + names.bit + ' {\nfoo:5;\n}') | ||
expect(css).to.contain('.bat {\nbar:6;\n}') | ||
}) | ||
test('one animation-name', function() { | ||
var css = j2c.sheet({p: {animation_name: 'bit'}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css + '').to.contain('animation-name:' + css.bit +';') | ||
}) | ||
test('a local wrapping a global block', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {'@global': {'.bat': {foo:5}}}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be(undefined) | ||
expect(css).to.contain('.' + names.bit + '.bat {\nfoo:5;\n}') | ||
}) | ||
test('two animation-name', function() { | ||
var css = j2c.sheet({p: {animation_name: 'bit, bat'}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bit.slice(4)).to.be(css.bat.slice(4)) | ||
expect(css + '').to.contain('animation-name:' + css.bit +', ' + css.bat) | ||
}) | ||
test('two local classes, nested', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {foo:5, '.bat': {bar:6}}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be('bat' + _j2c.suffix) | ||
expect(css).to.contain('.' + names.bit + ' {\nfoo:5;\n}') | ||
expect(css).to.contain('.' + names.bit +'.' + names.bat + ' {\nbar:6;\n}') | ||
}) | ||
test('two animation-name, one global', function() { | ||
var css = j2c.sheet({p: {animation_name: 'bit, :global(bat)'}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bat).to.be(undefined) | ||
expect(css + '').to.contain('animation-name:' + css.bit +', bat;') | ||
}) | ||
test('@keyframes', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'@keyframes bit': {}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(css).to.contain('@keyframes ' + names.bit +' {') | ||
}) | ||
test('a nested @global at-rule', function() { | ||
var css = j2c.sheet({'.bit': {'@global': {'.bat': {'foo':6}}}}) | ||
expect(css.bit.slice(0, 8)).to.be('bit_j2c_') | ||
expect(css.bat).to.be(undefined) | ||
expect(css + '').to.contain( css.bit +'.bat {') | ||
}) | ||
test('a global @keyframes', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'@keyframes global(bit)': {}}) | ||
expect(names.bit).to.be(undefined) | ||
expect(css).to.contain('@keyframes bit {') | ||
}) | ||
test('a @keyframe nested in a @global at-rule', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'@global': {'@keyframes bat': {'from':{foo:6}}}}) | ||
expect(names.bat).to.be(undefined) | ||
expect(css).to.contain('@keyframes bat {') | ||
}) | ||
/////////////////////////////////// | ||
/**/ suite('Foolproofing: ') /**/ | ||
/////////////////////////////////// | ||
test('one animation', function(){ | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({p: {animation: 'bit 1sec'}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(css).to.contain('animation:' + names.bit +' ') | ||
}) | ||
test('a global animation', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({p: {animation: 'global(bit) 1sec'}}) | ||
expect(names.bit).to.be(undefined) | ||
expect(css).to.contain('animation:bit ') | ||
}) | ||
test('an animation nested in a @global at-rule', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'@global': {p: {animation: 'bit 1sec'}}}) | ||
expect(names.bit).to.be(undefined) | ||
expect(css).to.contain('animation:bit ') | ||
}) | ||
test('property-like sub-selector', function() { | ||
var sheet = j2c.sheet({'.foo': {'g,p': {animation_name: 'bit, bat'}}}) | ||
test('one animation-name', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({p: {animation_name: 'bit'}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(css).to.contain('animation-name:' + names.bit +';') | ||
}) | ||
expect(''+sheet).to.contain(':-error-bad-sub-selector-g,:-error-bad-sub-selector-p') | ||
test('two animation-name', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({p: {animation_name: 'bit, bat'}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be('bat' + _j2c.suffix) | ||
expect(css).to.contain('animation-name:' + names.bit +', ' + names.bat) | ||
}) | ||
test('two animation-name, one global', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({p: {animation_name: 'bit, global(bat)'}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be(undefined) | ||
expect(css).to.contain('animation-name:' + names.bit +', bat;') | ||
}) | ||
test('a nested @global at-rule', function() { | ||
var _j2c = j2c(), names = _j2c.names | ||
var css = _j2c.sheet({'.bit': {'@global': {'.bat': {'foo':6}}}}) | ||
expect(names.bit).to.be('bit' + _j2c.suffix) | ||
expect(names.bat).to.be(undefined) | ||
expect(css).to.contain( names.bit +'.bat {') | ||
}) | ||
test('a @local rule nested in a @global block', function() { | ||
check( | ||
j2c().sheet({'@global':{ | ||
'.bit': { | ||
'@local': { | ||
':global(.bat)': {'foo':6} | ||
} | ||
} | ||
}}), | ||
'.bit.bat {foo:6}' | ||
) | ||
}) | ||
/////////////////////////////////// | ||
/**/ suite('Foolproofing: ') /**/ | ||
/////////////////////////////////// | ||
test('property-like sub-selector', function() { | ||
check( | ||
j2c().sheet('color:red'), | ||
':-error-no-selector {color:red}' | ||
) | ||
}) | ||
}) | ||
@@ -871,16 +1183,10 @@ | ||
test('two properties', function() { | ||
check(j2c.sheet({p: {foo: 'bar', baz: 'qux'}}), 'p {\nfoo:bar;\nbaz:qux;\n}') | ||
check(J2C().sheet({p: {foo: 'bar', baz: 'qux'}}), 'p {\nfoo:bar;\nbaz:qux;\n}') | ||
}) | ||
test('$ combiner', function() { | ||
check(j2c.sheet({p: {foo$baz: 'qux'}}), 'p {\nfoo:qux;\nbaz:qux;\n}') | ||
check(J2C().sheet({p: {foo$baz: 'qux'}}), 'p {\nfoo:qux;\nbaz:qux;\n}') | ||
}) | ||
// This was built with the assumption that | ||
// objects are totally unordered which isn't true in JS | ||
// As long as no properties are deleted and created again, | ||
// the insertion order is the iteration order in all | ||
// browsers and in node too. | ||
test('subselector > declaration > @media', function(){ | ||
test('source order is respected when mixing declarations, subselectors and at rules', function(){ | ||
var prop = randStr() | ||
@@ -916,2 +1222,20 @@ var klass = randStr() | ||
var J2C_1 = J2C() | ||
// This ensures that we don't rely on the falsy return value of the default | ||
// buffer methods. This happens here becasue this test harnesses most if not | ||
// all of the code paths where this may be relevant, especially in | ||
// `src/sheet.js` | ||
var J2C_2 = J2C().use({$filter:function (next) { | ||
return { | ||
x:next.x, | ||
i: function() {next.i.apply(next,arguments);return true}, | ||
a: function() {next.a.apply(next,arguments);return true}, | ||
A: function() {next.A.apply(next,arguments);return true}, | ||
d: function() {next.d.apply(next,arguments);return true}, | ||
s: function() {next.s.apply(next,arguments);return true}, | ||
S: function() {next.S.apply(next,arguments);return true} | ||
} | ||
}}) | ||
permutations.forEach(function(indices) { | ||
@@ -924,10 +1248,9 @@ var source = {p:{}} | ||
}) | ||
expect(normalize(j2c.sheet({'@global': source}))).to.be(normalize(CSS.join(''))) | ||
expect(normalize(J2C_1.sheet({'@global': source}))).to.be(normalize(CSS.join(''))) | ||
expect(normalize(J2C_2.sheet({'@global': source}))).to.be(normalize(CSS.join(''))) | ||
}) | ||
}) | ||
test('@namespace then selector', function() { | ||
check(j2c.sheet({ | ||
check(J2C().sheet({ | ||
'@namespace': "'foo'", | ||
@@ -938,148 +1261,482 @@ p: {foo: 'bar'} | ||
///////////////////////////////// | ||
/**/ suite('Namespaces: ') /**/ | ||
///////////////////////////////// | ||
///////////////////////////// | ||
/**/ suite('Extras: ') /**/ | ||
///////////////////////////// | ||
test('J2C.kv()', function() { | ||
expect(J2C.kv).to.be.a(Function) | ||
expect(J2C().kv).to.be.a(Function) | ||
expect(J2C.kv).to.be(J2C().kv) | ||
var o = J2C.kv('k','v') | ||
expect(o).to.have.key('k') | ||
expect(o.k).to.be('v') | ||
}) | ||
test('J2C.global()', function() { | ||
expect(J2C.global).to.be.a(Function) | ||
expect(J2C().global).to.be.a(Function) | ||
expect(J2C.global).to.be(J2C().global) | ||
expect(J2C.global('foo')).to.be(':global(foo)') | ||
}) | ||
test('J2C.at(), basics', function() { | ||
expect(J2C.at).to.be.a(Function) | ||
expect(J2C().at).to.be.a(Function) | ||
expect(J2C.at).to.be(J2C().at) | ||
}) | ||
test('J2C.at(), as an object key', function() { | ||
expect(J2C.at('foo', 'bar') + '').to.be('@foo bar') | ||
}) | ||
test('J2C.at(), as an object key, curried', function() { | ||
expect(J2C.at('foo')('bar') + '').to.be('@foo bar') | ||
expect(J2C.at()('foo')('bar') + '').to.be('@foo bar') | ||
expect(J2C.at('foo')()('bar') + '').to.be('@foo bar') | ||
expect(J2C.at('foo')('bar')() + '').to.be('@foo bar') | ||
}) | ||
test('J2C.at(), as an object generator', function() { | ||
var atFoo = J2C.at('foo', 'bar', {baz:'qux'}) | ||
expect(atFoo).to.have.key('@foo bar') | ||
expect(atFoo['@foo bar']).to.be.an(Object) | ||
expect(atFoo['@foo bar']).to.have.key('baz') | ||
expect(atFoo['@foo bar'].baz).to.be('qux') | ||
}) | ||
test('J2C.at(), as an object generator, curried 1', function() { | ||
var atFoo = J2C.at('foo')('bar', {baz:'qux'}) | ||
expect(atFoo).to.have.key('@foo bar') | ||
expect(atFoo['@foo bar']).to.be.an(Object) | ||
expect(atFoo['@foo bar']).to.have.key('baz') | ||
expect(atFoo['@foo bar'].baz).to.be('qux') | ||
}) | ||
test('J2C.at(), as an object generator, curried 2', function() { | ||
var atFoo = J2C.at('foo', 'bar')({baz:'qux'}) | ||
expect(atFoo).to.have.key('@foo bar') | ||
expect(atFoo['@foo bar']).to.be.an(Object) | ||
expect(atFoo['@foo bar']).to.have.key('baz') | ||
expect(atFoo['@foo bar'].baz).to.be('qux') | ||
}) | ||
test('J2C.at(), as an object generator, curried 3', function() { | ||
var atFoo = J2C.at('foo')('bar')({baz:'qux'}) | ||
expect(atFoo).to.have.key('@foo bar') | ||
expect(atFoo['@foo bar']).to.be.an(Object) | ||
expect(atFoo['@foo bar']).to.have.key('baz') | ||
expect(atFoo['@foo bar'].baz).to.be('qux') | ||
}) | ||
////////////////////////////// | ||
/**/ suite('Plugins: ') /**/ | ||
////////////////////////////// | ||
test('an empty array as plugin', function() { | ||
check( | ||
J2C().use([]).sheet({p:{color:'red'}}), | ||
'p{color:red;}' | ||
) | ||
}) | ||
test('an null plugin', function() { | ||
check( | ||
J2C().use(null).sheet({p:{color:'red'}}), | ||
'p{color:red;}' | ||
) | ||
}) | ||
test('an invalid $plugin', function() { | ||
var _J2C = J2C().use({$invalid:'foo'}) | ||
check( | ||
_J2C.sheet({p:{color:'red'}}), | ||
'p{color:red;}' | ||
) | ||
expect(_J2C).not.to.have.key('$invalid') | ||
}) | ||
test('a plugin that sets a property on the instance', function() { | ||
var _J2C = J2C().use({testProp:'foo'}) | ||
check( | ||
_J2C.sheet({p:{color:'red'}}), | ||
'p{color:red;}' | ||
) | ||
expect(_J2C).to.have.key('testProp') | ||
expect(_J2C.testProp).to.be('foo') | ||
}) | ||
test('a plugin that sets a property on the instance that already exists', function() { | ||
var _J2C = J2C().use({sheet:'foo'}) | ||
expect(_J2C.sheet).not.to.be('foo') | ||
check( | ||
_J2C.sheet({p:{color:'red'}}), | ||
'p{color:red;}' | ||
) | ||
expect(_J2C.sheet).not.to.be('foo') | ||
}) | ||
test('Plugin deduplication', function() { | ||
var p = {} | ||
var _J2C = J2C().use(p, p) | ||
expect(_J2C.$plugins.length).to.be(1) | ||
}) | ||
//////////////////////////////////////////////////// | ||
/**/ suite('names plugins for J2C.inline()') /**/ | ||
//////////////////////////////////////////////////// | ||
test('namespaced animation', function() { | ||
check( | ||
J2C().use( | ||
{names: {foo:'theFoo'}} | ||
).inline( | ||
{animation:'foo 1sec'} | ||
), | ||
'animation:theFoo 1sec;' | ||
) | ||
}) | ||
test('namespaced animation-name', function() { | ||
check( | ||
J2C().use( | ||
{names: {foo:'theFoo'}} | ||
).inline( | ||
{animation_name:'foo'} | ||
), | ||
'animation-name:theFoo;' | ||
) | ||
}) | ||
test('namespaced and non-namespaced animation-name', function() { | ||
var _J2C = J2C().use({names: {foo:'theFoo'}}) | ||
var result = _J2C.inline({animation_name:'foo, bar'}) | ||
check( | ||
result, | ||
'animation-name:theFoo, ' + _J2C.names.bar + ';' | ||
) | ||
}) | ||
test('two namespaced animations', function() { | ||
var result = J2C().use( | ||
{names: {foo:'theFoo', bar:'theBar'}} | ||
).inline( | ||
{animation:'foo 1sec, bar 2sec'} | ||
) | ||
check( | ||
result, | ||
'animation:theFoo 1sec, theBar 2sec;' | ||
) | ||
}) | ||
/////////////////////////////////////////////////// | ||
/**/ suite('names plugins for J2C.sheet()') /**/ | ||
/////////////////////////////////////////////////// | ||
test('namespaced class', function() { | ||
var css = j2c.sheet( | ||
{foo: 'FOO'}, | ||
{'.foo': {foo: 'bar', baz: 'qux'}} | ||
) | ||
check('' + css, '.FOO{foo:bar;baz:qux;}') | ||
expect(css.foo).to.be('FOO') | ||
var _J2C = J2C().use({names: {foo: 'FOOO'}}), names = _J2C.names | ||
var css = _J2C.sheet( | ||
{'.foo': {foo: 'bar', baz: 'qux'}} | ||
) | ||
check('' + css, '.FOOO{foo:bar;baz:qux;}') | ||
expect(names.foo).to.be('FOOO') | ||
}) | ||
test('namespaced class wrapping a global block', function() { | ||
var css = j2c.sheet( | ||
{foo: 'FOOO'}, | ||
{'.foo': {'@global': {'.foo': {foo: 'bar', baz: 'qux'}}}} | ||
) | ||
var _J2C = J2C().use({names: {foo: 'FOOO'}}), names = _J2C.names | ||
var css = _J2C.sheet( | ||
{'.foo': {'@global': {'.foo': {foo: 'bar', baz: 'qux'}}}} | ||
) | ||
check('' + css, '.FOOO.foo{foo:bar;baz:qux;}') | ||
expect(css.foo).to.be('FOOO') | ||
expect(names.foo).to.be('FOOO') | ||
}) | ||
test('namespaced @keyframes', function(){ | ||
var css = j2c.sheet( | ||
{bit: 'BOT'}, | ||
var _J2C = J2C().use({names: {bit: 'BOT'}}), names = _J2C.names | ||
var css = _J2C.sheet( | ||
{'@keyframes bit': {}} | ||
) | ||
expect(css.bit).to.be('BOT') | ||
expect(css + '').to.contain('@keyframes BOT {') | ||
expect(names.bit).to.be('BOT') | ||
expect(css).to.contain('@keyframes BOT {') | ||
}) | ||
test('namespaced animation', function(){ | ||
var css = j2c.sheet( | ||
{bit: 'BOT'}, | ||
var _J2C = J2C().use({names: {bit: 'BOT'}}), names = _J2C.names | ||
var css = _J2C.sheet( | ||
{p: {animation: 'bit 1sec'}} | ||
) | ||
expect(css.bit).to.be('BOT') | ||
check(css + '', 'p{' + webkitify('animation:BOT 1sec;') + '}') | ||
expect(names.bit).to.be('BOT') | ||
check(css, 'p{animation:BOT 1sec;}') | ||
}) | ||
test('namespaced animation-name', function() { | ||
var css = j2c.sheet( | ||
{bit: 'BOT'}, | ||
{p: {animation_name: 'bit'}} | ||
) | ||
expect(css.bit).to.be('BOT') | ||
check(css + '', 'p{' + webkitify('animation-name:BOT;') + '}') | ||
var _J2C = J2C().use({names: {bit: 'BOT'}}), names = _J2C.names | ||
var css = _J2C.sheet({p: {animation_name: 'bit'}}) | ||
expect(names.bit).to.be('BOT') | ||
check(css, 'p{animation-name:BOT;}') | ||
}) | ||
///////////////////////////// | ||
/**/ suite('extend: ') /**/ | ||
///////////////////////////// | ||
test('local extend', function() { | ||
var css = j2c.sheet({'.bit': {'@extend':'.bat'}}) | ||
expect(css.bit).to.contain('bit_j2c_') | ||
expect(css.bat).to.contain('bat_j2c_') | ||
expect(css.bat).not.to.contain('bit_j2c_') | ||
expect(css.bit).to.contain(css.bat + ' ') | ||
test("don't overwrite an existing name", function() { | ||
var _J2C = J2C().use({names: {bit: 'BOT'}}, {names: {bit: 'BUT'}}), names = _J2C.names | ||
expect(names.bit).to.be('BOT') | ||
}) | ||
test('global extend', function() { | ||
var css = j2c.sheet({'.bit': {'@extend':':global(.bat)'}}) | ||
expect(css.bit).to.contain('bit_j2c_') | ||
expect(css.bit).to.contain('bat ') | ||
}) | ||
test('two local extends', function() { | ||
var css = j2c.sheet({'.bit': {'@extends':['.bat', '.bot']}}) | ||
expect(css.bit).to.contain('bit_j2c_') | ||
expect(css.bat).to.contain('bat_j2c_') | ||
expect(css.bot).to.contain('bot_j2c_') | ||
expect(css.bat).not.to.contain('bit_j2c_') | ||
expect(css.bot).not.to.contain('bit_j2c_') | ||
expect(css.bit).to.contain(css.bat + ' ' + css.bot + ' ') | ||
//////////////////////////////////// | ||
/**/ suite('$filter plugins') /**/ | ||
//////////////////////////////////// | ||
test('a sheet filter', function() { | ||
function filter(J2C) { | ||
expect(J2C).to.be.an(Object) | ||
expect(J2C.sheet).to.be.a(Function) | ||
return {$filter: function(next, inline) { | ||
expect(next).to.be.an(Object) | ||
expect(next.i).to.be.a(Function) | ||
expect(next.x).to.be.a(Function) | ||
expect(next.d).to.be.a(Function) | ||
if(!inline) { | ||
expect(next.s).to.be.a(Function) | ||
expect(next.S).to.be.a(Function) | ||
expect(next.a).to.be.a(Function) | ||
expect(next.A).to.be.a(Function) | ||
} | ||
return { | ||
x: function() { | ||
var buf = next.x(1) | ||
expect(buf).to.be.an(Array) | ||
expect(buf.length).not.to.be(0) | ||
var txt = next.x() | ||
expect(txt).to.be.a('string') | ||
return txt | ||
}, | ||
i: function() { | ||
next.i() | ||
expect(!!inline).to.be(false) | ||
}, | ||
a: function(name, arg, hasBlock) { | ||
next.a(name+'o', 'a'+arg, hasBlock) | ||
}, | ||
A: function(name, arg) { | ||
next.A(name+'o', 'a'+arg) | ||
}, | ||
d: function(prop, value) { | ||
next.d('p'+prop, 'v'+value) | ||
}, | ||
s: function(selector) { | ||
next.s('h1, ' + selector) | ||
}, | ||
S: function(selector) { | ||
next.S('h1, ' + selector) | ||
} | ||
} | ||
}} | ||
} | ||
check( | ||
J2C().use(filter).sheet({'@global': { | ||
'@import': 'foo', | ||
p: {foo: 'bar'}, | ||
'@keyframes foo': {from:{foo:'baz'}} | ||
}}), | ||
'@importo afoo;' + | ||
'h1, p {pfoo:vbar}' + | ||
'@keyframeso afoo{h1, from{pfoo:vbaz}}' | ||
) | ||
}) | ||
test('extend applies only to the last class in the selector', function() { | ||
var css = j2c.sheet({'.bot p .bit': {'@extend':'.bat'}}) | ||
expect(css.bit).to.contain('bit_j2c_') | ||
expect(css.bat).to.contain('bat_j2c_') | ||
expect(css.bot).to.contain('bot_j2c_') | ||
expect(css.bat).not.to.contain('bit_j2c_') | ||
expect(css.bit).to.contain(css.bat + ' ') | ||
expect(css.bot).not.to.contain(css.bat + ' ') | ||
test('a declaration filter', function() { | ||
function filter(J2C) { | ||
expect(J2C).to.be.an(Object) | ||
expect(J2C.inline).to.be.a(Function) | ||
return {$filter: function(next, inline) { | ||
expect(next).to.be.an(Object) | ||
expect(next.i).to.be.a(Function) | ||
expect(next.x).to.be.a(Function) | ||
expect(next.d).to.be.a(Function) | ||
if(!inline) { | ||
expect(next.s).to.be.a(Function) | ||
expect(next.S).to.be.a(Function) | ||
expect(next.a).to.be.a(Function) | ||
expect(next.A).to.be.a(Function) | ||
} | ||
return { | ||
x: function() { | ||
var buf = next.x(true) | ||
expect(buf).to.be.an(Array) | ||
expect(buf.length).not.to.be(0) | ||
var txt = next.x() | ||
expect(txt).to.be.a('string') | ||
return txt | ||
}, | ||
i: function() { | ||
next.i() | ||
expect(!!inline).to.be(true) | ||
}, | ||
d: function(prop, value) { | ||
next.d('p'+prop, 'v'+value) | ||
} | ||
} | ||
}} | ||
} | ||
check( | ||
J2C().use(filter).inline({foo:'bar'}), | ||
'pfoo:vbar;' | ||
) | ||
}) | ||
test("if we can't find a class to extend, pass @extend as a property", function() { | ||
var css = j2c.sheet({'p > a': {'@extend':'.bat'}}) | ||
expect(css.bat).to.be(undefined) | ||
expect('' + css).to.contain('@-error-no-class-to-extend-in "p > a";') | ||
test('filter order', function() { | ||
var acc = [] | ||
function filter(x) { | ||
return {$filter: function(next) { | ||
return { | ||
i: next.i, | ||
x: next.x, | ||
a: next.a, | ||
A: next.A, | ||
d: next.d, | ||
s: function(){ | ||
acc.push(x) | ||
return next.s.apply(next, arguments) | ||
}, | ||
S: next.S | ||
} | ||
}} | ||
} | ||
J2C().use(filter(1), filter(2)).sheet({'.foo': 'bar:baz;'}) | ||
expect(acc.length).to.be(2) | ||
expect(acc[0]).to.be(1) | ||
expect(acc[1]).to.be(2) | ||
}) | ||
test("extend doesn't extend into global selectors", function() { | ||
// @extend thus extends the last local class in the stream | ||
var css = j2c.sheet({'.bit :global(.bot)': {'@extend':'.bat'}}) | ||
expect(css.bit).to.contain('bit_j2c_') | ||
expect(css.bat).to.be(undefined) | ||
expect(css.bot).to.be(undefined) | ||
expect(css.bit).not.to.contain(css.bat + ' ') | ||
expect('' + css).to.contain('@-error-cannot-extend-in-global-context ".bit :global(.bot)";') | ||
test('filter dedupe', function() { | ||
var acc = [] | ||
function filter(x) { | ||
return {$filter: function(next) { | ||
return { | ||
i: next.i, | ||
x: next.x, | ||
a: next.a, | ||
A: next.A, | ||
d: next.d, | ||
s: function(){ | ||
acc.push(x) | ||
next.s.apply(next, arguments) | ||
}, | ||
S: next.S | ||
} | ||
}} | ||
} | ||
var f = filter(1) | ||
J2C().use(f, f).sheet({'.foo': 'bar:baz;'}) | ||
expect(acc.length).to.be(1) | ||
expect(acc[0]).to.be(1) | ||
}) | ||
////////////////////////////// | ||
/**/ suite('Plugins: ') /**/ | ||
////////////////////////////// | ||
//////////////////////////////////// | ||
/**/ suite('$at plugins') /**/ | ||
//////////////////////////////////// | ||
test('one plugin that does nothing', function() { | ||
check(''+j2c().use(function(){}).sheet( | ||
{p: {foo: 'bar'}} | ||
), 'p{foo:bar;}') | ||
test('one $at plugin', function() { | ||
function plugin(name) { | ||
return {$at: function(parser, emit, match, v, prefix, local, inAtRule){ | ||
expect(match).to.be.an(Array) | ||
expect(parser).to.be.an(Object) | ||
expect(parser).to.have.key('$a') | ||
expect(parser).to.have.key('a') | ||
expect(parser).to.have.key('d') | ||
expect(parser).to.have.key('l') | ||
expect(parser).to.have.key('n') | ||
expect(parser).to.have.key('s') | ||
expect(emit).to.be.an(Object) | ||
expect(emit).to.have.key('a') | ||
expect(v).to.be('param') | ||
expect(prefix).to.be.a('string') | ||
// `local` can be many things, the only things that matters is its truthiness | ||
expect(!!local).to.be(true) | ||
// `inAtRule` can be many things, the only things that matters is its truthiness | ||
expect(!!inAtRule).to.be(false) | ||
if (match[2] !== name) return false | ||
emit.a(match[1], v) | ||
return true | ||
}} | ||
} | ||
check( | ||
J2C().use(plugin('foo')).sheet({ | ||
'@foo': 'param', | ||
'@bar': 'param', | ||
'@baz': 'param' | ||
}), | ||
'@foo param; @-error-unsupported-at-rule "@bar"; @-error-unsupported-at-rule "@baz";' | ||
) | ||
}) | ||
test('one plugin that mutates the buffer', function() { | ||
check(''+j2c().use( | ||
function(buf){ | ||
buf[0] = 'li' | ||
} | ||
).sheet( | ||
{p: {foo: 'bar'}} | ||
), 'li{foo:bar;}') | ||
test('two $at plugins', function() { | ||
function plugin(name) { | ||
return {$at: function(parser, emit, match, v){ | ||
if (match[2] !== name) return false | ||
emit.a(match[1], v) | ||
return true | ||
}} | ||
} | ||
check( | ||
J2C().use(plugin('foo'), plugin('bar')).sheet({ | ||
'@foo': 'bar', | ||
'@bar': 'baz', | ||
'@baz': 'qux' | ||
}), | ||
'@foo bar; @bar baz; @-error-unsupported-at-rule "@baz";' | ||
) | ||
}) | ||
test('one plugin that returns a new buffer', function() { | ||
check(''+j2c().use( | ||
function(){ | ||
return ['li{foo:bar;}'] | ||
} | ||
).sheet( | ||
{p: {foo: 'bar'}} | ||
), 'li{foo:bar;}') | ||
test('$at plugin has precedence over default at-rules', function() { | ||
var plugin = {$at: function(parser, emit, match, v){ | ||
if (match[2] !== 'import') return false | ||
emit.a('@intercepted', v) | ||
return true | ||
}} | ||
check( | ||
J2C().use(plugin).sheet({ | ||
'@import': 'foo' | ||
}), | ||
'@intercepted foo;' | ||
) | ||
}) | ||
test('two plugins that mutate the buffer', function() { | ||
check(''+j2c().use( | ||
function(buf){ | ||
buf[0]=buf[0].replace('p', 'a') | ||
}, | ||
function(buf){ | ||
buf[0]=buf[0].replace('a', 'i') | ||
} | ||
).sheet( | ||
{p: {fop: 'bar'}} | ||
), 'i{fop:bar;}') | ||
test('$at plugin that verifies malformed rules are properly passed unparsed', function() { | ||
var plugin = {$at: function(parser, emit, match, v){ | ||
expect(match[0]).to.be('@; hey') | ||
expect(match[1]).to.be('@') | ||
expect(match[2]).to.be('') | ||
expect(match[3]).to.be('') | ||
expect(v).to.be('foo') | ||
return true | ||
}} | ||
check( | ||
J2C().use(plugin).sheet({ | ||
'@; hey': 'foo' | ||
}), | ||
'' | ||
) | ||
}) | ||
}) | ||
}) // DONE! | ||
@@ -1089,1 +1746,13 @@ | ||
// TODO | ||
// - spy on String.prototype.replace and RegExp.prototype.* | ||
// to generate coverage reports for the branches hidden | ||
// in these native functions. | ||
// - verify that all at-rules behave properly in filters | ||
// (wrt selectors and definitions) | ||
// - verify that CSS variables are not localized as if they were names. | ||
// - test `inAtRule` from $at plugins (is it set appropriately? | ||
// - test the `parser` object from within $at and $filter plugins | ||
// - verify that custom at rules take precedence over default ones. | ||
// - test @keyframes nested in selector scope |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
195202
36
4108
706
3
17
1