summaryrefslogtreecommitdiff
path: root/bin/lib/less/tree/media.js
diff options
context:
space:
mode:
Diffstat (limited to 'bin/lib/less/tree/media.js')
-rw-r--r--bin/lib/less/tree/media.js114
1 files changed, 114 insertions, 0 deletions
diff --git a/bin/lib/less/tree/media.js b/bin/lib/less/tree/media.js
new file mode 100644
index 00000000..2b7b26e5
--- /dev/null
+++ b/bin/lib/less/tree/media.js
@@ -0,0 +1,114 @@
+(function (tree) {
+
+tree.Media = function (value, features) {
+ var el = new(tree.Element)('&', null, 0),
+ selectors = [new(tree.Selector)([el])];
+
+ this.features = new(tree.Value)(features);
+ this.ruleset = new(tree.Ruleset)(selectors, value);
+ this.ruleset.allowImports = true;
+};
+tree.Media.prototype = {
+ toCSS: function (ctx, env) {
+ var features = this.features.toCSS(env);
+
+ this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia);
+ return '@media ' + features + (env.compress ? '{' : ' {\n ') +
+ this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
+ (env.compress ? '}': '\n}\n');
+ },
+ eval: function (env) {
+ if (!env.mediaBlocks) {
+ env.mediaBlocks = [];
+ env.mediaPath = [];
+ }
+
+ var blockIndex = env.mediaBlocks.length;
+ env.mediaPath.push(this);
+ env.mediaBlocks.push(this);
+
+ var media = new(tree.Media)([], []);
+ media.features = this.features.eval(env);
+
+ env.frames.unshift(this.ruleset);
+ media.ruleset = this.ruleset.eval(env);
+ env.frames.shift();
+
+ env.mediaBlocks[blockIndex] = media;
+ env.mediaPath.pop();
+
+ return env.mediaPath.length === 0 ? media.evalTop(env) :
+ media.evalNested(env)
+ },
+ variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
+ find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
+ rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
+
+ evalTop: function (env) {
+ var result = this;
+
+ // Render all dependent Media blocks.
+ if (env.mediaBlocks.length > 1) {
+ var el = new(tree.Element)('&', null, 0);
+ var selectors = [new(tree.Selector)([el])];
+ result = new(tree.Ruleset)(selectors, env.mediaBlocks);
+ result.multiMedia = true;
+ }
+
+ delete env.mediaBlocks;
+ delete env.mediaPath;
+
+ return result;
+ },
+ evalNested: function (env) {
+ var i, value,
+ path = env.mediaPath.concat([this]);
+
+ // Extract the media-query conditions separated with `,` (OR).
+ for (i = 0; i < path.length; i++) {
+ value = path[i].features instanceof tree.Value ?
+ path[i].features.value : path[i].features;
+ path[i] = Array.isArray(value) ? value : [value];
+ }
+
+ // Trace all permutations to generate the resulting media-query.
+ //
+ // (a, b and c) with nested (d, e) ->
+ // a and d
+ // a and e
+ // b and c and d
+ // b and c and e
+ this.features = new(tree.Value)(this.permute(path).map(function (path) {
+ path = path.map(function (fragment) {
+ return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
+ });
+
+ for(i = path.length - 1; i > 0; i--) {
+ path.splice(i, 0, new(tree.Anonymous)("and"));
+ }
+
+ return new(tree.Expression)(path);
+ }));
+
+ // Fake a tree-node that doesn't output anything.
+ return new(tree.Ruleset)([], []);
+ },
+ permute: function (arr) {
+ if (arr.length === 0) {
+ return [];
+ } else if (arr.length === 1) {
+ return arr[0];
+ } else {
+ var result = [];
+ var rest = this.permute(arr.slice(1));
+ for (var i = 0; i < rest.length; i++) {
+ for (var j = 0; j < arr[0].length; j++) {
+ result.push([arr[0][j]].concat(rest[i]));
+ }
+ }
+ return result;
+ }
+ }
+};
+
+})(require('../tree'));