14ee2ad04344446e610172a0e73949212923014dfSebastian Redl// Copyright 2006 Google Inc.
22cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor//
32cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// Licensed under the Apache License, Version 2.0 (the "License");
42cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// you may not use this file except in compliance with the License.
52cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// You may obtain a copy of the License at
62cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor//
72cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// http://www.apache.org/licenses/LICENSE-2.0
82cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor//
92cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// Unless required by applicable law or agreed to in writing, software
10a4232eb646d89e7d52424bb42eb87d9061f39e63Sebastian Redl// distributed under the License is distributed on an "AS IS" BASIS,
112cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
122cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// implied. See the License for the specific language governing
132cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor// permissions and limitations under the License.
147faa2ec03a7ef120ac165bb45b6c70a8b20c9f1cSebastian Redl/**
150eca89e9890db4d8336ce762a5b359a1d58ca02bArgyrios Kyrtzidis * Author: Steffen Meschkat <mesch@google.com>
16e737f5041a36d0befb39ffeed8d50ba15916d3daDouglas Gregor *
17e737f5041a36d0befb39ffeed8d50ba15916d3daDouglas Gregor * @fileoverview This class is used to evaluate expressions in a local
182cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * context. Used by JstProcessor.
192cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor */
202cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
212a7fb27913999d132cf9e10e03dc5271faa2e9d3John McCall
2289eaf3af92c72c0c1aae807644e39cabc461d685Argyrios Kyrtzidis/**
230b7489194f9f89fac39d57211c1e7953ae50251fDouglas Gregor * Names of special variables defined by the jstemplate evaluation
247a1fad38256eb4c5129359be85ba1ea1678eb5c9John McCall * context. These can be used in js expression in jstemplate
252cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * attributes.
26a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall */
276ab7cd853e9c15cf986a8a7c3db1f8d20e275409Sebastian Redlvar VAR_index = '$index';
287c5d24efcd2e505b5739f7def08dfe25ce59a1b2Chris Lattnervar VAR_count = '$count';
296a5a23f8e7fb65e028c8092bc1d1a1d9dfe2e9bcDouglas Gregorvar VAR_this = '$this';
307c5d24efcd2e505b5739f7def08dfe25ce59a1b2Chris Lattnervar VAR_context = '$context';
3183d63c78810556d26b62ac4cbae2eda6cdd2570cSteve Naroffvar VAR_top = '$top';
3214f79002e58556798e86168c63e48d533287eda5Douglas Gregor
3310e286aa8d39fb51a21412850265d9dae74613eeChris Lattner
343251ceb90b3fec68e86d6dcfa58836e20a7205c3Douglas Gregor/**
3514f79002e58556798e86168c63e48d533287eda5Douglas Gregor * The name of the global variable which holds the value to be returned if
36bd94500d3aa60092fb0f1e90f53fb0d03fa502a8Douglas Gregor * context evaluation results in an error.
372bec0410d268779f601bd509e0302a500af7ac6aDouglas Gregor * Use JsEvalContext.setGlobal(GLOB_default, value) to set this.
38ab41e63821dc60ad144d0684df8d79a9eef86b75Douglas Gregor */
390a0d2b179085a52c10402feebeb6db8b4d96a140Douglas Gregorvar GLOB_default = '$default';
4017fc223395d51be582fc666bb6ea21bd1dff26dcDouglas Gregor
4117fc223395d51be582fc666bb6ea21bd1dff26dcDouglas Gregor
422596e429a61602312bdd149786045b8a90cd2d10Daniel Dunbar/**
432cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * Un-inlined literals, to avoid object creation in IE6. TODO(mesch):
44fbfd180495e7800975c6d9bdc6d24e706ef70e34Michael J. Spencer * So far, these are only used here, but we could use them thoughout
4514f79002e58556798e86168c63e48d533287eda5Douglas Gregor * the code and thus move them to constants.js.
4603013fa9a0bf1ef4b907f5fec006c8f4000fdd21Michael J. Spencer */
47f62d43d2afe1960755a1b5813cae1e5983bcac1bDouglas Gregorvar CHAR_colon = ':';
483c304bd9ec2b4611572d4cbae9e1727bbecb5dc9Chris Lattnervar REGEXP_semicolon = /\s*;\s*/;
49cfbf1c7536e016dc275139dd842d4a5f059a749fDouglas Gregor
50f62d43d2afe1960755a1b5813cae1e5983bcac1bDouglas Gregor
512cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor/**
528538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * See constructor_()
532cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Object|null} opt_data
54ade5000c8763f4bec41f452d7efa3a9b2a6d4712Sebastian Redl * @param {Object} opt_parent
555f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner * @constructor
565f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner */
575f9e272e632e951b1efe824cd16acb4d96077930Chris Lattnerfunction JsEvalContext(opt_data, opt_parent) {
586e089c687cc2b914c46859ab7e46fe4c3c6b0afbBenjamin Kramer  this.constructor_.apply(this, arguments);
59ade5000c8763f4bec41f452d7efa3a9b2a6d4712Sebastian Redl}
606e089c687cc2b914c46859ab7e46fe4c3c6b0afbBenjamin Kramer
616e089c687cc2b914c46859ab7e46fe4c3c6b0afbBenjamin Kramer/**
625f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner * Context for processing a jstemplate. The context contains a context
635f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner * object, whose properties can be referred to in jstemplate
646e089c687cc2b914c46859ab7e46fe4c3c6b0afbBenjamin Kramer * expressions, and it holds the locally defined variables.
65ade5000c8763f4bec41f452d7efa3a9b2a6d4712Sebastian Redl *
66ade5000c8763f4bec41f452d7efa3a9b2a6d4712Sebastian Redl * @param {Object|null} opt_data The context object. Null if no context.
672cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
682cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Object} opt_parent The parent context, from which local
692cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * variables are inherited. Normally the context object of the parent
7012b1c7615d4f9a2edc544be499f895f16ac100edChris Lattner * context is the object whose property the parent object is. Null for the
712cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * context of the root object.
723397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl */
73a4232eb646d89e7d52424bb42eb87d9061f39e63Sebastian RedlJsEvalContext.prototype.constructor_ = function(opt_data, opt_parent) {
7489eaf3af92c72c0c1aae807644e39cabc461d685Argyrios Kyrtzidis  var me = this;
752cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
762cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  /**
772cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * The context for variable definitions in which the jstemplate
788538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl   * expressions are evaluated. Other than for the local context,
792cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * which replaces the parent context, variable definitions of the
8089eaf3af92c72c0c1aae807644e39cabc461d685Argyrios Kyrtzidis   * parent are inherited. The special variable $this points to data_.
818538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl   *
822cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * If this instance is recycled from the cache, then the property is
832cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * already initialized.
842cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   *
852cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * @type {Object}
862cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   */
872cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  if (!me.vars_) {
882cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    me.vars_ = {};
892cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  }
902cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  if (opt_parent) {
912cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // If there is a parent node, inherit local variables from the
922cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // parent.
933397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl    copyProperties(me.vars_, opt_parent.vars_);
94b219cfc4d75f0a03630b7c4509ef791b7e97b2c8David Blaikie  } else {
952cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // If a root node, inherit global symbols. Since every parent
962cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // chain has a root with no parent, global variables will be
973397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl    // present in the case above too. This means that globals can be
982cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // overridden by locals, as it should be.
998538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl    copyProperties(me.vars_, JsEvalContext.globals_);
1002cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  }
1012cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1023397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  /**
1032cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * The current context object is assigned to the special variable
1048538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl   * $this so it is possible to use it in expressions.
1052cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * @type Object
1062cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   */
1073397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  me.vars_[VAR_this] = opt_data;
1081eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1098538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  /**
1102cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * The entire context structure is exposed as a variable so it can be
1112cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * passed to javascript invocations through jseval.
1123397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl   */
113df1550fc59b51681d37225934fe4e3acac321621Richard Smith  me.vars_[VAR_context] = me;
114df1550fc59b51681d37225934fe4e3acac321621Richard Smith
1158538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  /**
1162cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * The local context of the input data in which the jstemplate
1172cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * expressions are evaluated. Notice that this is usually an Object,
1183397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl   * but it can also be a scalar value (and then still the expression
119df1550fc59b51681d37225934fe4e3acac321621Richard Smith   * $this can be used to refer to it). Notice this can even be value,
1208538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl   * undefined or null. Hence, we have to protect jsexec() from using
1212cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * undefined or null, yet we want $this to reflect the true value of
1222cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   * the current context. Thus we assign the original value to $this,
1233397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl   * above, but for the expression context we replace null and
1241eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump   * undefined by the empty string.
1251eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump   *
1268538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl   * @type {Object|null}
1272cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor   */
1282cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  me.data_ = getDefaultObject(opt_data, STRING_empty);
1293397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl
1302cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  if (!opt_parent) {
1312cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // If this is a top-level context, create a variable reference to the data
1320953e767ff7817f97b3ab20896b229891eeff45bJohn McCall    // to allow for  accessing top-level properties of the original context
1332cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    // data from child contexts.
1342cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    me.vars_[VAR_top] = me.data_;
1353397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  }
1362cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor};
1372cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1388538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl
1392cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor/**
1402cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * A map of globally defined symbols. Every instance of JsExprContext
1413397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * inherits them in its vars_.
1422cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @type Object
1438538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl */
1442cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas GregorJsEvalContext.globals_ = {}
1452cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1463397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl
1472cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor/**
1487e7eb3da052a6d80ddf2377cab0384c798f73f75Douglas Gregor * Sets a global symbol. It will be available like a variable in every
1497e7eb3da052a6d80ddf2377cab0384c798f73f75Douglas Gregor * JsEvalContext instance. This is intended mainly to register
150c9490c000f515c29f200a1215328d8ab9a0f3818Douglas Gregor * immutable global objects, such as functions, at load time, and not
1518538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * to add global data at runtime. I.e. the same objections as to
1522cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * global variables in general apply also here. (Hence the name
1532cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * "global", and not "global var".)
1543397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * @param {string} name
1552cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Object|null} value
1562cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor */
157e86d78cf4754a6aef2cf9a33d847aa15338e276fBob WilsonJsEvalContext.setGlobal = function(name, value) {
1588538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  JsEvalContext.globals_[name] = value;
1592cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor};
1602cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1613397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl
1622cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor/**
1638538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * Set the default value to be returned if context evaluation results in an
1642cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * error. (This can occur if a non-existent value was requested).
1652cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor */
1663397c5570369f19b2d6c52e898f708d75ceede1fSebastian RedlJsEvalContext.setGlobal(GLOB_default, null);
1672cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
168264ba48dc98f3f843935a485d5b086f7e0fdc4f1Rafael Espindola
169264ba48dc98f3f843935a485d5b086f7e0fdc4f1Rafael Espindola/**
170a49218e17bcbb1acde0245773173e2c0c42f4f19Eli Friedman * A cache to reuse JsEvalContext instances. (IE6 perf)
171425ef72306d4ff6b3698b744353e5f0e56b4b884Rafael Espindola *
172ab8bbf4ebd3e3e6eab913cb044772a62b7581941Douglas Gregor * @type Array.<JsEvalContext>
173264ba48dc98f3f843935a485d5b086f7e0fdc4f1Rafael Espindola */
174f85e193739c953358c865005855253af4f68a497John McCallJsEvalContext.recycledInstances_ = [];
1752cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1762cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
1773397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl/**
1782cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * A factory to create a JsEvalContext instance, possibly reusing
1798538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * one from recycledInstances_. (IE6 perf)
1802cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
1812cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Object} opt_data
1823397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * @param {JsEvalContext} opt_parent
1832cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @return {JsEvalContext}
1842cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor */
1852cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas GregorJsEvalContext.create = function(opt_data, opt_parent) {
1862cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  if (jsLength(JsEvalContext.recycledInstances_) > 0) {
1872cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    var instance = JsEvalContext.recycledInstances_.pop();
1882cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor    JsEvalContext.call(instance, opt_data, opt_parent);
189c938c1668b4fd12af154e965dd935a89e4801a70Douglas Gregor    return instance;
19060618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl  } else {
19160618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl    return new JsEvalContext(opt_data, opt_parent);
19260618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl  }
19360618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl};
19460618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl
19560618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl
19660618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl/**
19760618fa7f88d5162bb5b40988b6b38d4d75d6fc6Sebastian Redl * Recycle a used JsEvalContext instance, so we can avoid creating one
1988538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * the next time we need one. (IE6 perf)
1992cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
2002cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {JsEvalContext} instance
2013397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl */
202ed97649e9574b9d854fa4d6109c9333ae0993554John McCallJsEvalContext.recycle = function(instance) {
2038538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  for (var i in instance.vars_) {
204ed97649e9574b9d854fa4d6109c9333ae0993554John McCall    // NOTE(mesch): We avoid object creation here. (IE6 perf)
205ed97649e9574b9d854fa4d6109c9333ae0993554John McCall    delete instance.vars_[i];
2063397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  }
2072cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  instance.data_ = null;
2089763e221e16026ddf487d2564ed349d2c874a1a1Argyrios Kyrtzidis  JsEvalContext.recycledInstances_.push(instance);
2099763e221e16026ddf487d2564ed349d2c874a1a1Argyrios Kyrtzidis};
2108538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl
2112cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
2122cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor/**
2133397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * Executes a function created using jsEvalToFunction() in the context
214c9490c000f515c29f200a1215328d8ab9a0f3818Douglas Gregor * of vars, data, and template.
2158538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl *
2162cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Function} exprFunction A javascript function created from
2172cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * a jstemplate attribute value.
2183397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl *
2192cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {Element} template DOM node of the template.
2208538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl *
2212cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @return {Object|null} The value of the expression from which
2222cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * exprFunction was created in the current js expression context and
2233397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * the context of template.
224395b475a4474f1c7574d927ad142ca0c7997cbcaAnders Carlsson */
2258538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian RedlJsEvalContext.prototype.jsexec = function(exprFunction, template) {
226395b475a4474f1c7574d927ad142ca0c7997cbcaAnders Carlsson  try {
227395b475a4474f1c7574d927ad142ca0c7997cbcaAnders Carlsson    return exprFunction.call(template, this.vars_, this.data_);
228ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt  } catch (e) {
229ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt    log('jsexec EXCEPTION: ' + e + ' at ' + template +
230ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt        ' with ' + exprFunction);
231ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt    return JsEvalContext.globals_[GLOB_default];
232ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt  }
233ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt};
234ca63c200346c0ca9e00194ec6e34a5a7b0ed9321Sean Hunt
23534b41d939a1328f484511c6002ba2456db879a29Richard Smith
23634b41d939a1328f484511c6002ba2456db879a29Richard Smith/**
23734b41d939a1328f484511c6002ba2456db879a29Richard Smith * Clones the current context for a new context object. The cloned
23834b41d939a1328f484511c6002ba2456db879a29Richard Smith * context has the data object as its context object and the current
23934b41d939a1328f484511c6002ba2456db879a29Richard Smith * context as its parent context. It also sets the $index variable to
2403397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * the given value. This value usually is the position of the data
241be191100e034b23a3e13053757a57b7f5068c24aArgyrios Kyrtzidis * object in a list for which a template is instantiated multiply.
2422cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
2431eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump * @param {Object} data The new context object.
2442cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
2452cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {number} index Position of the new context when multiply
2462cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * instantiated. (See implementation of jstSelect().)
2473397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl *
2482cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @param {number} count The total number of contexts that were multiply
2498538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * instantiated. (See implementation of jstSelect().)
2502cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
2512cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * @return {JsEvalContext}
2523397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl */
2532cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas GregorJsEvalContext.prototype.clone = function(data, index, count) {
2548538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  var ret = JsEvalContext.create(data, this);
2552cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  ret.setVariable(VAR_index, index);
2562cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  ret.setVariable(VAR_count, count);
2579d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall  return ret;
2589d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall};
2599d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall
2609d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall
2619d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall/**
2629d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall * Binds a local variable to the given value. If set from jstemplate
2639d156a7b1b2771e191f2f5a45a7b7a694129463bJohn McCall * jsvalue expressions, variable names must start with $, but in the
2641eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump * API they only have to be valid javascript identifier.
2653397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl *
26649a832bd499d6f61c23655f1fac99f0dd229756eJohn McCall * @param {string} name
26749a832bd499d6f61c23655f1fac99f0dd229756eJohn McCall *
26849a832bd499d6f61c23655f1fac99f0dd229756eJohn McCall * @param {Object?} value
2698538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl */
27049a832bd499d6f61c23655f1fac99f0dd229756eJohn McCallJsEvalContext.prototype.setVariable = function(name, value) {
27149a832bd499d6f61c23655f1fac99f0dd229756eJohn McCall  this.vars_[name] = value;
27249a832bd499d6f61c23655f1fac99f0dd229756eJohn McCall};
273c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor
274c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor
275c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor/**
276c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor * Returns the value bound to the local variable of the given name, or
277c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor * undefined if it wasn't set. There is no way to distinguish a
278c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor * variable that wasn't set from a variable that was set to
279c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor * undefined. Used mostly for testing.
280c3069d618f4661d923cb1b5c4525b082fce73b04Douglas Gregor *
2813397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * @param {string} name
2822cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
283be191100e034b23a3e13053757a57b7f5068c24aArgyrios Kyrtzidis * @return {Object?} value
28490b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis */
28590b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios KyrtzidisJsEvalContext.prototype.getVariable = function(name) {
28690b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis  return this.vars_[name];
28790b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis};
28890b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
2893e4c6c4c79a03f5cb0c4671d7c282d623c6dc35eRichard Smith
2903e4c6c4c79a03f5cb0c4671d7c282d623c6dc35eRichard Smith/**
2919763e221e16026ddf487d2564ed349d2c874a1a1Argyrios Kyrtzidis * Evaluates a string expression within the scope of this context
2929763e221e16026ddf487d2564ed349d2c874a1a1Argyrios Kyrtzidis * and returns the result.
2938538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl *
29490b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * @param {string} expr A javascript expression
29590b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * @param {Element} opt_template An optional node to serve as "this"
29690b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis *
2973397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * @return {Object?} value
298ae8b17f1d5d303af53db5a4f4a375ea6b9356566Argyrios Kyrtzidis */
299ae8b17f1d5d303af53db5a4f4a375ea6b9356566Argyrios KyrtzidisJsEvalContext.prototype.evalExpression = function(expr, opt_template) {
300ae8b17f1d5d303af53db5a4f4a375ea6b9356566Argyrios Kyrtzidis  var exprFunction = jsEvalToFunction(expr);
3018538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  return this.jsexec(exprFunction, opt_template);
30290b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis};
30390b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
30490b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
3053397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl/**
30690b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * Uninlined string literals for jsEvalToFunction() (IE6 perf).
30790b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis */
308b219cfc4d75f0a03630b7c4509ef791b7e97b2c8David Blaikievar STRING_a = 'a_';
30990b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidisvar STRING_b = 'b_';
31090b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidisvar STRING_with = 'with (a_) with (b_) return ';
31190b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
3123397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl
31390b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis/**
31490b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * Cache for jsEvalToFunction results.
31590b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * @type Object
3164fb86f8c4585e53c21c847ad3de9e3b2de123cd9Chandler Carruth */
3178538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian RedlJsEvalContext.evalToFunctionCache_ = {};
31890b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
31990b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis
32090b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis/**
3213397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * Evaluates the given expression as the body of a function that takes
3228dfbd8b252ba4e6cf4b7a3422f6ef0ca21312dfeArgyrios Kyrtzidis * vars and data as arguments. Since the resulting function depends
3238dfbd8b252ba4e6cf4b7a3422f6ef0ca21312dfeArgyrios Kyrtzidis * only on expr, we cache the result so we save some Function
3248dfbd8b252ba4e6cf4b7a3422f6ef0ca21312dfeArgyrios Kyrtzidis * invocations, and some object creations in IE6.
325f48d45e3e36c132bdee3373beec4e8b19ae3f9c4Argyrios Kyrtzidis *
326f48d45e3e36c132bdee3373beec4e8b19ae3f9c4Argyrios Kyrtzidis * @param {string} expr A javascript expression.
327f48d45e3e36c132bdee3373beec4e8b19ae3f9c4Argyrios Kyrtzidis *
3288538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * @return {Function} A function that returns the value of expr in the
32990b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis * context of vars and data.
33090b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis */
33190b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidisfunction jsEvalToFunction(expr) {
3323397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  if (!JsEvalContext.evalToFunctionCache_[expr]) {
33390b715e0df34eae2b50b9b43ec60828ed31dcf94Argyrios Kyrtzidis    try {
3343acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis      // NOTE(mesch): The Function constructor is faster than eval().
3353acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis      JsEvalContext.evalToFunctionCache_[expr] =
3363acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis        new Function(STRING_a, STRING_b, STRING_with + expr);
3373acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis    } catch (e) {
3383acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis      log('jsEvalToFunction (' + expr + ') EXCEPTION ' + e);
3393acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis    }
3403acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis  }
3418538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  return JsEvalContext.evalToFunctionCache_[expr];
3422cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor}
3432cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor
3447536dd5e6c99584481b7dab68b7e7d8df9c54054Douglas Gregor
3457536dd5e6c99584481b7dab68b7e7d8df9c54054Douglas Gregor/**
346cded4f649cd4b7ba7d461c25c6482ef52b8d3a2aDouglas Gregor * Evaluates the given expression to itself. This is meant to pass
347cded4f649cd4b7ba7d461c25c6482ef52b8d3a2aDouglas Gregor * through string attribute values.
348cded4f649cd4b7ba7d461c25c6482ef52b8d3a2aDouglas Gregor *
349cded4f649cd4b7ba7d461c25c6482ef52b8d3a2aDouglas Gregor * @param {string} expr
3507536dd5e6c99584481b7dab68b7e7d8df9c54054Douglas Gregor *
3517536dd5e6c99584481b7dab68b7e7d8df9c54054Douglas Gregor * @return {string}
3527536dd5e6c99584481b7dab68b7e7d8df9c54054Douglas Gregor */
353075f8f1b6bed4d1b224c74f87508534cc6392ce6Abramo Bagnarafunction jsEvalToSelf(expr) {
354075f8f1b6bed4d1b224c74f87508534cc6392ce6Abramo Bagnara  return expr;
355075f8f1b6bed4d1b224c74f87508534cc6392ce6Abramo Bagnara}
356075f8f1b6bed4d1b224c74f87508534cc6392ce6Abramo Bagnara
357075f8f1b6bed4d1b224c74f87508534cc6392ce6Abramo Bagnara
3583397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl/**
359465d41b92b2c862f3062c412a0538db65c6a2661Abramo Bagnara * Parses the value of the jsvalues attribute in jstemplates: splits
3603acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis * it up into a map of labels and expressions, and creates functions
3613acad62a239448bef0f5848b2a0d5f7dfefd3d14Argyrios Kyrtzidis * from the expressions that are suitable for execution by
3628538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl * JsEvalContext.jsexec(). All that is returned as a flattened array
3632cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * of pairs of a String and a Function.
3642cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor *
3653397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl * @param {string} expr
3663cb0ebd5f76abcb776f7cb4062bd79e3268c0dc4John McCall *
36731f17ecbef57b5679c017c375db330546b7b5145John McCall * @return {Array}
3688538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl */
3693cb0ebd5f76abcb776f7cb4062bd79e3268c0dc4John McCallfunction jsEvalToValues(expr) {
3703cb0ebd5f76abcb776f7cb4062bd79e3268c0dc4John McCall  // TODO(mesch): It is insufficient to split the values by simply
3713397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl  // finding semi-colons, as the semi-colon may be part of a string
372deacbdca554298ccdf636f19c6094a8825ec6b34Douglas Gregor  // constant or escaped.
3738538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl  var ret = [];
374c12c5bba6ceb6acd4e51e7a0fc03257da9cfd44eJohn McCall  var values = expr.split(REGEXP_semicolon);
375c12c5bba6ceb6acd4e51e7a0fc03257da9cfd44eJohn McCall  for (var i = 0, I = jsLength(values); i < I; ++i) {
3763397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl    var colon = values[i].indexOf(CHAR_colon);
377c12c5bba6ceb6acd4e51e7a0fc03257da9cfd44eJohn McCall    if (colon < 0) {
3782cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor      continue;
379c12c5bba6ceb6acd4e51e7a0fc03257da9cfd44eJohn McCall    }
380446ee4eb4fc4c705a59365252df7a5c253daafa1Steve Naroff    var label = stringTrim(values[i].substr(0, colon));
381446ee4eb4fc4c705a59365252df7a5c253daafa1Steve Naroff    var value = jsEvalToFunction(values[i].substr(colon + 1));
3828538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl    ret.push(label, value);
3832cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  }
3842cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor  return ret;
385d1b3c2dd5bc1f3103bee6137957aa7c5f8f2f0bcSteve Naroff}
3863397c5570369f19b2d6c52e898f708d75ceede1fSebastian Redl
3871eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
3888538e8d43a3a9bd439c987c0de37bcbf035dd391Sebastian Redl/**
3892cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * Parses the value of the jseval attribute of jstemplates: splits it
3902cf2634ffdb4f7c8d46cef3f8e60a55993f1c57aDouglas Gregor * up into a list of expressions, and creates functions from the
391b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman * expressions that are suitable for execution by
392b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman * JsEvalContext.jsexec(). All that is returned as an Array of
393b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman * Function.
394b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman *
395b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman * @param {string} expr
396b001de7458d17c17e6d8b8034c7cfcefd3b70c00Eli Friedman *
397a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall * @return {Array.<Function>}
398a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall */
399a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCallfunction jsEvalToExpressions(expr) {
400a4232eb646d89e7d52424bb42eb87d9061f39e63Sebastian Redl  var ret = [];
40189eaf3af92c72c0c1aae807644e39cabc461d685Argyrios Kyrtzidis  var values = expr.split(REGEXP_semicolon);
402a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall  for (var i = 0, I = jsLength(values); i < I; ++i) {
403a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall    if (values[i]) {
40489eaf3af92c72c0c1aae807644e39cabc461d685Argyrios Kyrtzidis      var value = jsEvalToFunction(values[i]);
405a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall      ret.push(value);
406a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall    }
40751bd803fbdade51d674598ed45da3d54190a656cJohn McCall  }
408a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall  return ret;
40951bd803fbdade51d674598ed45da3d54190a656cJohn McCall}
410a1ee0c548b8aa4aaf93d1917e304e3da13171a08John McCall