dust-rails 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/dust-rails.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.email = ["thefron@wafflestudio.com"]
10
10
  s.homepage = "https://github.com/thefron/dust-rails"
11
11
  s.summary = %q{Use dust.js with rails}
12
- s.description = %q{This gem makes you dust.js easy to use with rails.}
12
+ s.description = %q{This gem makes it easy to use dust.js with rails.}
13
13
 
14
14
  s.rubyforge_project = "dust-rails"
15
15
 
@@ -6,7 +6,7 @@ module Dust
6
6
 
7
7
  module Source
8
8
  def self.path
9
- @path ||= File.expand_path('../../../../vendor/assets/javascripts/dust-full-for-compile.js', __FILE__)
9
+ @path ||= File.expand_path('../../../../vendor/dustjs/lib/dust.js', __FILE__)
10
10
  end
11
11
 
12
12
  def self.contents
@@ -1,5 +1,5 @@
1
1
  module Dust
2
2
  module Rails
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  //
2
- // Dust - Asynchronous Templating v0.3.0
2
+ // Dust - Asynchronous Templating v1.0.0
3
3
  // http://akdubya.github.com/dustjs
4
4
  //
5
5
  // Copyright (c) 2010, Aleksander Williams
@@ -8,6 +8,12 @@
8
8
 
9
9
  var dust = {};
10
10
 
11
+ function getGlobal(){
12
+ return (function(){
13
+ return this.dust;
14
+ }).call(null);
15
+ }
16
+
11
17
  (function(dust) {
12
18
 
13
19
  dust.cache = {};
@@ -75,9 +81,15 @@ if (Array.isArray) {
75
81
  };
76
82
  }
77
83
 
78
- dust.nextTick = function(callback) {
79
- setTimeout(callback, 0);
80
- }
84
+ dust.nextTick = (function() {
85
+ if (typeof process !== "undefined") {
86
+ return process.nextTick;
87
+ } else {
88
+ return function(callback) {
89
+ setTimeout(callback,0);
90
+ }
91
+ }
92
+ } )();
81
93
 
82
94
  dust.isEmpty = function(value) {
83
95
  if (dust.isArray(value) && !value.length) return true;
@@ -106,8 +118,10 @@ dust.filters = {
106
118
  h: function(value) { return dust.escapeHtml(value); },
107
119
  j: function(value) { return dust.escapeJs(value); },
108
120
  u: encodeURI,
109
- uc: encodeURIComponent
110
- }
121
+ uc: encodeURIComponent,
122
+ js: function(value) { if (!JSON) { return value; } return JSON.stringify(value); },
123
+ jp: function(value) { if (!JSON) { return value; } return JSON.parse(value); }
124
+ };
111
125
 
112
126
  function Context(stack, global, blocks) {
113
127
  this.stack = stack;
@@ -117,14 +131,14 @@ function Context(stack, global, blocks) {
117
131
 
118
132
  dust.makeBase = function(global) {
119
133
  return new Context(new Stack(), global);
120
- }
134
+ };
121
135
 
122
136
  Context.wrap = function(context) {
123
137
  if (context instanceof Context) {
124
138
  return context;
125
139
  }
126
140
  return new Context(new Stack(context));
127
- }
141
+ };
128
142
 
129
143
  Context.prototype.get = function(key) {
130
144
  var ctx = this.stack, value;
@@ -146,7 +160,6 @@ Context.prototype.getPath = function(cur, down) {
146
160
  len = down.length;
147
161
 
148
162
  if (cur && len === 0) return ctx.head;
149
- if (!ctx.isObject) return undefined;
150
163
  ctx = ctx.head;
151
164
  var i = 0;
152
165
  while(ctx && i < len) {
@@ -168,7 +181,12 @@ Context.prototype.current = function() {
168
181
  return this.stack.head;
169
182
  };
170
183
 
171
- Context.prototype.getBlock = function(key) {
184
+ Context.prototype.getBlock = function(key, chk, ctx) {
185
+ if (typeof key === "function") {
186
+ key = key(chk, ctx).data;
187
+ chk.data = "";
188
+ }
189
+
172
190
  var blocks = this.blocks;
173
191
 
174
192
  if (!blocks) return;
@@ -177,7 +195,7 @@ Context.prototype.getBlock = function(key) {
177
195
  fn = blocks[len][key];
178
196
  if (fn) return fn;
179
197
  }
180
- }
198
+ };
181
199
 
182
200
  Context.prototype.shiftBlocks = function(locals) {
183
201
  var blocks = this.blocks;
@@ -191,7 +209,7 @@ Context.prototype.shiftBlocks = function(locals) {
191
209
  return new Context(this.stack, this.global, newBlocks);
192
210
  }
193
211
  return this;
194
- }
212
+ };
195
213
 
196
214
  function Stack(head, tail, idx, len) {
197
215
  this.tail = tail;
@@ -224,7 +242,7 @@ Stub.prototype.flush = function() {
224
242
  this.head = chunk;
225
243
  }
226
244
  this.callback(null, this.out);
227
- }
245
+ };
228
246
 
229
247
  function Stream() {
230
248
  this.head = new Chunk(this);
@@ -247,23 +265,46 @@ Stream.prototype.flush = function() {
247
265
  this.head = chunk;
248
266
  }
249
267
  this.emit('end');
250
- }
268
+ };
251
269
 
252
270
  Stream.prototype.emit = function(type, data) {
253
- var events = this.events;
254
-
255
- if (events && events[type]) {
256
- events[type](data);
271
+ if (!this.events) return false;
272
+ var handler = this.events[type];
273
+ if (!handler) return false;
274
+ if (typeof handler == 'function') {
275
+ handler(data);
276
+ } else {
277
+ var listeners = handler.slice(0);
278
+ for (var i = 0, l = listeners.length; i < l; i++) {
279
+ listeners[i](data);
280
+ }
257
281
  }
258
- }
282
+ };
259
283
 
260
284
  Stream.prototype.on = function(type, callback) {
261
285
  if (!this.events) {
262
286
  this.events = {};
263
287
  }
264
- this.events[type] = callback;
288
+ if (!this.events[type]) {
289
+ this.events[type] = callback;
290
+ } else if(typeof this.events[type] === 'function') {
291
+ this.events[type] = [this.events[type], callback];
292
+ } else {
293
+ this.events[type].push(callback);
294
+ }
265
295
  return this;
266
- }
296
+ };
297
+
298
+ Stream.prototype.pipe = function(stream) {
299
+ this.on("data", function(data) {
300
+ stream.write(data, "utf8");
301
+ }).on("end", function() {
302
+ stream.end();
303
+ }).on("error", function(err) {
304
+ stream.error(err);
305
+ });
306
+ return this;
307
+ };
267
308
 
268
309
  function Chunk(root, next, taps) {
269
310
  this.root = root;
@@ -281,7 +322,7 @@ Chunk.prototype.write = function(data) {
281
322
  }
282
323
  this.data += data;
283
324
  return this;
284
- }
325
+ };
285
326
 
286
327
  Chunk.prototype.end = function(data) {
287
328
  if (data) {
@@ -290,7 +331,7 @@ Chunk.prototype.end = function(data) {
290
331
  this.flushable = true;
291
332
  this.root.flush();
292
333
  return this;
293
- }
334
+ };
294
335
 
295
336
  Chunk.prototype.map = function(callback) {
296
337
  var cursor = new Chunk(this.root, this.next, this.taps),
@@ -300,7 +341,7 @@ Chunk.prototype.map = function(callback) {
300
341
  this.flushable = true;
301
342
  callback(branch);
302
343
  return cursor;
303
- }
344
+ };
304
345
 
305
346
  Chunk.prototype.tap = function(tap) {
306
347
  var taps = this.taps;
@@ -311,20 +352,22 @@ Chunk.prototype.tap = function(tap) {
311
352
  this.taps = new Tap(tap);
312
353
  }
313
354
  return this;
314
- }
355
+ };
315
356
 
316
357
  Chunk.prototype.untap = function() {
317
358
  this.taps = this.taps.tail;
318
359
  return this;
319
- }
360
+ };
320
361
 
321
362
  Chunk.prototype.render = function(body, context) {
322
363
  return body(this, context);
323
- }
364
+ };
324
365
 
325
366
  Chunk.prototype.reference = function(elem, context, auto, filters) {
326
367
  if (typeof elem === "function") {
327
- elem = elem(this, context, null, {auto: auto, filters: filters});
368
+ elem.isReference = true;
369
+ // Changed the function calling to use apply with the current context to make sure that "this" is wat we expect it to be inside the function
370
+ elem = elem.apply(context.current(), [this, context, null, {auto: auto, filters: filters}]);
328
371
  if (elem instanceof Chunk) {
329
372
  return elem;
330
373
  }
@@ -338,7 +381,7 @@ Chunk.prototype.reference = function(elem, context, auto, filters) {
338
381
 
339
382
  Chunk.prototype.section = function(elem, context, bodies, params) {
340
383
  if (typeof elem === "function") {
341
- elem = elem(this, context, bodies, params);
384
+ elem = elem.apply(context.current(), [this, context, bodies, params]);
342
385
  if (elem instanceof Chunk) {
343
386
  return elem;
344
387
  }
@@ -354,15 +397,26 @@ Chunk.prototype.section = function(elem, context, bodies, params) {
354
397
  if (dust.isArray(elem)) {
355
398
  if (body) {
356
399
  var len = elem.length, chunk = this;
400
+ context.stack.head['$len'] = len;
357
401
  for (var i=0; i<len; i++) {
402
+ context.stack.head['$idx'] = i;
358
403
  chunk = body(chunk, context.push(elem[i], i, len));
359
404
  }
405
+ context.stack.head['$idx'] = undefined;
406
+ context.stack.head['$len'] = undefined;
360
407
  return chunk;
361
408
  }
362
409
  } else if (elem === true) {
363
410
  if (body) return body(this, context);
364
411
  } else if (elem || elem === 0) {
365
- if (body) return body(this, context.push(elem));
412
+ if (body) {
413
+ context.stack.head['$idx'] = 0;
414
+ context.stack.head['$len'] = 1;
415
+ chunk = body(this, context.push(elem));
416
+ context.stack.head['$idx'] = undefined;
417
+ context.stack.head['$len'] = undefined;
418
+ return chunk;
419
+ }
366
420
  } else if (skip) {
367
421
  return skip(this, context);
368
422
  }
@@ -379,7 +433,7 @@ Chunk.prototype.exists = function(elem, context, bodies) {
379
433
  return skip(this, context);
380
434
  }
381
435
  return this;
382
- }
436
+ };
383
437
 
384
438
  Chunk.prototype.notexists = function(elem, context, bodies) {
385
439
  var body = bodies.block,
@@ -391,7 +445,7 @@ Chunk.prototype.notexists = function(elem, context, bodies) {
391
445
  return skip(this, context);
392
446
  }
393
447
  return this;
394
- }
448
+ };
395
449
 
396
450
  Chunk.prototype.block = function(elem, context, bodies) {
397
451
  var body = bodies.block;
@@ -406,7 +460,17 @@ Chunk.prototype.block = function(elem, context, bodies) {
406
460
  return this;
407
461
  };
408
462
 
409
- Chunk.prototype.partial = function(elem, context) {
463
+ Chunk.prototype.partial = function(elem, context, params) {
464
+ var ctx = context.stack, tempHead = ctx.head;
465
+ if (params){
466
+ //put the params context second to match what section does. {.} matches the current context without parameters
467
+ //remove head
468
+ context = context.rebase(ctx.tail);
469
+ //put params on
470
+ context = context.push(params);
471
+ //reattach the head
472
+ context = context.push(tempHead);
473
+ }
410
474
  if (typeof elem === "function") {
411
475
  return this.capture(elem, context, function(name, chunk) {
412
476
  dust.load(name, chunk, context).end();
@@ -438,19 +502,6 @@ Chunk.prototype.setError = function(err) {
438
502
  return this;
439
503
  };
440
504
 
441
- dust.helpers = {
442
- sep: function(chunk, context, bodies) {
443
- if (context.stack.index === context.stack.of - 1) {
444
- return chunk;
445
- }
446
- return bodies.block(chunk, context);
447
- },
448
-
449
- idx: function(chunk, context, bodies) {
450
- return bodies.block(chunk, context.push(context.stack.index));
451
- }
452
- }
453
-
454
505
  function Tap(head, tail) {
455
506
  this.head = head;
456
507
  this.tail = tail;
@@ -470,18 +521,19 @@ Tap.prototype.go = function(value) {
470
521
  return value;
471
522
  };
472
523
 
473
- var HCHARS = new RegExp(/[&<>\"]/),
524
+ var HCHARS = new RegExp(/[&<>\"\']/),
474
525
  AMP = /&/g,
475
526
  LT = /</g,
476
527
  GT = />/g,
477
- QUOT = /\"/g;
528
+ QUOT = /\"/g,
529
+ SQUOT = /\'/g;
478
530
 
479
531
  dust.escapeHtml = function(s) {
480
532
  if (typeof s === "string") {
481
533
  if (!HCHARS.test(s)) {
482
534
  return s;
483
535
  }
484
- return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;');
536
+ return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;').replace(SQUOT, '&#39;');
485
537
  }
486
538
  return s;
487
539
  };
@@ -515,8 +567,201 @@ dust.escapeJs = function(s) {
515
567
  })(dust);
516
568
 
517
569
  if (typeof exports !== "undefined") {
570
+ //TODO: Remove the helpers from dust core in the next release.
571
+ dust.helpers = require("./dust-helpers").helpers;
518
572
  if (typeof process !== "undefined") {
519
573
  require('./server')(dust);
520
574
  }
521
575
  module.exports = dust;
522
576
  }
577
+ (function(dust){
578
+
579
+ /* make a safe version of console if it is not available
580
+ * currently supporting:
581
+ * _console.log
582
+ * */
583
+ var _console = (typeof console !== 'undefined')? console: {
584
+ log: function(){
585
+ /* a noop*/
586
+ }
587
+ };
588
+
589
+ function isSelect(context) {
590
+ var value = context.current();
591
+ return typeof value === "object" && value.isSelect === true;
592
+ }
593
+
594
+ function filter(chunk, context, bodies, params, filter) {
595
+ var params = params || {},
596
+ actual,
597
+ expected;
598
+ if (params.key) {
599
+ actual = helpers.tap(params.key, chunk, context);
600
+ } else if (isSelect(context)) {
601
+ actual = context.current().selectKey;
602
+ if (context.current().isResolved) {
603
+ filter = function() { return false; };
604
+ }
605
+ } else {
606
+ throw "No key specified for filter and no key found in context from select statement";
607
+ }
608
+ expected = helpers.tap(params.value, chunk, context);
609
+ if (filter(expected, coerce(actual, params.type, context))) {
610
+ if (isSelect(context)) {
611
+ context.current().isResolved = true;
612
+ }
613
+ return chunk.render(bodies.block, context);
614
+ } else if (bodies['else']) {
615
+ return chunk.render(bodies['else'], context);
616
+ }
617
+
618
+ return chunk.write('');
619
+ }
620
+
621
+ function coerce (value, type, context) {
622
+ if (value) {
623
+ switch (type || typeof(value)) {
624
+ case 'number': return +value;
625
+ case 'string': return String(value);
626
+ case 'boolean': return Boolean(value);
627
+ case 'date': return new Date(value);
628
+ case 'context': return context.get(value);
629
+ }
630
+ }
631
+
632
+ return value;
633
+ }
634
+
635
+ var helpers = {
636
+
637
+ sep: function(chunk, context, bodies) {
638
+ if (context.stack.index === context.stack.of - 1) {
639
+ return chunk;
640
+ }
641
+ return bodies.block(chunk, context);
642
+ },
643
+
644
+ idx: function(chunk, context, bodies) {
645
+ return bodies.block(chunk, context.push(context.stack.index));
646
+ },
647
+
648
+ contextDump: function(chunk, context, bodies) {
649
+ _console.log(JSON.stringify(context.stack));
650
+ return chunk;
651
+ },
652
+
653
+ // Utility helping to resolve dust references in the given chunk
654
+ tap: function( input, chunk, context ){
655
+ // return given input if there is no dust reference to resolve
656
+ var output = input;
657
+ // dust compiles a string to function, if there are references
658
+ if( typeof input === "function"){
659
+ if( ( typeof input.isReference !== "undefined" ) && ( input.isReference === true ) ){ // just a plain function, not a dust `body` function
660
+ output = input();
661
+ } else {
662
+ output = '';
663
+ chunk.tap(function(data){
664
+ output += data;
665
+ return '';
666
+ }).render(input, context).untap();
667
+ if( output === '' ){
668
+ output = false;
669
+ }
670
+ }
671
+ }
672
+ return output;
673
+ },
674
+
675
+ /**
676
+ if helper
677
+ @param cond, either a string literal value or a dust reference
678
+ a string literal value, is enclosed in double quotes, e.g. cond="2>3"
679
+ a dust reference is also enclosed in double quotes, e.g. cond="'{val}'' > 3"
680
+ cond argument should evaluate to a valid javascript expression
681
+ **/
682
+
683
+ "if": function( chunk, context, bodies, params ){
684
+ if( params && params.cond ){
685
+ var cond = params.cond;
686
+ cond = this.tap(cond, chunk, context);
687
+ // eval expressions with given dust references
688
+ if( eval( cond ) ){
689
+ return chunk.render( bodies.block, context );
690
+ }
691
+ if( bodies['else'] ){
692
+ return chunk.render( bodies['else'], context );
693
+ }
694
+ }
695
+ // no condition
696
+ else {
697
+ _console.log( "No condition given in the if helper!" );
698
+ }
699
+ return chunk;
700
+ },
701
+
702
+ /**
703
+ select/eq/lt/lte/gt/gte/default helper
704
+ @param key, either a string literal value or a dust reference
705
+ a string literal value, is enclosed in double quotes, e.g. key="foo"
706
+ a dust reference may or may not be enclosed in double quotes, e.g. key="{val}" and key=val are both valid
707
+ @param type (optiona), supported types are number, boolean, string, date, context, defaults to string
708
+ **/
709
+ select: function(chunk, context, bodies, params) {
710
+ if( params && params.key){
711
+ // returns given input as output, if the input is not a dust reference, else does a context lookup
712
+ var key = this.tap(params.key, chunk, context);
713
+ return chunk.render(bodies.block, context.push({ isSelect: true, isResolved: false, selectKey: key }));
714
+ }
715
+ // no key
716
+ else {
717
+ _console.log( "No key given in the select helper!" );
718
+ }
719
+ return chunk;
720
+ },
721
+
722
+ eq: function(chunk, context, bodies, params) {
723
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual === expected; });
724
+ },
725
+
726
+ lt: function(chunk, context, bodies, params) {
727
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual < expected; });
728
+ },
729
+
730
+ lte: function(chunk, context, bodies, params) {
731
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual <= expected; });
732
+ },
733
+
734
+ gt: function(chunk, context, bodies, params) {
735
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual > expected; });
736
+ },
737
+
738
+ gte: function(chunk, context, bodies, params) {
739
+ return filter(chunk, context, bodies, params, function(expected, actual) { return actual >= expected; });
740
+ },
741
+
742
+ "default": function(chunk, context, bodies, params) {
743
+ return filter(chunk, context, bodies, params, function(expected, actual) { return true; });
744
+ },
745
+ size: function( chunk, context, bodies, params ) {
746
+ var subject = params.subject;
747
+ var value = 0;
748
+ if (!subject) { //undefined, "", 0
749
+ value = 0;
750
+ } else if(dust.isArray(subject)) { //array
751
+ value = subject.length;
752
+ } else if (!isNaN(subject)) { //numeric values
753
+ value = subject;
754
+ } else if (Object(subject) === subject) { //object test
755
+ var nr = 0;
756
+ for(var k in subject) if(Object.hasOwnProperty.call(subject,k)) nr++;
757
+ value = nr;
758
+ } else {
759
+ value = (subject + '').length; //any other value (strings etc.)
760
+ }
761
+ return chunk.write(value);
762
+ }
763
+ };
764
+
765
+ dust.helpers = helpers;
766
+
767
+ })(typeof exports !== 'undefined' ? exports : getGlobal());