csso
Advanced tools
Comparing version 1.2.9 to 1.2.10
@@ -132,2 +132,3 @@ function TRBL(name) { | ||
this.prules = {}; // prepare rules | ||
this.frrules = {}; // freeze ruleset rules | ||
this.msrules = {}; // mark shorthands rules | ||
@@ -142,2 +143,3 @@ this.csrules = {}; // clean shorthands rules | ||
this.initRules(this.ccrules, this.cleanCfg); | ||
this.initRules(this.frrules, this.frCfg); | ||
this.initRules(this.prules, this.preCfg); | ||
@@ -215,2 +217,6 @@ this.initRules(this.msrules, this.msCfg); | ||
CSSOCompressor.prototype.frCfg = { | ||
'freezeRulesets': 1 | ||
}; | ||
CSSOCompressor.prototype.csCfg = { | ||
@@ -233,2 +239,3 @@ 'cleanShorthands': 1, | ||
'compressBackground', | ||
'freezeRulesets', | ||
'destroyDelims', | ||
@@ -325,2 +332,5 @@ 'preTranslate', | ||
'declaration': 1 | ||
}, | ||
'freezeRulesets': { | ||
'ruleset': 1 | ||
} | ||
@@ -362,2 +372,3 @@ }; | ||
x = this.walk(this.prules, x, '/0'); | ||
x = this.walk(this.frrules, x, '/0'); | ||
ls = translator.translate(cleanInfo(x)).length; | ||
@@ -439,2 +450,123 @@ | ||
CSSOCompressor.prototype.freezeRulesets = function(token, rule, container, i) { | ||
var info = token[0], | ||
selector = token[2]; | ||
info.freeze = this.freezeNeeded(selector); | ||
info.freezeID = this.selectorSignature(selector); | ||
info.pseudoID = this.composePseudoID(selector); | ||
this.markSimplePseudo(selector); | ||
return token; | ||
}; | ||
CSSOCompressor.prototype.markSimplePseudo = function(selector) { | ||
var ss, sg = {}; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
ss[0].pseudo = this.containsPseudo(ss); | ||
ss[0].sg = sg; | ||
sg[ss[0].s] = 1; | ||
} | ||
}; | ||
CSSOCompressor.prototype.composePseudoID = function(selector) { | ||
var a = [], ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
if (this.containsPseudo(ss)) { | ||
a.push(ss[0].s); | ||
} | ||
} | ||
a.sort(); | ||
return a.join(','); | ||
}; | ||
CSSOCompressor.prototype.containsPseudo = function(sselector) { | ||
for (var j = 2; j < sselector.length; j++) { | ||
switch (sselector[j][1]) { | ||
case 'pseudoc': | ||
case 'pseudoe': | ||
case 'nthselector': | ||
if (!(sselector[j][2][2] in this.notFPClasses)) return true; | ||
} | ||
} | ||
}; | ||
CSSOCompressor.prototype.selectorSignature = function(selector) { | ||
var a = []; | ||
for (var i = 2; i < selector.length; i++) { | ||
a.push(translator.translate(cleanInfo(selector[i]))); | ||
} | ||
a.sort(); | ||
return a.join(','); | ||
}; | ||
CSSOCompressor.prototype.pseudoSelectorSignature = function(selector, exclude) { | ||
var a = [], b = {}, ss, wasExclude = false; | ||
exclude = exclude || {}; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
switch (ss[j][1]) { | ||
case 'pseudoc': | ||
case 'pseudoe': | ||
case 'nthselector': | ||
if (!(ss[j][2][2] in exclude)) b[ss[j][2][2]] = 1; | ||
else wasExclude = true; | ||
break; | ||
} | ||
} | ||
} | ||
for (var k in b) a.push(k); | ||
a.sort(); | ||
return a.join(',') + wasExclude; | ||
}; | ||
CSSOCompressor.prototype.notFPClasses = { | ||
'link': 1, | ||
'visited': 1, | ||
'hover': 1, | ||
'active': 1, | ||
'first-letter': 1, | ||
'first-line': 1 | ||
}; | ||
CSSOCompressor.prototype.notFPElements = { | ||
'first-letter': 1, | ||
'first-line': 1 | ||
}; | ||
CSSOCompressor.prototype.freezeNeeded = function(selector) { | ||
var ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
switch (ss[j][1]) { | ||
case 'pseudoc': | ||
if (!(ss[j][2][2] in this.notFPClasses)) return true; | ||
break; | ||
case 'pseudoe': | ||
if (!(ss[j][2][2] in this.notFPElements)) return true; | ||
break; | ||
case 'nthselector': | ||
return true; | ||
break; | ||
} | ||
} | ||
} | ||
return false; | ||
}; | ||
CSSOCompressor.prototype.cleanCharset = function(token, rule, container, i) { | ||
@@ -745,6 +877,13 @@ if (token[2][2][2] === 'charset') { | ||
CSSOCompressor.prototype.markShorthands = function(token, rule, container, j, path) { | ||
if (container[1] === 'ruleset') { | ||
var selector = container[2][2][0].s, | ||
freeze = container[0].freeze, | ||
freezeID = container[0].freezeID; | ||
} else { | ||
var selector = '', | ||
freeze = false, | ||
freezeID = 'fake'; | ||
} | ||
var x, p, v, imp, s, key, | ||
r = container[1], | ||
selector = r === 'ruleset' ? container[2][2][0].s : '', | ||
pre = this.pathUp(path) + '/' + selector + '/'; | ||
pre = this.pathUp(path) + '/' + (freeze ? '&' + freezeID + '&' : '') + selector + '/'; | ||
@@ -795,6 +934,20 @@ for (var i = token.length - 1; i > -1; i--) { | ||
CSSOCompressor.prototype.restructureBlock = function(token, rule, container, j, path) { | ||
if (container[1] === 'ruleset') { | ||
var props = this.props, | ||
isPseudo = container[2][2][0].pseudo, | ||
selector = container[2][2][0].s, | ||
freeze = container[0].freeze, | ||
freezeID = container[0].freezeID, | ||
pseudoID = container[0].pseudoID, | ||
sg = container[2][2][0].sg; | ||
} else { | ||
var props = {}, | ||
isPseudo = false, | ||
selector = '', | ||
freeze = false, | ||
freezeID = 'fake', | ||
pseudoID = 'fake', | ||
sg = {}; | ||
} | ||
var x, p, v, imp, t, | ||
r = container[1], | ||
props = r === 'ruleset' ? this.props : {}, | ||
selector = r === 'ruleset' ? container[2][2][0].s : '', | ||
pre = this.pathUp(path) + '/' + selector + '/', | ||
@@ -809,11 +962,22 @@ ppre; | ||
p = x[2][0].s; | ||
ppre = this.buildPPre(pre, p, v, x); | ||
ppre = this.buildPPre(pre, p, v, x, freeze); | ||
x[0].id = path + '/' + i; | ||
if (t = props[ppre]) { | ||
if (imp && !t.imp) { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id }; | ||
this.deleteProperty(t.block, t.id); | ||
} else token.splice(i, 1); | ||
} else if (this.needless(p, props, pre, imp, v, x)) token.splice(i, 1); | ||
else props[ppre] = { block: token, imp: imp, id: x[0].id }; | ||
if (p !== 'src' && (t = props[ppre])) { // see https://github.com/afelix/csso/issues/50 about 'src' | ||
if ((isPseudo && freezeID === t.freezeID) || // pseudo from equal selectors group | ||
(!isPseudo && pseudoID === t.pseudoID) || // not pseudo from equal pseudo signature group | ||
(isPseudo && pseudoID === t.pseudoID && this.hashInHash(sg, t.sg))) { // pseudo from covered selectors group | ||
if (imp && !t.imp) { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, | ||
freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; | ||
this.deleteProperty(t.block, t.id); | ||
} else { | ||
token.splice(i, 1); | ||
} | ||
} | ||
} else if (this.needless(p, props, pre, imp, v, x, freeze)) { | ||
token.splice(i, 1); | ||
} else { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, | ||
freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; | ||
} | ||
} | ||
@@ -824,4 +988,5 @@ } | ||
CSSOCompressor.prototype.buildPPre = function(pre, p, v, d) { | ||
if (p.indexOf('background') !== -1) return pre + d[0].s; | ||
CSSOCompressor.prototype.buildPPre = function(pre, p, v, d, freeze) { | ||
var fp = freeze ? 'ft:' : 'ff:'; | ||
if (p.indexOf('background') !== -1) return fp + pre + d[0].s; | ||
@@ -856,3 +1021,3 @@ var _v = v.slice(2), | ||
return pre + p + colorMark.join(''); | ||
return fp + pre + p + colorMark.join(''); | ||
}; | ||
@@ -909,3 +1074,3 @@ | ||
CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d) { | ||
CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d, freeze) { | ||
var hack = name.charAt(0); | ||
@@ -928,3 +1093,3 @@ if (hack === '*' || hack === '_') name = name.substr(1); | ||
for (i = 0; i < x.length; i++) { | ||
ppre = this.buildPPre(pre, hack + vendor + x[i], v, d); | ||
ppre = this.buildPPre(pre, hack + vendor + x[i], v, d, freeze); | ||
if (t = props[ppre]) return (!imp || t.imp); | ||
@@ -953,8 +1118,10 @@ } | ||
} | ||
// try to join by properties | ||
r = this.analyze(token, p); | ||
if (!r.ne1.length && !r.ne2.length) { | ||
p[2] = this.cleanSelector(p[2].concat(token[2].splice(2))); | ||
p[2][0].s = translator.translate(cleanInfo(p[2])); | ||
return null; | ||
if (this.okToJoinByProperties(token, p)) { | ||
// try to join by properties | ||
r = this.analyze(token, p); | ||
if (!r.ne1.length && !r.ne2.length) { | ||
p[2] = this.cleanSelector(p[2].concat(token[2].splice(2))); | ||
p[2][0].s = translator.translate(cleanInfo(p[2])); | ||
return null; | ||
} | ||
} | ||
@@ -964,2 +1131,38 @@ } | ||
CSSOCompressor.prototype.okToJoinByProperties = function(r0, r1) { | ||
var i0 = r0[0], i1 = r1[0]; | ||
// same frozen ruleset | ||
if (i0.freezeID === i1.freezeID) return true; | ||
// same pseudo-classes in selectors | ||
if (i0.pseudoID === i1.pseudoID) return true; | ||
// different frozen rulesets | ||
if (i0.freeze && i1.freeze) { | ||
return this.pseudoSelectorSignature(r0[2], this.allowedPClasses) === this.pseudoSelectorSignature(r1[2], this.allowedPClasses); | ||
} | ||
// is it frozen at all? | ||
return !(i0.freeze || i1.freeze); | ||
}; | ||
CSSOCompressor.prototype.allowedPClasses = { | ||
'after': 1, | ||
'before': 1 | ||
}; | ||
CSSOCompressor.prototype.containsOnlyAllowedPClasses = function(selector) { | ||
var ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
if (ss[j][1] == 'pseudoc' || ss[j][1] == 'pseudoe') { | ||
if (!(ss[j][2][2] in this.allowedPClasses)) return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}; | ||
CSSOCompressor.prototype.restructureRuleset = function(token, rule, container, i) { | ||
@@ -1028,4 +1231,2 @@ var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1], | ||
CSSOCompressor.prototype.calcLength = function(tokens) { | ||
@@ -1085,2 +1286,7 @@ var r = 0; | ||
CSSOCompressor.prototype.hashInHash = function(h0, h1) { | ||
for (var k in h0) if (!(k in h1)) return false; | ||
return true; | ||
}; | ||
CSSOCompressor.prototype.delimSelectors = function(token) { | ||
@@ -1087,0 +1293,0 @@ for (var i = token.length - 1; i > 2; i--) { |
118
MANUAL.ru.md
@@ -25,2 +25,6 @@ # Содержание | ||
* 2.2.8\. Минимизация margin и padding | ||
* 2.2.9\. Специальная минимизация псевдоклассов | ||
* 2.2.9.1\. Сохранение группы | ||
* 2.2.9.2\. Минимизация общеподдерживаемых псевдоклассов | ||
* 2.2.9.3\. Минимизация :before и :after | ||
* 3\. Рекомендации | ||
@@ -506,3 +510,2 @@ * 3.1\. Длина селекторов | ||
### 2.2.8. Минимизация margin и padding | ||
@@ -558,2 +561,115 @@ | ||
### 2.2.9. Специальная минимизация псевдоклассов | ||
Если в группе селекторов UA обнаружит неподдерживаемый селектор, он, согласно \[[CSS 3 / Selectors / 5. Groups of selectors](http://www.w3.org/TR/selectors/#grouping)\], посчитает неподдерживаемой всю группу и не применит стили к перечисленным в ней селекторам. Этим нередко пользуются для разграничения стилей по браузерам. Вот [пример](http://pornel.net/firefoxhack) метода: | ||
#hackme, x:-moz-any-link { Firefox 2.0 here } | ||
#hackme, x:-moz-any-link, x:default { Firefox 3.0 and newer } | ||
Чтобы сохранить такие блоки, но в то же время минимизировать то, что поддаётся оптимизации, CSSO применяет нижеперечисленные правила. Предполагается, что вместе эти правила составляют компромисс, удовлетворяющий большинство пользователей. | ||
#### 2.2.9.1. Сохранение группы | ||
В общем случае (исключения описаны ниже) минимизация удалением перекрываемого селектора не происходит, если группа селекторов включает псевдокласс или псевдоэлемент. | ||
* Было: | ||
a { | ||
property: value0 | ||
} | ||
a, x:-vendor-class { | ||
property: value1 | ||
} | ||
* Стало (структура не изменилась): | ||
a { | ||
property: value0 | ||
} | ||
a, x:-vendor-class { | ||
property: value1 | ||
} | ||
Если же группы селекторов образуют одинаковую "сигнатуру псевдоклассов" (исключается ситуация, в которой браузер поддерживает одну группу, но не поддерживает другую), минимизация происходит. | ||
* Было: | ||
a, x:-vendor-class { | ||
property: value0 | ||
} | ||
a, b, x:-vendor-class { | ||
property: value1 | ||
} | ||
* Стало: | ||
a, b, x:-vendor-class { | ||
property: value1 | ||
} | ||
#### 2.2.9.2. Минимизация общеподдерживаемых псевдоклассов и псевдоэлементов | ||
Существуют псевдоклассы и псевдоэлементы, поддерживаемые большинством браузеров: `:link`, `:visited`, `:hover`, `:active`, `:first-letter`, `:first-line`. Для них минимизация происходит в общем порядке без сохранения группы. | ||
* Было: | ||
a, x:active { | ||
color: red | ||
} | ||
a { | ||
color: green | ||
} | ||
* Стало: | ||
x:active { | ||
color: red | ||
} | ||
a { | ||
color: green | ||
} | ||
#### 2.2.9.3. Минимизация :before и :after | ||
Псевдоэлементы `:before` и `:after` обычно поддерживаются браузерами парно, потому объединение блоков с их участием безопасно. | ||
* Было: | ||
a, x:before { | ||
color: red | ||
} | ||
a, x:after { | ||
color: red | ||
} | ||
* Стало: | ||
a, x:before, x:after { | ||
color:red | ||
} | ||
Тем не менее, блоки, в которых участвует только один из этих псевдоэлементов, не объединяются: | ||
* Было: | ||
a { | ||
color: red | ||
} | ||
a, x:after { | ||
color: red | ||
} | ||
* Стало: | ||
a { | ||
color: red | ||
} | ||
a, x:after { | ||
color: red | ||
} | ||
В примере выше можно заметить, что удаление селектора `a` из второго блока не повлияло бы на итоговый рендеринг, но в общем случае это опасная минимизация, потому не применяется. | ||
# 3. Рекомендации | ||
@@ -560,0 +676,0 @@ |
{ | ||
"name": "csso", | ||
"description": "CSSO — CSS optimizer", | ||
"version": "1.2.9", | ||
"version": "1.2.10", | ||
"homepage": "http://github.com/afelix/csso", | ||
@@ -6,0 +6,0 @@ "author": "Sergey Kryzhanovsky <skryzhanovsky@ya.ru> (http://github.com/afelix)", |
@@ -9,2 +9,3 @@ function CSSOCompressor() {} | ||
this.prules = {}; // prepare rules | ||
this.frrules = {}; // freeze ruleset rules | ||
this.msrules = {}; // mark shorthands rules | ||
@@ -19,2 +20,3 @@ this.csrules = {}; // clean shorthands rules | ||
this.initRules(this.ccrules, this.cleanCfg); | ||
this.initRules(this.frrules, this.frCfg); | ||
this.initRules(this.prules, this.preCfg); | ||
@@ -92,2 +94,6 @@ this.initRules(this.msrules, this.msCfg); | ||
CSSOCompressor.prototype.frCfg = { | ||
'freezeRulesets': 1 | ||
}; | ||
CSSOCompressor.prototype.csCfg = { | ||
@@ -110,2 +116,3 @@ 'cleanShorthands': 1, | ||
'compressBackground', | ||
'freezeRulesets', | ||
'destroyDelims', | ||
@@ -202,2 +209,5 @@ 'preTranslate', | ||
'declaration': 1 | ||
}, | ||
'freezeRulesets': { | ||
'ruleset': 1 | ||
} | ||
@@ -239,2 +249,3 @@ }; | ||
x = this.walk(this.prules, x, '/0'); | ||
x = this.walk(this.frrules, x, '/0'); | ||
ls = translator.translate(cleanInfo(x)).length; | ||
@@ -316,2 +327,123 @@ | ||
CSSOCompressor.prototype.freezeRulesets = function(token, rule, container, i) { | ||
var info = token[0], | ||
selector = token[2]; | ||
info.freeze = this.freezeNeeded(selector); | ||
info.freezeID = this.selectorSignature(selector); | ||
info.pseudoID = this.composePseudoID(selector); | ||
this.markSimplePseudo(selector); | ||
return token; | ||
}; | ||
CSSOCompressor.prototype.markSimplePseudo = function(selector) { | ||
var ss, sg = {}; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
ss[0].pseudo = this.containsPseudo(ss); | ||
ss[0].sg = sg; | ||
sg[ss[0].s] = 1; | ||
} | ||
}; | ||
CSSOCompressor.prototype.composePseudoID = function(selector) { | ||
var a = [], ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
if (this.containsPseudo(ss)) { | ||
a.push(ss[0].s); | ||
} | ||
} | ||
a.sort(); | ||
return a.join(','); | ||
}; | ||
CSSOCompressor.prototype.containsPseudo = function(sselector) { | ||
for (var j = 2; j < sselector.length; j++) { | ||
switch (sselector[j][1]) { | ||
case 'pseudoc': | ||
case 'pseudoe': | ||
case 'nthselector': | ||
if (!(sselector[j][2][2] in this.notFPClasses)) return true; | ||
} | ||
} | ||
}; | ||
CSSOCompressor.prototype.selectorSignature = function(selector) { | ||
var a = []; | ||
for (var i = 2; i < selector.length; i++) { | ||
a.push(translator.translate(cleanInfo(selector[i]))); | ||
} | ||
a.sort(); | ||
return a.join(','); | ||
}; | ||
CSSOCompressor.prototype.pseudoSelectorSignature = function(selector, exclude) { | ||
var a = [], b = {}, ss, wasExclude = false; | ||
exclude = exclude || {}; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
switch (ss[j][1]) { | ||
case 'pseudoc': | ||
case 'pseudoe': | ||
case 'nthselector': | ||
if (!(ss[j][2][2] in exclude)) b[ss[j][2][2]] = 1; | ||
else wasExclude = true; | ||
break; | ||
} | ||
} | ||
} | ||
for (var k in b) a.push(k); | ||
a.sort(); | ||
return a.join(',') + wasExclude; | ||
}; | ||
CSSOCompressor.prototype.notFPClasses = { | ||
'link': 1, | ||
'visited': 1, | ||
'hover': 1, | ||
'active': 1, | ||
'first-letter': 1, | ||
'first-line': 1 | ||
}; | ||
CSSOCompressor.prototype.notFPElements = { | ||
'first-letter': 1, | ||
'first-line': 1 | ||
}; | ||
CSSOCompressor.prototype.freezeNeeded = function(selector) { | ||
var ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
switch (ss[j][1]) { | ||
case 'pseudoc': | ||
if (!(ss[j][2][2] in this.notFPClasses)) return true; | ||
break; | ||
case 'pseudoe': | ||
if (!(ss[j][2][2] in this.notFPElements)) return true; | ||
break; | ||
case 'nthselector': | ||
return true; | ||
break; | ||
} | ||
} | ||
} | ||
return false; | ||
}; | ||
CSSOCompressor.prototype.cleanCharset = function(token, rule, container, i) { | ||
@@ -622,6 +754,13 @@ if (token[2][2][2] === 'charset') { | ||
CSSOCompressor.prototype.markShorthands = function(token, rule, container, j, path) { | ||
if (container[1] === 'ruleset') { | ||
var selector = container[2][2][0].s, | ||
freeze = container[0].freeze, | ||
freezeID = container[0].freezeID; | ||
} else { | ||
var selector = '', | ||
freeze = false, | ||
freezeID = 'fake'; | ||
} | ||
var x, p, v, imp, s, key, | ||
r = container[1], | ||
selector = r === 'ruleset' ? container[2][2][0].s : '', | ||
pre = this.pathUp(path) + '/' + selector + '/'; | ||
pre = this.pathUp(path) + '/' + (freeze ? '&' + freezeID + '&' : '') + selector + '/'; | ||
@@ -672,6 +811,20 @@ for (var i = token.length - 1; i > -1; i--) { | ||
CSSOCompressor.prototype.restructureBlock = function(token, rule, container, j, path) { | ||
if (container[1] === 'ruleset') { | ||
var props = this.props, | ||
isPseudo = container[2][2][0].pseudo, | ||
selector = container[2][2][0].s, | ||
freeze = container[0].freeze, | ||
freezeID = container[0].freezeID, | ||
pseudoID = container[0].pseudoID, | ||
sg = container[2][2][0].sg; | ||
} else { | ||
var props = {}, | ||
isPseudo = false, | ||
selector = '', | ||
freeze = false, | ||
freezeID = 'fake', | ||
pseudoID = 'fake', | ||
sg = {}; | ||
} | ||
var x, p, v, imp, t, | ||
r = container[1], | ||
props = r === 'ruleset' ? this.props : {}, | ||
selector = r === 'ruleset' ? container[2][2][0].s : '', | ||
pre = this.pathUp(path) + '/' + selector + '/', | ||
@@ -686,11 +839,22 @@ ppre; | ||
p = x[2][0].s; | ||
ppre = this.buildPPre(pre, p, v, x); | ||
ppre = this.buildPPre(pre, p, v, x, freeze); | ||
x[0].id = path + '/' + i; | ||
if (t = props[ppre]) { | ||
if (imp && !t.imp) { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id }; | ||
this.deleteProperty(t.block, t.id); | ||
} else token.splice(i, 1); | ||
} else if (this.needless(p, props, pre, imp, v, x)) token.splice(i, 1); | ||
else props[ppre] = { block: token, imp: imp, id: x[0].id }; | ||
if (p !== 'src' && (t = props[ppre])) { // see https://github.com/afelix/csso/issues/50 about 'src' | ||
if ((isPseudo && freezeID === t.freezeID) || // pseudo from equal selectors group | ||
(!isPseudo && pseudoID === t.pseudoID) || // not pseudo from equal pseudo signature group | ||
(isPseudo && pseudoID === t.pseudoID && this.hashInHash(sg, t.sg))) { // pseudo from covered selectors group | ||
if (imp && !t.imp) { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, | ||
freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; | ||
this.deleteProperty(t.block, t.id); | ||
} else { | ||
token.splice(i, 1); | ||
} | ||
} | ||
} else if (this.needless(p, props, pre, imp, v, x, freeze)) { | ||
token.splice(i, 1); | ||
} else { | ||
props[ppre] = { block: token, imp: imp, id: x[0].id, sg: sg, | ||
freeze: freeze, path: path, freezeID: freezeID, pseudoID: pseudoID }; | ||
} | ||
} | ||
@@ -701,4 +865,5 @@ } | ||
CSSOCompressor.prototype.buildPPre = function(pre, p, v, d) { | ||
if (p.indexOf('background') !== -1) return pre + d[0].s; | ||
CSSOCompressor.prototype.buildPPre = function(pre, p, v, d, freeze) { | ||
var fp = freeze ? 'ft:' : 'ff:'; | ||
if (p.indexOf('background') !== -1) return fp + pre + d[0].s; | ||
@@ -733,3 +898,3 @@ var _v = v.slice(2), | ||
return pre + p + colorMark.join(''); | ||
return fp + pre + p + colorMark.join(''); | ||
}; | ||
@@ -786,3 +951,3 @@ | ||
CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d) { | ||
CSSOCompressor.prototype.needless = function(name, props, pre, imp, v, d, freeze) { | ||
var hack = name.charAt(0); | ||
@@ -805,3 +970,3 @@ if (hack === '*' || hack === '_') name = name.substr(1); | ||
for (i = 0; i < x.length; i++) { | ||
ppre = this.buildPPre(pre, hack + vendor + x[i], v, d); | ||
ppre = this.buildPPre(pre, hack + vendor + x[i], v, d, freeze); | ||
if (t = props[ppre]) return (!imp || t.imp); | ||
@@ -830,8 +995,10 @@ } | ||
} | ||
// try to join by properties | ||
r = this.analyze(token, p); | ||
if (!r.ne1.length && !r.ne2.length) { | ||
p[2] = this.cleanSelector(p[2].concat(token[2].splice(2))); | ||
p[2][0].s = translator.translate(cleanInfo(p[2])); | ||
return null; | ||
if (this.okToJoinByProperties(token, p)) { | ||
// try to join by properties | ||
r = this.analyze(token, p); | ||
if (!r.ne1.length && !r.ne2.length) { | ||
p[2] = this.cleanSelector(p[2].concat(token[2].splice(2))); | ||
p[2][0].s = translator.translate(cleanInfo(p[2])); | ||
return null; | ||
} | ||
} | ||
@@ -841,2 +1008,38 @@ } | ||
CSSOCompressor.prototype.okToJoinByProperties = function(r0, r1) { | ||
var i0 = r0[0], i1 = r1[0]; | ||
// same frozen ruleset | ||
if (i0.freezeID === i1.freezeID) return true; | ||
// same pseudo-classes in selectors | ||
if (i0.pseudoID === i1.pseudoID) return true; | ||
// different frozen rulesets | ||
if (i0.freeze && i1.freeze) { | ||
return this.pseudoSelectorSignature(r0[2], this.allowedPClasses) === this.pseudoSelectorSignature(r1[2], this.allowedPClasses); | ||
} | ||
// is it frozen at all? | ||
return !(i0.freeze || i1.freeze); | ||
}; | ||
CSSOCompressor.prototype.allowedPClasses = { | ||
'after': 1, | ||
'before': 1 | ||
}; | ||
CSSOCompressor.prototype.containsOnlyAllowedPClasses = function(selector) { | ||
var ss; | ||
for (var i = 2; i < selector.length; i++) { | ||
ss = selector[i]; | ||
for (var j = 2; j < ss.length; j++) { | ||
if (ss[j][1] == 'pseudoc' || ss[j][1] == 'pseudoe') { | ||
if (!(ss[j][2][2] in this.allowedPClasses)) return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}; | ||
CSSOCompressor.prototype.restructureRuleset = function(token, rule, container, i) { | ||
@@ -905,4 +1108,2 @@ var p = (i === 2 || container[i - 1][1] === 'unknown') ? null : container[i - 1], | ||
CSSOCompressor.prototype.calcLength = function(tokens) { | ||
@@ -962,2 +1163,7 @@ var r = 0; | ||
CSSOCompressor.prototype.hashInHash = function(h0, h1) { | ||
for (var k in h0) if (!(k in h1)) return false; | ||
return true; | ||
}; | ||
CSSOCompressor.prototype.delimSelectors = function(token) { | ||
@@ -964,0 +1170,0 @@ for (var i = token.length - 1; i > 2; i--) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
375557
1252
7179
0