canvas-lms/packages/ember/ember-template-compiler.js

324 lines
10 KiB
JavaScript

(function() {
var Ember = { assert: function() {}, FEATURES: { isEnabled: function() {} } };
/*!
* @overview Ember - JavaScript Application Framework
* @copyright Copyright 2011-2014 Tilde Inc. and contributors
* Portions Copyright 2006-2011 Strobe Inc.
* Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/LICENSE
* @version 1.4.0
*/
(function() {
/**
@module ember
@submodule ember-handlebars-compiler
*/
// Eliminate dependency on any Ember to simplify precompilation workflow
var objectCreate = Object.create || function(parent) {
function F() {}
F.prototype = parent;
return new F();
};
var Handlebars = require('handlebars')
Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " +
"a SCRIPT tag in the HTML HEAD linking to the Handlebars file " +
"before you link to Ember.", Handlebars);
Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " +
"COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION +
" - Please note: Builds of master may have other COMPILER_REVISION values.",
Handlebars.COMPILER_REVISION === 4);
/**
Prepares the Handlebars templating library for use inside Ember's view
system.
The `Ember.Handlebars` object is the standard Handlebars library, extended to
use Ember's `get()` method instead of direct property access, which allows
computed properties to be used inside templates.
To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`.
This will return a function that can be used by `Ember.View` for rendering.
@class Handlebars
@namespace Ember
*/
Ember.Handlebars = objectCreate(Handlebars);
/**
Register a bound helper or custom view helper.
## Simple bound helper example
```javascript
Ember.Handlebars.helper('capitalize', function(value) {
return value.toUpperCase();
});
```
The above bound helper can be used inside of templates as follows:
```handlebars
{{capitalize name}}
```
In this case, when the `name` property of the template's context changes,
the rendered value of the helper will update to reflect this change.
For more examples of bound helpers, see documentation for
`Ember.Handlebars.registerBoundHelper`.
## Custom view helper example
Assuming a view subclass named `App.CalendarView` were defined, a helper
for rendering instances of this view could be registered as follows:
```javascript
Ember.Handlebars.helper('calendar', App.CalendarView):
```
The above bound helper can be used inside of templates as follows:
```handlebars
{{calendar}}
```
Which is functionally equivalent to:
```handlebars
{{view App.CalendarView}}
```
Options in the helper will be passed to the view in exactly the same
manner as with the `view` helper.
@method helper
@for Ember.Handlebars
@param {String} name
@param {Function|Ember.View} function or view class constructor
@param {String} dependentKeys*
*/
Ember.Handlebars.helper = function(name, value) {
Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/));
if (Ember.View.detect(value)) {
Ember.Handlebars.registerHelper(name, Ember.Handlebars.makeViewHelper(value));
} else {
Ember.Handlebars.registerBoundHelper.apply(null, arguments);
}
};
/**
Returns a helper function that renders the provided ViewClass.
Used internally by Ember.Handlebars.helper and other methods
involving helper/component registration.
@private
@method helper
@for Ember.Handlebars
@param {Function} ViewClass view class constructor
*/
Ember.Handlebars.makeViewHelper = function(ViewClass) {
return function(options) {
Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2);
return Ember.Handlebars.helpers.view.call(this, ViewClass, options);
};
};
/**
@class helpers
@namespace Ember.Handlebars
*/
Ember.Handlebars.helpers = objectCreate(Handlebars.helpers);
/**
Override the the opcode compiler and JavaScript compiler for Handlebars.
@class Compiler
@namespace Ember.Handlebars
@private
@constructor
*/
Ember.Handlebars.Compiler = function() {};
// Handlebars.Compiler doesn't exist in runtime-only
if (Handlebars.Compiler) {
Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
}
Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler;
/**
@class JavaScriptCompiler
@namespace Ember.Handlebars
@private
@constructor
*/
Ember.Handlebars.JavaScriptCompiler = function() {};
// Handlebars.JavaScriptCompiler doesn't exist in runtime-only
if (Handlebars.JavaScriptCompiler) {
Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler;
}
Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars";
Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() {
return "''";
};
/**
Override the default buffer for Ember Handlebars. By default, Handlebars
creates an empty String at the beginning of each invocation and appends to
it. Ember's Handlebars overrides this to append to a single shared buffer.
@private
@method appendToBuffer
@param string {String}
*/
Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
return "data.buffer.push("+string+");";
};
// Hacks ahead:
// Handlebars presently has a bug where the `blockHelperMissing` hook
// doesn't get passed the name of the missing helper name, but rather
// gets passed the value of that missing helper evaluated on the current
// context, which is most likely `undefined` and totally useless.
//
// So we alter the compiled template function to pass the name of the helper
// instead, as expected.
//
// This can go away once the following is closed:
// https://github.com/wycats/handlebars.js/issues/634
var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/,
BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/,
INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/;
Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) {
var helperInvocation = source[source.length - 1],
helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1],
matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation);
source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3];
}
var stringifyBlockHelperMissing = Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation;
var originalBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.blockValue;
Ember.Handlebars.JavaScriptCompiler.prototype.blockValue = function() {
originalBlockValue.apply(this, arguments);
stringifyBlockHelperMissing(this.source);
};
var originalAmbiguousBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue;
Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() {
originalAmbiguousBlockValue.apply(this, arguments);
stringifyBlockHelperMissing(this.source);
};
var prefix = "ember" + (+new Date()), incr = 1;
/**
Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that
all simple mustaches in Ember's Handlebars will also set up an observer to
keep the DOM up to date when the underlying property changes.
@private
@method mustache
@for Ember.Handlebars.Compiler
@param mustache
*/
Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
if (mustache.isHelper && mustache.id.string === 'control') {
mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
} else if (mustache.params.length || mustache.hash) {
// no changes required
} else {
var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);
// Update the mustache node to include a hash value indicating whether the original node
// was escaped. This will allow us to properly escape values when the underlying value
// changes and we need to re-render the value.
if (!mustache.escaped) {
mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
}
mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
}
return Handlebars.Compiler.prototype.mustache.call(this, mustache);
};
/**
Used for precompilation of Ember Handlebars templates. This will not be used
during normal app execution.
@method precompile
@for Ember.Handlebars
@static
@param {String} string The template to precompile
*/
Ember.Handlebars.precompile = function(string) {
var ast = Handlebars.parse(string);
var options = {
knownHelpers: {
action: true,
unbound: true,
'bind-attr': true,
template: true,
view: true,
_triageMustache: true
},
data: true,
stringParams: true
};
var environment = new Ember.Handlebars.Compiler().compile(ast, options);
return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
};
// We don't support this for Handlebars runtime-only
if (Handlebars.compile) {
/**
The entry point for Ember Handlebars. This replaces the default
`Handlebars.compile` and turns on template-local data and String
parameters.
@method compile
@for Ember.Handlebars
@static
@param {String} string The template to compile
@return {Function}
*/
Ember.Handlebars.compile = function(string) {
var ast = Handlebars.parse(string);
var options = { data: true, stringParams: true };
var environment = new Ember.Handlebars.Compiler().compile(ast, options);
var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
var template = Ember.Handlebars.template(templateSpec);
template.isMethod = false; //Make sure we don't wrap templates with ._super
return template;
};
}
})();
exports.precompile = Ember.Handlebars.precompile;
exports.EmberHandlebars = Ember.Handlebars;
})();