2011-01-05 07:30:57 +08:00
|
|
|
(function(tree) {
|
2010-02-24 02:39:05 +08:00
|
|
|
|
2011-01-05 07:30:57 +08:00
|
|
|
tree.Ruleset = function(selectors, rules) {
|
2010-02-24 02:39:05 +08:00
|
|
|
this.selectors = selectors;
|
|
|
|
this.rules = rules;
|
2010-03-10 12:56:49 +08:00
|
|
|
this._lookups = {};
|
2010-02-24 02:39:05 +08:00
|
|
|
};
|
2010-03-02 04:32:21 +08:00
|
|
|
tree.Ruleset.prototype = {
|
2011-01-05 07:30:57 +08:00
|
|
|
eval: function(env) {
|
2010-07-24 07:46:48 +08:00
|
|
|
var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
|
2010-07-07 18:20:59 +08:00
|
|
|
|
2010-07-24 07:46:48 +08:00
|
|
|
ruleset.root = this.root;
|
2010-04-23 01:34:49 +08:00
|
|
|
|
2010-07-24 07:46:48 +08:00
|
|
|
// push the current ruleset to the frames stack
|
|
|
|
env.frames.unshift(ruleset);
|
2010-07-07 19:53:09 +08:00
|
|
|
|
|
|
|
// Evaluate imports
|
2010-07-24 07:46:48 +08:00
|
|
|
if (ruleset.root) {
|
|
|
|
for (var i = 0; i < ruleset.rules.length; i++) {
|
|
|
|
if (ruleset.rules[i] instanceof tree.Import) {
|
2010-07-07 19:53:09 +08:00
|
|
|
Array.prototype.splice
|
2010-07-24 07:46:48 +08:00
|
|
|
.apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
|
2010-07-07 19:53:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-24 07:46:48 +08:00
|
|
|
// Store the frames around mixin definitions,
|
|
|
|
// so they can be evaluated like closures when the time comes.
|
|
|
|
for (var i = 0; i < ruleset.rules.length; i++) {
|
|
|
|
if (ruleset.rules[i] instanceof tree.mixin.Definition) {
|
|
|
|
ruleset.rules[i].frames = env.frames.slice(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-07 19:53:09 +08:00
|
|
|
// Evaluate mixin calls.
|
2010-07-24 07:46:48 +08:00
|
|
|
for (var i = 0; i < ruleset.rules.length; i++) {
|
|
|
|
if (ruleset.rules[i] instanceof tree.mixin.Call) {
|
2010-07-07 19:53:09 +08:00
|
|
|
Array.prototype.splice
|
2010-07-24 07:46:48 +08:00
|
|
|
.apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
|
2010-07-07 19:53:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate everything else
|
2010-07-24 07:46:48 +08:00
|
|
|
for (var i = 0, rule; i < ruleset.rules.length; i++) {
|
|
|
|
rule = ruleset.rules[i];
|
2010-07-07 19:53:09 +08:00
|
|
|
|
|
|
|
if (! (rule instanceof tree.mixin.Definition)) {
|
2010-07-24 07:46:48 +08:00
|
|
|
ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
|
2010-07-07 19:53:09 +08:00
|
|
|
}
|
|
|
|
}
|
2010-07-07 18:20:59 +08:00
|
|
|
|
2010-07-07 19:53:09 +08:00
|
|
|
// Pop the stack
|
|
|
|
env.frames.shift();
|
|
|
|
|
2010-07-24 07:46:48 +08:00
|
|
|
return ruleset;
|
2010-04-23 01:07:41 +08:00
|
|
|
},
|
2011-01-05 07:30:57 +08:00
|
|
|
match: function(args) {
|
2010-04-25 12:09:40 +08:00
|
|
|
return !args || args.length === 0;
|
|
|
|
},
|
2011-01-05 07:30:57 +08:00
|
|
|
variables: function() {
|
2010-07-09 01:04:36 +08:00
|
|
|
if (this._variables) { return this._variables }
|
2010-03-08 12:02:16 +08:00
|
|
|
else {
|
2011-01-05 07:30:57 +08:00
|
|
|
return this._variables = this.rules.reduce(function(hash, r) {
|
2010-04-24 05:52:36 +08:00
|
|
|
if (r instanceof tree.Rule && r.variable === true) {
|
|
|
|
hash[r.name] = r;
|
|
|
|
}
|
|
|
|
return hash;
|
2010-07-09 01:04:36 +08:00
|
|
|
}, {});
|
2010-03-08 12:02:16 +08:00
|
|
|
}
|
2010-02-24 02:39:05 +08:00
|
|
|
},
|
2011-01-05 07:30:57 +08:00
|
|
|
variable: function(name) {
|
2010-07-09 01:04:36 +08:00
|
|
|
return this.variables()[name];
|
|
|
|
},
|
2011-01-05 07:30:57 +08:00
|
|
|
rulesets: function() {
|
2010-03-08 12:11:20 +08:00
|
|
|
if (this._rulesets) { return this._rulesets }
|
|
|
|
else {
|
2011-01-05 07:30:57 +08:00
|
|
|
return this._rulesets = this.rules.filter(function(r) {
|
2010-07-07 17:59:09 +08:00
|
|
|
return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
|
2010-03-08 12:11:20 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2011-01-05 07:30:57 +08:00
|
|
|
find: function(selector, self) {
|
2010-03-05 02:50:52 +08:00
|
|
|
self = self || this;
|
2010-03-10 12:56:49 +08:00
|
|
|
var rules = [], rule, match,
|
|
|
|
key = selector.toCSS();
|
|
|
|
|
|
|
|
if (key in this._lookups) { return this._lookups[key] }
|
2010-03-05 02:50:52 +08:00
|
|
|
|
2011-01-05 07:30:57 +08:00
|
|
|
this.rulesets().forEach(function(rule) {
|
2010-03-08 12:11:20 +08:00
|
|
|
if (rule !== self) {
|
2010-03-05 02:50:52 +08:00
|
|
|
for (var j = 0; j < rule.selectors.length; j++) {
|
|
|
|
if (match = selector.match(rule.selectors[j])) {
|
|
|
|
if (selector.elements.length > 1) {
|
|
|
|
Array.prototype.push.apply(rules, rule.find(
|
|
|
|
new(tree.Selector)(selector.elements.slice(1)), self));
|
|
|
|
} else {
|
|
|
|
rules.push(rule);
|
|
|
|
}
|
|
|
|
break;
|
2010-03-04 10:21:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-08 12:11:20 +08:00
|
|
|
});
|
2010-03-10 12:56:49 +08:00
|
|
|
return this._lookups[key] = rules;
|
2010-03-04 10:21:32 +08:00
|
|
|
},
|
2010-03-02 08:47:32 +08:00
|
|
|
//
|
|
|
|
// Entry point for code generation
|
|
|
|
//
|
2010-03-06 00:42:52 +08:00
|
|
|
// `context` holds an array of arrays.
|
|
|
|
//
|
2011-01-05 07:30:57 +08:00
|
|
|
toCSS: function(context, env) {
|
2010-03-02 08:47:32 +08:00
|
|
|
var css = [], // The CSS output
|
|
|
|
rules = [], // node.Rule instances
|
|
|
|
rulesets = [], // node.Ruleset instances
|
|
|
|
paths = [], // Current selectors
|
2010-03-06 00:42:52 +08:00
|
|
|
selector, // The fully rendered selector
|
2010-12-14 11:48:10 +08:00
|
|
|
symbolizers = {},
|
2010-03-06 00:42:52 +08:00
|
|
|
rule;
|
2010-02-24 02:39:05 +08:00
|
|
|
|
2010-02-28 14:06:54 +08:00
|
|
|
if (! this.root) {
|
|
|
|
if (context.length === 0) {
|
2011-01-05 07:30:57 +08:00
|
|
|
paths = this.selectors.map(function(s) { return [s] });
|
2010-02-28 14:06:54 +08:00
|
|
|
} else {
|
|
|
|
for (var s = 0; s < this.selectors.length; s++) {
|
|
|
|
for (var c = 0; c < context.length; c++) {
|
2010-03-01 01:50:41 +08:00
|
|
|
paths.push(context[c].concat([this.selectors[s]]));
|
2010-02-28 14:06:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-22 07:18:47 +08:00
|
|
|
}
|
2010-03-20 08:14:35 +08:00
|
|
|
|
2010-07-07 19:53:09 +08:00
|
|
|
// Compile rules and rulesets
|
2010-02-24 02:39:05 +08:00
|
|
|
for (var i = 0; i < this.rules.length; i++) {
|
2010-03-06 00:42:52 +08:00
|
|
|
rule = this.rules[i];
|
|
|
|
|
2010-07-07 19:53:09 +08:00
|
|
|
if (rule.rules || (rule instanceof tree.Directive)) {
|
2010-03-06 00:42:52 +08:00
|
|
|
rulesets.push(rule.toCSS(paths, env));
|
2010-03-07 06:53:55 +08:00
|
|
|
} else if (rule instanceof tree.Comment) {
|
2010-06-16 14:13:59 +08:00
|
|
|
if (!rule.silent) {
|
|
|
|
if (this.root) {
|
|
|
|
rulesets.push(rule.toCSS(env));
|
|
|
|
} else {
|
|
|
|
rules.push(rule.toCSS(env));
|
|
|
|
}
|
2010-03-07 06:53:55 +08:00
|
|
|
}
|
2010-02-24 02:39:05 +08:00
|
|
|
} else {
|
2010-03-06 00:42:52 +08:00
|
|
|
if (rule.toCSS && !rule.variable) {
|
2010-12-14 11:48:10 +08:00
|
|
|
symbolizers[rule.symbolizer] = symbolizers[rule.symbolizer] ?
|
2011-01-05 07:30:57 +08:00
|
|
|
symbolizers[rule.symbolizer].concat(rule.toCSS(env)) :
|
2010-12-14 11:48:10 +08:00
|
|
|
[rule.toCSS(env)];
|
2010-07-07 19:53:09 +08:00
|
|
|
rules.push(rule.toCSS(env));
|
2010-03-06 00:42:52 +08:00
|
|
|
} else if (rule.value && !rule.variable) {
|
|
|
|
rules.push(rule.value.toString());
|
2010-02-24 02:39:05 +08:00
|
|
|
}
|
|
|
|
}
|
2011-01-05 07:30:57 +08:00
|
|
|
}
|
2010-02-28 14:06:54 +08:00
|
|
|
|
2011-01-11 00:47:34 +08:00
|
|
|
|
2010-02-28 14:06:54 +08:00
|
|
|
rulesets = rulesets.join('');
|
2010-03-02 08:47:32 +08:00
|
|
|
|
|
|
|
// If this is the root node, we don't render
|
|
|
|
// a selector, or {}.
|
|
|
|
// Otherwise, only output if this ruleset has rules.
|
2010-02-28 14:06:54 +08:00
|
|
|
if (this.root) {
|
2010-06-12 09:45:51 +08:00
|
|
|
css.push(rules.join(env.compress ? '' : '\n'));
|
2010-02-28 14:06:54 +08:00
|
|
|
} else {
|
|
|
|
if (rules.length > 0) {
|
2011-01-05 07:30:57 +08:00
|
|
|
selector = paths.map(function(p) {
|
|
|
|
return p.map(function(s) {
|
2010-06-12 09:45:51 +08:00
|
|
|
return s.toCSS(env);
|
2010-03-06 00:39:39 +08:00
|
|
|
}).join('').trim();
|
2010-06-12 09:45:51 +08:00
|
|
|
}).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
|
2011-01-11 00:47:34 +08:00
|
|
|
|
|
|
|
filters = (function(paths) {
|
|
|
|
var out = [];
|
|
|
|
_.each(paths, function(path) {
|
|
|
|
_.each(path, function(selector) {
|
|
|
|
_.each(selector.filters, function(filter) {
|
|
|
|
out.push(filter.toCSS());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return out;
|
|
|
|
})(paths);
|
|
|
|
|
2010-12-14 11:48:10 +08:00
|
|
|
if (symbolizers) {
|
|
|
|
rules = (function(symbolizers) {
|
|
|
|
var out = [];
|
|
|
|
for (i in symbolizers) {
|
|
|
|
var symname = i.charAt(0).toUpperCase() + i.slice(1) + 'Symbolizer';
|
|
|
|
out.push(' <' +
|
2011-01-05 07:51:12 +08:00
|
|
|
symname + ' ' +
|
2010-12-14 11:48:10 +08:00
|
|
|
symbolizers[i].join('\n ') +
|
2011-01-05 07:51:12 +08:00
|
|
|
'/>');
|
2010-12-14 11:48:10 +08:00
|
|
|
}
|
|
|
|
return out;
|
|
|
|
})(symbolizers);
|
|
|
|
}
|
2011-01-04 05:35:52 +08:00
|
|
|
if (this.selectors[0].elements[0].value !== 'Map') {
|
|
|
|
css.push('<Style name="' +
|
|
|
|
selector +
|
|
|
|
'">\n<Rule>\n' +
|
2011-01-11 00:47:34 +08:00
|
|
|
filters.join('\n') + '\n' +
|
2011-01-04 05:35:52 +08:00
|
|
|
rules.join('\n') +
|
|
|
|
'\n</Rule>\n' +
|
|
|
|
'</Style>\n');
|
|
|
|
}
|
2010-02-24 02:39:05 +08:00
|
|
|
}
|
|
|
|
}
|
2010-02-28 14:06:54 +08:00
|
|
|
css.push(rulesets);
|
2010-02-24 02:39:05 +08:00
|
|
|
|
2011-01-05 06:35:21 +08:00
|
|
|
return css.join('') + (env.compress ? '\n' : '');
|
2010-02-24 02:39:05 +08:00
|
|
|
}
|
|
|
|
};
|
2011-01-06 03:23:28 +08:00
|
|
|
})(require('mess/tree'));
|