jader 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +11 -0
  3. data/LICENSE +22 -0
  4. data/README.md +356 -0
  5. data/Rakefile +4 -0
  6. data/jader.gemspec +28 -0
  7. data/lib/jader.rb +10 -0
  8. data/lib/jader/compiler.rb +70 -0
  9. data/lib/jader/configuration.rb +19 -0
  10. data/lib/jader/engine.rb +11 -0
  11. data/lib/jader/renderer.rb +30 -0
  12. data/lib/jader/serialize.rb +82 -0
  13. data/lib/jader/source.rb +19 -0
  14. data/lib/jader/template.rb +29 -0
  15. data/lib/jader/version.rb +3 -0
  16. data/spec/compiler_spec.rb +32 -0
  17. data/spec/dummy/README.rdoc +261 -0
  18. data/spec/dummy/Rakefile +7 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  20. data/spec/dummy/app/assets/javascripts/includes/util.js +8 -0
  21. data/spec/dummy/app/assets/javascripts/mixins/application_mixins.jade +3 -0
  22. data/spec/dummy/app/assets/javascripts/mixins/users_mixins.jade +3 -0
  23. data/spec/dummy/app/assets/javascripts/sample.jst.jade +5 -0
  24. data/spec/dummy/app/assets/javascripts/views/users/index.jst.jade +2 -0
  25. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  26. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  27. data/spec/dummy/app/controllers/users_controller.rb +10 -0
  28. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  29. data/spec/dummy/app/mailers/.gitkeep +0 -0
  30. data/spec/dummy/app/models/.gitkeep +0 -0
  31. data/spec/dummy/app/models/user.rb +4 -0
  32. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/spec/dummy/app/views/users/index.jade +4 -0
  34. data/spec/dummy/app/views/users/show.jade +5 -0
  35. data/spec/dummy/config.ru +4 -0
  36. data/spec/dummy/config/application.rb +65 -0
  37. data/spec/dummy/config/boot.rb +10 -0
  38. data/spec/dummy/config/database.yml +7 -0
  39. data/spec/dummy/config/environment.rb +5 -0
  40. data/spec/dummy/config/environments/development.rb +31 -0
  41. data/spec/dummy/config/environments/production.rb +64 -0
  42. data/spec/dummy/config/environments/test.rb +35 -0
  43. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/dummy/config/initializers/inflections.rb +15 -0
  45. data/spec/dummy/config/initializers/jader.rb +4 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  47. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  48. data/spec/dummy/config/initializers/session_store.rb +8 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +10 -0
  50. data/spec/dummy/config/locales/en.yml +5 -0
  51. data/spec/dummy/config/routes.rb +60 -0
  52. data/spec/dummy/db/migrate/20120816043248_user.rb +11 -0
  53. data/spec/dummy/db/schema.rb +21 -0
  54. data/spec/dummy/lib/assets/.gitkeep +0 -0
  55. data/spec/dummy/log/.gitkeep +0 -0
  56. data/spec/dummy/public/404.html +26 -0
  57. data/spec/dummy/public/422.html +26 -0
  58. data/spec/dummy/public/500.html +25 -0
  59. data/spec/dummy/public/favicon.ico +0 -0
  60. data/spec/dummy/script/rails +6 -0
  61. data/spec/integration/renderer_spec.rb +59 -0
  62. data/spec/source_spec.rb +19 -0
  63. data/spec/spec_helper.rb +22 -0
  64. data/spec/support/global.rb +7 -0
  65. data/spec/support/matchers.rb +17 -0
  66. data/spec/template_spec.rb +40 -0
  67. data/vendor/assets/javascripts/jade/jade.js +3168 -0
  68. data/vendor/assets/javascripts/jade/runtime.js +128 -0
  69. metadata +278 -0
@@ -0,0 +1,11 @@
1
+ class User < ActiveRecord::Migration
2
+ def up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.string :email
6
+ end
7
+ end
8
+
9
+ def down
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended to check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(:version => 20120816043248) do
15
+
16
+ create_table "users", :force => true do |t|
17
+ t.string "name"
18
+ t.string "email"
19
+ end
20
+
21
+ end
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jader::Renderer do
4
+
5
+ context 'users/index' do
6
+
7
+ before do
8
+ User.all.each do |user|
9
+ user.destroy
10
+ end
11
+ @joe = User.create(:name => 'Joe', :email => 'joe@gmail.com')
12
+ @mike = User.create(:name => 'Mike', :email => 'mike@gmail.com')
13
+ end
14
+
15
+ before :each do
16
+ get '/users'
17
+ end
18
+
19
+ it 'render users index page' do
20
+ response.should render_template(:index)
21
+ end
22
+
23
+ it 'renders the template HTML' do
24
+ response.body.should include '<h1 id="topHeading">Hello All Users</h1>'
25
+ end
26
+
27
+ it 'renders users' do
28
+ response.body.should include @mike.name
29
+ response.body.should include @joe.name
30
+ end
31
+
32
+ it 'uses a function available from server-side includes' do
33
+ response.body.should =~ /\d{4}\-\d{2}\-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/
34
+ end
35
+ end
36
+
37
+ context 'users/show' do
38
+
39
+ before :each do
40
+ @user = stub_model(User, :name => 'Sam', :email => 'sam@gmail.com')
41
+ User.should_receive(:find).and_return(@user)
42
+ end
43
+
44
+ it 'renders instance variables' do
45
+ get user_path(@user)
46
+ response.body.should include 'My name is %s' % @user.name
47
+ end
48
+
49
+ it 'renders HTML generated by application mixin' do
50
+ get user_path(@user)
51
+ response.body.should include @user.name.downcase
52
+ end
53
+
54
+ it 'renders HTML generated by users mixin' do
55
+ get user_path(@user)
56
+ response.body.should include @user.email.upcase
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jader::Source do
4
+ it "should contain jade asset" do
5
+ File.exist?(Jader::Source::jade_path).should be_true
6
+ end
7
+
8
+ it "should contain runtime asset" do
9
+ File.exist?(Jader::Source::runtime_path).should be_true
10
+ end
11
+
12
+ it "should be able to read jade source" do
13
+ IO.read(Jader::Source::jade_path).should_not be_empty
14
+ end
15
+
16
+ it "should be able to read runtime source" do
17
+ IO.read(Jader::Source::runtime_path).should_not be_empty
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'pry'
6
+
7
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
8
+ require "rails/test_help"
9
+ require "rspec/rails"
10
+
11
+ require 'jader'
12
+
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14
+
15
+ Rails.backtrace_cleaner.remove_silencers!
16
+
17
+ RSpec.configure do |config|
18
+ require 'rspec/mocks'
19
+ require 'rspec/expectations'
20
+ config.include RSpec::Matchers
21
+ config.mock_with :rspec
22
+ end
@@ -0,0 +1,7 @@
1
+ def assets
2
+ Rails.application.assets
3
+ end
4
+
5
+ def asset_for(name)
6
+ assets[name]
7
+ end
@@ -0,0 +1,17 @@
1
+ RSpec::Matchers.define :serve do |asset_name|
2
+ match do |sprockets|
3
+ !!sprockets[asset_name]
4
+ end
5
+
6
+ failure_message_for_should do |sprockets|
7
+ "expected #{asset_name} to be served, but it wasn't"
8
+ end
9
+
10
+ failure_message_for_should_not do |sprockets|
11
+ "expected #{asset_name} NOT to be served, but it was"
12
+ end
13
+
14
+ description do
15
+ "serve #{asset_name}"
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+ describe Jader::Compiler do
4
+
5
+ before :all do
6
+ d = Rails.root.join('tmp','cache','assets')
7
+ FileUtils.rm_r d if Dir.exists? d
8
+ end
9
+
10
+ def template(source, file)
11
+ Jader::Template.new(file){source}
12
+ end
13
+
14
+ it 'should have default mime type' do
15
+ Jader::Template.default_mime_type.should == 'application/javascript'
16
+ end
17
+
18
+ it 'should be served' do
19
+ assets.should serve 'sample.js'
20
+ asset_for('sample.js').body.should include "Yap, it works"
21
+ end
22
+
23
+ it 'should work fine with JST' do
24
+ context = ExecJS.compile %{
25
+ #{asset_for('application.js').to_s}
26
+ html = JST['sample']({name: 'Yorik'})
27
+ }
28
+ context.eval('html').should == "<!DOCTYPE html><head><title>Hello, Yorik :)</title></head><body>Yap, it works\n</body>"
29
+ end
30
+
31
+ it 'should use mixins in JST' do
32
+ phrase = 'Hi There'
33
+ context = ExecJS.compile %{
34
+ #{asset_for('application.js').to_s}
35
+ html = JST['views/users/index']({phrase: '#{phrase}'})
36
+ }
37
+ context.eval('html').should include(phrase.upcase)
38
+ end
39
+
40
+ end
@@ -0,0 +1,3168 @@
1
+ (function() {
2
+
3
+ // CommonJS require()
4
+
5
+ function require(p){
6
+ var path = require.resolve(p)
7
+ , mod = require.modules[path];
8
+ if (!mod) throw new Error('failed to require "' + p + '"');
9
+ if (!mod.exports) {
10
+ mod.exports = {};
11
+ mod.call(mod.exports, mod, mod.exports, require.relative(path));
12
+ }
13
+ return mod.exports;
14
+ }
15
+
16
+ require.modules = {};
17
+
18
+ require.resolve = function (path){
19
+ var orig = path
20
+ , reg = path + '.js'
21
+ , index = path + '/index.js';
22
+ return require.modules[reg] && reg
23
+ || require.modules[index] && index
24
+ || orig;
25
+ };
26
+
27
+ require.register = function (path, fn){
28
+ require.modules[path] = fn;
29
+ };
30
+
31
+ require.relative = function (parent) {
32
+ return function(p){
33
+ if ('.' != p[0]) return require(p);
34
+
35
+ var path = parent.split('/')
36
+ , segs = p.split('/');
37
+ path.pop();
38
+
39
+ for (var i = 0; i < segs.length; i++) {
40
+ var seg = segs[i];
41
+ if ('..' == seg) path.pop();
42
+ else if ('.' != seg) path.push(seg);
43
+ }
44
+
45
+ return require(path.join('/'));
46
+ };
47
+ };
48
+
49
+
50
+ require.register("compiler.js", function(module, exports, require){
51
+
52
+ /*!
53
+ * Jade - Compiler
54
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
55
+ * MIT Licensed
56
+ */
57
+
58
+ /**
59
+ * Module dependencies.
60
+ */
61
+
62
+ var nodes = require('./nodes')
63
+ , filters = require('./filters')
64
+ , doctypes = require('./doctypes')
65
+ , selfClosing = require('./self-closing')
66
+ , inlineTags = require('./inline-tags')
67
+ , utils = require('./utils');
68
+
69
+
70
+ if (!Object.keys) {
71
+ Object.keys = function(obj){
72
+ var arr = [];
73
+ for (var key in obj) {
74
+ if (obj.hasOwnProperty(key)) {
75
+ arr.push(key);
76
+ }
77
+ }
78
+ return arr;
79
+ }
80
+ }
81
+
82
+ if (!String.prototype.trimLeft) {
83
+ String.prototype.trimLeft = function(){
84
+ return this.replace(/^\s+/, '');
85
+ }
86
+ }
87
+
88
+
89
+
90
+ /**
91
+ * Initialize `Compiler` with the given `node`.
92
+ *
93
+ * @param {Node} node
94
+ * @param {Object} options
95
+ * @api public
96
+ */
97
+
98
+ var Compiler = module.exports = function Compiler(node, options) {
99
+ this.options = options = options || {};
100
+ this.node = node;
101
+ this.hasCompiledDoctype = false;
102
+ this.hasCompiledTag = false;
103
+ this.pp = options.pretty || false;
104
+ this.debug = false !== options.compileDebug;
105
+ this.indents = 0;
106
+ if (options.doctype) this.setDoctype(options.doctype);
107
+ };
108
+
109
+ /**
110
+ * Compiler prototype.
111
+ */
112
+
113
+ Compiler.prototype = {
114
+
115
+ /**
116
+ * Compile parse tree to JavaScript.
117
+ *
118
+ * @api public
119
+ */
120
+
121
+ compile: function(){
122
+ this.buf = ['var interp;'];
123
+ this.lastBufferedIdx = -1
124
+ this.visit(this.node);
125
+ return this.buf.join('\n');
126
+ },
127
+
128
+ /**
129
+ * Sets the default doctype `name`. Sets terse mode to `true` when
130
+ * html 5 is used, causing self-closing tags to end with ">" vs "/>",
131
+ * and boolean attributes are not mirrored.
132
+ *
133
+ * @param {string} name
134
+ * @api public
135
+ */
136
+
137
+ setDoctype: function(name){
138
+ var doctype = doctypes[(name || 'default').toLowerCase()];
139
+ doctype = doctype || '<!DOCTYPE ' + name + '>';
140
+ this.doctype = doctype;
141
+ this.terse = '5' == name || 'html' == name;
142
+ this.xml = 0 == this.doctype.indexOf('<?xml');
143
+ },
144
+
145
+ /**
146
+ * Buffer the given `str` optionally escaped.
147
+ *
148
+ * @param {String} str
149
+ * @param {Boolean} esc
150
+ * @api public
151
+ */
152
+
153
+ buffer: function(str, esc){
154
+ if (esc) str = utils.escape(str);
155
+
156
+ if (this.lastBufferedIdx == this.buf.length) {
157
+ this.lastBuffered += str;
158
+ this.buf[this.lastBufferedIdx - 1] = "buf.push('" + this.lastBuffered + "');"
159
+ } else {
160
+ this.buf.push("buf.push('" + str + "');");
161
+ this.lastBuffered = str;
162
+ this.lastBufferedIdx = this.buf.length;
163
+ }
164
+ },
165
+
166
+ /**
167
+ * Visit `node`.
168
+ *
169
+ * @param {Node} node
170
+ * @api public
171
+ */
172
+
173
+ visit: function(node){
174
+ var debug = this.debug;
175
+
176
+ if (debug) {
177
+ this.buf.push('__jade.unshift({ lineno: ' + node.line
178
+ + ', filename: ' + (node.filename
179
+ ? '"' + node.filename + '"'
180
+ : '__jade[0].filename')
181
+ + ' });');
182
+ }
183
+
184
+ // Massive hack to fix our context
185
+ // stack for - else[ if] etc
186
+ if (false === node.debug && this.debug) {
187
+ this.buf.pop();
188
+ this.buf.pop();
189
+ }
190
+
191
+ this.visitNode(node);
192
+
193
+ if (debug) this.buf.push('__jade.shift();');
194
+ },
195
+
196
+ /**
197
+ * Visit `node`.
198
+ *
199
+ * @param {Node} node
200
+ * @api public
201
+ */
202
+
203
+ visitNode: function(node){
204
+ var name = node.constructor.name
205
+ || node.constructor.toString().match(/function ([^(\s]+)()/)[1];
206
+ return this['visit' + name](node);
207
+ },
208
+
209
+ /**
210
+ * Visit case `node`.
211
+ *
212
+ * @param {Literal} node
213
+ * @api public
214
+ */
215
+
216
+ visitCase: function(node){
217
+ var _ = this.withinCase;
218
+ this.withinCase = true;
219
+ this.buf.push('switch (' + node.expr + '){');
220
+ this.visit(node.block);
221
+ this.buf.push('}');
222
+ this.withinCase = _;
223
+ },
224
+
225
+ /**
226
+ * Visit when `node`.
227
+ *
228
+ * @param {Literal} node
229
+ * @api public
230
+ */
231
+
232
+ visitWhen: function(node){
233
+ if ('default' == node.expr) {
234
+ this.buf.push('default:');
235
+ } else {
236
+ this.buf.push('case ' + node.expr + ':');
237
+ }
238
+ this.visit(node.block);
239
+ this.buf.push(' break;');
240
+ },
241
+
242
+ /**
243
+ * Visit literal `node`.
244
+ *
245
+ * @param {Literal} node
246
+ * @api public
247
+ */
248
+
249
+ visitLiteral: function(node){
250
+ var str = node.str.replace(/\n/g, '\\\\n');
251
+ this.buffer(str);
252
+ },
253
+
254
+ /**
255
+ * Visit all nodes in `block`.
256
+ *
257
+ * @param {Block} block
258
+ * @api public
259
+ */
260
+
261
+ visitBlock: function(block){
262
+ var len = block.nodes.length;
263
+ for (var i = 0; i < len; ++i) {
264
+ this.visit(block.nodes[i]);
265
+ }
266
+ },
267
+
268
+ /**
269
+ * Visit `doctype`. Sets terse mode to `true` when html 5
270
+ * is used, causing self-closing tags to end with ">" vs "/>",
271
+ * and boolean attributes are not mirrored.
272
+ *
273
+ * @param {Doctype} doctype
274
+ * @api public
275
+ */
276
+
277
+ visitDoctype: function(doctype){
278
+ if (doctype && (doctype.val || !this.doctype)) {
279
+ this.setDoctype(doctype.val || 'default');
280
+ }
281
+
282
+ if (this.doctype) this.buffer(this.doctype);
283
+ this.hasCompiledDoctype = true;
284
+ },
285
+
286
+ /**
287
+ * Visit `mixin`, generating a function that
288
+ * may be called within the template.
289
+ *
290
+ * @param {Mixin} mixin
291
+ * @api public
292
+ */
293
+
294
+ visitMixin: function(mixin){
295
+ var name = mixin.name.replace(/-/g, '_') + '_mixin'
296
+ , args = mixin.args || '';
297
+
298
+ if (mixin.block) {
299
+ this.buf.push('var ' + name + ' = function(' + args + '){');
300
+ this.visit(mixin.block);
301
+ this.buf.push('}');
302
+ } else {
303
+ this.buf.push(name + '(' + args + ');');
304
+ }
305
+ },
306
+
307
+ /**
308
+ * Visit `tag` buffering tag markup, generating
309
+ * attributes, visiting the `tag`'s code and block.
310
+ *
311
+ * @param {Tag} tag
312
+ * @api public
313
+ */
314
+
315
+ visitTag: function(tag){
316
+ this.indents++;
317
+ var name = tag.name;
318
+
319
+ if (!this.hasCompiledTag) {
320
+ if (!this.hasCompiledDoctype && 'html' == name) {
321
+ this.visitDoctype();
322
+ }
323
+ this.hasCompiledTag = true;
324
+ }
325
+
326
+ // pretty print
327
+ if (this.pp && inlineTags.indexOf(name) == -1) {
328
+ this.buffer('\\n' + Array(this.indents).join(' '));
329
+ }
330
+
331
+ if (~selfClosing.indexOf(name) && !this.xml) {
332
+ this.buffer('<' + name);
333
+ this.visitAttributes(tag.attrs);
334
+ this.terse
335
+ ? this.buffer('>')
336
+ : this.buffer('/>');
337
+ } else {
338
+ // Optimize attributes buffering
339
+ if (tag.attrs.length) {
340
+ this.buffer('<' + name);
341
+ if (tag.attrs.length) this.visitAttributes(tag.attrs);
342
+ this.buffer('>');
343
+ } else {
344
+ this.buffer('<' + name + '>');
345
+ }
346
+ if (tag.code) this.visitCode(tag.code);
347
+ if (tag.text) this.buffer(utils.text(tag.text.nodes[0].trimLeft()));
348
+ this.escape = 'pre' == tag.name;
349
+ this.visit(tag.block);
350
+
351
+ // pretty print
352
+ if (this.pp && !~inlineTags.indexOf(name) && !tag.textOnly) {
353
+ this.buffer('\\n' + Array(this.indents).join(' '));
354
+ }
355
+
356
+ this.buffer('</' + name + '>');
357
+ }
358
+ this.indents--;
359
+ },
360
+
361
+ /**
362
+ * Visit `filter`, throwing when the filter does not exist.
363
+ *
364
+ * @param {Filter} filter
365
+ * @api public
366
+ */
367
+
368
+ visitFilter: function(filter){
369
+ var fn = filters[filter.name];
370
+
371
+ // unknown filter
372
+ if (!fn) {
373
+ if (filter.isASTFilter) {
374
+ throw new Error('unknown ast filter "' + filter.name + ':"');
375
+ } else {
376
+ throw new Error('unknown filter ":' + filter.name + '"');
377
+ }
378
+ }
379
+ if (filter.isASTFilter) {
380
+ this.buf.push(fn(filter.block, this, filter.attrs));
381
+ } else {
382
+ var text = filter.block.nodes.join('');
383
+ this.buffer(utils.text(fn(text, filter.attrs)));
384
+ }
385
+ },
386
+
387
+ /**
388
+ * Visit `text` node.
389
+ *
390
+ * @param {Text} text
391
+ * @api public
392
+ */
393
+
394
+ visitText: function(text){
395
+ text = utils.text(text.nodes.join(''));
396
+ if (this.escape) text = escape(text);
397
+ this.buffer(text);
398
+ this.buffer('\\n');
399
+ },
400
+
401
+ /**
402
+ * Visit a `comment`, only buffering when the buffer flag is set.
403
+ *
404
+ * @param {Comment} comment
405
+ * @api public
406
+ */
407
+
408
+ visitComment: function(comment){
409
+ if (!comment.buffer) return;
410
+ if (this.pp) this.buffer('\\n' + Array(this.indents + 1).join(' '));
411
+ this.buffer('<!--' + utils.escape(comment.val) + '-->');
412
+ },
413
+
414
+ /**
415
+ * Visit a `BlockComment`.
416
+ *
417
+ * @param {Comment} comment
418
+ * @api public
419
+ */
420
+
421
+ visitBlockComment: function(comment){
422
+ if (!comment.buffer) return;
423
+ if (0 == comment.val.trim().indexOf('if')) {
424
+ this.buffer('<!--[' + comment.val.trim() + ']>');
425
+ this.visit(comment.block);
426
+ this.buffer('<![endif]-->');
427
+ } else {
428
+ this.buffer('<!--' + comment.val);
429
+ this.visit(comment.block);
430
+ this.buffer('-->');
431
+ }
432
+ },
433
+
434
+ /**
435
+ * Visit `code`, respecting buffer / escape flags.
436
+ * If the code is followed by a block, wrap it in
437
+ * a self-calling function.
438
+ *
439
+ * @param {Code} code
440
+ * @api public
441
+ */
442
+
443
+ visitCode: function(code){
444
+ // Wrap code blocks with {}.
445
+ // we only wrap unbuffered code blocks ATM
446
+ // since they are usually flow control
447
+
448
+ // Buffer code
449
+ if (code.buffer) {
450
+ var val = code.val.trimLeft();
451
+ this.buf.push('var __val__ = ' + val);
452
+ val = 'null == __val__ ? "" : __val__';
453
+ if (code.escape) val = 'escape(' + val + ')';
454
+ this.buf.push("buf.push(" + val + ");");
455
+ } else {
456
+ this.buf.push(code.val);
457
+ }
458
+
459
+ // Block support
460
+ if (code.block) {
461
+ if (!code.buffer) this.buf.push('{');
462
+ this.visit(code.block);
463
+ if (!code.buffer) this.buf.push('}');
464
+ }
465
+ },
466
+
467
+ /**
468
+ * Visit `each` block.
469
+ *
470
+ * @param {Each} each
471
+ * @api public
472
+ */
473
+
474
+ visitEach: function(each){
475
+ this.buf.push(''
476
+ + '// iterate ' + each.obj + '\n'
477
+ + '(function(){\n'
478
+ + ' if (\'number\' == typeof ' + each.obj + '.length) {\n'
479
+ + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
480
+ + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');
481
+
482
+ this.visit(each.block);
483
+
484
+ this.buf.push(''
485
+ + ' }\n'
486
+ + ' } else {\n'
487
+ + ' for (var ' + each.key + ' in ' + each.obj + ') {\n'
488
+ + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){'
489
+ + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');
490
+
491
+ this.visit(each.block);
492
+
493
+ this.buf.push(' }\n');
494
+
495
+ this.buf.push(' }\n }\n}).call(this);\n');
496
+ },
497
+
498
+ /**
499
+ * Visit `attrs`.
500
+ *
501
+ * @param {Array} attrs
502
+ * @api public
503
+ */
504
+
505
+ visitAttributes: function(attrs){
506
+ var buf = []
507
+ , classes = [];
508
+
509
+ if (this.terse) buf.push('terse: true');
510
+
511
+ attrs.forEach(function(attr){
512
+ if (attr.name == 'class') {
513
+ classes.push('(' + attr.val + ')');
514
+ } else {
515
+ var pair = "'" + attr.name + "':(" + attr.val + ')';
516
+ buf.push(pair);
517
+ }
518
+ });
519
+
520
+ if (classes.length) {
521
+ classes = classes.join(" + ' ' + ");
522
+ buf.push("class: " + classes);
523
+ }
524
+
525
+ buf = buf.join(', ').replace('class:', '"class":');
526
+
527
+ this.buf.push("buf.push(attrs({ " + buf + " }));");
528
+ }
529
+ };
530
+
531
+ /**
532
+ * Escape the given string of `html`.
533
+ *
534
+ * @param {String} html
535
+ * @return {String}
536
+ * @api private
537
+ */
538
+
539
+ function escape(html){
540
+ return String(html)
541
+ .replace(/&(?!\w+;)/g, '&amp;')
542
+ .replace(/</g, '&lt;')
543
+ .replace(/>/g, '&gt;')
544
+ .replace(/"/g, '&quot;');
545
+ }
546
+
547
+ }); // module: compiler.js
548
+
549
+ require.register("doctypes.js", function(module, exports, require){
550
+
551
+ /*!
552
+ * Jade - doctypes
553
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
554
+ * MIT Licensed
555
+ */
556
+
557
+ module.exports = {
558
+ '5': '<!DOCTYPE html>'
559
+ , 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
560
+ , 'default': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
561
+ , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
562
+ , 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
563
+ , 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
564
+ , '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
565
+ , 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
566
+ , 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
567
+ };
568
+ }); // module: doctypes.js
569
+
570
+ require.register("filters.js", function(module, exports, require){
571
+
572
+ /*!
573
+ * Jade - filters
574
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
575
+ * MIT Licensed
576
+ */
577
+
578
+ module.exports = {
579
+
580
+ /**
581
+ * Wrap text with CDATA block.
582
+ */
583
+
584
+ cdata: function(str){
585
+ return '<![CDATA[\\n' + str + '\\n]]>';
586
+ },
587
+
588
+ /**
589
+ * Transform sass to css, wrapped in style tags.
590
+ */
591
+
592
+ sass: function(str){
593
+ str = str.replace(/\\n/g, '\n');
594
+ var sass = require('sass').render(str).replace(/\n/g, '\\n');
595
+ return '<style type="text/css">' + sass + '</style>';
596
+ },
597
+
598
+ /**
599
+ * Transform stylus to css, wrapped in style tags.
600
+ */
601
+
602
+ stylus: function(str, options){
603
+ var ret;
604
+ str = str.replace(/\\n/g, '\n');
605
+ var stylus = require('stylus');
606
+ stylus(str, options).render(function(err, css){
607
+ if (err) throw err;
608
+ ret = css.replace(/\n/g, '\\n');
609
+ });
610
+ return '<style type="text/css">' + ret + '</style>';
611
+ },
612
+
613
+ /**
614
+ * Transform less to css, wrapped in style tags.
615
+ */
616
+
617
+ less: function(str){
618
+ var ret;
619
+ str = str.replace(/\\n/g, '\n');
620
+ require('less').render(str, function(err, css){
621
+ if (err) throw err;
622
+ ret = '<style type="text/css">' + css.replace(/\n/g, '\\n') + '</style>';
623
+ });
624
+ return ret;
625
+ },
626
+
627
+ /**
628
+ * Transform markdown to html.
629
+ */
630
+
631
+ markdown: function(str){
632
+ var md;
633
+
634
+ // support markdown / discount
635
+ try {
636
+ md = require('markdown');
637
+ } catch (err){
638
+ try {
639
+ md = require('discount');
640
+ } catch (err) {
641
+ try {
642
+ md = require('markdown-js');
643
+ } catch (err) {
644
+ try {
645
+ md = require('marked');
646
+ } catch (err) {
647
+ throw new
648
+ Error('Cannot find markdown library, install markdown, discount, or marked.');
649
+ }
650
+ }
651
+ }
652
+ }
653
+
654
+ str = str.replace(/\\n/g, '\n');
655
+ return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'&#39;');
656
+ },
657
+
658
+ /**
659
+ * Transform coffeescript to javascript.
660
+ */
661
+
662
+ coffeescript: function(str){
663
+ str = str.replace(/\\n/g, '\n');
664
+ var js = require('coffee-script').compile(str).replace(/\n/g, '\\n');
665
+ return '<script type="text/javascript">\\n' + js + '</script>';
666
+ }
667
+ };
668
+
669
+ }); // module: filters.js
670
+
671
+ require.register("inline-tags.js", function(module, exports, require){
672
+
673
+ /*!
674
+ * Jade - inline tags
675
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
676
+ * MIT Licensed
677
+ */
678
+
679
+ module.exports = [
680
+ 'a'
681
+ , 'abbr'
682
+ , 'acronym'
683
+ , 'b'
684
+ , 'br'
685
+ , 'code'
686
+ , 'em'
687
+ , 'font'
688
+ , 'i'
689
+ , 'img'
690
+ , 'ins'
691
+ , 'kbd'
692
+ , 'map'
693
+ , 'samp'
694
+ , 'small'
695
+ , 'span'
696
+ , 'strong'
697
+ , 'sub'
698
+ , 'sup'
699
+ ];
700
+ }); // module: inline-tags.js
701
+
702
+ require.register("jade.js", function(module, exports, require){
703
+ /*!
704
+ * Jade
705
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
706
+ * MIT Licensed
707
+ */
708
+
709
+ /**
710
+ * Module dependencies.
711
+ */
712
+
713
+ var Parser = require('./parser')
714
+ , Lexer = require('./lexer')
715
+ , Compiler = require('./compiler')
716
+ , runtime = require('./runtime')
717
+
718
+ /**
719
+ * Library version.
720
+ */
721
+
722
+ exports.version = '0.20.0';
723
+
724
+ /**
725
+ * Expose self closing tags.
726
+ */
727
+
728
+ exports.selfClosing = require('./self-closing');
729
+
730
+ /**
731
+ * Default supported doctypes.
732
+ */
733
+
734
+ exports.doctypes = require('./doctypes');
735
+
736
+ /**
737
+ * Text filters.
738
+ */
739
+
740
+ exports.filters = require('./filters');
741
+
742
+ /**
743
+ * Utilities.
744
+ */
745
+
746
+ exports.utils = require('./utils');
747
+
748
+ /**
749
+ * Expose `Compiler`.
750
+ */
751
+
752
+ exports.Compiler = Compiler;
753
+
754
+ /**
755
+ * Expose `Parser`.
756
+ */
757
+
758
+ exports.Parser = Parser;
759
+
760
+ /**
761
+ * Expose `Lexer`.
762
+ */
763
+
764
+ exports.Lexer = Lexer;
765
+
766
+ /**
767
+ * Nodes.
768
+ */
769
+
770
+ exports.nodes = require('./nodes');
771
+
772
+ /**
773
+ * Jade runtime mixins.
774
+ */
775
+
776
+ exports.runtime = runtime;
777
+
778
+ /**
779
+ * Template function cache.
780
+ */
781
+
782
+ exports.cache = {};
783
+
784
+ /**
785
+ * Parse the given `str` of jade and return a function body.
786
+ *
787
+ * @param {String} str
788
+ * @param {Object} options
789
+ * @return {String}
790
+ * @api private
791
+ */
792
+
793
+ function parse(str, options){
794
+ try {
795
+ // Parse
796
+ var parser = new Parser(str, options.filename, options);
797
+
798
+ // Compile
799
+ var compiler = new (options.compiler || Compiler)(parser.parse(), options)
800
+ , js = compiler.compile();
801
+
802
+ // Debug compiler
803
+ if (options.debug) {
804
+ console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' '));
805
+ }
806
+
807
+ return ''
808
+ + 'var buf = [];\n'
809
+ + (options.self
810
+ ? 'var self = locals || {};\n' + js
811
+ : 'with (locals || {}) {\n' + js + '\n}\n')
812
+ + 'return buf.join("");';
813
+ } catch (err) {
814
+ parser = parser.context();
815
+ runtime.rethrow(err, parser.filename, parser.lexer.lineno);
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Precompile a string representation of the given jade `str`.
821
+ *
822
+ * Options:
823
+ *
824
+ * - `compileDebug` when `false` debugging code is stripped from the compiled template
825
+ * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()`
826
+ * for use with the Jade client-side runtime.js
827
+ *
828
+ * @param {String} str
829
+ * @param {Options} options
830
+ * @return {Function}
831
+ * @api public
832
+ */
833
+
834
+ exports.precompile = function(str, options){
835
+ var options = options || {}
836
+ , client = options.client
837
+ , filename = options.filename
838
+ ? JSON.stringify(options.filename)
839
+ : 'undefined'
840
+ , fn;
841
+
842
+ if (options.compileDebug !== false) {
843
+ fn = [
844
+ 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];'
845
+ , 'try {'
846
+ , parse(String(str), options)
847
+ , '} catch (err) {'
848
+ , ' rethrow(err, __jade[0].filename, __jade[0].lineno);'
849
+ , '}'
850
+ ].join('\n');
851
+ } else {
852
+ fn = parse(String(str), options);
853
+ }
854
+
855
+ if (client) {
856
+ fn = 'var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow;\n' + fn;
857
+ }
858
+
859
+ return fn;
860
+ }
861
+
862
+ /**
863
+ * Compile a `Function` representation of the given jade `str`.
864
+ *
865
+ * Options:
866
+ *
867
+ * - `compileDebug` when `false` debugging code is stripped from the compiled template
868
+ * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()`
869
+ * for use with the Jade client-side runtime.js
870
+ *
871
+ * @param {String} str
872
+ * @param {Options} options
873
+ * @return {Function}
874
+ * @api public
875
+ */
876
+
877
+ exports.compile = function(str, options){
878
+ var options = options || {}
879
+ , client = options.client,
880
+ fn;
881
+
882
+ fn = new Function('locals, attrs, escape, rethrow', exports.precompile(str, options));
883
+
884
+ if (client) return fn;
885
+
886
+ return function(locals){
887
+ return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow);
888
+ };
889
+ };
890
+
891
+ /**
892
+ * Render the given `str` of jade and invoke
893
+ * the callback `fn(err, str)`.
894
+ *
895
+ * Options:
896
+ *
897
+ * - `cache` enable template caching
898
+ * - `filename` filename required for `include` / `extends` and caching
899
+ *
900
+ * @param {String} str
901
+ * @param {Object|Function} options or fn
902
+ * @param {Function} fn
903
+ * @api public
904
+ */
905
+
906
+ exports.render = function(str, options, fn){
907
+ // swap args
908
+ if ('function' == typeof options) {
909
+ fn = options, options = {};
910
+ }
911
+
912
+ // cache requires .filename
913
+ if (options.cache && !options.filename) {
914
+ return fn(new Error('the "filename" option is required for caching'));
915
+ }
916
+
917
+ try {
918
+ var path = options.filename;
919
+ var tmpl = options.cache
920
+ ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options))
921
+ : exports.compile(str, options);
922
+ fn(null, tmpl(options));
923
+ } catch (err) {
924
+ fn(err);
925
+ }
926
+ };
927
+
928
+ /**
929
+ * Render a Jade file at the given `path` and callback `fn(err, str)`.
930
+ *
931
+ * @param {String} path
932
+ * @param {Object|Function} options or callback
933
+ * @param {Function} fn
934
+ * @api public
935
+ */
936
+
937
+ exports.renderFile = function(path, options, fn){
938
+ var key = path + ':string';
939
+
940
+ if ('function' == typeof options) {
941
+ fn = options, options = {};
942
+ }
943
+
944
+ try {
945
+ options.filename = path;
946
+ var str = options.cache
947
+ ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8'))
948
+ : fs.readFileSync(path, 'utf8');
949
+ exports.render(str, options, fn);
950
+ } catch (err) {
951
+ fn(err);
952
+ }
953
+ };
954
+
955
+ /**
956
+ * Express support.
957
+ */
958
+
959
+ exports.__express = exports.renderFile;
960
+
961
+ }); // module: jade.js
962
+
963
+ require.register("lexer.js", function(module, exports, require){
964
+
965
+ /*!
966
+ * Jade - Lexer
967
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
968
+ * MIT Licensed
969
+ */
970
+
971
+ /**
972
+ * Initialize `Lexer` with the given `str`.
973
+ *
974
+ * Options:
975
+ *
976
+ * - `colons` allow colons for attr delimiters
977
+ *
978
+ * @param {String} str
979
+ * @param {Object} options
980
+ * @api private
981
+ */
982
+
983
+ var Lexer = module.exports = function Lexer(str, options) {
984
+ options = options || {};
985
+ this.input = str.replace(/\r\n|\r/g, '\n');
986
+ this.colons = options.colons;
987
+ this.deferredTokens = [];
988
+ this.lastIndents = 0;
989
+ this.lineno = 1;
990
+ this.stash = [];
991
+ this.indentStack = [];
992
+ this.indentRe = null;
993
+ this.pipeless = false;
994
+ };
995
+
996
+ /**
997
+ * Lexer prototype.
998
+ */
999
+
1000
+ Lexer.prototype = {
1001
+
1002
+ /**
1003
+ * Construct a token with the given `type` and `val`.
1004
+ *
1005
+ * @param {String} type
1006
+ * @param {String} val
1007
+ * @return {Object}
1008
+ * @api private
1009
+ */
1010
+
1011
+ tok: function(type, val){
1012
+ return {
1013
+ type: type
1014
+ , line: this.lineno
1015
+ , val: val
1016
+ }
1017
+ },
1018
+
1019
+ /**
1020
+ * Consume the given `len` of input.
1021
+ *
1022
+ * @param {Number} len
1023
+ * @api private
1024
+ */
1025
+
1026
+ consume: function(len){
1027
+ this.input = this.input.substr(len);
1028
+ },
1029
+
1030
+ /**
1031
+ * Scan for `type` with the given `regexp`.
1032
+ *
1033
+ * @param {String} type
1034
+ * @param {RegExp} regexp
1035
+ * @return {Object}
1036
+ * @api private
1037
+ */
1038
+
1039
+ scan: function(regexp, type){
1040
+ var captures;
1041
+ if (captures = regexp.exec(this.input)) {
1042
+ this.consume(captures[0].length);
1043
+ return this.tok(type, captures[1]);
1044
+ }
1045
+ },
1046
+
1047
+ /**
1048
+ * Defer the given `tok`.
1049
+ *
1050
+ * @param {Object} tok
1051
+ * @api private
1052
+ */
1053
+
1054
+ defer: function(tok){
1055
+ this.deferredTokens.push(tok);
1056
+ },
1057
+
1058
+ /**
1059
+ * Lookahead `n` tokens.
1060
+ *
1061
+ * @param {Number} n
1062
+ * @return {Object}
1063
+ * @api private
1064
+ */
1065
+
1066
+ lookahead: function(n){
1067
+ var fetch = n - this.stash.length;
1068
+ while (fetch-- > 0) this.stash.push(this.next());
1069
+ return this.stash[--n];
1070
+ },
1071
+
1072
+ /**
1073
+ * Return the indexOf `start` / `end` delimiters.
1074
+ *
1075
+ * @param {String} start
1076
+ * @param {String} end
1077
+ * @return {Number}
1078
+ * @api private
1079
+ */
1080
+
1081
+ indexOfDelimiters: function(start, end){
1082
+ var str = this.input
1083
+ , nstart = 0
1084
+ , nend = 0
1085
+ , pos = 0;
1086
+ for (var i = 0, len = str.length; i < len; ++i) {
1087
+ if (start == str[i]) {
1088
+ ++nstart;
1089
+ } else if (end == str[i]) {
1090
+ if (++nend == nstart) {
1091
+ pos = i;
1092
+ break;
1093
+ }
1094
+ }
1095
+ }
1096
+ return pos;
1097
+ },
1098
+
1099
+ /**
1100
+ * Stashed token.
1101
+ */
1102
+
1103
+ stashed: function() {
1104
+ return this.stash.length
1105
+ && this.stash.shift();
1106
+ },
1107
+
1108
+ /**
1109
+ * Deferred token.
1110
+ */
1111
+
1112
+ deferred: function() {
1113
+ return this.deferredTokens.length
1114
+ && this.deferredTokens.shift();
1115
+ },
1116
+
1117
+ /**
1118
+ * end-of-source.
1119
+ */
1120
+
1121
+ eos: function() {
1122
+ if (this.input.length) return;
1123
+ if (this.indentStack.length) {
1124
+ this.indentStack.shift();
1125
+ return this.tok('outdent');
1126
+ } else {
1127
+ return this.tok('eos');
1128
+ }
1129
+ },
1130
+
1131
+ /**
1132
+ * Comment.
1133
+ */
1134
+
1135
+ comment: function() {
1136
+ var captures;
1137
+ if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) {
1138
+ this.consume(captures[0].length);
1139
+ var tok = this.tok('comment', captures[2]);
1140
+ tok.buffer = '-' != captures[1];
1141
+ return tok;
1142
+ }
1143
+ },
1144
+
1145
+ /**
1146
+ * Tag.
1147
+ */
1148
+
1149
+ tag: function() {
1150
+ var captures;
1151
+ if (captures = /^(\w[-:\w]*)/.exec(this.input)) {
1152
+ this.consume(captures[0].length);
1153
+ var tok, name = captures[1];
1154
+ if (':' == name[name.length - 1]) {
1155
+ name = name.slice(0, -1);
1156
+ tok = this.tok('tag', name);
1157
+ this.defer(this.tok(':'));
1158
+ while (' ' == this.input[0]) this.input = this.input.substr(1);
1159
+ } else {
1160
+ tok = this.tok('tag', name);
1161
+ }
1162
+ return tok;
1163
+ }
1164
+ },
1165
+
1166
+ /**
1167
+ * Filter.
1168
+ */
1169
+
1170
+ filter: function() {
1171
+ return this.scan(/^:(\w+)/, 'filter');
1172
+ },
1173
+
1174
+ /**
1175
+ * Doctype.
1176
+ */
1177
+
1178
+ doctype: function() {
1179
+ return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype');
1180
+ },
1181
+
1182
+ /**
1183
+ * Id.
1184
+ */
1185
+
1186
+ id: function() {
1187
+ return this.scan(/^#([\w-]+)/, 'id');
1188
+ },
1189
+
1190
+ /**
1191
+ * Class.
1192
+ */
1193
+
1194
+ className: function() {
1195
+ return this.scan(/^\.([\w-]+)/, 'class');
1196
+ },
1197
+
1198
+ /**
1199
+ * Text.
1200
+ */
1201
+
1202
+ text: function() {
1203
+ return this.scan(/^(?:\| ?)?([^\n]+)/, 'text');
1204
+ },
1205
+
1206
+ /**
1207
+ * Extends.
1208
+ */
1209
+
1210
+ extends: function() {
1211
+ return this.scan(/^extends +([^\n]+)/, 'extends');
1212
+ },
1213
+
1214
+ /**
1215
+ * Block prepend.
1216
+ */
1217
+
1218
+ prepend: function() {
1219
+ var captures;
1220
+ if (captures = /^prepend +([^\n]+)/.exec(this.input)) {
1221
+ this.consume(captures[0].length);
1222
+ var mode = 'prepend'
1223
+ , name = captures[1]
1224
+ , tok = this.tok('block', name);
1225
+ tok.mode = mode;
1226
+ return tok;
1227
+ }
1228
+ },
1229
+
1230
+ /**
1231
+ * Block append.
1232
+ */
1233
+
1234
+ append: function() {
1235
+ var captures;
1236
+ if (captures = /^append +([^\n]+)/.exec(this.input)) {
1237
+ this.consume(captures[0].length);
1238
+ var mode = 'append'
1239
+ , name = captures[1]
1240
+ , tok = this.tok('block', name);
1241
+ tok.mode = mode;
1242
+ return tok;
1243
+ }
1244
+ },
1245
+
1246
+ /**
1247
+ * Block.
1248
+ */
1249
+
1250
+ block: function() {
1251
+ var captures;
1252
+ if (captures = /^block +(?:(prepend|append) +)?([^\n]+)/.exec(this.input)) {
1253
+ this.consume(captures[0].length);
1254
+ var mode = captures[1] || 'replace'
1255
+ , name = captures[2]
1256
+ , tok = this.tok('block', name);
1257
+ tok.mode = mode;
1258
+ return tok;
1259
+ }
1260
+ },
1261
+
1262
+ /**
1263
+ * Yield.
1264
+ */
1265
+
1266
+ yield: function() {
1267
+ return this.scan(/^yield */, 'yield');
1268
+ },
1269
+
1270
+ /**
1271
+ * Include.
1272
+ */
1273
+
1274
+ include: function() {
1275
+ return this.scan(/^include +([^\n]+)/, 'include');
1276
+ },
1277
+
1278
+ /**
1279
+ * Case.
1280
+ */
1281
+
1282
+ case: function() {
1283
+ return this.scan(/^case +([^\n]+)/, 'case');
1284
+ },
1285
+
1286
+ /**
1287
+ * When.
1288
+ */
1289
+
1290
+ when: function() {
1291
+ return this.scan(/^when +([^:\n]+)/, 'when');
1292
+ },
1293
+
1294
+ /**
1295
+ * Default.
1296
+ */
1297
+
1298
+ default: function() {
1299
+ return this.scan(/^default */, 'default');
1300
+ },
1301
+
1302
+ /**
1303
+ * Assignment.
1304
+ */
1305
+
1306
+ assignment: function() {
1307
+ var captures;
1308
+ if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) {
1309
+ this.consume(captures[0].length);
1310
+ var name = captures[1]
1311
+ , val = captures[2];
1312
+ return this.tok('code', 'var ' + name + ' = (' + val + ');');
1313
+ }
1314
+ },
1315
+
1316
+ /**
1317
+ * Mixin.
1318
+ */
1319
+
1320
+ mixin: function(){
1321
+ var captures;
1322
+ if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) {
1323
+ this.consume(captures[0].length);
1324
+ var tok = this.tok('mixin', captures[1]);
1325
+ tok.args = captures[2];
1326
+ return tok;
1327
+ }
1328
+ },
1329
+
1330
+ /**
1331
+ * Conditional.
1332
+ */
1333
+
1334
+ conditional: function() {
1335
+ var captures;
1336
+ if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
1337
+ this.consume(captures[0].length);
1338
+ var type = captures[1]
1339
+ , js = captures[2];
1340
+
1341
+ switch (type) {
1342
+ case 'if': js = 'if (' + js + ')'; break;
1343
+ case 'unless': js = 'if (!(' + js + '))'; break;
1344
+ case 'else if': js = 'else if (' + js + ')'; break;
1345
+ case 'else': js = 'else'; break;
1346
+ }
1347
+
1348
+ return this.tok('code', js);
1349
+ }
1350
+ },
1351
+
1352
+ /**
1353
+ * While.
1354
+ */
1355
+
1356
+ while: function() {
1357
+ var captures;
1358
+ if (captures = /^while +([^\n]+)/.exec(this.input)) {
1359
+ this.consume(captures[0].length);
1360
+ return this.tok('code', 'while (' + captures[1] + ')');
1361
+ }
1362
+ },
1363
+
1364
+ /**
1365
+ * Each.
1366
+ */
1367
+
1368
+ each: function() {
1369
+ var captures;
1370
+ if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
1371
+ this.consume(captures[0].length);
1372
+ var tok = this.tok('each', captures[1]);
1373
+ tok.key = captures[2] || '$index';
1374
+ tok.code = captures[3];
1375
+ return tok;
1376
+ }
1377
+ },
1378
+
1379
+ /**
1380
+ * Code.
1381
+ */
1382
+
1383
+ code: function() {
1384
+ var captures;
1385
+ if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
1386
+ this.consume(captures[0].length);
1387
+ var flags = captures[1];
1388
+ captures[1] = captures[2];
1389
+ var tok = this.tok('code', captures[1]);
1390
+ tok.escape = flags[0] === '=';
1391
+ tok.buffer = flags[0] === '=' || flags[1] === '=';
1392
+ return tok;
1393
+ }
1394
+ },
1395
+
1396
+ /**
1397
+ * Attributes.
1398
+ */
1399
+
1400
+ attrs: function() {
1401
+ if ('(' == this.input[0]) {
1402
+ var index = this.indexOfDelimiters('(', ')')
1403
+ , str = this.input.substr(1, index-1)
1404
+ , tok = this.tok('attrs')
1405
+ , len = str.length
1406
+ , colons = this.colons
1407
+ , states = ['key']
1408
+ , key = ''
1409
+ , val = ''
1410
+ , quote
1411
+ , c;
1412
+
1413
+ function state(){
1414
+ return states[states.length - 1];
1415
+ }
1416
+
1417
+ function interpolate(attr) {
1418
+ return attr.replace(/#\{([^}]+)\}/g, function(_, expr){
1419
+ return quote + " + (" + expr + ") + " + quote;
1420
+ });
1421
+ }
1422
+
1423
+ this.consume(index + 1);
1424
+ tok.attrs = {};
1425
+
1426
+ function parse(c) {
1427
+ var real = c;
1428
+ // TODO: remove when people fix ":"
1429
+ if (colons && ':' == c) c = '=';
1430
+ switch (c) {
1431
+ case ',':
1432
+ case '\n':
1433
+ switch (state()) {
1434
+ case 'expr':
1435
+ case 'array':
1436
+ case 'string':
1437
+ case 'object':
1438
+ val += c;
1439
+ break;
1440
+ default:
1441
+ states.push('key');
1442
+ val = val.trim();
1443
+ key = key.trim();
1444
+ if ('' == key) return;
1445
+ tok.attrs[key.replace(/^['"]|['"]$/g, '')] = '' == val
1446
+ ? true
1447
+ : interpolate(val);
1448
+ key = val = '';
1449
+ }
1450
+ break;
1451
+ case '=':
1452
+ switch (state()) {
1453
+ case 'key char':
1454
+ key += real;
1455
+ break;
1456
+ case 'val':
1457
+ case 'expr':
1458
+ case 'array':
1459
+ case 'string':
1460
+ case 'object':
1461
+ val += real;
1462
+ break;
1463
+ default:
1464
+ states.push('val');
1465
+ }
1466
+ break;
1467
+ case '(':
1468
+ if ('val' == state()
1469
+ || 'expr' == state()) states.push('expr');
1470
+ val += c;
1471
+ break;
1472
+ case ')':
1473
+ if ('expr' == state()
1474
+ || 'val' == state()) states.pop();
1475
+ val += c;
1476
+ break;
1477
+ case '{':
1478
+ if ('val' == state()) states.push('object');
1479
+ val += c;
1480
+ break;
1481
+ case '}':
1482
+ if ('object' == state()) states.pop();
1483
+ val += c;
1484
+ break;
1485
+ case '[':
1486
+ if ('val' == state()) states.push('array');
1487
+ val += c;
1488
+ break;
1489
+ case ']':
1490
+ if ('array' == state()) states.pop();
1491
+ val += c;
1492
+ break;
1493
+ case '"':
1494
+ case "'":
1495
+ switch (state()) {
1496
+ case 'key':
1497
+ states.push('key char');
1498
+ break;
1499
+ case 'key char':
1500
+ states.pop();
1501
+ break;
1502
+ case 'string':
1503
+ if (c == quote) states.pop();
1504
+ val += c;
1505
+ break;
1506
+ default:
1507
+ states.push('string');
1508
+ val += c;
1509
+ quote = c;
1510
+ }
1511
+ break;
1512
+ case '':
1513
+ break;
1514
+ default:
1515
+ switch (state()) {
1516
+ case 'key':
1517
+ case 'key char':
1518
+ key += c;
1519
+ break;
1520
+ default:
1521
+ val += c;
1522
+ }
1523
+ }
1524
+ }
1525
+
1526
+ for (var i = 0; i < len; ++i) {
1527
+ parse(str[i]);
1528
+ }
1529
+
1530
+ parse(',');
1531
+
1532
+ return tok;
1533
+ }
1534
+ },
1535
+
1536
+ /**
1537
+ * Indent | Outdent | Newline.
1538
+ */
1539
+
1540
+ indent: function() {
1541
+ var captures, re;
1542
+
1543
+ // established regexp
1544
+ if (this.indentRe) {
1545
+ captures = this.indentRe.exec(this.input);
1546
+ // determine regexp
1547
+ } else {
1548
+ // tabs
1549
+ re = /^\n(\t*) */;
1550
+ captures = re.exec(this.input);
1551
+
1552
+ // spaces
1553
+ if (captures && !captures[1].length) {
1554
+ re = /^\n( *)/;
1555
+ captures = re.exec(this.input);
1556
+ }
1557
+
1558
+ // established
1559
+ if (captures && captures[1].length) this.indentRe = re;
1560
+ }
1561
+
1562
+ if (captures) {
1563
+ var tok
1564
+ , indents = captures[1].length;
1565
+
1566
+ ++this.lineno;
1567
+ this.consume(indents + 1);
1568
+
1569
+ if (' ' == this.input[0] || '\t' == this.input[0]) {
1570
+ throw new Error('Invalid indentation, you can use tabs or spaces but not both');
1571
+ }
1572
+
1573
+ // blank line
1574
+ if ('\n' == this.input[0]) return this.tok('newline');
1575
+
1576
+ // outdent
1577
+ if (this.indentStack.length && indents < this.indentStack[0]) {
1578
+ while (this.indentStack.length && this.indentStack[0] > indents) {
1579
+ this.stash.push(this.tok('outdent'));
1580
+ this.indentStack.shift();
1581
+ }
1582
+ tok = this.stash.pop();
1583
+ // indent
1584
+ } else if (indents && indents != this.indentStack[0]) {
1585
+ this.indentStack.unshift(indents);
1586
+ tok = this.tok('indent', indents);
1587
+ // newline
1588
+ } else {
1589
+ tok = this.tok('newline');
1590
+ }
1591
+
1592
+ return tok;
1593
+ }
1594
+ },
1595
+
1596
+ /**
1597
+ * Pipe-less text consumed only when
1598
+ * pipeless is true;
1599
+ */
1600
+
1601
+ pipelessText: function() {
1602
+ if (this.pipeless) {
1603
+ if ('\n' == this.input[0]) return;
1604
+ var i = this.input.indexOf('\n');
1605
+ if (-1 == i) i = this.input.length;
1606
+ var str = this.input.substr(0, i);
1607
+ this.consume(str.length);
1608
+ return this.tok('text', str);
1609
+ }
1610
+ },
1611
+
1612
+ /**
1613
+ * ':'
1614
+ */
1615
+
1616
+ colon: function() {
1617
+ return this.scan(/^: */, ':');
1618
+ },
1619
+
1620
+ /**
1621
+ * Return the next token object, or those
1622
+ * previously stashed by lookahead.
1623
+ *
1624
+ * @return {Object}
1625
+ * @api private
1626
+ */
1627
+
1628
+ advance: function(){
1629
+ return this.stashed()
1630
+ || this.next();
1631
+ },
1632
+
1633
+ /**
1634
+ * Return the next token object.
1635
+ *
1636
+ * @return {Object}
1637
+ * @api private
1638
+ */
1639
+
1640
+ next: function() {
1641
+ return this.deferred()
1642
+ || this.eos()
1643
+ || this.pipelessText()
1644
+ || this.yield()
1645
+ || this.doctype()
1646
+ || this.case()
1647
+ || this.when()
1648
+ || this.default()
1649
+ || this.extends()
1650
+ || this.append()
1651
+ || this.prepend()
1652
+ || this.block()
1653
+ || this.include()
1654
+ || this.mixin()
1655
+ || this.conditional()
1656
+ || this.each()
1657
+ || this.while()
1658
+ || this.assignment()
1659
+ || this.tag()
1660
+ || this.filter()
1661
+ || this.code()
1662
+ || this.id()
1663
+ || this.className()
1664
+ || this.attrs()
1665
+ || this.indent()
1666
+ || this.comment()
1667
+ || this.colon()
1668
+ || this.text();
1669
+ }
1670
+ };
1671
+
1672
+ }); // module: lexer.js
1673
+
1674
+ require.register("nodes/block-comment.js", function(module, exports, require){
1675
+
1676
+ /*!
1677
+ * Jade - nodes - BlockComment
1678
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1679
+ * MIT Licensed
1680
+ */
1681
+
1682
+ /**
1683
+ * Module dependencies.
1684
+ */
1685
+
1686
+ var Node = require('./node');
1687
+
1688
+ /**
1689
+ * Initialize a `BlockComment` with the given `block`.
1690
+ *
1691
+ * @param {String} val
1692
+ * @param {Block} block
1693
+ * @param {Boolean} buffer
1694
+ * @api public
1695
+ */
1696
+
1697
+ var BlockComment = module.exports = function BlockComment(val, block, buffer) {
1698
+ this.block = block;
1699
+ this.val = val;
1700
+ this.buffer = buffer;
1701
+ };
1702
+
1703
+ /**
1704
+ * Inherit from `Node`.
1705
+ */
1706
+
1707
+ BlockComment.prototype = new Node;
1708
+ BlockComment.prototype.constructor = BlockComment;
1709
+
1710
+ }); // module: nodes/block-comment.js
1711
+
1712
+ require.register("nodes/block.js", function(module, exports, require){
1713
+
1714
+ /*!
1715
+ * Jade - nodes - Block
1716
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1717
+ * MIT Licensed
1718
+ */
1719
+
1720
+ /**
1721
+ * Module dependencies.
1722
+ */
1723
+
1724
+ var Node = require('./node');
1725
+
1726
+ /**
1727
+ * Initialize a new `Block` with an optional `node`.
1728
+ *
1729
+ * @param {Node} node
1730
+ * @api public
1731
+ */
1732
+
1733
+ var Block = module.exports = function Block(node){
1734
+ this.nodes = [];
1735
+ if (node) this.push(node);
1736
+ };
1737
+
1738
+ /**
1739
+ * Inherit from `Node`.
1740
+ */
1741
+
1742
+ Block.prototype = new Node;
1743
+ Block.prototype.constructor = Block;
1744
+
1745
+
1746
+ /**
1747
+ * Replace the nodes in `other` with the nodes
1748
+ * in `this` block.
1749
+ *
1750
+ * @param {Block} other
1751
+ * @api private
1752
+ */
1753
+
1754
+ Block.prototype.replace = function(other){
1755
+ other.nodes = this.nodes;
1756
+ };
1757
+
1758
+ /**
1759
+ * Pust the given `node`.
1760
+ *
1761
+ * @param {Node} node
1762
+ * @return {Number}
1763
+ * @api public
1764
+ */
1765
+
1766
+ Block.prototype.push = function(node){
1767
+ return this.nodes.push(node);
1768
+ };
1769
+
1770
+ /**
1771
+ * Check if this block is empty.
1772
+ *
1773
+ * @return {Boolean}
1774
+ * @api public
1775
+ */
1776
+
1777
+ Block.prototype.isEmpty = function(){
1778
+ return 0 == this.nodes.length;
1779
+ };
1780
+
1781
+ /**
1782
+ * Unshift the given `node`.
1783
+ *
1784
+ * @param {Node} node
1785
+ * @return {Number}
1786
+ * @api public
1787
+ */
1788
+
1789
+ Block.prototype.unshift = function(node){
1790
+ return this.nodes.unshift(node);
1791
+ };
1792
+
1793
+ /**
1794
+ * Return the "last" block, or the first `yield` node.
1795
+ *
1796
+ * @return {Block}
1797
+ * @api private
1798
+ */
1799
+
1800
+ Block.prototype.includeBlock = function(){
1801
+ var ret = this
1802
+ , node;
1803
+
1804
+ for (var i = 0, len = this.nodes.length; i < len; ++i) {
1805
+ node = this.nodes[i];
1806
+ if (node.yield) return node;
1807
+ else if (node.textOnly) continue;
1808
+ else if (node.includeBlock) ret = node.includeBlock();
1809
+ else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock();
1810
+ }
1811
+
1812
+ return ret;
1813
+ };
1814
+
1815
+
1816
+ }); // module: nodes/block.js
1817
+
1818
+ require.register("nodes/case.js", function(module, exports, require){
1819
+
1820
+ /*!
1821
+ * Jade - nodes - Case
1822
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1823
+ * MIT Licensed
1824
+ */
1825
+
1826
+ /**
1827
+ * Module dependencies.
1828
+ */
1829
+
1830
+ var Node = require('./node');
1831
+
1832
+ /**
1833
+ * Initialize a new `Case` with `expr`.
1834
+ *
1835
+ * @param {String} expr
1836
+ * @api public
1837
+ */
1838
+
1839
+ var Case = exports = module.exports = function Case(expr, block){
1840
+ this.expr = expr;
1841
+ this.block = block;
1842
+ };
1843
+
1844
+ /**
1845
+ * Inherit from `Node`.
1846
+ */
1847
+
1848
+ Case.prototype = new Node;
1849
+ Case.prototype.constructor = Case;
1850
+
1851
+
1852
+ var When = exports.When = function When(expr, block){
1853
+ this.expr = expr;
1854
+ this.block = block;
1855
+ this.debug = false;
1856
+ };
1857
+
1858
+ /**
1859
+ * Inherit from `Node`.
1860
+ */
1861
+
1862
+ When.prototype = new Node;
1863
+ When.prototype.constructor = When;
1864
+
1865
+
1866
+
1867
+ }); // module: nodes/case.js
1868
+
1869
+ require.register("nodes/code.js", function(module, exports, require){
1870
+
1871
+ /*!
1872
+ * Jade - nodes - Code
1873
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1874
+ * MIT Licensed
1875
+ */
1876
+
1877
+ /**
1878
+ * Module dependencies.
1879
+ */
1880
+
1881
+ var Node = require('./node');
1882
+
1883
+ /**
1884
+ * Initialize a `Code` node with the given code `val`.
1885
+ * Code may also be optionally buffered and escaped.
1886
+ *
1887
+ * @param {String} val
1888
+ * @param {Boolean} buffer
1889
+ * @param {Boolean} escape
1890
+ * @api public
1891
+ */
1892
+
1893
+ var Code = module.exports = function Code(val, buffer, escape) {
1894
+ this.val = val;
1895
+ this.buffer = buffer;
1896
+ this.escape = escape;
1897
+ if (val.match(/^ *else/)) this.debug = false;
1898
+ };
1899
+
1900
+ /**
1901
+ * Inherit from `Node`.
1902
+ */
1903
+
1904
+ Code.prototype = new Node;
1905
+ Code.prototype.constructor = Code;
1906
+
1907
+ }); // module: nodes/code.js
1908
+
1909
+ require.register("nodes/comment.js", function(module, exports, require){
1910
+
1911
+ /*!
1912
+ * Jade - nodes - Comment
1913
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1914
+ * MIT Licensed
1915
+ */
1916
+
1917
+ /**
1918
+ * Module dependencies.
1919
+ */
1920
+
1921
+ var Node = require('./node');
1922
+
1923
+ /**
1924
+ * Initialize a `Comment` with the given `val`, optionally `buffer`,
1925
+ * otherwise the comment may render in the output.
1926
+ *
1927
+ * @param {String} val
1928
+ * @param {Boolean} buffer
1929
+ * @api public
1930
+ */
1931
+
1932
+ var Comment = module.exports = function Comment(val, buffer) {
1933
+ this.val = val;
1934
+ this.buffer = buffer;
1935
+ };
1936
+
1937
+ /**
1938
+ * Inherit from `Node`.
1939
+ */
1940
+
1941
+ Comment.prototype = new Node;
1942
+ Comment.prototype.constructor = Comment;
1943
+
1944
+ }); // module: nodes/comment.js
1945
+
1946
+ require.register("nodes/doctype.js", function(module, exports, require){
1947
+
1948
+ /*!
1949
+ * Jade - nodes - Doctype
1950
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1951
+ * MIT Licensed
1952
+ */
1953
+
1954
+ /**
1955
+ * Module dependencies.
1956
+ */
1957
+
1958
+ var Node = require('./node');
1959
+
1960
+ /**
1961
+ * Initialize a `Doctype` with the given `val`.
1962
+ *
1963
+ * @param {String} val
1964
+ * @api public
1965
+ */
1966
+
1967
+ var Doctype = module.exports = function Doctype(val) {
1968
+ this.val = val;
1969
+ };
1970
+
1971
+ /**
1972
+ * Inherit from `Node`.
1973
+ */
1974
+
1975
+ Doctype.prototype = new Node;
1976
+ Doctype.prototype.constructor = Doctype;
1977
+
1978
+ }); // module: nodes/doctype.js
1979
+
1980
+ require.register("nodes/each.js", function(module, exports, require){
1981
+
1982
+ /*!
1983
+ * Jade - nodes - Each
1984
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
1985
+ * MIT Licensed
1986
+ */
1987
+
1988
+ /**
1989
+ * Module dependencies.
1990
+ */
1991
+
1992
+ var Node = require('./node');
1993
+
1994
+ /**
1995
+ * Initialize an `Each` node, representing iteration
1996
+ *
1997
+ * @param {String} obj
1998
+ * @param {String} val
1999
+ * @param {String} key
2000
+ * @param {Block} block
2001
+ * @api public
2002
+ */
2003
+
2004
+ var Each = module.exports = function Each(obj, val, key, block) {
2005
+ this.obj = obj;
2006
+ this.val = val;
2007
+ this.key = key;
2008
+ this.block = block;
2009
+ };
2010
+
2011
+ /**
2012
+ * Inherit from `Node`.
2013
+ */
2014
+
2015
+ Each.prototype = new Node;
2016
+ Each.prototype.constructor = Each;
2017
+
2018
+ }); // module: nodes/each.js
2019
+
2020
+ require.register("nodes/filter.js", function(module, exports, require){
2021
+
2022
+ /*!
2023
+ * Jade - nodes - Filter
2024
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2025
+ * MIT Licensed
2026
+ */
2027
+
2028
+ /**
2029
+ * Module dependencies.
2030
+ */
2031
+
2032
+ var Node = require('./node')
2033
+ , Block = require('./block');
2034
+
2035
+ /**
2036
+ * Initialize a `Filter` node with the given
2037
+ * filter `name` and `block`.
2038
+ *
2039
+ * @param {String} name
2040
+ * @param {Block|Node} block
2041
+ * @api public
2042
+ */
2043
+
2044
+ var Filter = module.exports = function Filter(name, block, attrs) {
2045
+ this.name = name;
2046
+ this.block = block;
2047
+ this.attrs = attrs;
2048
+ this.isASTFilter = block instanceof Block;
2049
+ };
2050
+
2051
+ /**
2052
+ * Inherit from `Node`.
2053
+ */
2054
+
2055
+ Filter.prototype = new Node;
2056
+ Filter.prototype.constructor = Filter;
2057
+
2058
+ }); // module: nodes/filter.js
2059
+
2060
+ require.register("nodes/index.js", function(module, exports, require){
2061
+
2062
+ /*!
2063
+ * Jade - nodes
2064
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2065
+ * MIT Licensed
2066
+ */
2067
+
2068
+ exports.Node = require('./node');
2069
+ exports.Tag = require('./tag');
2070
+ exports.Code = require('./code');
2071
+ exports.Each = require('./each');
2072
+ exports.Case = require('./case');
2073
+ exports.Text = require('./text');
2074
+ exports.Block = require('./block');
2075
+ exports.Mixin = require('./mixin');
2076
+ exports.Filter = require('./filter');
2077
+ exports.Comment = require('./comment');
2078
+ exports.Literal = require('./literal');
2079
+ exports.BlockComment = require('./block-comment');
2080
+ exports.Doctype = require('./doctype');
2081
+
2082
+ }); // module: nodes/index.js
2083
+
2084
+ require.register("nodes/literal.js", function(module, exports, require){
2085
+
2086
+ /*!
2087
+ * Jade - nodes - Literal
2088
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2089
+ * MIT Licensed
2090
+ */
2091
+
2092
+ /**
2093
+ * Module dependencies.
2094
+ */
2095
+
2096
+ var Node = require('./node');
2097
+
2098
+ /**
2099
+ * Initialize a `Literal` node with the given `str.
2100
+ *
2101
+ * @param {String} str
2102
+ * @api public
2103
+ */
2104
+
2105
+ var Literal = module.exports = function Literal(str) {
2106
+ this.str = str
2107
+ .replace(/\n/g, "\\n")
2108
+ .replace(/'/g, "\\'");
2109
+ };
2110
+
2111
+ /**
2112
+ * Inherit from `Node`.
2113
+ */
2114
+
2115
+ Literal.prototype = new Node;
2116
+ Literal.prototype.constructor = Literal;
2117
+
2118
+
2119
+ }); // module: nodes/literal.js
2120
+
2121
+ require.register("nodes/mixin.js", function(module, exports, require){
2122
+
2123
+ /*!
2124
+ * Jade - nodes - Mixin
2125
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2126
+ * MIT Licensed
2127
+ */
2128
+
2129
+ /**
2130
+ * Module dependencies.
2131
+ */
2132
+
2133
+ var Node = require('./node');
2134
+
2135
+ /**
2136
+ * Initialize a new `Mixin` with `name` and `block`.
2137
+ *
2138
+ * @param {String} name
2139
+ * @param {String} args
2140
+ * @param {Block} block
2141
+ * @api public
2142
+ */
2143
+
2144
+ var Mixin = module.exports = function Mixin(name, args, block){
2145
+ this.name = name;
2146
+ this.args = args;
2147
+ this.block = block;
2148
+ };
2149
+
2150
+ /**
2151
+ * Inherit from `Node`.
2152
+ */
2153
+
2154
+ Mixin.prototype = new Node;
2155
+ Mixin.prototype.constructor = Mixin;
2156
+
2157
+
2158
+
2159
+ }); // module: nodes/mixin.js
2160
+
2161
+ require.register("nodes/node.js", function(module, exports, require){
2162
+
2163
+ /*!
2164
+ * Jade - nodes - Node
2165
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2166
+ * MIT Licensed
2167
+ */
2168
+
2169
+ /**
2170
+ * Initialize a `Node`.
2171
+ *
2172
+ * @api public
2173
+ */
2174
+
2175
+ var Node = module.exports = function Node(){};
2176
+ }); // module: nodes/node.js
2177
+
2178
+ require.register("nodes/tag.js", function(module, exports, require){
2179
+
2180
+ /*!
2181
+ * Jade - nodes - Tag
2182
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2183
+ * MIT Licensed
2184
+ */
2185
+
2186
+ /**
2187
+ * Module dependencies.
2188
+ */
2189
+
2190
+ var Node = require('./node'),
2191
+ Block = require('./block');
2192
+
2193
+ /**
2194
+ * Initialize a `Tag` node with the given tag `name` and optional `block`.
2195
+ *
2196
+ * @param {String} name
2197
+ * @param {Block} block
2198
+ * @api public
2199
+ */
2200
+
2201
+ var Tag = module.exports = function Tag(name, block) {
2202
+ this.name = name;
2203
+ this.attrs = [];
2204
+ this.block = block || new Block;
2205
+ };
2206
+
2207
+ /**
2208
+ * Inherit from `Node`.
2209
+ */
2210
+
2211
+ Tag.prototype = new Node;
2212
+ Tag.prototype.constructor = Tag;
2213
+
2214
+
2215
+ /**
2216
+ * Set attribute `name` to `val`, keep in mind these become
2217
+ * part of a raw js object literal, so to quote a value you must
2218
+ * '"quote me"', otherwise or example 'user.name' is literal JavaScript.
2219
+ *
2220
+ * @param {String} name
2221
+ * @param {String} val
2222
+ * @return {Tag} for chaining
2223
+ * @api public
2224
+ */
2225
+
2226
+ Tag.prototype.setAttribute = function(name, val){
2227
+ this.attrs.push({ name: name, val: val });
2228
+ return this;
2229
+ };
2230
+
2231
+ /**
2232
+ * Remove attribute `name` when present.
2233
+ *
2234
+ * @param {String} name
2235
+ * @api public
2236
+ */
2237
+
2238
+ Tag.prototype.removeAttribute = function(name){
2239
+ for (var i = 0, len = this.attrs.length; i < len; ++i) {
2240
+ if (this.attrs[i] && this.attrs[i].name == name) {
2241
+ delete this.attrs[i];
2242
+ }
2243
+ }
2244
+ };
2245
+
2246
+ /**
2247
+ * Get attribute value by `name`.
2248
+ *
2249
+ * @param {String} name
2250
+ * @return {String}
2251
+ * @api public
2252
+ */
2253
+
2254
+ Tag.prototype.getAttribute = function(name){
2255
+ for (var i = 0, len = this.attrs.length; i < len; ++i) {
2256
+ if (this.attrs[i] && this.attrs[i].name == name) {
2257
+ return this.attrs[i].val;
2258
+ }
2259
+ }
2260
+ };
2261
+
2262
+ }); // module: nodes/tag.js
2263
+
2264
+ require.register("nodes/text.js", function(module, exports, require){
2265
+
2266
+ /*!
2267
+ * Jade - nodes - Text
2268
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2269
+ * MIT Licensed
2270
+ */
2271
+
2272
+ /**
2273
+ * Module dependencies.
2274
+ */
2275
+
2276
+ var Node = require('./node');
2277
+
2278
+ /**
2279
+ * Initialize a `Text` node with optional `line`.
2280
+ *
2281
+ * @param {String} line
2282
+ * @api public
2283
+ */
2284
+
2285
+ var Text = module.exports = function Text(line) {
2286
+ this.nodes = [];
2287
+ if ('string' == typeof line) this.push(line);
2288
+ };
2289
+
2290
+ /**
2291
+ * Inherit from `Node`.
2292
+ */
2293
+
2294
+ Text.prototype = new Node;
2295
+ Text.prototype.constructor = Text;
2296
+
2297
+
2298
+ /**
2299
+ * Push the given `node.`
2300
+ *
2301
+ * @param {Node} node
2302
+ * @return {Number}
2303
+ * @api public
2304
+ */
2305
+
2306
+ Text.prototype.push = function(node){
2307
+ return this.nodes.push(node);
2308
+ };
2309
+
2310
+ }); // module: nodes/text.js
2311
+
2312
+ require.register("parser.js", function(module, exports, require){
2313
+
2314
+ /*!
2315
+ * Jade - Parser
2316
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2317
+ * MIT Licensed
2318
+ */
2319
+
2320
+ /**
2321
+ * Module dependencies.
2322
+ */
2323
+
2324
+ var Lexer = require('./lexer')
2325
+ , nodes = require('./nodes');
2326
+
2327
+ /**
2328
+ * Initialize `Parser` with the given input `str` and `filename`.
2329
+ *
2330
+ * @param {String} str
2331
+ * @param {String} filename
2332
+ * @param {Object} options
2333
+ * @api public
2334
+ */
2335
+
2336
+ var Parser = exports = module.exports = function Parser(str, filename, options){
2337
+ this.input = str;
2338
+ this.lexer = new Lexer(str, options);
2339
+ this.filename = filename;
2340
+ this.blocks = {};
2341
+ this.options = options;
2342
+ this.contexts = [this];
2343
+ };
2344
+
2345
+ /**
2346
+ * Tags that may not contain tags.
2347
+ */
2348
+
2349
+ var textOnly = exports.textOnly = ['script', 'style'];
2350
+
2351
+ /**
2352
+ * Parser prototype.
2353
+ */
2354
+
2355
+ Parser.prototype = {
2356
+
2357
+ /**
2358
+ * Push `parser` onto the context stack,
2359
+ * or pop and return a `Parser`.
2360
+ */
2361
+
2362
+ context: function(parser){
2363
+ if (parser) {
2364
+ this.contexts.push(parser);
2365
+ } else {
2366
+ return this.contexts.pop();
2367
+ }
2368
+ },
2369
+
2370
+ /**
2371
+ * Return the next token object.
2372
+ *
2373
+ * @return {Object}
2374
+ * @api private
2375
+ */
2376
+
2377
+ advance: function(){
2378
+ return this.lexer.advance();
2379
+ },
2380
+
2381
+ /**
2382
+ * Skip `n` tokens.
2383
+ *
2384
+ * @param {Number} n
2385
+ * @api private
2386
+ */
2387
+
2388
+ skip: function(n){
2389
+ while (n--) this.advance();
2390
+ },
2391
+
2392
+ /**
2393
+ * Single token lookahead.
2394
+ *
2395
+ * @return {Object}
2396
+ * @api private
2397
+ */
2398
+
2399
+ peek: function() {
2400
+ return this.lookahead(1);
2401
+ },
2402
+
2403
+ /**
2404
+ * Return lexer lineno.
2405
+ *
2406
+ * @return {Number}
2407
+ * @api private
2408
+ */
2409
+
2410
+ line: function() {
2411
+ return this.lexer.lineno;
2412
+ },
2413
+
2414
+ /**
2415
+ * `n` token lookahead.
2416
+ *
2417
+ * @param {Number} n
2418
+ * @return {Object}
2419
+ * @api private
2420
+ */
2421
+
2422
+ lookahead: function(n){
2423
+ return this.lexer.lookahead(n);
2424
+ },
2425
+
2426
+ /**
2427
+ * Parse input returning a string of js for evaluation.
2428
+ *
2429
+ * @return {String}
2430
+ * @api public
2431
+ */
2432
+
2433
+ parse: function(){
2434
+ var block = new nodes.Block, parser;
2435
+ block.line = this.line();
2436
+
2437
+ while ('eos' != this.peek().type) {
2438
+ if ('newline' == this.peek().type) {
2439
+ this.advance();
2440
+ } else {
2441
+ block.push(this.parseExpr());
2442
+ }
2443
+ }
2444
+
2445
+ if (parser = this.extending) {
2446
+ this.context(parser);
2447
+ var ast = parser.parse();
2448
+ this.context();
2449
+ return ast;
2450
+ }
2451
+
2452
+ return block;
2453
+ },
2454
+
2455
+ /**
2456
+ * Expect the given type, or throw an exception.
2457
+ *
2458
+ * @param {String} type
2459
+ * @api private
2460
+ */
2461
+
2462
+ expect: function(type){
2463
+ if (this.peek().type === type) {
2464
+ return this.advance();
2465
+ } else {
2466
+ throw new Error('expected "' + type + '", but got "' + this.peek().type + '"');
2467
+ }
2468
+ },
2469
+
2470
+ /**
2471
+ * Accept the given `type`.
2472
+ *
2473
+ * @param {String} type
2474
+ * @api private
2475
+ */
2476
+
2477
+ accept: function(type){
2478
+ if (this.peek().type === type) {
2479
+ return this.advance();
2480
+ }
2481
+ },
2482
+
2483
+ /**
2484
+ * tag
2485
+ * | doctype
2486
+ * | mixin
2487
+ * | include
2488
+ * | filter
2489
+ * | comment
2490
+ * | text
2491
+ * | each
2492
+ * | code
2493
+ * | yield
2494
+ * | id
2495
+ * | class
2496
+ */
2497
+
2498
+ parseExpr: function(){
2499
+ switch (this.peek().type) {
2500
+ case 'tag':
2501
+ return this.parseTag();
2502
+ case 'mixin':
2503
+ return this.parseMixin();
2504
+ case 'block':
2505
+ return this.parseBlock();
2506
+ case 'case':
2507
+ return this.parseCase();
2508
+ case 'when':
2509
+ return this.parseWhen();
2510
+ case 'default':
2511
+ return this.parseDefault();
2512
+ case 'extends':
2513
+ return this.parseExtends();
2514
+ case 'include':
2515
+ return this.parseInclude();
2516
+ case 'doctype':
2517
+ return this.parseDoctype();
2518
+ case 'filter':
2519
+ return this.parseFilter();
2520
+ case 'comment':
2521
+ return this.parseComment();
2522
+ case 'text':
2523
+ return this.parseText();
2524
+ case 'each':
2525
+ return this.parseEach();
2526
+ case 'code':
2527
+ return this.parseCode();
2528
+ case 'yield':
2529
+ this.advance();
2530
+ var block = new nodes.Block;
2531
+ block.yield = true;
2532
+ return block;
2533
+ case 'id':
2534
+ case 'class':
2535
+ var tok = this.advance();
2536
+ this.lexer.defer(this.lexer.tok('tag', 'div'));
2537
+ this.lexer.defer(tok);
2538
+ return this.parseExpr();
2539
+ default:
2540
+ throw new Error('unexpected token "' + this.peek().type + '"');
2541
+ }
2542
+ },
2543
+
2544
+ /**
2545
+ * Text
2546
+ */
2547
+
2548
+ parseText: function(){
2549
+ var tok = this.expect('text')
2550
+ , node = new nodes.Text(tok.val);
2551
+ node.line = this.line();
2552
+ return node;
2553
+ },
2554
+
2555
+ /**
2556
+ * ':' expr
2557
+ * | block
2558
+ */
2559
+
2560
+ parseBlockExpansion: function(){
2561
+ if (':' == this.peek().type) {
2562
+ this.advance();
2563
+ return new nodes.Block(this.parseExpr());
2564
+ } else {
2565
+ return this.block();
2566
+ }
2567
+ },
2568
+
2569
+ /**
2570
+ * case
2571
+ */
2572
+
2573
+ parseCase: function(){
2574
+ var val = this.expect('case').val
2575
+ , node = new nodes.Case(val);
2576
+ node.line = this.line();
2577
+ node.block = this.block();
2578
+ return node;
2579
+ },
2580
+
2581
+ /**
2582
+ * when
2583
+ */
2584
+
2585
+ parseWhen: function(){
2586
+ var val = this.expect('when').val
2587
+ return new nodes.Case.When(val, this.parseBlockExpansion());
2588
+ },
2589
+
2590
+ /**
2591
+ * default
2592
+ */
2593
+
2594
+ parseDefault: function(){
2595
+ this.expect('default');
2596
+ return new nodes.Case.When('default', this.parseBlockExpansion());
2597
+ },
2598
+
2599
+ /**
2600
+ * code
2601
+ */
2602
+
2603
+ parseCode: function(){
2604
+ var tok = this.expect('code')
2605
+ , node = new nodes.Code(tok.val, tok.buffer, tok.escape)
2606
+ , block
2607
+ , i = 1;
2608
+ node.line = this.line();
2609
+ while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i;
2610
+ block = 'indent' == this.lookahead(i).type;
2611
+ if (block) {
2612
+ this.skip(i-1);
2613
+ node.block = this.block();
2614
+ }
2615
+ return node;
2616
+ },
2617
+
2618
+ /**
2619
+ * comment
2620
+ */
2621
+
2622
+ parseComment: function(){
2623
+ var tok = this.expect('comment')
2624
+ , node;
2625
+
2626
+ if ('indent' == this.peek().type) {
2627
+ node = new nodes.BlockComment(tok.val, this.block(), tok.buffer);
2628
+ } else {
2629
+ node = new nodes.Comment(tok.val, tok.buffer);
2630
+ }
2631
+
2632
+ node.line = this.line();
2633
+ return node;
2634
+ },
2635
+
2636
+ /**
2637
+ * doctype
2638
+ */
2639
+
2640
+ parseDoctype: function(){
2641
+ var tok = this.expect('doctype')
2642
+ , node = new nodes.Doctype(tok.val);
2643
+ node.line = this.line();
2644
+ return node;
2645
+ },
2646
+
2647
+ /**
2648
+ * filter attrs? text-block
2649
+ */
2650
+
2651
+ parseFilter: function(){
2652
+ var block
2653
+ , tok = this.expect('filter')
2654
+ , attrs = this.accept('attrs');
2655
+
2656
+ this.lexer.pipeless = true;
2657
+ block = this.parseTextBlock();
2658
+ this.lexer.pipeless = false;
2659
+
2660
+ var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs);
2661
+ node.line = this.line();
2662
+ return node;
2663
+ },
2664
+
2665
+ /**
2666
+ * tag ':' attrs? block
2667
+ */
2668
+
2669
+ parseASTFilter: function(){
2670
+ var block
2671
+ , tok = this.expect('tag')
2672
+ , attrs = this.accept('attrs');
2673
+
2674
+ this.expect(':');
2675
+ block = this.block();
2676
+
2677
+ var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs);
2678
+ node.line = this.line();
2679
+ return node;
2680
+ },
2681
+
2682
+ /**
2683
+ * each block
2684
+ */
2685
+
2686
+ parseEach: function(){
2687
+ var tok = this.expect('each')
2688
+ , node = new nodes.Each(tok.code, tok.val, tok.key);
2689
+ node.line = this.line();
2690
+ node.block = this.block();
2691
+ return node;
2692
+ },
2693
+
2694
+ /**
2695
+ * 'extends' name
2696
+ */
2697
+
2698
+ parseExtends: function(){
2699
+ var path = require('path')
2700
+ , fs = require('fs')
2701
+ , dirname = path.dirname
2702
+ , basename = path.basename
2703
+ , join = path.join;
2704
+
2705
+ if (!this.filename)
2706
+ throw new Error('the "filename" option is required to extend templates');
2707
+
2708
+ var path = this.expect('extends').val.trim()
2709
+ , dir = dirname(this.filename);
2710
+
2711
+ var path = join(dir, path + '.jade')
2712
+ , str = fs.readFileSync(path, 'utf8')
2713
+ , parser = new Parser(str, path, this.options);
2714
+
2715
+ parser.blocks = this.blocks;
2716
+ parser.contexts = this.contexts;
2717
+ this.extending = parser;
2718
+
2719
+ // TODO: null node
2720
+ return new nodes.Literal('');
2721
+ },
2722
+
2723
+ /**
2724
+ * 'block' name block
2725
+ */
2726
+
2727
+ parseBlock: function(){
2728
+ var block = this.expect('block')
2729
+ , mode = block.mode
2730
+ , name = block.val.trim();
2731
+
2732
+ block = 'indent' == this.peek().type
2733
+ ? this.block()
2734
+ : new nodes.Block(new nodes.Literal(''));
2735
+
2736
+ var prev = this.blocks[name];
2737
+
2738
+ if (prev) {
2739
+ switch (prev.mode) {
2740
+ case 'append':
2741
+ block.nodes = block.nodes.concat(prev.nodes);
2742
+ prev = block;
2743
+ break;
2744
+ case 'prepend':
2745
+ block.nodes = prev.nodes.concat(block.nodes);
2746
+ prev = block;
2747
+ break;
2748
+ }
2749
+ }
2750
+
2751
+ block.mode = mode;
2752
+ return this.blocks[name] = prev || block;
2753
+ },
2754
+
2755
+ /**
2756
+ * include block?
2757
+ */
2758
+
2759
+ parseInclude: function(){
2760
+ var path = require('path')
2761
+ , fs = require('fs')
2762
+ , dirname = path.dirname
2763
+ , basename = path.basename
2764
+ , join = path.join;
2765
+
2766
+ var path = this.expect('include').val.trim()
2767
+ , dir = dirname(this.filename);
2768
+
2769
+ if (!this.filename)
2770
+ throw new Error('the "filename" option is required to use includes');
2771
+
2772
+ // no extension
2773
+ if (!~basename(path).indexOf('.')) {
2774
+ path += '.jade';
2775
+ }
2776
+
2777
+ // non-jade
2778
+ if ('.jade' != path.substr(-5)) {
2779
+ var path = join(dir, path)
2780
+ , str = fs.readFileSync(path, 'utf8');
2781
+ return new nodes.Literal(str);
2782
+ }
2783
+
2784
+ var path = join(dir, path)
2785
+ , str = fs.readFileSync(path, 'utf8')
2786
+ , parser = new Parser(str, path, this.options);
2787
+
2788
+ this.context(parser);
2789
+ var ast = parser.parse();
2790
+ this.context();
2791
+ ast.filename = path;
2792
+
2793
+ if ('indent' == this.peek().type) {
2794
+ ast.includeBlock().push(this.block());
2795
+ }
2796
+
2797
+ return ast;
2798
+ },
2799
+
2800
+ /**
2801
+ * mixin block
2802
+ */
2803
+
2804
+ parseMixin: function(){
2805
+ var tok = this.expect('mixin')
2806
+ , name = tok.val
2807
+ , args = tok.args;
2808
+ var block = 'indent' == this.peek().type
2809
+ ? this.block()
2810
+ : null;
2811
+ return new nodes.Mixin(name, args, block);
2812
+ },
2813
+
2814
+ /**
2815
+ * indent (text | newline)* outdent
2816
+ */
2817
+
2818
+ parseTextBlock: function(){
2819
+ var text = new nodes.Text;
2820
+ text.line = this.line();
2821
+ var spaces = this.expect('indent').val;
2822
+ if (null == this._spaces) this._spaces = spaces;
2823
+ var indent = Array(spaces - this._spaces + 1).join(' ');
2824
+ while ('outdent' != this.peek().type) {
2825
+ switch (this.peek().type) {
2826
+ case 'newline':
2827
+ text.push('\\n');
2828
+ this.advance();
2829
+ break;
2830
+ case 'indent':
2831
+ text.push('\\n');
2832
+ this.parseTextBlock().nodes.forEach(function(node){
2833
+ text.push(node);
2834
+ });
2835
+ text.push('\\n');
2836
+ break;
2837
+ default:
2838
+ text.push(indent + this.advance().val);
2839
+ }
2840
+ }
2841
+
2842
+ if (spaces == this._spaces) this._spaces = null;
2843
+ this.expect('outdent');
2844
+ return text;
2845
+ },
2846
+
2847
+ /**
2848
+ * indent expr* outdent
2849
+ */
2850
+
2851
+ block: function(){
2852
+ var block = new nodes.Block;
2853
+ block.line = this.line();
2854
+ this.expect('indent');
2855
+ while ('outdent' != this.peek().type) {
2856
+ if ('newline' == this.peek().type) {
2857
+ this.advance();
2858
+ } else {
2859
+ block.push(this.parseExpr());
2860
+ }
2861
+ }
2862
+ this.expect('outdent');
2863
+ return block;
2864
+ },
2865
+
2866
+ /**
2867
+ * tag (attrs | class | id)* (text | code | ':')? newline* block?
2868
+ */
2869
+
2870
+ parseTag: function(){
2871
+ // ast-filter look-ahead
2872
+ var i = 2;
2873
+ if ('attrs' == this.lookahead(i).type) ++i;
2874
+ if (':' == this.lookahead(i).type) {
2875
+ if ('indent' == this.lookahead(++i).type) {
2876
+ return this.parseASTFilter();
2877
+ }
2878
+ }
2879
+
2880
+ var name = this.advance().val
2881
+ , tag = new nodes.Tag(name)
2882
+ , dot;
2883
+
2884
+ tag.line = this.line();
2885
+
2886
+ // (attrs | class | id)*
2887
+ out:
2888
+ while (true) {
2889
+ switch (this.peek().type) {
2890
+ case 'id':
2891
+ case 'class':
2892
+ var tok = this.advance();
2893
+ tag.setAttribute(tok.type, "'" + tok.val + "'");
2894
+ continue;
2895
+ case 'attrs':
2896
+ var obj = this.advance().attrs
2897
+ , names = Object.keys(obj);
2898
+ for (var i = 0, len = names.length; i < len; ++i) {
2899
+ var name = names[i]
2900
+ , val = obj[name];
2901
+ tag.setAttribute(name, val);
2902
+ }
2903
+ continue;
2904
+ default:
2905
+ break out;
2906
+ }
2907
+ }
2908
+
2909
+ // check immediate '.'
2910
+ if ('.' == this.peek().val) {
2911
+ dot = tag.textOnly = true;
2912
+ this.advance();
2913
+ }
2914
+
2915
+ // (text | code | ':')?
2916
+ switch (this.peek().type) {
2917
+ case 'text':
2918
+ tag.text = this.parseText();
2919
+ break;
2920
+ case 'code':
2921
+ tag.code = this.parseCode();
2922
+ break;
2923
+ case ':':
2924
+ this.advance();
2925
+ tag.block = new nodes.Block;
2926
+ tag.block.push(this.parseTag());
2927
+ break;
2928
+ }
2929
+
2930
+ // newline*
2931
+ while ('newline' == this.peek().type) this.advance();
2932
+
2933
+ tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name);
2934
+
2935
+ // script special-case
2936
+ if ('script' == tag.name) {
2937
+ var type = tag.getAttribute('type');
2938
+ if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) {
2939
+ tag.textOnly = false;
2940
+ }
2941
+ }
2942
+
2943
+ // block?
2944
+ if ('indent' == this.peek().type) {
2945
+ if (tag.textOnly) {
2946
+ this.lexer.pipeless = true;
2947
+ tag.block = this.parseTextBlock();
2948
+ this.lexer.pipeless = false;
2949
+ } else {
2950
+ var block = this.block();
2951
+ if (tag.block) {
2952
+ for (var i = 0, len = block.nodes.length; i < len; ++i) {
2953
+ tag.block.push(block.nodes[i]);
2954
+ }
2955
+ } else {
2956
+ tag.block = block;
2957
+ }
2958
+ }
2959
+ }
2960
+
2961
+ return tag;
2962
+ }
2963
+ };
2964
+
2965
+ }); // module: parser.js
2966
+
2967
+ require.register("runtime.js", function(module, exports, require){
2968
+
2969
+ /*!
2970
+ * Jade - runtime
2971
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
2972
+ * MIT Licensed
2973
+ */
2974
+
2975
+ /**
2976
+ * Lame Array.isArray() polyfill for now.
2977
+ */
2978
+
2979
+ if (!Array.isArray) {
2980
+ Array.isArray = function(arr){
2981
+ return '[object Array]' == Object.prototype.toString.call(arr);
2982
+ };
2983
+ }
2984
+
2985
+ /**
2986
+ * Lame Object.keys() polyfill for now.
2987
+ */
2988
+
2989
+ if (!Object.keys) {
2990
+ Object.keys = function(obj){
2991
+ var arr = [];
2992
+ for (var key in obj) {
2993
+ if (obj.hasOwnProperty(key)) {
2994
+ arr.push(key);
2995
+ }
2996
+ }
2997
+ return arr;
2998
+ }
2999
+ }
3000
+
3001
+ /**
3002
+ * Render the given attributes object.
3003
+ *
3004
+ * @param {Object} obj
3005
+ * @return {String}
3006
+ * @api private
3007
+ */
3008
+
3009
+ exports.attrs = function attrs(obj){
3010
+ var buf = []
3011
+ , terse = obj.terse;
3012
+ delete obj.terse;
3013
+ var keys = Object.keys(obj)
3014
+ , len = keys.length;
3015
+ if (len) {
3016
+ buf.push('');
3017
+ for (var i = 0; i < len; ++i) {
3018
+ var key = keys[i]
3019
+ , val = obj[key];
3020
+ if ('boolean' == typeof val || null == val) {
3021
+ if (val) {
3022
+ terse
3023
+ ? buf.push(key)
3024
+ : buf.push(key + '="' + key + '"');
3025
+ }
3026
+ } else if ('class' == key && Array.isArray(val)) {
3027
+ buf.push(key + '="' + exports.escape(val.join(' ')) + '"');
3028
+ } else {
3029
+ buf.push(key + '="' + exports.escape(val) + '"');
3030
+ }
3031
+ }
3032
+ }
3033
+ return buf.join(' ');
3034
+ };
3035
+
3036
+ /**
3037
+ * Escape the given string of `html`.
3038
+ *
3039
+ * @param {String} html
3040
+ * @return {String}
3041
+ * @api private
3042
+ */
3043
+
3044
+ exports.escape = function escape(html){
3045
+ return String(html)
3046
+ .replace(/&(?!\w+;)/g, '&amp;')
3047
+ .replace(/</g, '&lt;')
3048
+ .replace(/>/g, '&gt;')
3049
+ .replace(/"/g, '&quot;');
3050
+ };
3051
+
3052
+ /**
3053
+ * Re-throw the given `err` in context to the
3054
+ * the jade in `filename` at the given `lineno`.
3055
+ *
3056
+ * @param {Error} err
3057
+ * @param {String} filename
3058
+ * @param {String} lineno
3059
+ * @api private
3060
+ */
3061
+
3062
+ exports.rethrow = function rethrow(err, filename, lineno){
3063
+ if (!filename) throw err;
3064
+
3065
+ // If we can't catch the context we still output line and file
3066
+ try {
3067
+ var context = 3
3068
+ , str = require('fs').readFileSync(filename, 'utf8')
3069
+ , lines = str.split('\n')
3070
+ , start = Math.max(lineno - context, 0)
3071
+ , end = Math.min(lines.length, lineno + context);
3072
+
3073
+ // Error context
3074
+ var context = lines.slice(start, end).map(function(line, i){
3075
+ var curr = i + start + 1;
3076
+ return (curr == lineno ? ' > ' : ' ')
3077
+ + curr
3078
+ + '| '
3079
+ + line;
3080
+ }).join('\n') + '\n\n';
3081
+ } catch(failure) {
3082
+ var context = '';
3083
+ }
3084
+
3085
+ // Alter exception message
3086
+ err.path = filename;
3087
+ err.message = (filename || 'Jade') + ':' + lineno
3088
+ + '\n' + context + err.message;
3089
+ throw err;
3090
+ };
3091
+
3092
+ }); // module: runtime.js
3093
+
3094
+ require.register("self-closing.js", function(module, exports, require){
3095
+
3096
+ /*!
3097
+ * Jade - self closing tags
3098
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
3099
+ * MIT Licensed
3100
+ */
3101
+
3102
+ module.exports = [
3103
+ 'meta'
3104
+ , 'img'
3105
+ , 'link'
3106
+ , 'input'
3107
+ , 'area'
3108
+ , 'base'
3109
+ , 'col'
3110
+ , 'br'
3111
+ , 'hr'
3112
+ ];
3113
+ }); // module: self-closing.js
3114
+
3115
+ require.register("utils.js", function(module, exports, require){
3116
+
3117
+ /*!
3118
+ * Jade - utils
3119
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
3120
+ * MIT Licensed
3121
+ */
3122
+
3123
+ /**
3124
+ * Convert interpolation in the given string to JavaScript.
3125
+ *
3126
+ * @param {String} str
3127
+ * @return {String}
3128
+ * @api private
3129
+ */
3130
+
3131
+ var interpolate = exports.interpolate = function(str){
3132
+ return str.replace(/(\\)?([#!]){(.*?)}/g, function(str, escape, flag, code){
3133
+ return escape
3134
+ ? str
3135
+ : "' + "
3136
+ + ('!' == flag ? '' : 'escape')
3137
+ + "((interp = " + code.replace(/\\'/g, "'")
3138
+ + ") == null ? '' : interp) + '";
3139
+ });
3140
+ };
3141
+
3142
+ /**
3143
+ * Escape single quotes in `str`.
3144
+ *
3145
+ * @param {String} str
3146
+ * @return {String}
3147
+ * @api private
3148
+ */
3149
+
3150
+ var escape = exports.escape = function(str) {
3151
+ return str.replace(/'/g, "\\'");
3152
+ };
3153
+
3154
+ /**
3155
+ * Interpolate, and escape the given `str`.
3156
+ *
3157
+ * @param {String} str
3158
+ * @return {String}
3159
+ * @api private
3160
+ */
3161
+
3162
+ exports.text = function(str){
3163
+ return interpolate(escape(str));
3164
+ };
3165
+ }); // module: utils.js
3166
+
3167
+ window.jade = require("jade");
3168
+ })();