18ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Copyright 2006 Google Inc. 28ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// 38ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Licensed under the Apache License, Version 2.0 (the "License"); 48ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// you may not use this file except in compliance with the License. 58ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// You may obtain a copy of the License at 68ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// 78ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// http://www.apache.org/licenses/LICENSE-2.0 88ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// 98ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// Unless required by applicable law or agreed to in writing, software 108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// distributed under the License is distributed on an "AS IS" BASIS, 118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// implied. See the License for the specific language governing 138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen// permissions and limitations under the License. 148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Author: Steffen Meschkat <mesch@google.com> 168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @fileoverview This class is used to evaluate expressions in a local 188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context. Used by JstProcessor. 198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Names of special variables defined by the jstemplate evaluation 248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context. These can be used in js expression in jstemplate 258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * attributes. 268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar VAR_index = '$index'; 288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar VAR_count = '$count'; 298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar VAR_this = '$this'; 308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar VAR_context = '$context'; 318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar VAR_top = '$top'; 328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The name of the global variable which holds the value to be returned if 368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context evaluation results in an error. 378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Use JsEvalContext.setGlobal(GLOB_default, value) to set this. 388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar GLOB_default = '$default'; 408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Un-inlined literals, to avoid object creation in IE6. TODO(mesch): 448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * So far, these are only used here, but we could use them thoughout 458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the code and thus move them to constants.js. 468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar CHAR_colon = ':'; 488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar REGEXP_semicolon = /\s*;\s*/; 498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * See constructor_() 538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null} opt_data 548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} opt_parent 558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @constructor 568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction JsEvalContext(opt_data, opt_parent) { 588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen this.constructor_.apply(this, arguments); 598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Context for processing a jstemplate. The context contains a context 638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * object, whose properties can be referred to in jstemplate 648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * expressions, and it holds the locally defined variables. 658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null} opt_data The context object. Null if no context. 678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} opt_parent The parent context, from which local 698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * variables are inherited. Normally the context object of the parent 708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context is the object whose property the parent object is. Null for the 718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context of the root object. 728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 738ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.constructor_ = function(opt_data, opt_parent) { 748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var me = this; 758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The context for variable definitions in which the jstemplate 788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * expressions are evaluated. Other than for the local context, 798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * which replaces the parent context, variable definitions of the 808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * parent are inherited. The special variable $this points to data_. 818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * If this instance is recycled from the cache, then the property is 838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * already initialized. 848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type {Object} 868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!me.vars_) { 888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen me.vars_ = {}; 898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (opt_parent) { 918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // If there is a parent node, inherit local variables from the 928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // parent. 938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen copyProperties(me.vars_, opt_parent.vars_); 948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // If a root node, inherit global symbols. Since every parent 968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // chain has a root with no parent, global variables will be 978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // present in the case above too. This means that globals can be 988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // overridden by locals, as it should be. 998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen copyProperties(me.vars_, JsEvalContext.globals_); 1008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 1038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The current context object is assigned to the special variable 1048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $this so it is possible to use it in expressions. 1058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type Object 1068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen me.vars_[VAR_this] = opt_data; 1088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 1108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The entire context structure is exposed as a variable so it can be 1118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * passed to javascript invocations through jseval. 1128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen me.vars_[VAR_context] = me; 1148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen /** 1168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * The local context of the input data in which the jstemplate 1178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * expressions are evaluated. Notice that this is usually an Object, 1188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * but it can also be a scalar value (and then still the expression 1198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * $this can be used to refer to it). Notice this can even be value, 1208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * undefined or null. Hence, we have to protect jsexec() from using 1218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * undefined or null, yet we want $this to reflect the true value of 1228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the current context. Thus we assign the original value to $this, 1238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * above, but for the expression context we replace null and 1248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * undefined by the empty string. 1258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 1268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type {Object|null} 1278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen me.data_ = getDefaultObject(opt_data, STRING_empty); 1298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!opt_parent) { 1318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // If this is a top-level context, create a variable reference to the data 1328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // to allow for accessing top-level properties of the original context 1338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // data from child contexts. 1348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen me.vars_[VAR_top] = me.data_; 1358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 1378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * A map of globally defined symbols. Every instance of JsExprContext 1418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * inherits them in its vars_. 1428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type Object 1438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1448ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.globals_ = {} 1458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Sets a global symbol. It will be available like a variable in every 1498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * JsEvalContext instance. This is intended mainly to register 1508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * immutable global objects, such as functions, at load time, and not 1518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * to add global data at runtime. I.e. the same objections as to 1528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * global variables in general apply also here. (Hence the name 1538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * "global", and not "global var".) 1548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name 1558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object|null} value 1568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1578ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.setGlobal = function(name, value) { 1588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen JsEvalContext.globals_[name] = value; 1598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 1608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Set the default value to be returned if context evaluation results in an 1648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * error. (This can occur if a non-existent value was requested). 1658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1668ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.setGlobal(GLOB_default, null); 1678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * A cache to reuse JsEvalContext instances. (IE6 perf) 1718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 1728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type Array.<JsEvalContext> 1738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1748ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.recycledInstances_ = []; 1758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * A factory to create a JsEvalContext instance, possibly reusing 1798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * one from recycledInstances_. (IE6 perf) 1808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 1818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} opt_data 1828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {JsEvalContext} opt_parent 1838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {JsEvalContext} 1848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 1858ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.create = function(opt_data, opt_parent) { 1868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (jsLength(JsEvalContext.recycledInstances_) > 0) { 1878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var instance = JsEvalContext.recycledInstances_.pop(); 1888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen JsEvalContext.call(instance, opt_data, opt_parent); 1898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return instance; 1908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } else { 1918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return new JsEvalContext(opt_data, opt_parent); 1928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 1938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 1948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 1968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 1978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Recycle a used JsEvalContext instance, so we can avoid creating one 1988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the next time we need one. (IE6 perf) 1998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {JsEvalContext} instance 2018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2028ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.recycle = function(instance) { 2038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen for (var i in instance.vars_) { 2048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // NOTE(mesch): We avoid object creation here. (IE6 perf) 2058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen delete instance.vars_[i]; 2068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen instance.data_ = null; 2088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen JsEvalContext.recycledInstances_.push(instance); 2098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 2108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 2138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Executes a function created using jsEvalToFunction() in the context 2148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * of vars, data, and template. 2158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Function} exprFunction A javascript function created from 2178ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * a jstemplate attribute value. 2188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} template DOM node of the template. 2208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Object|null} The value of the expression from which 2228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * exprFunction was created in the current js expression context and 2238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the context of template. 2248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2258ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.jsexec = function(exprFunction, template) { 2268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen try { 2278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return exprFunction.call(template, this.vars_, this.data_); 2288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } catch (e) { 2298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen log('jsexec EXCEPTION: ' + e + ' at ' + template + 2308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ' with ' + exprFunction); 2318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return JsEvalContext.globals_[GLOB_default]; 2328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 2338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 2348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 2378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Clones the current context for a new context object. The cloned 2388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context has the data object as its context object and the current 2398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context as its parent context. It also sets the $index variable to 2408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * the given value. This value usually is the position of the data 2418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * object in a list for which a template is instantiated multiply. 2428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object} data The new context object. 2448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {number} index Position of the new context when multiply 2468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * instantiated. (See implementation of jstSelect().) 2478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {number} count The total number of contexts that were multiply 2498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * instantiated. (See implementation of jstSelect().) 2508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {JsEvalContext} 2528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2538ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.clone = function(data, index, count) { 2548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var ret = JsEvalContext.create(data, this); 2558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ret.setVariable(VAR_index, index); 2568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ret.setVariable(VAR_count, count); 2578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return ret; 2588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 2598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 2628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Binds a local variable to the given value. If set from jstemplate 2638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * jsvalue expressions, variable names must start with $, but in the 2648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * API they only have to be valid javascript identifier. 2658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name 2678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Object?} value 2698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2708ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.setVariable = function(name, value) { 2718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen this.vars_[name] = value; 2728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 2738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 2768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Returns the value bound to the local variable of the given name, or 2778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * undefined if it wasn't set. There is no way to distinguish a 2788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * variable that wasn't set from a variable that was set to 2798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * undefined. Used mostly for testing. 2808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} name 2828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Object?} value 2848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2858ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.getVariable = function(name) { 2868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return this.vars_[name]; 2878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 2888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 2908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 2918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Evaluates a string expression within the scope of this context 2928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * and returns the result. 2938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr A javascript expression 2958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {Element} opt_template An optional node to serve as "this" 2968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 2978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Object?} value 2988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 2998ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.prototype.evalExpression = function(expr, opt_template) { 3008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var exprFunction = jsEvalToFunction(expr); 3018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return this.jsexec(exprFunction, opt_template); 3028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen}; 3038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Uninlined string literals for jsEvalToFunction() (IE6 perf). 3078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar STRING_a = 'a_'; 3098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar STRING_b = 'b_'; 3108ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenvar STRING_with = 'with (a_) with (b_) return '; 3118ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3128ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3138ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3148ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Cache for jsEvalToFunction results. 3158ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @type Object 3168ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3178ae428e0fb7feea16d79853f29447469a93bedffKristian MonsenJsEvalContext.evalToFunctionCache_ = {}; 3188ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3198ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3208ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3218ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Evaluates the given expression as the body of a function that takes 3228ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * vars and data as arguments. Since the resulting function depends 3238ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * only on expr, we cache the result so we save some Function 3248ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * invocations, and some object creations in IE6. 3258ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3268ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr A javascript expression. 3278ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3288ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Function} A function that returns the value of expr in the 3298ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * context of vars and data. 3308ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3318ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsEvalToFunction(expr) { 3328ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (!JsEvalContext.evalToFunctionCache_[expr]) { 3338ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen try { 3348ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // NOTE(mesch): The Function constructor is faster than eval(). 3358ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen JsEvalContext.evalToFunctionCache_[expr] = 3368ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen new Function(STRING_a, STRING_b, STRING_with + expr); 3378ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } catch (e) { 3388ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen log('jsEvalToFunction (' + expr + ') EXCEPTION ' + e); 3398ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3408ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3418ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return JsEvalContext.evalToFunctionCache_[expr]; 3428ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 3438ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3448ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3458ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3468ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Evaluates the given expression to itself. This is meant to pass 3478ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * through string attribute values. 3488ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3498ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr 3508ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3518ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {string} 3528ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3538ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsEvalToSelf(expr) { 3548ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return expr; 3558ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 3568ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3578ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3588ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3598ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Parses the value of the jsvalues attribute in jstemplates: splits 3608ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * it up into a map of labels and expressions, and creates functions 3618ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * from the expressions that are suitable for execution by 3628ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * JsEvalContext.jsexec(). All that is returned as a flattened array 3638ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * of pairs of a String and a Function. 3648ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3658ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr 3668ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3678ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Array} 3688ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3698ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsEvalToValues(expr) { 3708ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // TODO(mesch): It is insufficient to split the values by simply 3718ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // finding semi-colons, as the semi-colon may be part of a string 3728ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen // constant or escaped. 3738ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var ret = []; 3748ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var values = expr.split(REGEXP_semicolon); 3758ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen for (var i = 0, I = jsLength(values); i < I; ++i) { 3768ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var colon = values[i].indexOf(CHAR_colon); 3778ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (colon < 0) { 3788ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen continue; 3798ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3808ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var label = stringTrim(values[i].substr(0, colon)); 3818ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var value = jsEvalToFunction(values[i].substr(colon + 1)); 3828ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ret.push(label, value); 3838ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 3848ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return ret; 3858ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 3868ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3878ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen 3888ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen/** 3898ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Parses the value of the jseval attribute of jstemplates: splits it 3908ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * up into a list of expressions, and creates functions from the 3918ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * expressions that are suitable for execution by 3928ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * JsEvalContext.jsexec(). All that is returned as an Array of 3938ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * Function. 3948ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3958ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @param {string} expr 3968ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * 3978ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen * @return {Array.<Function>} 3988ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen */ 3998ae428e0fb7feea16d79853f29447469a93bedffKristian Monsenfunction jsEvalToExpressions(expr) { 4008ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var ret = []; 4018ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var values = expr.split(REGEXP_semicolon); 4028ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen for (var i = 0, I = jsLength(values); i < I; ++i) { 4038ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen if (values[i]) { 4048ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen var value = jsEvalToFunction(values[i]); 4058ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen ret.push(value); 4068ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4078ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen } 4088ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen return ret; 4098ae428e0fb7feea16d79853f29447469a93bedffKristian Monsen} 410