Inital Commit
[oweals/finalsclub.git] / node_modules / jade / support / stylus / lib / visitor / compiler.js
1
2 /*!
3  * Stylus - Compiler
4  * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
5  * MIT Licensed
6  */
7
8 /**
9  * Module dependencies.
10  */
11
12 var Visitor = require('./')
13   , nodes = require('../nodes');
14
15 /**
16  * Initialize a new `Compiler` with the given `root` Node
17  * and the following `options`.
18  *
19  * Options:
20  *
21  *   - `compress`  Compress the css output, defaults to false
22  *
23  * @param {Node} root
24  * @api public
25  */
26
27 var Compiler = module.exports = function Compiler(root, options) {
28   options = options || {};
29   this.compress = options.compress;
30   this.indents = 1;
31   Visitor.call(this, root);
32   this.tree = [];
33 };
34
35 /**
36  * Inherit from `Visitor.prototype`.
37  */
38
39 Compiler.prototype.__proto__ = Visitor.prototype;
40
41 /**
42  * Compile to css, and callback `fn(err, css)`.
43  *
44  * @param {Function} fn
45  * @api public
46  */
47
48 Compiler.prototype.compile = function(fn){
49   this.callback = fn;
50   this.css = this.visit(this.root);
51   fn(null, this.css);
52 };
53
54 /**
55  * Return indentation string.
56  *
57  * @return {String}
58  * @api private
59  */
60
61 Compiler.prototype.__defineGetter__('indent', function(){
62   return this.compress
63      ? ''
64      : new Array(this.indents).join('  ');
65 });
66
67 /**
68  * Visit Root.
69  */
70
71 Compiler.prototype.visitRoot = function(block){
72   this.buf = '';
73   for (var i = 0, len = block.nodes.length; i < len; ++i) {
74     var node = block.nodes[i];
75     if (node instanceof nodes.Null
76       || node instanceof nodes.Expression
77       || node instanceof nodes.Function
78       || node instanceof nodes.Unit) continue;
79     var ret = this.visit(node);
80     if (ret) this.buf += ret + '\n';
81   }
82   return this.buf;
83 };
84
85 /**
86  * Visit Block.
87  */
88
89 Compiler.prototype.visitBlock = function(block){
90   if (block.hasProperties) {
91     var arr = [this.compress ? '{' : ' {'];
92     ++this.indents;
93     for (var i = 0, len = block.nodes.length; i < len; ++i) {
94       this.last = len - 1 == i;
95       var node = block.nodes[i];
96       if (node instanceof nodes.Null
97         || node instanceof nodes.Expression
98         || node instanceof nodes.Function
99         || node instanceof nodes.Group
100         || node instanceof nodes.Unit) continue;
101       arr.push(this.visit(node));
102     }
103     --this.indents;
104     arr.push(this.indent + '}');
105     this.buf += arr.join(this.compress ? '' : '\n');
106     this.buf += '\n';
107   }
108
109   // Nesting
110   for (var i = 0, len = block.nodes.length; i < len; ++i) {
111     this.visit(block.nodes[i]);
112   }
113 };
114
115 /**
116  * Visit Keyframes.
117  */
118
119 Compiler.prototype.visitKeyframes = function(node){
120   this.buf += '@-webkit-keyframes '
121     + this.visit(node.name)
122     + (this.compress ? '{' : ' {');
123   ++this.indents;
124   node.frames.forEach(function(frame){
125     if (!this.compress) this.buf += '\n  ';
126     this.buf += this.visit(frame.pos);
127     this.visit(frame.block);
128   }, this);
129   --this.indents;
130   this.buf += '}' + (this.compress ? '' : '\n');
131 };
132
133 /**
134  * Visit Media.
135  */
136
137 Compiler.prototype.visitMedia = function(media){
138   this.buf += '@media ' + media.val;
139   this.buf += this.compress ? '{' : ' {\n';
140   ++this.indents;
141   this.visit(media.block);
142   --this.indents;
143   this.buf += '}' + (this.compress ? '' : '\n');
144 };
145
146 /**
147  * Visit Page.
148  */
149
150 Compiler.prototype.visitPage = function(page){
151   this.buf += this.indent + '@page';
152   this.buf += page.selector ? ' ' + page.selector : '';
153   this.visit(page.block);
154 };
155
156 /**
157  * Visit Function.
158  */
159
160 Compiler.prototype.visitFunction = function(fn){
161   return fn.name;
162 };
163
164 /**
165  * Visit Variable.
166  */
167
168 Compiler.prototype.visitVariable = function(variable){
169   return '';
170 };
171
172 /**
173  * Visit Charset.
174  */
175
176 Compiler.prototype.visitCharset = function(charset){
177   return '@charset ' + this.visit(charset.val);
178 };
179
180 /**
181  * Visit Literal.
182  */
183
184 Compiler.prototype.visitLiteral = function(lit){
185   return lit.val.trim().replace(/^  /gm, '');
186 };
187
188 /**
189  * Visit Boolean.
190  */
191
192 Compiler.prototype.visitBoolean = function(bool){
193   return bool.toString();
194 };
195
196 /**
197  * Visit RGBA.
198  */
199
200 Compiler.prototype.visitRGBA = function(rgba){
201   return rgba.toString();
202 };
203
204 /**
205  * Visit HSLA.
206  */
207
208 Compiler.prototype.visitHSLA = function(hsla){
209   return hsla.rgba.toString();
210 };
211
212 /**
213  * Visit Unit.
214  */
215
216 Compiler.prototype.visitUnit = function(unit){
217   var type = unit.type || ''
218     , n = unit.val
219     , float = n != (n | 0);
220
221   // Int
222   if ('px' == type) n = n.toFixed(0);
223   // Compress
224   if (this.compress) {
225     // Zero is always '0'
226     if (0 == n) return '0';
227     // Omit leading '0' on floats
228     if (float && n < 1 && n > -1) {
229       return n.toString().replace('0.', '.') + type;
230     }
231   }
232
233   return n.toString() + type;
234 };
235
236 /**
237  * Visit Group.
238  */
239
240 Compiler.prototype.visitGroup = function(group){
241   var self = this
242     , tree = this.tree
243     , prev = tree[tree.length - 1]
244     , curr = [];
245
246   // Construct an array of arrays
247   // representing the selector hierarchy
248   group.nodes.forEach(function(node){
249     curr.push(node.parent
250         ? node
251         : node.val);
252   });
253
254   tree.push(curr);
255
256   // Reverse recurse the
257   // hierarchy array to build
258   // up the selector permutations.
259   // When we reach root, we have our
260   // selector string built
261   var selectors = []
262     , buf = [];
263   function join(arr, i) {
264     if (i) {
265       arr[i].forEach(function(str){
266         buf.unshift(str);
267         join(arr, i - 1);
268         buf.shift();
269       });
270     } else {
271       arr[0].forEach(function(selector){
272         var str = selector;
273         if (buf.length) {
274           for (var i = 0, len = buf.length; i < len; ++i) {
275             if (~buf[i].indexOf('&')) {
276               str = buf[i].replace('&', str);
277             } else {
278               str += ' ' + buf[i];
279             }
280           }
281         }
282         selectors.push(self.indent + str);
283       });
284     }
285   }
286
287   // Join selectors
288   if (group.block.hasProperties) {
289     join(tree, tree.length - 1);
290     this.buf += selectors.join(this.compress ? ',' : ',\n');
291   }
292
293   // Output blocks
294   this.visit(group.block);
295   tree.pop();
296 };
297
298 /**
299  * Visit Ident.
300  */
301
302 Compiler.prototype.visitIdent = function(ident){
303   return ident.name;
304 };
305
306 /**
307  * Visit String.
308  */
309
310 Compiler.prototype.visitString = function(string){
311   return this.isURL
312     ? string.val
313     : string.toString();
314 };
315
316 /**
317  * Visit Null.
318  */
319
320 Compiler.prototype.visitNull = function(node){
321   return '';
322 };
323
324 /**
325  * Visit Call.
326  */
327
328 Compiler.prototype.visitCall = function(call){
329   this.isURL = 'url' == call.name;
330   var args = call.args.nodes.map(function(arg){
331     return this.visit(arg);
332   }, this).join(this.compress ? ',' : ', ');
333   if (this.isURL) args = '"' + args + '"';
334   delete this.isURL;
335   return call.name + '(' + args + ')';
336 };
337
338 /**
339  * Visit Import.
340  */
341
342 Compiler.prototype.visitImport = function(import){
343   return '@import ' + this.visit(import.path) + ';';
344 };
345
346 /**
347  * Visit Expression.
348  */
349
350 Compiler.prototype.visitExpression = function(expr){
351   return expr.nodes.map(function(node){
352     return this.visit(node);
353   }, this).join(expr.isList
354       ? (this.compress ? ',' : ', ')
355       : (this.isURL ? '' : ' '));
356 };
357
358 /**
359  * Visit Property.
360  */
361
362 Compiler.prototype.visitProperty = function(prop){
363   var self = this
364     , val = this.visit(prop.expr);
365   return this.indent + (prop.name || prop.segments.join(''))
366     + (this.compress ? ':' + val : ': ' + val)
367     + (this.compress
368         ? (this.last ? '' : ';')
369         : ';');
370 };