15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2006 Google Inc.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Licensed under the Apache License, Version 2.0 (the "License");
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// you may not use this file except in compliance with the License.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// You may obtain a copy of the License at
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://www.apache.org/licenses/LICENSE-2.0
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Unless required by applicable law or agreed to in writing, software
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// distributed under the License is distributed on an "AS IS" BASIS,
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// implied. See the License for the specific language governing
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// permissions and limitations under the License.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Author: Steffen Meschkat <mesch@google.com>
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview This class is used to evaluate expressions in a local
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context. Used by JstProcessor.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Names of special variables defined by the jstemplate evaluation
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context. These can be used in js expression in jstemplate
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * attributes.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var VAR_index = '$index';
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var VAR_count = '$count';
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var VAR_this = '$this';
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var VAR_context = '$context';
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var VAR_top = '$top';
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The name of the global variable which holds the value to be returned if
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context evaluation results in an error.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Use JsEvalContext.setGlobal(GLOB_default, value) to set this.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var GLOB_default = '$default';
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Un-inlined literals, to avoid object creation in IE6. TODO(mesch):
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * So far, these are only used here, but we could use them thoughout
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the code and thus move them to constants.js.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var CHAR_colon = ':';
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var REGEXP_semicolon = /\s*;\s*/;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * See constructor_()
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object|null=} opt_data
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object=} opt_parent
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function JsEvalContext(opt_data, opt_parent) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.constructor_.apply(this, arguments);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Context for processing a jstemplate. The context contains a context
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * object, whose properties can be referred to in jstemplate
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * expressions, and it holds the locally defined variables.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object|null} opt_data The context object. Null if no context.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} opt_parent The parent context, from which local
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * variables are inherited. Normally the context object of the parent
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context is the object whose property the parent object is. Null for the
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context of the root object.
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.constructor_ = function(opt_data, opt_parent) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var me = this;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!me.vars_) {
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    /**
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * The context for variable definitions in which the jstemplate
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * expressions are evaluated. Other than for the local context,
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * which replaces the parent context, variable definitions of the
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * parent are inherited. The special variable $this points to data_.
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     *
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * If this instance is recycled from the cache, then the property is
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * already initialized.
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     *
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     * @type {Object}
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci     */
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    me.vars_ = {};
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_parent) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If there is a parent node, inherit local variables from the
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // parent.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    copyProperties(me.vars_, opt_parent.vars_);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If a root node, inherit global symbols. Since every parent
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // chain has a root with no parent, global variables will be
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // present in the case above too. This means that globals can be
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // overridden by locals, as it should be.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    copyProperties(me.vars_, JsEvalContext.globals_);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The current context object is assigned to the special variable
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * $this so it is possible to use it in expressions.
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @type {Object}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  me.vars_[VAR_this] = opt_data;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The entire context structure is exposed as a variable so it can be
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * passed to javascript invocations through jseval.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  me.vars_[VAR_context] = me;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The local context of the input data in which the jstemplate
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * expressions are evaluated. Notice that this is usually an Object,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * but it can also be a scalar value (and then still the expression
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * $this can be used to refer to it). Notice this can even be value,
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * undefined or null. Hence, we have to protect jsexec() from using
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * undefined or null, yet we want $this to reflect the true value of
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * the current context. Thus we assign the original value to $this,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * above, but for the expression context we replace null and
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * undefined by the empty string.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @type {*}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  me.data_ = getDefaultObject(opt_data, STRING_empty);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!opt_parent) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If this is a top-level context, create a variable reference to the data
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to allow for  accessing top-level properties of the original context
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // data from child contexts.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    me.vars_[VAR_top] = me.data_;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A map of globally defined symbols. Every instance of JsExprContext
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * inherits them in its vars_.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type Object
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.globals_ = {}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets a global symbol. It will be available like a variable in every
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * JsEvalContext instance. This is intended mainly to register
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * immutable global objects, such as functions, at load time, and not
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to add global data at runtime. I.e. the same objections as to
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * global variables in general apply also here. (Hence the name
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * "global", and not "global var".)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object|null} value
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.setGlobal = function(name, value) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JsEvalContext.globals_[name] = value;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set the default value to be returned if context evaluation results in an
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * error. (This can occur if a non-existent value was requested).
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.setGlobal(GLOB_default, null);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A cache to reuse JsEvalContext instances. (IE6 perf)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type Array.<JsEvalContext>
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.recycledInstances_ = [];
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A factory to create a JsEvalContext instance, possibly reusing
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * one from recycledInstances_. (IE6 perf)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} opt_data
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {JsEvalContext} opt_parent
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {JsEvalContext}
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.create = function(opt_data, opt_parent) {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (jsLength(JsEvalContext.recycledInstances_) > 0) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var instance = JsEvalContext.recycledInstances_.pop();
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    JsEvalContext.call(instance, opt_data, opt_parent);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return instance;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return new JsEvalContext(opt_data, opt_parent);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Recycle a used JsEvalContext instance, so we can avoid creating one
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the next time we need one. (IE6 perf)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {JsEvalContext} instance
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.recycle = function(instance) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i in instance.vars_) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // NOTE(mesch): We avoid object creation here. (IE6 perf)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete instance.vars_[i];
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  instance.data_ = null;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JsEvalContext.recycledInstances_.push(instance);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Executes a function created using jsEvalToFunction() in the context
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of vars, data, and template.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Function} exprFunction A javascript function created from
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a jstemplate attribute value.
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} template DOM node of the template.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object|null} The value of the expression from which
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * exprFunction was created in the current js expression context and
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the context of template.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.jsexec = function(exprFunction, template) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return exprFunction.call(template, this.vars_, this.data_);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } catch (e) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    log('jsexec EXCEPTION: ' + e + ' at ' + template +
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ' with ' + exprFunction);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return JsEvalContext.globals_[GLOB_default];
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Clones the current context for a new context object. The cloned
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context has the data object as its context object and the current
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context as its parent context. It also sets the $index variable to
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the given value. This value usually is the position of the data
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * object in a list for which a template is instantiated multiply.
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} data The new context object.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} index Position of the new context when multiply
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * instantiated. (See implementation of jstSelect().)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} count The total number of contexts that were multiply
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * instantiated. (See implementation of jstSelect().)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {JsEvalContext}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.clone = function(data, index, count) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ret = JsEvalContext.create(data, this);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ret.setVariable(VAR_index, index);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ret.setVariable(VAR_count, count);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ret;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Binds a local variable to the given value. If set from jstemplate
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * jsvalue expressions, variable names must start with $, but in the
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * API they only have to be valid javascript identifier.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name
2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {*} value
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.setVariable = function(name, value) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.vars_[name] = value;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns the value bound to the local variable of the given name, or
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * undefined if it wasn't set. There is no way to distinguish a
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * variable that wasn't set from a variable that was set to
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * undefined. Used mostly for testing.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {*} value
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.getVariable = function(name) {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.vars_[name];
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Evaluates a string expression within the scope of this context
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * and returns the result.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} expr A javascript expression
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} opt_template An optional node to serve as "this"
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object?} value
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.prototype.evalExpression = function(expr, opt_template) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var exprFunction = jsEvalToFunction(expr);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.jsexec(exprFunction, opt_template);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Uninlined string literals for jsEvalToFunction() (IE6 perf).
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var STRING_a = 'a_';
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var STRING_b = 'b_';
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var STRING_with = 'with (a_) with (b_) return ';
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Cache for jsEvalToFunction results.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type Object
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)JsEvalContext.evalToFunctionCache_ = {};
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Evaluates the given expression as the body of a function that takes
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * vars and data as arguments. Since the resulting function depends
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * only on expr, we cache the result so we save some Function
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * invocations, and some object creations in IE6.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} expr A javascript expression.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Function} A function that returns the value of expr in the
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * context of vars and data.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function jsEvalToFunction(expr) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!JsEvalContext.evalToFunctionCache_[expr]) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // NOTE(mesch): The Function constructor is faster than eval().
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      JsEvalContext.evalToFunctionCache_[expr] =
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new Function(STRING_a, STRING_b, STRING_with + expr);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (e) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      log('jsEvalToFunction (' + expr + ') EXCEPTION ' + e);
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return JsEvalContext.evalToFunctionCache_[expr];
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Evaluates the given expression to itself. This is meant to pass
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * through string attribute values.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} expr
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function jsEvalToSelf(expr) {
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return expr;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Parses the value of the jsvalues attribute in jstemplates: splits
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * it up into a map of labels and expressions, and creates functions
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * from the expressions that are suitable for execution by
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * JsEvalContext.jsexec(). All that is returned as a flattened array
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of pairs of a String and a Function.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} expr
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Array}
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function jsEvalToValues(expr) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(mesch): It is insufficient to split the values by simply
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // finding semi-colons, as the semi-colon may be part of a string
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // constant or escaped.
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ret = [];
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var values = expr.split(REGEXP_semicolon);
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0, I = jsLength(values); i < I; ++i) {
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var colon = values[i].indexOf(CHAR_colon);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (colon < 0) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var label = stringTrim(values[i].substr(0, colon));
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var value = jsEvalToFunction(values[i].substr(colon + 1));
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ret.push(label, value);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ret;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Parses the value of the jseval attribute of jstemplates: splits it
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * up into a list of expressions, and creates functions from the
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * expressions that are suitable for execution by
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * JsEvalContext.jsexec(). All that is returned as an Array of
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Function.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} expr
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Array.<Function>}
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function jsEvalToExpressions(expr) {
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ret = [];
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var values = expr.split(REGEXP_semicolon);
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0, I = jsLength(values); i < I; ++i) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (values[i]) {
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var value = jsEvalToFunction(values[i]);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ret.push(value);
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ret;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
410