1 | var isMergeable = require('./is-mergeable');
|
---|
2 |
|
---|
3 | var optimizeProperties = require('./properties/optimize');
|
---|
4 |
|
---|
5 | var cloneArray = require('../../utils/clone-array');
|
---|
6 |
|
---|
7 | var Token = require('../../tokenizer/token');
|
---|
8 |
|
---|
9 | var serializeBody = require('../../writer/one-time').body;
|
---|
10 | var serializeRules = require('../../writer/one-time').rules;
|
---|
11 |
|
---|
12 | function reduceNonAdjacent(tokens, context) {
|
---|
13 | var options = context.options;
|
---|
14 | var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
|
---|
15 | var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
|
---|
16 | var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
|
---|
17 | var candidates = {};
|
---|
18 | var repeated = [];
|
---|
19 |
|
---|
20 | for (var i = tokens.length - 1; i >= 0; i--) {
|
---|
21 | var token = tokens[i];
|
---|
22 |
|
---|
23 | if (token[0] != Token.RULE) {
|
---|
24 | continue;
|
---|
25 | } else if (token[2].length === 0) {
|
---|
26 | continue;
|
---|
27 | }
|
---|
28 |
|
---|
29 | var selectorAsString = serializeRules(token[1]);
|
---|
30 | var isComplexAndNotSpecial = token[1].length > 1
|
---|
31 | && isMergeable(selectorAsString, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging);
|
---|
32 | var wrappedSelectors = wrappedSelectorsFrom(token[1]);
|
---|
33 | var selectors = isComplexAndNotSpecial
|
---|
34 | ? [selectorAsString].concat(wrappedSelectors)
|
---|
35 | : [selectorAsString];
|
---|
36 |
|
---|
37 | for (var j = 0, m = selectors.length; j < m; j++) {
|
---|
38 | var selector = selectors[j];
|
---|
39 |
|
---|
40 | if (!candidates[selector]) { candidates[selector] = []; } else { repeated.push(selector); }
|
---|
41 |
|
---|
42 | candidates[selector].push({
|
---|
43 | where: i,
|
---|
44 | list: wrappedSelectors,
|
---|
45 | isPartial: isComplexAndNotSpecial && j > 0,
|
---|
46 | isComplex: isComplexAndNotSpecial && j === 0
|
---|
47 | });
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context);
|
---|
52 | reduceComplexNonAdjacentCases(tokens, candidates, options, context);
|
---|
53 | }
|
---|
54 |
|
---|
55 | function wrappedSelectorsFrom(list) {
|
---|
56 | var wrapped = [];
|
---|
57 |
|
---|
58 | for (var i = 0; i < list.length; i++) {
|
---|
59 | wrapped.push([list[i][1]]);
|
---|
60 | }
|
---|
61 |
|
---|
62 | return wrapped;
|
---|
63 | }
|
---|
64 |
|
---|
65 | function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) {
|
---|
66 | function filterOut(idx, bodies) {
|
---|
67 | return data[idx].isPartial && bodies.length === 0;
|
---|
68 | }
|
---|
69 |
|
---|
70 | function reduceBody(token, newBody, processedCount, tokenIdx) {
|
---|
71 | if (!data[processedCount - tokenIdx - 1].isPartial) { token[2] = newBody; }
|
---|
72 | }
|
---|
73 |
|
---|
74 | for (var i = 0, l = repeated.length; i < l; i++) {
|
---|
75 | var selector = repeated[i];
|
---|
76 | var data = candidates[selector];
|
---|
77 |
|
---|
78 | reduceSelector(tokens, data, {
|
---|
79 | filterOut: filterOut,
|
---|
80 | callback: reduceBody
|
---|
81 | }, options, context);
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | function reduceComplexNonAdjacentCases(tokens, candidates, options, context) {
|
---|
86 | var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
|
---|
87 | var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
|
---|
88 | var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
|
---|
89 | var localContext = {};
|
---|
90 |
|
---|
91 | function filterOut(idx) {
|
---|
92 | return localContext.data[idx].where < localContext.intoPosition;
|
---|
93 | }
|
---|
94 |
|
---|
95 | function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
|
---|
96 | if (tokenIdx === 0) { localContext.reducedBodies.push(newBody); }
|
---|
97 | }
|
---|
98 |
|
---|
99 | allSelectors:
|
---|
100 | for (var complexSelector in candidates) {
|
---|
101 | var into = candidates[complexSelector];
|
---|
102 | if (!into[0].isComplex) { continue; }
|
---|
103 |
|
---|
104 | var intoPosition = into[into.length - 1].where;
|
---|
105 | var intoToken = tokens[intoPosition];
|
---|
106 | var reducedBodies = [];
|
---|
107 |
|
---|
108 | var selectors = isMergeable(complexSelector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging)
|
---|
109 | ? into[0].list
|
---|
110 | : [complexSelector];
|
---|
111 |
|
---|
112 | localContext.intoPosition = intoPosition;
|
---|
113 | localContext.reducedBodies = reducedBodies;
|
---|
114 |
|
---|
115 | for (var j = 0, m = selectors.length; j < m; j++) {
|
---|
116 | var selector = selectors[j];
|
---|
117 | var data = candidates[selector];
|
---|
118 |
|
---|
119 | if (data.length < 2) { continue allSelectors; }
|
---|
120 |
|
---|
121 | localContext.data = data;
|
---|
122 |
|
---|
123 | reduceSelector(tokens, data, {
|
---|
124 | filterOut: filterOut,
|
---|
125 | callback: collectReducedBodies
|
---|
126 | }, options, context);
|
---|
127 |
|
---|
128 | if (serializeBody(reducedBodies[reducedBodies.length - 1]) != serializeBody(reducedBodies[0])) {
|
---|
129 | continue allSelectors;
|
---|
130 | }
|
---|
131 | }
|
---|
132 |
|
---|
133 | intoToken[2] = reducedBodies[0];
|
---|
134 | }
|
---|
135 | }
|
---|
136 |
|
---|
137 | function reduceSelector(tokens, data, context, options, outerContext) {
|
---|
138 | var bodies = [];
|
---|
139 | var bodiesAsList = [];
|
---|
140 | var processedTokens = [];
|
---|
141 |
|
---|
142 | for (var j = data.length - 1; j >= 0; j--) {
|
---|
143 | if (context.filterOut(j, bodies)) { continue; }
|
---|
144 |
|
---|
145 | var where = data[j].where;
|
---|
146 | var token = tokens[where];
|
---|
147 | var clonedBody = cloneArray(token[2]);
|
---|
148 |
|
---|
149 | bodies = bodies.concat(clonedBody);
|
---|
150 | bodiesAsList.push(clonedBody);
|
---|
151 | processedTokens.push(where);
|
---|
152 | }
|
---|
153 |
|
---|
154 | optimizeProperties(bodies, true, false, outerContext);
|
---|
155 |
|
---|
156 | var processedCount = processedTokens.length;
|
---|
157 | var propertyIdx = bodies.length - 1;
|
---|
158 | var tokenIdx = processedCount - 1;
|
---|
159 |
|
---|
160 | while (tokenIdx >= 0) {
|
---|
161 | if ((tokenIdx === 0
|
---|
162 | || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
|
---|
163 | propertyIdx--;
|
---|
164 | continue;
|
---|
165 | }
|
---|
166 |
|
---|
167 | var newBody = bodies.splice(propertyIdx + 1);
|
---|
168 | context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
|
---|
169 |
|
---|
170 | tokenIdx--;
|
---|
171 | }
|
---|
172 | }
|
---|
173 |
|
---|
174 | module.exports = reduceNonAdjacent;
|
---|