Path: blob/trunk/third_party/closure/goog/soy/renderer.js
2868 views
// Copyright 2010 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Provides a soy renderer that allows registration of16* injected data ("globals") that will be passed into the rendered17* templates.18*19* There is also an interface {@link goog.soy.InjectedDataSupplier} that20* user should implement to provide the injected data for a specific21* application. The injected data format is a JavaScript object:22* <pre>23* {'dataKey': 'value', 'otherDataKey': 'otherValue'}24* </pre>25*26* The injected data can then be referred to in any soy templates as27* part of a magic "ij" parameter. For example, {@code $ij.dataKey}28* will evaluate to 'value' with the above injected data.29*30* @author [email protected] (Henry Wong)31* @author [email protected] (Chris Henry)32*/3334goog.provide('goog.soy.InjectedDataSupplier');35goog.provide('goog.soy.Renderer');3637goog.require('goog.asserts');38goog.require('goog.dom');39goog.require('goog.html.uncheckedconversions');40goog.require('goog.soy');41goog.require('goog.soy.data.SanitizedContent');42goog.require('goog.soy.data.SanitizedContentKind');43goog.require('goog.string.Const');44454647/**48* Creates a new soy renderer. Note that the renderer will only be49* guaranteed to work correctly within the document scope provided in50* the DOM helper.51*52* @param {goog.soy.InjectedDataSupplier=} opt_injectedDataSupplier A supplier53* that provides an injected data.54* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper;55* defaults to that provided by {@code goog.dom.getDomHelper()}.56* @constructor57*/58goog.soy.Renderer = function(opt_injectedDataSupplier, opt_domHelper) {59/**60* @type {goog.dom.DomHelper}61* @private62*/63this.dom_ = opt_domHelper || goog.dom.getDomHelper();6465/**66* @type {goog.soy.InjectedDataSupplier}67* @private68*/69this.supplier_ = opt_injectedDataSupplier || null;7071/**72* Map from template name to the data used to render that template.73* @type {!goog.soy.Renderer.SavedTemplateRender}74* @private75*/76this.savedTemplateRenders_ = [];77};787980/**81* @typedef {Array<{template: string, data: Object, ijData: Object}>}82*/83goog.soy.Renderer.SavedTemplateRender;848586/**87* Renders a Soy template into a single node or a document fragment.88* Delegates to {@code goog.soy.renderAsFragment}.89*90* @param {?function(ARG_TYPES, Object<string, *>=):*|91* ?function(ARG_TYPES, null=, Object<string, *>=):*} template92* The Soy template defining the element's content.93* @param {ARG_TYPES=} opt_templateData The data for the template.94* @return {!Node} The resulting node or document fragment.95* @template ARG_TYPES96*/97goog.soy.Renderer.prototype.renderAsFragment = function(98template, opt_templateData) {99this.saveTemplateRender_(template, opt_templateData);100var node = goog.soy.renderAsFragment(101template, opt_templateData, this.getInjectedData_(), this.dom_);102this.handleRender(node);103return node;104};105106107/**108* Renders a Soy template into a single node. If the rendered HTML109* string represents a single node, then that node is returned.110* Otherwise, a DIV element is returned containing the rendered nodes.111* Delegates to {@code goog.soy.renderAsElement}.112*113* @param {?function(ARG_TYPES, Object<string, *>=):*|114* ?function(ARG_TYPES, null=, Object<string, *>=):*} template115* The Soy template defining the element's content.116* @param {ARG_TYPES=} opt_templateData The data for the template.117* @return {!Element} Rendered template contents, wrapped in a parent DIV118* element if necessary.119* @template ARG_TYPES120*/121goog.soy.Renderer.prototype.renderAsElement = function(122template, opt_templateData) {123this.saveTemplateRender_(template, opt_templateData);124var element = goog.soy.renderAsElement(125template, opt_templateData, this.getInjectedData_(), this.dom_);126this.handleRender(element);127return element;128};129130131/**132* Renders a Soy template and then set the output string as the133* innerHTML of the given element. Delegates to {@code goog.soy.renderElement}.134*135* @param {Element} element The element whose content we are rendering.136* @param {?function(ARG_TYPES, Object<string, *>=):*|137* ?function(ARG_TYPES, null=, Object<string, *>=):*} template138* The Soy template defining the element's content.139* @param {ARG_TYPES=} opt_templateData The data for the template.140* @template ARG_TYPES141*/142goog.soy.Renderer.prototype.renderElement = function(143element, template, opt_templateData) {144this.saveTemplateRender_(template, opt_templateData);145goog.soy.renderElement(146element, template, opt_templateData, this.getInjectedData_());147this.handleRender(element);148};149150151/**152* Renders a Soy template and returns the output string.153* If the template is strict, it must be of kind HTML. To render strict154* templates of other kinds, use {@code renderText} (for {@code kind="text"}) or155* {@code renderStrictOfKind}.156*157* @param {?function(ARG_TYPES, Object<string, *>=):*|158* ?function(ARG_TYPES, null=, Object<string, *>=):*} template159* The Soy template to render.160* @param {ARG_TYPES=} opt_templateData The data for the template.161* @return {string} The return value of rendering the template directly.162* @template ARG_TYPES163*/164goog.soy.Renderer.prototype.render = function(template, opt_templateData) {165var result =166template(opt_templateData || {}, undefined, this.getInjectedData_());167goog.asserts.assert(168!(result instanceof goog.soy.data.SanitizedContent) ||169result.contentKind === goog.soy.data.SanitizedContentKind.HTML,170'render was called with a strict template of kind other than "html"' +171' (consider using renderText or renderStrict)');172this.saveTemplateRender_(template, opt_templateData);173this.handleRender();174return String(result);175};176177178/**179* Renders a strict Soy template of kind="text" and returns the output string.180* It is an error to use renderText on non-strict templates, or strict templates181* of kinds other than "text".182*183* @param {?function(ARG_TYPES, Object<string, *>=):?goog.soy.data.UnsanitizedText|184* ?function(ARG_TYPES, null=, Object<string, *>=):185* ?goog.soy.data.UnsanitizedText} template The Soy template to render.186* @param {ARG_TYPES=} opt_templateData The data for the template.187* @return {string} The return value of rendering the template directly.188* @template ARG_TYPES189*/190goog.soy.Renderer.prototype.renderText = function(template, opt_templateData) {191var result =192template(opt_templateData || {}, undefined, this.getInjectedData_());193goog.asserts.assertInstanceof(194result, goog.soy.data.SanitizedContent,195'renderText cannot be called on a non-strict soy template');196goog.asserts.assert(197result.contentKind === goog.soy.data.SanitizedContentKind.TEXT,198'renderText was called with a template of kind other than "text"');199this.saveTemplateRender_(template, opt_templateData);200this.handleRender();201return String(result);202};203204205/**206* Renders a strict Soy HTML template and returns the output SanitizedHtml207* object.208* @param {?function(ARG_TYPES, Object<string, *>=):!goog.soy.data.SanitizedHtml|209* ?function(ARG_TYPES, null=, Object<string, *>=):210* !goog.soy.data.SanitizedHtml} template The Soy template to render.211* @param {ARG_TYPES=} opt_templateData The data for the template.212* @return {!goog.soy.data.SanitizedHtml}213* @template ARG_TYPES214*/215goog.soy.Renderer.prototype.renderStrict = function(216template, opt_templateData) {217return this.renderStrictOfKind(218template, opt_templateData, goog.soy.data.SanitizedContentKind.HTML);219};220221222/**223* Renders a strict Soy template and returns the output SanitizedUri object.224*225* @param {!function(ARG_TYPES, ?Object<string, *>=):!goog.soy.data.SanitizedUri|226* !function(ARG_TYPES, null=, ?Object<string, *>=):227* !goog.soy.data.SanitizedUri} template The Soy template to render.228* @param {ARG_TYPES=} opt_templateData The data for the template.229* @return {!goog.soy.data.SanitizedUri}230* @template ARG_TYPES231*/232goog.soy.Renderer.prototype.renderStrictUri = function(233template, opt_templateData) {234return this.renderStrictOfKind(235template, opt_templateData, goog.soy.data.SanitizedContentKind.URI);236};237238239/**240* Renders a strict Soy template and returns the output SanitizedContent object.241*242* @param {?function(ARG_TYPES, ?Object<string, *>=): RETURN_TYPE|243* ?function(ARG_TYPES, null=, ?Object<string, *>=): RETURN_TYPE}244* template The Soy template to render.245* @param {ARG_TYPES=} opt_templateData The data for the template.246* @param {goog.soy.data.SanitizedContentKind=} opt_kind The output kind to247* assert. If null, the template must be of kind="html" (i.e., opt_kind248* defaults to goog.soy.data.SanitizedContentKind.HTML).249* @return {RETURN_TYPE} The SanitizedContent object. This return type is250* generic based on the return type of the template, such as251* goog.soy.data.SanitizedHtml.252* @template ARG_TYPES, RETURN_TYPE253*/254goog.soy.Renderer.prototype.renderStrictOfKind = function(255template, opt_templateData, opt_kind) {256var result =257template(opt_templateData || {}, undefined, this.getInjectedData_());258goog.asserts.assertInstanceof(259result, goog.soy.data.SanitizedContent,260'renderStrict cannot be called on a non-strict soy template');261goog.asserts.assert(262result.contentKind ===263(opt_kind || goog.soy.data.SanitizedContentKind.HTML),264'renderStrict was called with the wrong kind of template');265this.saveTemplateRender_(template, opt_templateData);266this.handleRender();267return result;268};269270271/**272* Renders a strict Soy template of kind="html" and returns the result as273* a goog.html.SafeHtml object.274*275* Rendering a template that is not a strict template of kind="html" results in276* a runtime error.277*278* @param {?function(ARG_TYPES, Object<string, *>=): !goog.soy.data.SanitizedHtml|279* ?function(ARG_TYPES, null=, Object<string, *>=):280* !goog.soy.data.SanitizedHtml} template The Soy template to render.281* @param {ARG_TYPES=} opt_templateData The data for the template.282* @return {!goog.html.SafeHtml}283* @template ARG_TYPES284*/285goog.soy.Renderer.prototype.renderSafeHtml = function(286template, opt_templateData) {287var result = this.renderStrict(template, opt_templateData);288// Convert from SanitizedHtml to SafeHtml.289return result.toSafeHtml();290};291292293/**294* Renders a strict Soy template of kind="css" and returns the result as295* a goog.html.SafeStyleSheet object.296*297* Rendering a template that is not a strict template of kind="css" results in298* a runtime and compile-time error.299*300* @param {?function(ARG_TYPES, Object<string, *>=): !goog.soy.data.SanitizedCss|301* ?function(ARG_TYPES, null=, Object<string, *>=):302* !goog.soy.data.SanitizedCss} template The Soy template to render.303* @param {ARG_TYPES=} opt_templateData The data for the template.304* @return {!goog.html.SafeStyleSheet}305* @template ARG_TYPES306*/307goog.soy.Renderer.prototype.renderSafeStyleSheet = function(308template, opt_templateData) {309var result = this.renderStrictOfKind(310template, opt_templateData, goog.soy.data.SanitizedContentKind.CSS);311// TODO(mlourenco): Call result.toSafeStyleSheet() once that exists.312return goog.html.uncheckedconversions313.safeStyleSheetFromStringKnownToSatisfyTypeContract(314goog.string.Const.from(315'Soy templates of kind CSS produce ' +316'SafeStyleSheet-contract-compliant value.'),317result.toString());318};319320321/**322* @return {!goog.soy.Renderer.SavedTemplateRender} Saved template data for323* the renders that have happened so far.324*/325goog.soy.Renderer.prototype.getSavedTemplateRenders = function() {326return this.savedTemplateRenders_;327};328329330/**331* Observes rendering of templates by this renderer.332* @param {Node=} opt_node Relevant node, if available. The node may or may333* not be in the document, depending on whether Soy is creating an element334* or writing into an existing one.335* @protected336*/337goog.soy.Renderer.prototype.handleRender = goog.nullFunction;338339340/**341* Saves information about the current template render for debug purposes.342* @param {Function} template The Soy template defining the element's content.343* @param {Object=} opt_templateData The data for the template.344* @private345* @suppress {missingProperties} SoyJs compiler adds soyTemplateName to the346* template.347*/348goog.soy.Renderer.prototype.saveTemplateRender_ = function(349template, opt_templateData) {350if (goog.DEBUG) {351this.savedTemplateRenders_.push({352template: template.soyTemplateName,353data: opt_templateData || null,354ijData: this.getInjectedData_()355});356}357};358359360/**361* Creates the injectedParams map if necessary and calls the configuration362* service to prepopulate it.363* @return {Object} The injected params.364* @private365*/366goog.soy.Renderer.prototype.getInjectedData_ = function() {367return this.supplier_ ? this.supplier_.getData() : {};368};369370371372/**373* An interface for a supplier that provides Soy injected data.374* @interface375*/376goog.soy.InjectedDataSupplier = function() {};377378379/**380* Gets the injected data. Implementation may assume that381* {@code goog.soy.Renderer} will treat the returned data as382* immutable. The renderer will call this every time one of its383* {@code render*} methods is called.384* @return {Object} A key-value pair representing the injected data.385*/386goog.soy.InjectedDataSupplier.prototype.getData = function() {};387388389