text: /**
   * Module dependencies.
   */
  
  var adapter = require('./adapter');
  var AttrBinding = require('./attr-binding');
  var TextBinding = require('./text-binding');
  var debug = require('debug')('reactive');
  var bindings = require('./bindings');
  var Binding = require('./binding');
  var utils = require('./utils');
  var query = require('component-query');
  
  /**
   * Expose `Reactive`.
   */
  
  exports = module.exports = Reactive;
  
  /**
   * Bindings.
   */
  
  exports.bindings = {};
  
  /**
   * Define subscription function.
   *
   * @param {Function} fn
   * @api public
   */
  
  exports.subscribe = function(fn){
    adapter.subscribe = fn;
  };
  
  /**
   * Define unsubscribe function.
   *
   * @param {Function} fn
   * @api public
   */
  
  exports.unsubscribe = function(fn){
    adapter.unsubscribe = fn;
  };
  
  /**
   * Define a get function.
   *
   * @param {Function} fn
   * @api public
   */
  
  exports.get = function(fn) {
    adapter.get = fn;
  };
  
  /**
   * Define a set function.
   *
   * @param {Function} fn
   * @api public
   */
  
  exports.set = function(fn) {
    adapter.set = fn;
  };
  
  /**
   * Expose adapter
   */
  
  exports.adapter = adapter;
  
  /**
   * Define binding `name` with callback `fn(el, val)`.
   *
   * @param {String} name or object
   * @param {String|Object} name
   * @param {Function} fn
   * @api public
   */
  
  exports.bind = function(name, fn){
    if ('object' == typeof name) {
      for (var key in name) {
        exports.bind(key, name[key]);
      }
      return;
    }
  
    exports.bindings[name] = fn;
  };
  
  /**
   * Middleware
   * @param {Function} fn
   * @api public
   */
  
  exports.use = function(fn) {
    fn(exports);
    return this;
  };
  
  /**
   * Initialize a reactive template for `el` and `obj`.
   *
   * @param {Element} el
   * @param {Element} obj
   * @param {Object} options
   * @api public
   */
  
  function Reactive(el, model, view) {
    if (!(this instanceof Reactive)) return new Reactive(el, model, view);
    this.adapter = exports.adapter;
    this.el = el;
    this.model = model;
    this.els = [];
    this.view = view || {};
    this.bindAll();
    this.bindInterpolation(this.el, []);
  }
  
  /**
   * Subscribe to changes on `prop`.
   *
   * @param {String} prop
   * @param {Function} fn
   * @return {Reactive}
   * @api private
   */
  
  Reactive.prototype.sub = function(prop, fn){
    this.adapter.subscribe(this.model, prop, fn);
    return this;
  };
  
  /**
   * Unsubscribe to changes from `prop`.
   *
   * @param {String} prop
   * @param {Function} fn
   * @return {Reactive}
   * @api private
   */
  
  Reactive.prototype.unsub = function(prop, fn){
    this.adapter.unsubscribe(this.model, prop, fn);
    return this;
  };
  
  /**
   * Get a `prop`
   *
   * @param {String} prop
   * @param {Mixed} val
   * @return {Mixed}
   * @api private
   */
  
  Reactive.prototype.get = function(prop) {
    return this.adapter.get(this.model, prop);
  };
  
  /**
   * Set a `prop`
   *
   * @param {String} prop
   * @param {Mixed} val
   * @return {Reactive}
   * @api private
   */
  
  Reactive.prototype.set = function(prop, val) {
    this.adapter.set(this.model, prop, val);
    return this;
  };
  
  /**
   * Traverse and bind all interpolation within attributes and text.
   *
   * @param {Element} el
   * @api private
   */
  
  Reactive.prototype.bindInterpolation = function(el, els){
  
    // element
    if (el.nodeType == 1) {
      for (var i = 0; i < el.attributes.length; i++) {
        var attr = el.attributes[i];
        if (utils.hasInterpolation(attr.value)) {
          new AttrBinding(this, el, attr);
        }
      }
    }
  
    // text node
    if (el.nodeType == 3) {
      if (utils.hasInterpolation(el.data)) {
        debug('bind text "%s"', el.data);
        new TextBinding(this, el);
      }
    }
  
    // walk nodes
    for (var i = 0; i < el.childNodes.length; i++) {
      var node = el.childNodes[i];
      this.bindInterpolation(node, els);
    }
  };
  
  /**
   * Apply all bindings.
   *
   * @api private
   */
  
  Reactive.prototype.bindAll = function() {
    for (var name in exports.bindings) {
      this.bind(name, exports.bindings[name]);
    }
  };
  
  /**
   * Bind `name` to `fn`.
   *
   * @param {String|Object} name or object
   * @param {Function} fn
   * @api public
   */
  
  Reactive.prototype.bind = function(name, fn) {
    if ('object' == typeof name) {
      for (var key in name) {
        this.bind(key, name[key]);
      }
      return;
    }
  
    var els = query.all('[' + name + ']', this.el);
    if (this.el.hasAttribute && this.el.hasAttribute(name)) {
      els = [].slice.call(els);
      els.unshift(this.el);
    }
    if (!els.length) return;
  
    debug('bind [%s] (%d elements)', name, els.length);
    for (var i = 0; i < els.length; i++) {
      var binding = new Binding(name, this, els[i], fn);
      binding.bind();
    }
  };
  
  /**
   * Use middleware
   *
   * @api public
   */
  
  Reactive.prototype.use = function(fn) {
    fn(this);
    return this;
  };
  
  // bundled bindings
  
  exports.use(bindings);
  