Skip to content

Commit 936ab7d

Browse files
committed
Added automatic merging of media-query conditions
1 parent 80e8b42 commit 936ab7d

File tree

3 files changed

+129
-28
lines changed

3 files changed

+129
-28
lines changed

lib/less/tree/media.js

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,113 @@
11
(function (tree) {
22

33
tree.Media = function (value, features) {
4-
var selectors;
4+
var el = new(tree.Element)('&', null, 0),
5+
selectors = [new(tree.Selector)([el])];
56

6-
this.features = features && new(tree.Value)(features);
7-
8-
if (Array.isArray(value)) {
9-
selectors = [new(tree.Selector)([new(tree.Element)('&', null, 0)])];
10-
this.ruleset = new(tree.Ruleset)(selectors, value);
11-
this.ruleset.allowImports = true;
12-
} else {
13-
this.value = value;
14-
}
7+
this.features = new(tree.Value)(features);
8+
this.ruleset = new(tree.Ruleset)(selectors, value);
9+
this.ruleset.allowImports = true;
1510
};
1611
tree.Media.prototype = {
1712
toCSS: function (ctx, env) {
18-
var features = this.features ? ' ' + this.features.toCSS(env) : '';
13+
var features = this.features.toCSS(env);
1914

20-
if (this.ruleset) {
21-
this.ruleset.root = (ctx.length === 0);
22-
return '@media' + features + (env.compress ? '{' : ' {\n ') +
23-
this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
24-
(env.compress ? '}': '\n}\n');
25-
} else {
26-
return '@media ' + this.value.toCSS() + ';\n';
27-
}
15+
this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia);
16+
return '@media ' + features + (env.compress ? '{' : ' {\n ') +
17+
this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
18+
(env.compress ? '}': '\n}\n');
2819
},
2920
eval: function (env) {
30-
this.features = this.features && this.features.eval(env);
21+
if (!env.mediaBlocks) {
22+
env.mediaBlocks = [];
23+
env.mediaPath = [];
24+
}
25+
26+
env.mediaBlocks.push(this);
27+
env.mediaPath.push(this);
28+
29+
this.features = this.features.eval(env);
3130
env.frames.unshift(this);
32-
this.ruleset = this.ruleset && this.ruleset.eval(env);
31+
this.ruleset = this.ruleset.eval(env);
3332
env.frames.shift();
34-
return this;
33+
34+
env.mediaPath.pop();
35+
36+
if (env.mediaPath.length === 0) {
37+
return this.evalTop(env);
38+
} else {
39+
return this.evalNested(env);
40+
}
3541
},
3642
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
3743
find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
38-
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
44+
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
45+
46+
evalTop: function (env) {
47+
var result = this;
48+
49+
// Render all dependent Media blocks.
50+
if (env.mediaBlocks.length > 1) {
51+
var el = new(tree.Element)('&', null, 0);
52+
var selectors = [new(tree.Selector)([el])];
53+
result = new(tree.Ruleset)(selectors, env.mediaBlocks);
54+
result.multiMedia = true;
55+
}
56+
57+
delete env.mediaBlocks;
58+
delete env.mediaPath;
59+
60+
return result;
61+
},
62+
evalNested: function (env) {
63+
var i, value,
64+
path = env.mediaPath.concat([this]);
65+
66+
// Extract the media-query conditions separated with `,` (OR).
67+
for (i = 0; i < path.length; i++) {
68+
value = path[i].features instanceof tree.Value ?
69+
path[i].features.value : path[i].features;
70+
path[i] = Array.isArray(value) ? value : [value];
71+
}
72+
73+
// Trace all permutations to generate the resulting media-query.
74+
//
75+
// (a, b and c) with nested (d, e) ->
76+
// a and d
77+
// a and e
78+
// b and c and d
79+
// b and c and e
80+
this.features = new(tree.Value)(this.permute(path).map(function (path) {
81+
path = path.map(function (fragment) {
82+
return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
83+
});
84+
85+
for(i = path.length - 1; i > 0; i--) {
86+
path.splice(i, 0, new(tree.Anonymous)("and"));
87+
}
88+
89+
return new(tree.Expression)(path);
90+
}));
91+
92+
// Fake a tree-node that doesn't output anything.
93+
return new(tree.Ruleset)([], []);
94+
},
95+
permute: function (arr) {
96+
if (arr.length == 0) {
97+
return [];
98+
} else if (arr.length == 1) {
99+
return arr[0];
100+
} else {
101+
var result = [];
102+
var rest = this.permute(arr.slice(1));
103+
for (var i = 0; i < rest.length; i++) {
104+
for (var j = 0; j < arr[0].length; j++) {
105+
result.push([arr[0][j]].concat(rest[i]));
106+
}
107+
}
108+
return result;
109+
}
110+
}
39111
};
40112

41113
})(require('../tree'));

test/css/media.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,18 @@
3333
background-color: red;
3434
}
3535
}
36+
@media print and (orientation: landscape) {
37+
body {
38+
margin-left: 20px;
39+
}
40+
}
41+
@media a, b and c {
42+
body {
43+
width: 95%;
44+
}
45+
}
46+
@media a and x, b and c and x, a and y, b and c and y {
47+
body {
48+
width: 100%;
49+
}
50+
}

test/less/media.less

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,25 @@
3131
}
3232

3333
body {
34-
@media print {
35-
padding: 20px;
34+
@media print {
35+
padding: 20px;
3636

37-
header {
38-
background-color: red;
37+
header {
38+
background-color: red;
39+
}
40+
41+
@media (orientation:landscape) {
42+
margin-left: 20px;
43+
}
3944
}
40-
}
4145
}
46+
47+
body {
48+
@media a, b and c {
49+
width: 95%;
50+
51+
@media x, y {
52+
width: 100%;
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)