source: Dev/branches/rest-dojo-ui/client/util/less/tree/ruleset.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

  • Property svn:executable set to *
File size: 7.0 KB
Line 
1(function (tree) {
2
3tree.Ruleset = function (selectors, rules) {
4    this.selectors = selectors;
5    this.rules = rules;
6    this._lookups = {};
7};
8tree.Ruleset.prototype = {
9    eval: function (env) {
10        var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
11
12        ruleset.root = this.root;
13
14        // push the current ruleset to the frames stack
15        env.frames.unshift(ruleset);
16
17        // Evaluate imports
18        if (ruleset.root) {
19            for (var i = 0; i < ruleset.rules.length; i++) {
20                if (ruleset.rules[i] instanceof tree.Import) {
21                    Array.prototype.splice
22                         .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
23                }
24            }
25        }
26
27        // Store the frames around mixin definitions,
28        // so they can be evaluated like closures when the time comes.
29        for (var i = 0; i < ruleset.rules.length; i++) {
30            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
31                ruleset.rules[i].frames = env.frames.slice(0);
32            }
33        }
34
35        // Evaluate mixin calls.
36        for (var i = 0; i < ruleset.rules.length; i++) {
37            if (ruleset.rules[i] instanceof tree.mixin.Call) {
38                Array.prototype.splice
39                     .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
40            }
41        }
42
43        // Evaluate everything else
44        for (var i = 0, rule; i < ruleset.rules.length; i++) {
45            rule = ruleset.rules[i];
46
47            if (! (rule instanceof tree.mixin.Definition)) {
48                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
49            }
50        }
51
52        // Pop the stack
53        env.frames.shift();
54
55        return ruleset;
56    },
57    match: function (args) {
58        return !args || args.length === 0;
59    },
60    variables: function () {
61        if (this._variables) { return this._variables }
62        else {
63            return this._variables = this.rules.reduce(function (hash, r) {
64                if (r instanceof tree.Rule && r.variable === true) {
65                    hash[r.name] = r;
66                }
67                return hash;
68            }, {});
69        }
70    },
71    variable: function (name) {
72        return this.variables()[name];
73    },
74    rulesets: function () {
75        if (this._rulesets) { return this._rulesets }
76        else {
77            return this._rulesets = this.rules.filter(function (r) {
78                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
79            });
80        }
81    },
82    find: function (selector, self) {
83        self = self || this;
84        var rules = [], rule, match,
85            key = selector.toCSS();
86
87        if (key in this._lookups) { return this._lookups[key] }
88
89        this.rulesets().forEach(function (rule) {
90            if (rule !== self) {
91                for (var j = 0; j < rule.selectors.length; j++) {
92                    if (match = selector.match(rule.selectors[j])) {
93                        if (selector.elements.length > 1) {
94                            Array.prototype.push.apply(rules, rule.find(
95                                new(tree.Selector)(selector.elements.slice(1)), self));
96                        } else {
97                            rules.push(rule);
98                        }
99                        break;
100                    }
101                }
102            }
103        });
104        return this._lookups[key] = rules;
105    },
106    //
107    // Entry point for code generation
108    //
109    //     `context` holds an array of arrays.
110    //
111    toCSS: function (context, env) {
112        var css = [],      // The CSS output
113            rules = [],    // node.Rule instances
114            rulesets = [], // node.Ruleset instances
115            paths = [],    // Current selectors
116            selector,      // The fully rendered selector
117            rule;
118
119        if (! this.root) {
120            if (context.length === 0) {
121                paths = this.selectors.map(function (s) { return [s] });
122            } else {
123                this.joinSelectors( paths, context, this.selectors );
124            }
125        }
126
127        // Compile rules and rulesets
128        for (var i = 0; i < this.rules.length; i++) {
129            rule = this.rules[i];
130
131            if (rule.rules || (rule instanceof tree.Directive)) {
132                rulesets.push(rule.toCSS(paths, env));
133            } else if (rule instanceof tree.Comment) {
134                if (!rule.silent) {
135                    if (this.root) {
136                        rulesets.push(rule.toCSS(env));
137                    } else {
138                        rules.push(rule.toCSS(env));
139                    }
140                }
141            } else {
142                if (rule.toCSS && !rule.variable) {
143                    rules.push(rule.toCSS(env));
144                } else if (rule.value && !rule.variable) {
145                    rules.push(rule.value.toString());
146                }
147            }
148        }
149
150        rulesets = rulesets.join('');
151
152        // If this is the root node, we don't render
153        // a selector, or {}.
154        // Otherwise, only output if this ruleset has rules.
155        if (this.root) {
156            css.push(rules.join(env.compress ? '' : '\n'));
157        } else {
158            if (rules.length > 0) {
159                selector = paths.map(function (p) {
160                    return p.map(function (s) {
161                        return s.toCSS(env);
162                    }).join('').trim();
163                }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
164                css.push(selector,
165                        (env.compress ? '{' : ' {\n  ') +
166                        rules.join(env.compress ? '' : '\n  ') +
167                        (env.compress ? '}' : '\n}\n'));
168            }
169        }
170        css.push(rulesets);
171
172        return css.join('') + (env.compress ? '\n' : '');
173    },
174
175    joinSelectors: function (paths, context, selectors) {
176        for (var s = 0; s < selectors.length; s++) {
177            this.joinSelector(paths, context, selectors[s]);
178        }
179    },
180
181    joinSelector: function (paths, context, selector) {
182        var before = [], after = [], beforeElements = [],
183            afterElements = [], hasParentSelector = false, el;
184
185        for (var i = 0; i < selector.elements.length; i++) {
186            el = selector.elements[i];
187            if (el.combinator.value[0] === '&') {
188                hasParentSelector = true;
189            }
190            if (hasParentSelector) afterElements.push(el);
191            else                   beforeElements.push(el);
192        }
193
194        if (! hasParentSelector) {
195            afterElements = beforeElements;
196            beforeElements = [];
197        }
198
199        if (beforeElements.length > 0) {
200            before.push(new(tree.Selector)(beforeElements));
201        }
202
203        if (afterElements.length > 0) {
204            after.push(new(tree.Selector)(afterElements));
205        }
206
207        for (var c = 0; c < context.length; c++) {
208            paths.push(before.concat(context[c]).concat(after));
209        }
210    }
211};
212})(require('less/tree'));
Note: See TracBrowser for help on using the repository browser.