1 sc_require('handlebars');
  2 
  3 /**
  4   Prepares the Handlebars templating library for use inside SproutCore's view
  5   system.
  6 
  7   The SC.Handlebars object is the standard Handlebars library, extended to use
  8   SproutCore's get() method instead of direct property access, which allows
  9   computed properties to be used inside templates.
 10 
 11   To use SC.Handlebars, call SC.Handlebars.compile().  This will return a
 12   function that you can call multiple times, with a context object as the first
 13   parameter:
 14 
 15       var template = SC.Handlebars.compile("my {{cool}} template");
 16       var result = template({
 17         cool: "awesome"
 18       });
 19 
 20       console.log(result); // prints "my awesome template"
 21 
 22   Note that you won't usually need to use SC.Handlebars yourself. Instead, use
 23   SC.TemplateView, which takes care of integration into the view layer for you.
 24 */
 25 
 26 SC.Handlebars = {};
 27 
 28 SC.Handlebars.Compiler = function() {};
 29 SC.Handlebars.Compiler.prototype = SC.beget(Handlebars.Compiler.prototype);
 30 SC.Handlebars.Compiler.prototype.compiler = SC.Handlebars.Compiler;
 31 
 32 SC.Handlebars.JavaScriptCompiler = function() {};
 33 SC.Handlebars.JavaScriptCompiler.prototype = SC.beget(Handlebars.JavaScriptCompiler.prototype);
 34 SC.Handlebars.JavaScriptCompiler.prototype.compiler = SC.Handlebars.JavaScriptCompiler;
 35 
 36 SC.Handlebars.JavaScriptCompiler.prototype.nameLookup = function(parent, name, type) {
 37   if (type === 'context') {
 38     return "SC.get(" + parent + ", " + this.quotedString(name) + ")";
 39   } else {
 40     return Handlebars.JavaScriptCompiler.prototype.nameLookup.call(this, parent, name, type);
 41   }
 42 };
 43 
 44 /**
 45   Rewrite simple mustaches from {{foo}} to {{bind "foo"}}. This means that all simple
 46   mustaches in SproutCore's Handlebars will also set up an observer to keep the DOM
 47   up to date when the underlying property changes.
 48 
 49   @private
 50 */
 51 SC.Handlebars.Compiler.prototype.mustache = function(mustache) {
 52   if (mustache.params.length || mustache.hash) {
 53     return Handlebars.Compiler.prototype.mustache.call(this, mustache);
 54   } else {
 55     var id = new Handlebars.AST.IdNode(['bind']);
 56 
 57     // Update the mustache node to include a hash value indicating whether the original node
 58     // was escaped. This will allow us to properly escape values when the underlying value
 59     // changes and we need to re-render the value.
 60     if(mustache.escaped) {
 61       mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
 62       mustache.hash.pairs.push(["escaped", new Handlebars.AST.StringNode("true")]);
 63     }
 64     mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
 65     return Handlebars.Compiler.prototype.mustache.call(this, mustache);
 66   }
 67 };
 68 
 69 /**
 70   The entry point for SproutCore Handlebars. This replaces the default Handlebars.compile and turns on
 71   template-local data and String parameters.
 72 
 73   @param {String} string The template to compile
 74 */
 75 SC.Handlebars.compile = function(string) {
 76   var ast = Handlebars.parse(string);
 77   var options = { data: true, stringParams: true };
 78   var environment = new SC.Handlebars.Compiler().compile(ast, options);
 79   var templateSpec = new SC.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
 80 
 81   return Handlebars.template(templateSpec);
 82 };
 83 
 84 /**
 85   Registers a helper in Handlebars that will be called if no property with the
 86   given name can be found on the current context object, and no helper with
 87   that name is registered.
 88 
 89   This throws an exception with a more helpful error message so the user can
 90   track down where the problem is happening.
 91 */
 92 Handlebars.registerHelper('helperMissing', function(path, options) {
 93   var error;
 94 
 95   error = "%@ Handlebars error: Could not find property '%@' on object %@.";
 96   throw new Error(error.fmt(options.data.view, path, this));
 97 });
 98