text: // [new] Blaze.Template([viewName], renderFunction)
  //
  // `Blaze.Template` is the class of templates, like `Template.foo` in
  // Meteor, which is `instanceof Template`.
  //
  // `viewKind` is a string that looks like "Template.foo" for templates
  // defined by the compiler.
  
  /**
   * @class
   * @summary Constructor for a Template, which is used to construct Views with particular name and content.
   * @locus Client
   * @param {String} [viewName] Optional.  A name for Views constructed by this Template.  See [`view.name`](#view_name).
   * @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content).  This function is used as the `renderFunction` for Views constructed by this Template.
   */
  Blaze.Template = function (viewName, renderFunction) {
    if (! (this instanceof Blaze.Template))
      // called without `new`
      return new Blaze.Template(viewName, renderFunction);
  
    if (typeof viewName === 'function') {
      // omitted "viewName" argument
      renderFunction = viewName;
      viewName = '';
    }
    if (typeof viewName !== 'string')
      throw new Error("viewName must be a String (or omitted)");
    if (typeof renderFunction !== 'function')
      throw new Error("renderFunction must be a function");
  
    this.viewName = viewName;
    this.renderFunction = renderFunction;
  
    this.__helpers = new HelperMap;
    this.__eventMaps = [];
  
    this._callbacks = {
      created: [],
      rendered: [],
      destroyed: []
    };
  };
  var Template = Blaze.Template;
  
  var HelperMap = function () {};
  HelperMap.prototype.get = function (name) {
    return this[' '+name];
  };
  HelperMap.prototype.set = function (name, helper) {
    this[' '+name] = helper;
  };
  HelperMap.prototype.has = function (name) {
    return (' '+name) in this;
  };
  
  /**
   * @summary Returns true if `value` is a template object like `Template.myTemplate`.
   * @locus Client
   * @param {Any} value The value to test.
   */
  Blaze.isTemplate = function (t) {
    return (t instanceof Blaze.Template);
  };
  
  /**
   * @name  onCreated
   * @instance
   * @memberOf Template
   * @summary Register a function to be called when an instance of this template is created.
   * @param {Function} callback A function to be added as a callback.
   * @locus Client
   */
  Template.prototype.onCreated = function (cb) {
    this._callbacks.created.push(cb);
  };
  
  /**
   * @name  onRendered
   * @instance
   * @memberOf Template
   * @summary Register a function to be called when an instance of this template is inserted into the DOM.
   * @param {Function} callback A function to be added as a callback.
   * @locus Client
   */
  Template.prototype.onRendered = function (cb) {
    this._callbacks.rendered.push(cb);
  };
  
  /**
   * @name  onDestroyed
   * @instance
   * @memberOf Template
   * @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed.
   * @param {Function} callback A function to be added as a callback.
   * @locus Client
   */
  Template.prototype.onDestroyed = function (cb) {
    this._callbacks.destroyed.push(cb);
  };
  
  Template.prototype._getCallbacks = function (which) {
    var self = this;
    var callbacks = self[which] ? [self[which]] : [];
    // Fire all callbacks added with the new API (Template.onRendered())
    // as well as the old-style callback (e.g. Template.rendered) for
    // backwards-compatibility.
    callbacks = callbacks.concat(self._callbacks[which]);
    return callbacks;
  };
  
  var fireCallbacks = function (callbacks, template) {
    Template._withTemplateInstanceFunc(
      function () { return template; },
      function () {
        for (var i = 0, N = callbacks.length; i < N; i++) {
          callbacks[i].call(template);
        }
      });
  };
  
  Template.prototype.constructView = function (contentFunc, elseFunc) {
    var self = this;
    var view = Blaze.View(self.viewName, self.renderFunction);
    view.template = self;
  
    view.templateContentBlock = (
      contentFunc ? new Template('(contentBlock)', contentFunc) : null);
    view.templateElseBlock = (
      elseFunc ? new Template('(elseBlock)', elseFunc) : null);
  
    if (self.__eventMaps || typeof self.events === 'object') {
      view._onViewRendered(function () {
        if (view.renderCount !== 1)
          return;
  
        if (! self.__eventMaps.length && typeof self.events === "object") {
          // Provide limited back-compat support for `.events = {...}`
          // syntax.  Pass `template.events` to the original `.events(...)`
          // function.  This code must run only once per template, in
          // order to not bind the handlers more than once, which is
          // ensured by the fact that we only do this when `__eventMaps`
          // is falsy, and we cause it to be set now.
          Template.prototype.events.call(self, self.events);
        }
  
        _.each(self.__eventMaps, function (m) {
          Blaze._addEventMap(view, m, view);
        });
      });
    }
  
    view._templateInstance = new Blaze.TemplateInstance(view);
    view.templateInstance = function () {
      // Update data, firstNode, and lastNode, and return the TemplateInstance
      // object.
      var inst = view._templateInstance;
  
      /**
       * @instance
       * @memberOf Blaze.TemplateInstance
       * @name  data
       * @summary The data context of this instance's latest invocation.
       * @locus Client
       */
      inst.data = Blaze.getData(view);
  
      if (view._domrange && !view.isDestroyed) {
        inst.firstNode = view._domrange.firstNode();
        inst.lastNode = view._domrange.lastNode();
      } else {
        // on 'created' or 'destroyed' callbacks we don't have a DomRange
        inst.firstNode = null;
        inst.lastNode = null;
      }
  
      return inst;
    };
  
    /**
     * @name  created
     * @instance
     * @memberOf Template
     * @summary Provide a callback when an instance of a template is created.
     * @locus Client
     * @deprecated in 1.1
     */
    // To avoid situations when new callbacks are added in between view
    // instantiation and event being fired, decide on all callbacks to fire
    // immediately and then fire them on the event.
    var createdCallbacks = self._getCallbacks('created');
    view.onViewCreated(function () {
      fireCallbacks(createdCallbacks, view.templateInstance());
    });
  
    /**
     * @name  rendered
     * @instance
     * @memberOf Template
     * @summary Provide a callback when an instance of a template is rendered.
     * @locus Client
     * @deprecated in 1.1
     */
    var renderedCallbacks = self._getCallbacks('rendered');
    view.onViewReady(function () {
      fireCallbacks(renderedCallbacks, view.templateInstance());
    });
  
    /**
     * @name  destroyed
     * @instance
     * @memberOf Template
     * @summary Provide a callback when an instance of a template is destroyed.
     * @locus Client
     * @deprecated in 1.1
     */
    var destroyedCallbacks = self._getCallbacks('destroyed');
    view.onViewDestroyed(function () {
      fireCallbacks(destroyedCallbacks, view.templateInstance());
    });
  
    return view;
  };
  
  /**
   * @class
   * @summary The class for template instances
   * @param {Blaze.View} view
   * @instanceName template
   */
  Blaze.TemplateInstance = function (view) {
    if (! (this instanceof Blaze.TemplateInstance))
      // called without `new`
      return new Blaze.TemplateInstance(view);
  
    if (! (view instanceof Blaze.View))
      throw new Error("View required");
  
    view._templateInstance = this;
  
    /**
     * @name view
     * @memberOf Blaze.TemplateInstance
     * @instance
     * @summary The [View](#blaze_view) object for this invocation of the template.
     * @locus Client
     * @type {Blaze.View}
     */
    this.view = view;
    this.data = null;
  
    /**
     * @name firstNode
     * @memberOf Blaze.TemplateInstance
     * @instance
     * @summary The first top-level DOM node in this template instance.
     * @locus Client
     * @type {DOMNode}
     */
    this.firstNode = null;
  
    /**
     * @name lastNode
     * @memberOf Blaze.TemplateInstance
     * @instance
     * @summary The last top-level DOM node in this template instance.
     * @locus Client
     * @type {DOMNode}
     */
    this.lastNode = null;
  
    // This dependency is used to identify state transitions in
    // _subscriptionHandles which could cause the result of
    // TemplateInstance#subscriptionsReady to change. Basically this is triggered
    // whenever a new subscription handle is added or when a subscription handle
    // is removed and they are not ready.
    this._allSubsReadyDep = new Tracker.Dependency();
    this._allSubsReady = false;
  
    this._subscriptionHandles = {};
  };
  
  /**
   * @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object.
   * @locus Client
   * @param {String} selector The CSS selector to match, scoped to the template contents.
   * @returns {DOMNode[]}
   */
  Blaze.TemplateInstance.prototype.$ = function (selector) {
    var view = this.view;
    if (! view._domrange)
      throw new Error("Can't use $ on template instance with no DOM");
    return view._domrange.$(selector);
  };
  
  /**
   * @summary Find all elements matching `selector` in this template instance.
   * @locus Client
   * @param {String} selector The CSS selector to match, scoped to the template contents.
   * @returns {DOMElement[]}
   */
  Blaze.TemplateInstance.prototype.findAll = function (selector) {
    return Array.prototype.slice.call(this.$(selector));
  };
  
  /**
   * @summary Find one element matching `selector` in this template instance.
   * @locus Client
   * @param {String} selector The CSS selector to match, scoped to the template contents.
   * @returns {DOMElement}
   */
  Blaze.TemplateInstance.prototype.find = function (selector) {
    var result = this.$(selector);
    return result[0] || null;
  };
  
  /**
   * @summary A version of [Tracker.autorun](#tracker_autorun) that is stopped when the template is destroyed.
   * @locus Client
   * @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object.
   */
  Blaze.TemplateInstance.prototype.autorun = function (f) {
    return this.view.autorun(f);
  };
  
  /**
   * @summary A version of [Meteor.subscribe](#meteor_subscribe) that is stopped
   * when the template is destroyed.
   * @return {SubscriptionHandle} The subscription handle to the newly made
   * subscription. Call `handle.stop()` to manually stop the subscription, or
   * `handle.ready()` to find out if this particular subscription has loaded all
   * of its inital data.
   * @locus Client
   * @param {String} name Name of the subscription.  Matches the name of the
   * server's `publish()` call.
   * @param {Any} [arg1,arg2...] Optional arguments passed to publisher function
   * on server.
   * @param {Function|Object} [options] If a function is passed instead of an
   * object, it is interpreted as an `onReady` callback.
   * @param {Function} [options.onReady] Passed to [`Meteor.subscribe`](#meteor_subscribe).
   * @param {Function} [options.onStop] Passed to [`Meteor.subscribe`](#meteor_subscribe).
   * @param {DDP.Connection} [options.connection] The connection on which to make the
   * subscription.
   */
  Blaze.TemplateInstance.prototype.subscribe = function (/* arguments */) {
    var self = this;
  
    var subHandles = self._subscriptionHandles;
    var args = _.toArray(arguments);
  
    // Duplicate logic from Meteor.subscribe
    var options = {};
    if (args.length) {
      var lastParam = _.last(args);
  
      // Match pattern to check if the last arg is an options argument
      var lastParamOptionsPattern = {
        onReady: Match.Optional(Function),
        // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use
        // onStop with an error callback instead.
        onError: Match.Optional(Function),
        onStop: Match.Optional(Function),
        connection: Match.Optional(Match.Any)
      };
  
      if (_.isFunction(lastParam)) {
        options.onReady = args.pop();
      } else if (lastParam && ! _.isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) {
        options = args.pop();
      }
    }
  
    var subHandle;
    var oldStopped = options.onStop;
    options.onStop = function (error) {
      // When the subscription is stopped, remove it from the set of tracked
      // subscriptions to avoid this list growing without bound
      delete subHandles[subHandle.subscriptionId];
  
      // Removing a subscription can only change the result of subscriptionsReady
      // if we are not ready (that subscription could be the one blocking us being
      // ready).
      if (! self._allSubsReady) {
        self._allSubsReadyDep.changed();
      }
  
      if (oldStopped) {
        oldStopped(error);
      }
    };
  
    var connection = options.connection;
    var callbacks = _.pick(options, ["onReady", "onError", "onStop"]);
  
    // The callbacks are passed as the last item in the arguments array passed to
    // View#subscribe
    args.push(callbacks);
  
    // View#subscribe takes the connection as one of the options in the last
    // argument
    subHandle = self.view.subscribe.call(self.view, args, {
      connection: connection
    });
  
    if (! _.has(subHandles, subHandle.subscriptionId)) {
      subHandles[subHandle.subscriptionId] = subHandle;
  
      // Adding a new subscription will always cause us to transition from ready
      // to not ready, but if we are already not ready then this can't make us
      // ready.
      if (self._allSubsReady) {
        self._allSubsReadyDep.changed();
      }
    }
  
    return subHandle;
  };
  
  /**
   * @summary A reactive function that returns true when all of the subscriptions
   * called with [this.subscribe](#TemplateInstance-subscribe) are ready.
   * @return {Boolean} True if all subscriptions on this template instance are
   * ready.
   */
  Blaze.TemplateInstance.prototype.subscriptionsReady = function () {
    this._allSubsReadyDep.depend();
  
    this._allSubsReady = _.all(this._subscriptionHandles, function (handle) {
      return handle.ready();
    });
  
    return this._allSubsReady;
  };
  
  /**
   * @summary Specify template helpers available to this template.
   * @locus Client
   * @param {Object} helpers Dictionary of helper functions by name.
   */
  Template.prototype.helpers = function (dict) {
    for (var k in dict)
      this.__helpers.set(k, dict[k]);
  };
  
  // Kind of like Blaze.currentView but for the template instance.
  // This is a function, not a value -- so that not all helpers
  // are implicitly dependent on the current template instance's `data` property,
  // which would make them dependenct on the data context of the template
  // inclusion.
  Template._currentTemplateInstanceFunc = null;
  
  Template._withTemplateInstanceFunc = function (templateInstanceFunc, func) {
    if (typeof func !== 'function')
      throw new Error("Expected function, got: " + func);
    var oldTmplInstanceFunc = Template._currentTemplateInstanceFunc;
    try {
      Template._currentTemplateInstanceFunc = templateInstanceFunc;
      return func();
    } finally {
      Template._currentTemplateInstanceFunc = oldTmplInstanceFunc;
    }
  };
  
  /**
   * @summary Specify event handlers for this template.
   * @locus Client
   * @param {EventMap} eventMap Event handlers to associate with this template.
   */
  Template.prototype.events = function (eventMap) {
    var template = this;
    var eventMap2 = {};
    for (var k in eventMap) {
      eventMap2[k] = (function (k, v) {
        return function (event/*, ...*/) {
          var view = this; // passed by EventAugmenter
          var data = Blaze.getData(event.currentTarget);
          if (data == null)
            data = {};
          var args = Array.prototype.slice.call(arguments);
          var tmplInstanceFunc = _.bind(view.templateInstance, view);
          args.splice(1, 0, tmplInstanceFunc());
  
          return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () {
            return v.apply(data, args);
          });
        };
      })(k, eventMap[k]);
    }
  
    template.__eventMaps.push(eventMap2);
  };
  
  /**
   * @function
   * @name instance
   * @memberOf Template
   * @summary The [template instance](#template_inst) corresponding to the current template helper, event handler, callback, or autorun.  If there isn't one, `null`.
   * @locus Client
   * @returns {Blaze.TemplateInstance}
   */
  Template.instance = function () {
    return Template._currentTemplateInstanceFunc
      && Template._currentTemplateInstanceFunc();
  };
  
  // Note: Template.currentData() is documented to take zero arguments,
  // while Blaze.getData takes up to one.
  
  /**
   * @summary
   *
   * - Inside an `onCreated`, `onRendered`, or `onDestroyed` callback, returns
   * the data context of the template.
   * - Inside an event handler, returns the data context of the template on which
   * this event handler was defined.
   * - Inside a helper, returns the data context of the DOM node where the helper
   * was used.
   *
   * Establishes a reactive dependency on the result.
   * @locus Client
   * @function
   */
  Template.currentData = Blaze.getData;
  
  /**
   * @summary Accesses other data contexts that enclose the current data context.
   * @locus Client
   * @function
   * @param {Integer} [numLevels] The number of levels beyond the current data context to look. Defaults to 1.
   */
  Template.parentData = Blaze._parentData;
  
  /**
   * @summary Defines a [helper function](#template_helpers) which can be used from all templates.
   * @locus Client
   * @function
   * @param {String} name The name of the helper function you are defining.
   * @param {Function} function The helper function itself.
   */
  Template.registerHelper = Blaze.registerHelper;
  