jade-js-source 0.15.4 → 0.16.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jade_js/jade.js CHANGED
@@ -135,7 +135,7 @@ Compiler.prototype = {
135
135
 
136
136
  setDoctype: function(name){
137
137
  var doctype = doctypes[(name || 'default').toLowerCase()];
138
- if (!doctype) throw new Error('unknown doctype "' + name + '"');
138
+ doctype = doctype || '<!DOCTYPE ' + name + '>';
139
139
  this.doctype = doctype;
140
140
  this.terse = '5' == name || 'html' == name;
141
141
  this.xml = 0 == this.doctype.indexOf('<?xml');
@@ -162,18 +162,6 @@ Compiler.prototype = {
162
162
  }
163
163
  },
164
164
 
165
- /**
166
- * Buffer the given `node`'s lineno.
167
- *
168
- * @param {Node} node
169
- * @api public
170
- */
171
-
172
- line: function(node){
173
- if (false === node.instrumentLineNumber) return;
174
- this.buf.push('__.lineno = ' + node.line + ';');
175
- },
176
-
177
165
  /**
178
166
  * Visit `node`.
179
167
  *
@@ -182,8 +170,26 @@ Compiler.prototype = {
182
170
  */
183
171
 
184
172
  visit: function(node){
185
- if (this.debug) this.line(node);
186
- return this.visitNode(node);
173
+ var debug = this.debug;
174
+
175
+ if (debug) {
176
+ this.buf.push('__.unshift({ lineno: ' + node.line
177
+ + ', filename: ' + (node.filename
178
+ ? '"' + node.filename + '"'
179
+ : '__[0].filename')
180
+ + ' });');
181
+ }
182
+
183
+ // Massive hack to fix our context
184
+ // stack for - else[ if] etc
185
+ if (false === node.debug && this.debug) {
186
+ this.buf.pop();
187
+ this.buf.pop();
188
+ }
189
+
190
+ this.visitNode(node);
191
+
192
+ if (debug) this.buf.push('__.shift();');
187
193
  },
188
194
 
189
195
  /**
@@ -516,7 +522,6 @@ require.register("doctypes.js", function(module, exports, require){
516
522
 
517
523
  module.exports = {
518
524
  '5': '<!DOCTYPE html>'
519
- , 'html': '<!DOCTYPE html>'
520
525
  , 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
521
526
  , 'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
522
527
  , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
@@ -553,7 +558,7 @@ module.exports = {
553
558
  sass: function(str){
554
559
  str = str.replace(/\\n/g, '\n');
555
560
  var sass = require('sass').render(str).replace(/\n/g, '\\n');
556
- return '<style>' + sass + '</style>';
561
+ return '<style type="text/css">' + sass + '</style>';
557
562
  },
558
563
 
559
564
  /**
@@ -568,11 +573,11 @@ module.exports = {
568
573
  if (err) throw err;
569
574
  ret = css.replace(/\n/g, '\\n');
570
575
  });
571
- return '<style>' + ret + '</style>';
576
+ return '<style type="text/css">' + ret + '</style>';
572
577
  },
573
578
 
574
579
  /**
575
- * Transform sass to css, wrapped in style tags.
580
+ * Transform less to css, wrapped in style tags.
576
581
  */
577
582
 
578
583
  less: function(str){
@@ -580,7 +585,7 @@ module.exports = {
580
585
  str = str.replace(/\\n/g, '\n');
581
586
  require('less').render(str, function(err, css){
582
587
  if (err) throw err;
583
- ret = '<style>' + css.replace(/\n/g, '\\n') + '</style>';
588
+ ret = '<style type="text/css">' + css.replace(/\n/g, '\\n') + '</style>';
584
589
  });
585
590
  return ret;
586
591
  },
@@ -621,6 +626,7 @@ module.exports = {
621
626
  return '<script type="text/javascript">\\n' + js + '</script>';
622
627
  }
623
628
  };
629
+
624
630
  }); // module: filters.js
625
631
 
626
632
  require.register("inline-tags.js", function(module, exports, require){
@@ -667,6 +673,7 @@ require.register("jade.js", function(module, exports, require){
667
673
  */
668
674
 
669
675
  var Parser = require('./parser')
676
+ , Lexer = require('./lexer')
670
677
  , Compiler = require('./compiler')
671
678
  , runtime = require('./runtime')
672
679
 
@@ -674,13 +681,7 @@ var Parser = require('./parser')
674
681
  * Library version.
675
682
  */
676
683
 
677
- exports.version = '0.15.4';
678
-
679
- /**
680
- * Intermediate JavaScript cache.
681
- */
682
-
683
- var cache = exports.cache = {};
684
+ exports.version = '0.16.1';
684
685
 
685
686
  /**
686
687
  * Expose self closing tags.
@@ -718,6 +719,12 @@ exports.Compiler = Compiler;
718
719
 
719
720
  exports.Parser = Parser;
720
721
 
722
+ /**
723
+ * Expose `Lexer`.
724
+ */
725
+
726
+ exports.Lexer = Lexer;
727
+
721
728
  /**
722
729
  * Nodes.
723
730
  */
@@ -730,6 +737,12 @@ exports.nodes = require('./nodes');
730
737
 
731
738
  exports.runtime = runtime;
732
739
 
740
+ /**
741
+ * Template function cache.
742
+ */
743
+
744
+ exports.cache = {};
745
+
733
746
  /**
734
747
  * Parse the given `str` of jade and return a function body.
735
748
  *
@@ -740,11 +753,9 @@ exports.runtime = runtime;
740
753
  */
741
754
 
742
755
  function parse(str, options){
743
- var filename = options.filename;
744
-
745
756
  try {
746
757
  // Parse
747
- var parser = new Parser(str, filename, options);
758
+ var parser = new Parser(str, options.filename, options);
748
759
 
749
760
  // Compile
750
761
  var compiler = new (options.compiler || Compiler)(parser.parse(), options)
@@ -752,23 +763,18 @@ function parse(str, options){
752
763
 
753
764
  // Debug compiler
754
765
  if (options.debug) {
755
- console.log('\n\x1b[1mCompiled Function\x1b[0m:\n\n%s', js.replace(/^/gm, ' '));
766
+ console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' '));
756
767
  }
757
768
 
758
- try {
759
- return ''
760
- + 'var buf = [];\n'
761
- + (options.self
762
- ? 'var self = locals || {};\n' + js
763
- : 'with (locals || {}) {\n' + js + '\n}\n')
764
- + 'return buf.join("");';
765
-
766
- } catch (err) {
767
- process.compile(js, filename || 'Jade');
768
- return;
769
- }
769
+ return ''
770
+ + 'var buf = [];\n'
771
+ + (options.self
772
+ ? 'var self = locals || {};\n' + js
773
+ : 'with (locals || {}) {\n' + js + '\n}\n')
774
+ + 'return buf.join("");';
770
775
  } catch (err) {
771
- runtime.rethrow(err, str, filename, parser.lexer.lineno);
776
+ parser = parser.context();
777
+ runtime.rethrow(err, parser.filename, parser.lexer.lineno);
772
778
  }
773
779
  }
774
780
 
@@ -789,7 +795,6 @@ function parse(str, options){
789
795
 
790
796
  exports.compile = function(str, options){
791
797
  var options = options || {}
792
- , input = JSON.stringify(str)
793
798
  , client = options.client
794
799
  , filename = options.filename
795
800
  ? JSON.stringify(options.filename)
@@ -798,11 +803,11 @@ exports.compile = function(str, options){
798
803
 
799
804
  if (options.compileDebug !== false) {
800
805
  fn = [
801
- 'var __ = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };'
806
+ 'var __ = [{ lineno: 1, filename: ' + filename + ' }];'
802
807
  , 'try {'
803
808
  , parse(String(str), options || {})
804
809
  , '} catch (err) {'
805
- , ' rethrow(err, __.input, __.filename, __.lineno);'
810
+ , ' rethrow(err, __[0].filename, __[0].lineno);'
806
811
  , '}'
807
812
  ].join('\n');
808
813
  } else {
@@ -822,6 +827,42 @@ exports.compile = function(str, options){
822
827
  };
823
828
  };
824
829
 
830
+ /**
831
+ * Render the given `str` of jade and invoke
832
+ * the callback `fn(err, str)`.
833
+ *
834
+ * Options:
835
+ *
836
+ * - `cache` enable template caching
837
+ * - `filename` filename required for `include` / `extends` and caching
838
+ *
839
+ * @param {String} str
840
+ * @param {Object|Function} options or fn
841
+ * @param {Function} fn
842
+ * @api public
843
+ */
844
+
845
+ exports.render = function(str, options, fn){
846
+ // swap args
847
+ if ('function' == typeof options) {
848
+ fn = options, options = {};
849
+ }
850
+
851
+ // cache requires .filename
852
+ if (options.cache && !options.filename) {
853
+ return fn(new Error('the "filename" option is required for caching'));
854
+ }
855
+
856
+ try {
857
+ var path = options.filename;
858
+ var tmpl = options.cache
859
+ ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options))
860
+ : exports.compile(str, options);
861
+ fn(null, tmpl(options));
862
+ } catch (err) {
863
+ fn(err);
864
+ }
865
+ };
825
866
  }); // module: jade.js
826
867
 
827
868
  require.register("lexer.js", function(module, exports, require){
@@ -1018,7 +1059,7 @@ Lexer.prototype = {
1018
1059
  if (':' == name[name.length - 1]) {
1019
1060
  name = name.slice(0, -1);
1020
1061
  tok = this.tok('tag', name);
1021
- this.deferredTokens.push(this.tok(':'));
1062
+ this.defer(this.tok(':'));
1022
1063
  while (' ' == this.input[0]) this.input = this.input.substr(1);
1023
1064
  } else {
1024
1065
  tok = this.tok('tag', name);
@@ -1040,7 +1081,7 @@ Lexer.prototype = {
1040
1081
  */
1041
1082
 
1042
1083
  doctype: function() {
1043
- return this.scan(/^(?:!!!|doctype) *(\w+)?/, 'doctype');
1084
+ return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype');
1044
1085
  },
1045
1086
 
1046
1087
  /**
@@ -1067,6 +1108,22 @@ Lexer.prototype = {
1067
1108
  return this.scan(/^(?:\| ?)?([^\n]+)/, 'text');
1068
1109
  },
1069
1110
 
1111
+ /**
1112
+ * Extends.
1113
+ */
1114
+
1115
+ extends: function() {
1116
+ return this.scan(/^extends +([^\n]+)/, 'extends');
1117
+ },
1118
+
1119
+ /**
1120
+ * Block.
1121
+ */
1122
+
1123
+ block: function() {
1124
+ return this.scan(/^block +([^\n]+)/, 'block');
1125
+ },
1126
+
1070
1127
  /**
1071
1128
  * Include.
1072
1129
  */
@@ -1075,6 +1132,20 @@ Lexer.prototype = {
1075
1132
  return this.scan(/^include +([^\n]+)/, 'include');
1076
1133
  },
1077
1134
 
1135
+ /**
1136
+ * Assignment.
1137
+ */
1138
+
1139
+ assignment: function() {
1140
+ var captures;
1141
+ if (captures = /^(\w+) += *([^\n]+)/.exec(this.input)) {
1142
+ this.consume(captures[0].length);
1143
+ var name = captures[1]
1144
+ , val = captures[2];
1145
+ return this.tok('code', 'var ' + name + ' = (' + val + ')');
1146
+ }
1147
+ },
1148
+
1078
1149
  /**
1079
1150
  * Mixin.
1080
1151
  */
@@ -1111,6 +1182,18 @@ Lexer.prototype = {
1111
1182
  }
1112
1183
  },
1113
1184
 
1185
+ /**
1186
+ * While.
1187
+ */
1188
+
1189
+ while: function() {
1190
+ var captures;
1191
+ if (captures = /^while +([^\n]+)/.exec(this.input)) {
1192
+ this.consume(captures[0].length);
1193
+ return this.tok('code', 'while (' + captures[1] + ')');
1194
+ }
1195
+ },
1196
+
1114
1197
  /**
1115
1198
  * Each.
1116
1199
  */
@@ -1120,7 +1203,7 @@ Lexer.prototype = {
1120
1203
  if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
1121
1204
  this.consume(captures[0].length);
1122
1205
  var tok = this.tok('each', captures[1]);
1123
- tok.key = captures[2] || 'index';
1206
+ tok.key = captures[2] || '$index';
1124
1207
  tok.code = captures[3];
1125
1208
  return tok;
1126
1209
  }
@@ -1215,11 +1298,13 @@ Lexer.prototype = {
1215
1298
  }
1216
1299
  break;
1217
1300
  case '(':
1218
- if ('val' == state()) states.push('expr');
1301
+ if ('val' == state()
1302
+ || 'expr' == state()) states.push('expr');
1219
1303
  val += c;
1220
1304
  break;
1221
1305
  case ')':
1222
- if ('expr' == state()) states.pop();
1306
+ if ('expr' == state()
1307
+ || 'val' == state()) states.pop();
1223
1308
  val += c;
1224
1309
  break;
1225
1310
  case '{':
@@ -1390,10 +1475,14 @@ Lexer.prototype = {
1390
1475
  || this.eos()
1391
1476
  || this.pipelessText()
1392
1477
  || this.doctype()
1478
+ || this.extends()
1479
+ || this.block()
1393
1480
  || this.include()
1394
1481
  || this.mixin()
1395
1482
  || this.conditional()
1396
1483
  || this.each()
1484
+ || this.while()
1485
+ || this.assignment()
1397
1486
  || this.tag()
1398
1487
  || this.filter()
1399
1488
  || this.code()
@@ -1481,6 +1570,18 @@ Block.prototype = new Node;
1481
1570
  Block.prototype.constructor = Block;
1482
1571
 
1483
1572
 
1573
+ /**
1574
+ * Replace the nodes in `other` with the nodes
1575
+ * in `this` block.
1576
+ *
1577
+ * @param {Block} other
1578
+ * @api private
1579
+ */
1580
+
1581
+ Block.prototype.replace = function(other){
1582
+ other.nodes = this.nodes;
1583
+ };
1584
+
1484
1585
  /**
1485
1586
  * Pust the given `node`.
1486
1587
  *
@@ -1493,6 +1594,17 @@ Block.prototype.push = function(node){
1493
1594
  return this.nodes.push(node);
1494
1595
  };
1495
1596
 
1597
+ /**
1598
+ * Check if this block is empty.
1599
+ *
1600
+ * @return {Boolean}
1601
+ * @api public
1602
+ */
1603
+
1604
+ Block.prototype.isEmpty = function(){
1605
+ return 0 == this.nodes.length;
1606
+ };
1607
+
1496
1608
  /**
1497
1609
  * Unshift the given `node`.
1498
1610
  *
@@ -1505,6 +1617,25 @@ Block.prototype.unshift = function(node){
1505
1617
  return this.nodes.unshift(node);
1506
1618
  };
1507
1619
 
1620
+ /**
1621
+ * Return the "last" block.
1622
+ *
1623
+ * @return {Block}
1624
+ * @api private
1625
+ */
1626
+
1627
+ Block.prototype.lastBlock = function(){
1628
+ var last = this
1629
+ , node;
1630
+ for (var i = 0, len = this.nodes.length; i < len; ++i) {
1631
+ node = this.nodes[i];
1632
+ if (node.nodes) last = node.lastBlock();
1633
+ else if (node.block && !node.block.isEmpty()) last = node.block.lastBlock();
1634
+ }
1635
+ return last;
1636
+ };
1637
+
1638
+
1508
1639
  }); // module: nodes/block.js
1509
1640
 
1510
1641
  require.register("nodes/code.js", function(module, exports, require){
@@ -1535,7 +1666,7 @@ var Code = module.exports = function Code(val, buffer, escape) {
1535
1666
  this.val = val;
1536
1667
  this.buffer = buffer;
1537
1668
  this.escape = escape;
1538
- if (/^ *else/.test(val)) this.instrumentLineNumber = false;
1669
+ if (val.match(/^ *else/)) this.debug = false;
1539
1670
  };
1540
1671
 
1541
1672
  /**
@@ -1975,6 +2106,9 @@ var Parser = exports = module.exports = function Parser(str, filename, options){
1975
2106
  this.input = str;
1976
2107
  this.lexer = new Lexer(str, options);
1977
2108
  this.filename = filename;
2109
+ this.blocks = {};
2110
+ this.options = options;
2111
+ this.contexts = [this];
1978
2112
  };
1979
2113
 
1980
2114
  /**
@@ -1988,7 +2122,20 @@ var textOnly = exports.textOnly = ['code', 'script', 'textarea', 'style', 'title
1988
2122
  */
1989
2123
 
1990
2124
  Parser.prototype = {
1991
-
2125
+
2126
+ /**
2127
+ * Push `parser` onto the context stack,
2128
+ * or pop and return a `Parser`.
2129
+ */
2130
+
2131
+ context: function(parser){
2132
+ if (parser) {
2133
+ this.contexts.push(parser);
2134
+ } else {
2135
+ return this.contexts.pop();
2136
+ }
2137
+ },
2138
+
1992
2139
  /**
1993
2140
  * Return the next token object.
1994
2141
  *
@@ -2053,8 +2200,9 @@ Parser.prototype = {
2053
2200
  */
2054
2201
 
2055
2202
  parse: function(){
2056
- var block = new nodes.Block;
2203
+ var block = new nodes.Block, parser;
2057
2204
  block.line = this.line();
2205
+
2058
2206
  while ('eos' != this.peek().type) {
2059
2207
  if ('newline' == this.peek().type) {
2060
2208
  this.advance();
@@ -2062,6 +2210,14 @@ Parser.prototype = {
2062
2210
  block.push(this.parseExpr());
2063
2211
  }
2064
2212
  }
2213
+
2214
+ if (parser = this.extending) {
2215
+ this.context(parser);
2216
+ var ast = parser.parse();
2217
+ this.context();
2218
+ return ast;
2219
+ }
2220
+
2065
2221
  return block;
2066
2222
  },
2067
2223
 
@@ -2113,6 +2269,10 @@ Parser.prototype = {
2113
2269
  return this.parseTag();
2114
2270
  case 'mixin':
2115
2271
  return this.parseMixin();
2272
+ case 'block':
2273
+ return this.parseBlock();
2274
+ case 'extends':
2275
+ return this.parseExtends();
2116
2276
  case 'include':
2117
2277
  return this.parseInclude();
2118
2278
  case 'doctype':
@@ -2163,7 +2323,7 @@ Parser.prototype = {
2163
2323
  block = 'indent' == this.lookahead(i).type;
2164
2324
  if (block) {
2165
2325
  this.skip(i-1);
2166
- node.block = this.parseBlock();
2326
+ node.block = this.block();
2167
2327
  }
2168
2328
  return node;
2169
2329
  },
@@ -2177,7 +2337,7 @@ Parser.prototype = {
2177
2337
  , node;
2178
2338
 
2179
2339
  if ('indent' == this.peek().type) {
2180
- node = new nodes.BlockComment(tok.val, this.parseBlock(), tok.buffer);
2340
+ node = new nodes.BlockComment(tok.val, this.block(), tok.buffer);
2181
2341
  } else {
2182
2342
  node = new nodes.Comment(tok.val, tok.buffer);
2183
2343
  }
@@ -2225,7 +2385,7 @@ Parser.prototype = {
2225
2385
  , attrs = this.accept('attrs');
2226
2386
 
2227
2387
  this.expect(':');
2228
- block = this.parseBlock();
2388
+ block = this.block();
2229
2389
 
2230
2390
  var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs);
2231
2391
  node.line = this.line();
@@ -2238,16 +2398,16 @@ Parser.prototype = {
2238
2398
 
2239
2399
  parseEach: function(){
2240
2400
  var tok = this.expect('each')
2241
- , node = new nodes.Each(tok.code, tok.val, tok.key, this.parseBlock());
2401
+ , node = new nodes.Each(tok.code, tok.val, tok.key, this.block());
2242
2402
  node.line = this.line();
2243
2403
  return node;
2244
2404
  },
2245
2405
 
2246
2406
  /**
2247
- * include
2407
+ * 'extends' name
2248
2408
  */
2249
2409
 
2250
- parseInclude: function(){
2410
+ parseExtends: function(){
2251
2411
  var path = require('path')
2252
2412
  , fs = require('fs')
2253
2413
  , dirname = path.dirname
@@ -2255,11 +2415,52 @@ Parser.prototype = {
2255
2415
  , join = path.join;
2256
2416
 
2257
2417
  if (!this.filename)
2258
- throw new Error('the "filename" option is required to use includes');
2418
+ throw new Error('the "filename" option is required to extend templates');
2419
+
2420
+ var path = name = this.expect('extends').val.trim()
2421
+ , dir = dirname(this.filename);
2422
+
2423
+ var path = join(dir, path + '.jade')
2424
+ , str = fs.readFileSync(path, 'utf8')
2425
+ , parser = new Parser(str, path, this.options);
2426
+
2427
+ parser.blocks = this.blocks;
2428
+ parser.contexts = this.contexts;
2429
+ this.extending = parser;
2430
+
2431
+ // TODO: null node
2432
+ return new nodes.Literal('');
2433
+ },
2434
+
2435
+ /**
2436
+ * 'block' name block
2437
+ */
2438
+
2439
+ parseBlock: function(){
2440
+ var name = this.expect('block').val.trim();
2441
+ var block = 'indent' == this.peek().type
2442
+ ? this.block()
2443
+ : new nodes.Block(new nodes.Literal(''));
2444
+ return this.blocks[name] = this.blocks[name] || block;
2445
+ },
2446
+
2447
+ /**
2448
+ * include block?
2449
+ */
2450
+
2451
+ parseInclude: function(){
2452
+ var path = require('path')
2453
+ , fs = require('fs')
2454
+ , dirname = path.dirname
2455
+ , basename = path.basename
2456
+ , join = path.join;
2259
2457
 
2260
2458
  var path = name = this.expect('include').val.trim()
2261
2459
  , dir = dirname(this.filename);
2262
2460
 
2461
+ if (!this.filename)
2462
+ throw new Error('the "filename" option is required to use includes');
2463
+
2263
2464
  // non-jade
2264
2465
  if (~basename(path).indexOf('.')) {
2265
2466
  var path = join(dir, path)
@@ -2269,8 +2470,16 @@ Parser.prototype = {
2269
2470
 
2270
2471
  var path = join(dir, path + '.jade')
2271
2472
  , str = fs.readFileSync(path, 'utf8')
2272
- , parser = new Parser(str, path)
2273
- , ast = parser.parse();
2473
+ , parser = new Parser(str, path, this.options);
2474
+
2475
+ this.context(parser);
2476
+ var ast = parser.parse();
2477
+ this.context();
2478
+ ast.filename = path;
2479
+
2480
+ if ('indent' == this.peek().type) {
2481
+ ast.lastBlock().push(this.block());
2482
+ }
2274
2483
 
2275
2484
  return ast;
2276
2485
  },
@@ -2284,7 +2493,7 @@ Parser.prototype = {
2284
2493
  , name = tok.val
2285
2494
  , args = tok.args;
2286
2495
  var block = 'indent' == this.peek().type
2287
- ? this.parseBlock()
2496
+ ? this.block()
2288
2497
  : null;
2289
2498
  return new nodes.Mixin(name, args, block);
2290
2499
  },
@@ -2326,7 +2535,7 @@ Parser.prototype = {
2326
2535
  * indent expr* outdent
2327
2536
  */
2328
2537
 
2329
- parseBlock: function(){
2538
+ block: function(){
2330
2539
  var block = new nodes.Block;
2331
2540
  block.line = this.line();
2332
2541
  this.expect('indent');
@@ -2425,7 +2634,7 @@ Parser.prototype = {
2425
2634
  tag.block = this.parseTextBlock();
2426
2635
  this.lexer.pipeless = false;
2427
2636
  } else {
2428
- var block = this.parseBlock();
2637
+ var block = this.block();
2429
2638
  if (tag.block) {
2430
2639
  for (var i = 0, len = block.nodes.length; i < len; ++i) {
2431
2640
  tag.block.push(block.nodes[i]);
@@ -2456,7 +2665,7 @@ require.register("runtime.js", function(module, exports, require){
2456
2665
 
2457
2666
  if (!Array.isArray) {
2458
2667
  Array.isArray = function(arr){
2459
- return '[object Array]' == toString.call(arr);
2668
+ return '[object Array]' == Object.prototype.toString.call(arr);
2460
2669
  };
2461
2670
  }
2462
2671
 
@@ -2469,7 +2678,7 @@ if (!Object.keys) {
2469
2678
  var arr = [];
2470
2679
  for (var key in obj) {
2471
2680
  if (obj.hasOwnProperty(key)) {
2472
- arr.push(obj);
2681
+ arr.push(key);
2473
2682
  }
2474
2683
  }
2475
2684
  return arr;
@@ -2529,17 +2738,19 @@ exports.escape = function escape(html){
2529
2738
 
2530
2739
  /**
2531
2740
  * Re-throw the given `err` in context to the
2532
- * `str` of jade, `filename`, and `lineno`.
2741
+ * the jade in `filename` at the given `lineno`.
2533
2742
  *
2534
2743
  * @param {Error} err
2535
- * @param {String} str
2536
2744
  * @param {String} filename
2537
2745
  * @param {String} lineno
2538
2746
  * @api private
2539
2747
  */
2540
2748
 
2541
- exports.rethrow = function rethrow(err, str, filename, lineno){
2749
+ exports.rethrow = function rethrow(err, filename, lineno){
2750
+ if (!filename) throw err;
2751
+
2542
2752
  var context = 3
2753
+ , str = require('fs').readFileSync(filename, 'utf8')
2543
2754
  , lines = str.split('\n')
2544
2755
  , start = Math.max(lineno - context, 0)
2545
2756
  , end = Math.min(lines.length, lineno + context);
@@ -0,0 +1,123 @@
1
+
2
+ var jade = (function(exports){
3
+ /*!
4
+ * Jade - runtime
5
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
6
+ * MIT Licensed
7
+ */
8
+
9
+ /**
10
+ * Lame Array.isArray() polyfill for now.
11
+ */
12
+
13
+ if (!Array.isArray) {
14
+ Array.isArray = function(arr){
15
+ return '[object Array]' == Object.prototype.toString.call(arr);
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Lame Object.keys() polyfill for now.
21
+ */
22
+
23
+ if (!Object.keys) {
24
+ Object.keys = function(obj){
25
+ var arr = [];
26
+ for (var key in obj) {
27
+ if (obj.hasOwnProperty(key)) {
28
+ arr.push(key);
29
+ }
30
+ }
31
+ return arr;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Render the given attributes object.
37
+ *
38
+ * @param {Object} obj
39
+ * @return {String}
40
+ * @api private
41
+ */
42
+
43
+ exports.attrs = function attrs(obj){
44
+ var buf = []
45
+ , terse = obj.terse;
46
+ delete obj.terse;
47
+ var keys = Object.keys(obj)
48
+ , len = keys.length;
49
+ if (len) {
50
+ buf.push('');
51
+ for (var i = 0; i < len; ++i) {
52
+ var key = keys[i]
53
+ , val = obj[key];
54
+ if ('boolean' == typeof val || null == val) {
55
+ if (val) {
56
+ terse
57
+ ? buf.push(key)
58
+ : buf.push(key + '="' + key + '"');
59
+ }
60
+ } else if ('class' == key && Array.isArray(val)) {
61
+ buf.push(key + '="' + exports.escape(val.join(' ')) + '"');
62
+ } else {
63
+ buf.push(key + '="' + exports.escape(val) + '"');
64
+ }
65
+ }
66
+ }
67
+ return buf.join(' ');
68
+ };
69
+
70
+ /**
71
+ * Escape the given string of `html`.
72
+ *
73
+ * @param {String} html
74
+ * @return {String}
75
+ * @api private
76
+ */
77
+
78
+ exports.escape = function escape(html){
79
+ return String(html)
80
+ .replace(/&(?!\w+;)/g, '&amp;')
81
+ .replace(/</g, '&lt;')
82
+ .replace(/>/g, '&gt;')
83
+ .replace(/"/g, '&quot;');
84
+ };
85
+
86
+ /**
87
+ * Re-throw the given `err` in context to the
88
+ * the jade in `filename` at the given `lineno`.
89
+ *
90
+ * @param {Error} err
91
+ * @param {String} filename
92
+ * @param {String} lineno
93
+ * @api private
94
+ */
95
+
96
+ exports.rethrow = function rethrow(err, filename, lineno){
97
+ if (!filename) throw err;
98
+
99
+ var context = 3
100
+ , str = require('fs').readFileSync(filename, 'utf8')
101
+ , lines = str.split('\n')
102
+ , start = Math.max(lineno - context, 0)
103
+ , end = Math.min(lines.length, lineno + context);
104
+
105
+ // Error context
106
+ var context = lines.slice(start, end).map(function(line, i){
107
+ var curr = i + start + 1;
108
+ return (curr == lineno ? ' > ' : ' ')
109
+ + curr
110
+ + '| '
111
+ + line;
112
+ }).join('\n');
113
+
114
+ // Alter exception message
115
+ err.path = filename;
116
+ err.message = (filename || 'Jade') + ':' + lineno
117
+ + '\n' + context + '\n\n' + err.message;
118
+ throw err;
119
+ };
120
+
121
+ return exports;
122
+
123
+ })({});
@@ -3,5 +3,8 @@ module JadeJs
3
3
  def self.bundled_path
4
4
  File.expand_path("../jade.js", __FILE__)
5
5
  end
6
+ def self.bundled_runtime_path
7
+ File.expand_path("../runtime.js", __FILE__)
8
+ end
6
9
  end
7
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jade-js-source
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.4
4
+ version: 0.16.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,8 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-09-07 00:00:00.000000000 -04:00
14
- default_executable:
13
+ date: 2011-10-07 00:00:00.000000000Z
15
14
  dependencies: []
16
15
  description: ! " Jade is a high performance template engine heavily influenced
17
16
  by Haml \n and implemented with JavaScript for node.\n"
@@ -21,8 +20,8 @@ extensions: []
21
20
  extra_rdoc_files: []
22
21
  files:
23
22
  - lib/jade_js/jade.js
23
+ - lib/jade_js/runtime.js
24
24
  - lib/jade_js/source.rb
25
- has_rdoc: true
26
25
  homepage: https://github.com/visionmedia/jade
27
26
  licenses: []
28
27
  post_install_message:
@@ -43,7 +42,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
42
  version: '0'
44
43
  requirements: []
45
44
  rubyforge_project: jade-js-source
46
- rubygems_version: 1.6.2
45
+ rubygems_version: 1.8.10
47
46
  signing_key:
48
47
  specification_version: 3
49
48
  summary: Jade - template engine