CONFIG = {
  ext: function(o) {
    merge(this, o)
  }  
};

var exec, ext, log = /localhost/.test(this.location || '') ? alert : function() {};
  
var IE = !!(typeof document != 'undefined'
  && document.expando && document.uniqueID);

offset = function(node, id) {
  var n = 0;
  do {
    n   += node['offset' + id] || 0;
    node = node.offsetParent;
  } while (node);
  return n;
};

new function() {

// Components: interface modeling framework.
// Created by Adam Bones (adam@oxdi.eu)

Class = function() {
  function klass() {
    this.klass = klass;
    
    if (this.initialize)
      this.initialize.apply(this, arguments);
  };
  
  for (var i = 0; i < arguments.length; i++)
    cp(klass.prototype, arguments[i].prototype || arguments[i]);

  return klass;
}

// =================================================================================
// utils

cp = copy = function(o) {
  for (var i = 1, vs = $A(arguments); i < vs.length; i++) {
    for (var id in vs[i])
      o[id] = vs[i][id];
    if (IE)
      if (vs[i].toString.toString().indexOf('[native code]') == -1)
        o.toString = vs[i].toString;
  }
  return o;
};

merge = function(base) {
  for (var o, i = 1; o = arguments[i]; i++)
    for (var id in o) {
      switch (typeof o[id]) {
        case 'object':
          if (hash(o[id])) {
            if (!hash(base[id]))
              base[id] = {};
            merge(base[id], o[id]);
            break;
          }
        case 'function':
        case 'number':
        case 'boolean':
        case 'string':
          base[id] = o[id];
      }
    }
  return base;
}

function hash(v) {
  return typeof v == 'object' && v && !((typeof v.length == 'number') || v.constructor == Array || v.node || v.nodeType);
}


map = function() {
  var data = {}, list = typeof arguments[0] == 'string' ? arguments : arguments[0];
  for (var i = 0; i < list.length; i++)
    data[list[i]] = true;
  return data;
};

$A = function(v) { // from Prototype
  if (!v)
    return [];
  var n = v.length || 0, vs = new Array(n);
  while (n--)
    vs[n] = v[n];
  return vs;
};

// A controller for commands sent from the UI
Component = Class({
   
  fire: function() {
    var com = this, v;
    do
      v = com.exec.apply(com, arguments);
    while (v !== true && v !== false && (com = com.parent));
    return v;
  },
  
  exec: function(cmd) {
    var name, values = [];
    
    if (cmd.indexOf(' ') == -1) {
      name = Component.Translations[cmd] || cmd;
      name = 'on' + name.charAt(0).toUpperCase() + name.substring(1);
    } else
      name = cmd;
    
    if (typeof this[name] == 'function') {
      for (var j = 1; j < arguments.length; j++)
        values.push(arguments[j]);

      try {
        return this[name].apply(this, values);
      } catch (e) {
        throw this.name + '#' + name + ': ' + (e.message || e);
      }      
    }
  },
  
  start: function(callback, period) {
    var com = this, id = setInterval(function() {
      if (callback.apply(com) === false)
        clearInterval(id);
    }, period || 20);
    return this;
  }
});

Component.Translations = {
  mousedown: 'mouseDown',
  mouseup:   'mouseUp',
  mousemove: 'mouseMove',
  keypress:  'keyPress',
  keyup:     'keyUp',
  keydown:   'keyDown',
  backspace: 'backSpace'
}

Container = Class(Component, {
  
  initialize: function(node, name, flags, parent) {
    this.setHandlers(this.node = node);
    this.name   = name;
    this.flags  = flags || {};
    if (this.parent = parent)
      ;//this[parent.name] = parent;
    this.data = node.textContent;
    // DEP: properties for flags
    for (var flag in flags)
      if (typeof this[flag] == 'undefined')
        this[flag] = flags[flag];
  },
  
  ext: function(o) {
    return cp(this, o);
  },
  
  observe: function(type, b, c) {
    var com  = this, id = 'on' + type,
        node = c ? b : this.node,
        _f = node[id], f = c || b;

    node[id] = function(e) {
      var v;
      if (typeof window != 'undefined')
        e = e || window.event;

      if (_f)
        if (_f.call(com, e) === false)
          v = false;
      if (f.call(com, e) === false)
        v = false;
      if (v === false)
        if (e) {
          if (e.stopPropagation)
            e.stopPropagation();
          else
            e.cancelBubble = e.returnValue = true;
        }
      return v;
    }
    return this;
  },
  
  // until can remove deprecated exec()..
  // vs: list part of href="action:1:x"
  exec2: function(vs) {},
  
  observes: function(h) {
    cp(this, {
      exec2: function(id) {
        if (typeof h[id] == 'function') {
          h[id].apply(this, $A(arguments).slice(1));
          return true;
        }
      },
      
      observes: function(hh) {
        cp(h, hh);
        return this;
      }
    })
    return this.observe('click', function(e) {
      var node = e.target || e.srcElement;
      for (var i = 0; i < 2 && node && node.tagName != 'A'; i++)
        node = node.parentNode;
      if (node && node.href) {
        var v = node.getAttribute('href', 2);
        if (v.length > 2)
          if (v.charAt(0) == '#')
            if (this.exec2.apply(this, v.slice(1).split(':')))
              return false;
      }
    });
  },
    
  handle: function(event) {
    var x = event[IE ? 'returnValue' : 'v'];
    if (x === true || x === false)
      return x;

    var v, com, id = Component.Translations[event.type] || event.type, node = event.target || event.srcElement;
    do {
      for (var name in this)
        if (this[name] == node || (this[name] || {}).node == node) {
          v = this.exec(name == 'node' ?
            id : id + name.charAt(0).toUpperCase() + name.substring(1), event);
          if (v === true || v === false)
            return event[IE ? 'returnValue' : 'v'] = v;
        }
      if (com = this.find(function() { return this.node == node }))
        for (var name in com.flags)
          this.exec(id + name.charAt(0).toUpperCase() + name.substring(1), event);
    }
    while ((node = node.parentNode) && node != this.node.parentNode);

    return v;
  },
  
  handlers: function() {
    var match, map = {};
    for (var name in this)
      if (typeof this[name] == 'function')
        if (match = name.match(/^(on(abort|beforeunload|blur|change|click|dblclick|error|focus|keydown|keypress|keyup|load|mousedown|mousemove|mouseout|mouseover|mouseup|reset|resize|select|submit|unload))/i))
          map[match[1].toLowerCase()] = true;
    return map;
  },
  
  setHandlers: function(node, map) {
    map = map || this.klass.handlers;
    
    var com = this, handle = function(event) {
      return com.handle(event || window.event);
    };
    for (var name in map)
      if (map[name])
        node[name] = handle;    
  },
    
  reset: function(o) {
    var prev = this._prev;
    var next = (this.last() || this)._next;
    var s = '';
    
    if (typeof o == 'string')
      s = o;
    else
      for (var id in o)
        s += tag(id, o[id]);
    
    return load(this.send(function() {
      this.node.innerHTML = s; 
    }), this.parent, prev, next);
  },
    
  update: function(v) {
    return this.empty().sets(v);
  },
  
  // Set the value of an attribute on the container
  set: function(name, v) {
    name = name.toLowerCase();
    if (name != 'class' && name != 'id')
      v ? this.node.setAttribute(name, v) : this.node.removeAttribute(name);
    return this;
  },
  
  // Read the value of an attribute on the container
  read: function(name) {
    if (!name)
      return this.node.textContent || this.reads().join('');

    name = name.toLowerCase();
    if (name == 'class')
      return this.node.className;

    return this.node.getAttribute(name, 2) || '';
  },
  
  /*
    Updates the text content or value of leaf elements and text input elements in document order within the subtree.
    Accepts any number of string arguments.
    Examples:
    load('<div class="x"> <label></label> <a href="#"><strong></strong></a> </div>').sets('One', 'Two')
      // <div class="x"> <label>One</label> <a href="#"><strong>Two</strong></a> </div>    
  */
  sets: function() {
    var i = 0, list = arguments;

    this._labels(function(element) {
      if (i == list.length)
        return true;
      if (element.type == 'text')
        element.value = list[i++];
      else
        (element.firstChild || element.appendChild(element.ownerDocument.createTextNode(''))).data = list[i++];
    });
    return this;
  },
  
  // Returns an array of document ordered values for leaf element text and input values
  reads: function() {
    var data = [];
    this._labels(function(element) {
      data.push((element.firstChild ? element.firstChild.data : element.value) || '');
    });
    return data;
  },

  _labels: function(callback) {
    var v, excludes = { IMG: true, BR: true, HR: true };
    visit(this.node);
    return v;
    
    function visit(node) {
      if (typeof v == 'undefined' && node.nodeType == 1 && !excludes[node.tagName]) {
        var nodes = node.childNodes;
        if (nodes.length == 0 || (nodes.length == 1 && nodes[0].nodeType == 3))
          return v = callback(node);
        for (var i = 0; i < nodes.length; i++)
          visit(nodes[i]);        
      }
    }
  },
  
  select: function(v) {
    var flagged = this.selected === true || this.selected === false;
    
    var a = flagged ? this.find('selected') : this.selected;
    var b = v && !v.name ? this.find(v) : v;
    
    if (a != b) {
      if (a)
        a.clear('selected');
      if (b)
        b.apply('selected');
      if (!flagged)
        this.selected = b;
    }
    return b;
  },
    
  toggle: function(v, name) {
    name = name || 'on';
    if (arguments.length == 0)
      v = !this.flags[name];
    return v ? this.apply(name) : this.clear(name);
  },
  
  apply: function(name) {
    if (name == this.name)
      throw new Error('Attempting to assign .' + name + ' as a flag when it is the component name');
      
    this.flags[name] = true;

    if (typeof this[name] != 'function' &&
        typeof this[name] != 'object')
      this[name] = true;

    return this.updateNames();
  },
  
  clear: function(name) {
    if (name == this.node.id)
      return this;

    this.flags[name] = false;

    if (typeof this[name] == 'boolean')
      this[name] = false;

    return this.updateNames();
  },
    
  updateNames: function() {
    var parts = [];
        
    for (var name in this.flags)
      if (this.flags[name])
        parts.unshift(name);
    
    if (this.name != this.node.id)
      parts.push(this.name);
    
    this.node.className = parts.join(' ');    
    return this;
  },
  
  //[dep]
  remote: function(id, callback) {
    var com = this, data;
    fetch(id, function(o) {
      if (!o.error && (data = o.ok || o)) {
        if (data.constructor == Array) {
          for (var i in data)
            callback.call(com, data[i]);
        } else {
          callback.call(com, data);
        }
      }
    });
    return this;
  },
  
  ammend: function(callback) {
    callback.apply(this);
    return this;
  },
  
  fills: function(list, callback) {
    this.empty();
    if (list.constructor == Array || list.length)
      for (var i = 0; i < list.length; i++)
        callback.call(this, list[i], i);
    else
      for (var id in list)
        callback.call(this, list[id], id);
    return this;
  },
  
  append: function(name) {
    return this.insert(name);
  },
  
  fill: function(a, b, debug) {
    this.empty();
    
    var name = b ? a : false, list = b || a;

    for (var com, values, i = 0; i < list.length; i++) {
      if (typeof list[i] == 'string')
        list[i] = [list[i]];

      com    = this.add(name || list[i][0]);
      values = name ? list[i] : list[i].slice(1);

      if (values.length > 0)
        com.update.apply(com, values);
    }

    return this;
  },
  // [/dep]

  sends: function(o, f) {
    return this.send(function() {
      if (o.constructor == Array || o.length)
        for (var i = 0; i < o.length; i++)
          f.call(this, o[i], i);
      else
        for (var id in o)
          f.call(this, o[id], id);      
    });
  },
  
  send: function(f) {
    var com = this;
    this.visit('sub', function() {
      if (this.parent == com)
        return com = this;
    });
    f.apply(com);
    return this;
  },

  add: function(name, inner) {
    var names = name.split(/\.|#/);
    if (names.length > 1) {
      if (Com[names[0]]) {
        var com = this.insert(names[0]);
        for (var i = 1; i < names.length; i++)
          com.apply(names[i]);
        return com;
      } else {
        var prev, next;
        if (prev = (this.last() || this)._next)
          next = prev._next;
        return load(this.node.appendChild(build(name, inner)), this, prev, next);
      }
    }
    return this.insert(name);
  },
  
  prepend: function(name) {
    return this.insert(name, this.first());
  },
    
  insert: function(name, next) {
    return spawn(name).move(this, next);
    //return ((Com[name] && Com[name].spawn) ? Com[name].spawn() : spawn(name)).move(this, next);
  },
  
  replace: function(com) {
    //var com = this.parent.insert(name, this);
    var parent = this.parent, next = (this.last() || this)._next;
    this.remove();
    return com.move(parent, next);
  },
  
  remove: function(match) {
    if (match)
      return this.find(match).remove();

    this.detach();
    this.node.parentNode.removeChild(this.node);
    return this;
  },
  
  clone: function() {
    return load(this.node.cloneNode(true));
  },
  
  move: function(parent, next) {
    if (next)
      next.node.parentNode.insertBefore(this.node, next.node);
    else
      parent.node.appendChild(this.node);

    var last;
    if (!next) last = parent.last();

    this.detach();
    next = next || (last || parent)._next;
    this.attach(next ? next._prev : last || parent, parent, next);
        
    return this;
  },
  
  attach: function(prev, parent, next) {
    var i = this, j = this.last() || this;
    
    if (i._prev = prev) prev._next = i;
    if (j._next = next) next._prev = j;

    if (this.parent = parent) {
      this[parent.name] = parent;

      if (!parent[this.name] || this.next(this.name) == parent[this.name])
        parent[this.name] = this;
    }
  },
  
  detach: function() {
    if (this.parent) {
      if (this.parent[this.name] == this)
        this.parent[this.name] = this.next(this.name, true);
      
      this.parent = this[this.parent.name] = null;
    }
    var i = this, j = this.last() || this;

    if (i._prev) i._prev._next = j._next;
    if (j._next) j._next._prev = i._prev;
  },
  
  empty: function() {
    var com = this;
    while ((com = this._next) && this.contains(com)) com.detach();
    while (this.node.firstChild)
      this.node.removeChild(this.node.firstChild);
    return this;
  },
        
  find: function(matcher) {
    if (arguments.length < 2)
      return this.first(matcher);
      
    var com = this;
    for (var i = 0; com && (i < arguments.length); i++)
      com = com.find(arguments[i]);
    return com;
  },
    
  first: function(v) {
    var root = this;
    return this.visit(v, function() {
      if (this != root)
        return this
    });
  },
  
  last: function(v) {
    var com, root = this;
    this.visit(v, function() {
      if (this != root)
        com = this
    });
    return com;
  },
  
  up: function(matcher) {
    var com = this;
    while (com = com.parent)
      if (com.match(matcher))
        return com;
  },
  
  // collect: function(matcher, callback) {
  //   var collection = [];
  //   this.each(matcher, function() { collection.push(callback ? callback.apply(this) : this) });
  //   return collection;
  // },
  
  collect: function(m, f) {
    var vs = [];
    this.visit(m, function() { vs.push(f ? f.apply(this) : this) });
    return vs;
  },
  
  count: function(m) {
    var n = 0;
    this.visit(m, function() { n++ });
    return n;
  },
  
  each: function() {
    var matcher  = arguments[arguments.length - 2];
    var callback = arguments[arguments.length - 1];
    
    var result, com = this;
    while ((com = com._next) && this.contains(com))
      if (com.match(matcher) && typeof (result = callback.apply(com)) != 'undefined')
        return result;
  },
  
  // Apply f to each matching component of this tree, or until f returns something:
  visit: function(a, b) {
    var v, match, f = b || a;
    
    if (b)
      switch (typeof a) {
        case 'undefined': break;
        case 'function':
          match = a; break;
        case 'string':
          match = function() {
            return this.name == a 
              || this.flags[a] 
              || this[a] === true 
              || (typeof this.id == 'function' && this.id() == a)
          }; break;
        default:
          throw '#visit: Invalid matcher: ' + a;
      }

    var h = {}, com = this;
    do {
      h[com._i] = true;
      if (!match || match.apply(com))
        if (typeof (v = f.apply(com)) != 'undefined')
          return v;
    } while ((com = com._next) && com.parent && h[com.parent._i])
  },
  
  prev: function() {
    var args = [true];
    for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
    return this.seek.apply(this, args);
  },
  
  next: function() {
    var args = [false];
    for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
    return this.seek.apply(this, args);
  },

  seek: function(back) {
    var id = back ? '_prev' : '_next', com = this, matcher, sibling = false;
    
    switch (arguments.length) {
      case 2: {
        if (arguments[1] === true || arguments[1] === false)
          sibling = arguments[1];
        else
          matcher = arguments[1];
        break;
      }
      case 3: {
        sibling = arguments[2];
        matcher = arguments[1];
      }
    }

    while ((com = com[id]) && (com != template) && (!sibling || !this.parent || this.parent.contains(com)))
      if (com.match(matcher) && (!sibling || com.parent == this.parent))
        return com;
  },

  contains: function(o) {
    if (o.node) {
      while (o = o.parent)
        if (o == this) return true;
    } else {
      do
        if (o == this.node)
          return true;
      while (o = o.parentNode);
    }
    
    return false;
  },
    
  match: function(v) {
    if (!v) return true;
    if (typeof v == 'function') return v.apply(this);
    return this.name == v || this[v] === true || this.flags[v] || (typeof this.id == 'function' && this.id() == v);
  },
     
  toHTML: function() {
    return this.node.innerHTML;
  },
  
  // inspect: function() {
  //   return this.name;
  // },
  
  toJSON: function() {
    return this.name;
  },
    
  toString: function() {
    return this.name;
  }
});

/* ================================================================================= */

Com = {};

bind = function(name, x) {
  var klass, chain = [Com[name] || Container];
  for (var i = 1; i < arguments.length; i++) {
    var o = arguments[i];
    if (typeof o == 'string') {
      if (!Com[o])
        throw new Error(o + ' is not a Component type');
      chain.push(Com[o]);
    } else
      chain.push(o);
  }
  klass = Class.apply(this, chain);
  klass.handlers  = klass.prototype.handlers();

  klass.ext = function(o) {
    return cp(klass, o)
  }
  return Com[name] = klass;
};

def = function(id, inner) {
  var names, name, klass;
  
  if ((names = id.split(/\.|#/)).length < 2)
    throw 'Invalid container id: ' + id;  
  
  name = names[1];
  // if (Com[name = names[1]])
  //   throw 'Attempting to redefine ' + name + ' with ' + id;
  
  //var klass = bind.apply(this, [name].concat($A(arguments).slice(2)));
  klass = Com[name] = Class(Container);  
  
  
  ext.apply(this, [name].concat($A(arguments).slice(2)));

  klass.spawn = function() {
    return load(build(id, inner));
  }
  return klass;
};

ext = function(name) {
  // if (!Com[name])
  //   throw name + ' is not defined';
  var o = (Com[name] || bind(name)).prototype;
  for (var i = 0; i < arguments.length; i++) {
    var m = arguments[i];
    if (Com[m]) m = Com[m];
    m = m.prototype || m;
    cp(o, m);
  }
  return Com[name];
}

spawn = function(name) {
  if (Com[name])
    if (Com[name].spawn)
      return Com[name].spawn();
    else if (Com[name].tag)
      return load(build(Com[name].tag()));

  var com;

  if (com = template.find(name))
    return load(com.node.cloneNode(true));

  com = new Container(template.node.ownerDocument.createElement('div'), name);
  com.updateNames();
  return com;
}

base = function(methods) {
  Container = Class(Container, methods);
  for (var name in Com)
    cp(Com[name].prototype, methods);
};


/* ================================================================================= */
new function() {

var n = 0; // counter for components yielded so far
var h = {}; // map for components yielded so far (index by position in sequence)

load = function(o, parent, prev, next) {
  if (typeof o == 'string') 
    return load(build.apply(this, arguments))

  var top, last = prev || parent, inits = [];
  
  function link(com) {
    if (last && com)
      (last._next = com)._prev = last;
  }
  
  function visit(o, parent) {
    var com, node;
    
    if (o.node) {
      node = o.node;
      com = o;
    } else {
      node = o;
    }
    if (node.nodeType == 1) {
      if (!com) {
        var lead, name = node.id, names = load.names(node);
        for (var nname in names) {
          lead = lead || nname;
          if (Com[nname]) {
            name = nname;
            break;
          }
        }
        if (!name && parent && parent.name == 'template') // DEP: yield for template components
          bind(name = lead);
        if (name) {
          delete(names[name]);
          com = new (Com[name] || Container)(node, name, names, parent);
          (h[name] = h[name] || {})[com._i = ++n] = com; // add to the map
          if (parent) // DEP: first component of type as property
            if (!parent[name]
              || (parent[name].node && !parent[name].node.parentNode)) // fix for #reset not removing dead properties after setting innerHTML
              parent[name] = com;
          if (com.init)
            inits.push(com);        
        } 
      }      
      link(com);
      last = com || last;
      for (var i = 0, nodes = node.childNodes; i < nodes.length; i++)
        visit(nodes[i], com || parent);

      return com; 
    }    
  }
  
  top = visit(o, parent);

  link(next);
  
  for (var i = 0; i < inits.length; i++)
    inits[i].init();

  return top;
}

// Get the enclosing component (from the map)
com = lookup = function(node, containers) {
  if (node) {
    if (node.nodeType == 1)
      for (var name in load.names(node))
        if (h[name])
          for (var i in h[name])
            if (h[name][i].node == node
              || (node.e4xNode && node.e4xNode == h[name][i].node.e4xNode)) // fix for == bug in o3/dom
              return h[name][i];
    if (containers)
      return lookup(node.parentNode);  
  }
};

// Returns a map of all the names that can be extracted from class and id attributes
load.names = function(node) {
  var all = [], names = {};
  if (node.className)
    all = node.className.split(' ');
  if (node.id)
    all.unshift(node.id.toString());
  for (var i = 0; i < all.length; i++)
    if (all[i])
      names[all[i]] = true;
  return names;
};

load.count = function() {
  return n;
}

}
/* ================================================================================= */

template = null;
content  = null;

new function() {
  
start = function(callback) {
  start.callbacks.push(callback);
};
start.callbacks = [];

boot = function() {
  var node;
  
  if (node = document.getElementById('template'))
    template = load(node)
  if (node = document.getElementById('content'))
    return content = load(node)
  else
    return load(document.body);
};

var done = false, f = function() {
  var com = boot();
  for (var i = 0; i < start.callbacks.length; i++)
    start.callbacks[i].apply(com || document.body);
}

if (typeof navigator != 'undefined')
  if (/webkit/i.test(navigator.userAgent)) {
    var timeout = setTimeout(function() {
      if (document.readyState == 'loaded' || document.readyState == 'complete' ) {
        f();
      } else {
        setTimeout(arguments.callee, 10);
      }
    }, 10); 
  } else if ((/mozilla/i.test(navigator.userAgent) && !/(compatible)/i.test(navigator.userAgent)) || (/opera/i.test(navigator.userAgent))) {
    document.addEventListener('DOMContentLoaded', f, false);
  } else if (document.uniqueID && document.expando) { // http://www.hedgerwow.com/360/dhtml/ie-dom-ondocumentready.html
    var element = document.createElement('span'); 

    (function () { 
      if (done) return;

      try {
        element.doScroll('left');
    
        if (!document.body)
          throw new Error();
    
        done = true;
        f();
        element = null; 
      } catch(e) {
        setTimeout(arguments.callee, 0); 
      } 
    })();
  }

}

/* ================================================================================= */
// Raw ajaxy request.

xhr = function(method, url, body, headers, f) {
  var o;
  try {
    o = new ActiveXObject('Msxml2.XMLHTTP')
  } catch(e) {
    try {
      o = new ActiveXObject('Microsoft.XMLHTTP')
    } catch(e) {
      o = new XMLHttpRequest()
    }
  }
  
  o.open(method, url, true);
  for (var id in headers)
    o.setRequestHeader(id, headers[id]);
  
  o.onreadystatechange = function() {
    switch (o.readyState) {
      case 4: {
        if (f)
          f(o.responseText, o);
        o.onreadystatechange = function() {};
      }
    }
  }
  o.send(body);
  return o;
}
/* ================================================================================= */

bind('sub');

// commands:
new function() {
  var ctx = {}, h = {};

  cmd = function(a, b) {
    var o = this, hh = h, x = b || a;
    if (b) {
      var id = a;
      if (a.name) {
        id = a._i; ctx[id] = a;
      }
      hh = hh[id] = (hh[id] || {});
    }
    for (var id in x)
      hh[id.toLowerCase()] = x[id];
    return b || a;
  }
  exec = function(vs) {
    var o = this, hh = h, i = 0, v;
    while (typeof (v = vs[i++]) == 'string' && (v = v.toLowerCase()) &&
           typeof hh[v] == 'object') {
      hh = hh[v]; o = ctx[v] || this;
    }
    if (typeof hh[v] == 'function') {
      //try {
        hh[v].apply(o, vs.slice(i));
      // } catch (e) {
      //   log(v + ': ' + (e.message || e));
      // }
      return true;
    }
  };
  if (typeof document != 'undefined')
    new Container(document).observe('click', function(e) {
      var v, node = e.target || e.srcElement;

      for (var i = 0; i < 2 && node && node.tagName != 'A'; i++)
        node = node.parentNode;

      if (node && node.getAttribute)
        if (v = node.getAttribute('href', 2))
          if (v.length > 1) // ignore solo hash
            if (v.charAt(0) == '#')
              //if (exec(v.slice(1).split(':').concat([node])))
              if (exec(v.slice(1).split(':')))
                return false;
    });
}

}

// =================================================================================

var tag, tags, html, build, $;

new function() {

  var CLOSING = map('meta', 'link', 'img', 'br', 'hr', 'link', 'input', 'embed'),
      ATTR    = map(
        'class', 'name', 'id', 'lang',
        'style', 'src', 'width', 'height', 'alt', 'media',
        'href', 'rel', 'content', 'title', 'id', 'charset', 'http-equiv',
        'action', 'method', 'enctype', 'target', 'name', 'value', 'type', 'selected', 'checked',
        'rowspan', 'colspan', 'cellspacing', 'cellpadding',
        'classid', 'codebase', 'autoplay', 'controller', 'pluginspage', 'wmode', 'flashVars', 'scale',
        'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onchange', 'onload', 'onsubmit',
        'colSpan', 'rowSpan'
      ),
      WRAP = {
        'td': 'tr', 'tr': 'tbody', 'tbody': 'table',
        'li': 'ul',
        'option': 'select'
      };

  tag = function(id, o) {
    var name, names = {}, attr = {}, inner = '', sep, s = '';
  
    function word() {
      switch (sep) {
        case '.': {
          names[s] = true;
          break;        
        }
        case '#': {
          attr['id'] = s;
          break;
        }
        case '$': {
          if (!$[s])
            throw 'Unknown $: ' + s;
        
          o = $[s](o);
          break;
        }
        default: {
          name = s || 'div';
        }
      }    
      s = ''; sep = '';
    }
  //try {
    for (var ch, i = 0; i < id.length; i++)
      if ((ch = id.charAt(i)) == '.' || ch == '#' || ch == '$') {
        word();
        sep = ch;
      } else
        s += ch;
  //} catch (e) { throw id }
    word();
  
    function exclude(v) {
      return v === null || v === false || typeof v == 'undefined';
    }
  
    function unpack(o) {
      if (!exclude(o))
        switch (typeof o) {
          case 'number':
          case 'string': {
            inner += encode('' + o);
            break;
          }
          case 'object': {
            var v;
            if (o)
              for (var id in o) 
                if (!exclude(v = o[id])) {
                  id = id.replace(/:.*$/, '');

                  if (typeof v == 'function') // code
                    v = v.toString().replace(/^function\s*\(\)\s*\{\s*/, '').replace(/\s*}$/, '');

                  switch (id) {
                    case 'data':
                      inner += encode('' + v); break;
                    case '':
                    case 'inner':
                      switch (typeof v) {
                        case 'number':
                        case 'string': {
                          inner += '' + v;
                          break;
                        }
                        case 'object':
                          unpack(v);
                      }
                      break;
                    case 'tag':
                      name = v; break;
                    case 'flags': {
                      for (var id in v)
                        cp(names, v);
                      break;
                    }
                    case 'classes':
                      for (var i = 0; i < v.length; i++)
                        names[v[i]] = true;
                      break;
                    default:
                      switch (typeof v) {
                        case 'boolean':
                          names[id] = v; break;
                          // if (!ATTR[id]) {
                          //   names[id] = v;
                          //   break;
                          // }
                        case 'number':
                        case 'string':
                          if (ATTR[id] || id.indexOf('data-') == 0) {
                            attr[id] = encode(v);
                            break;
                          }
                        default: 
                           inner += tag(id, v);
                      }
                  }
                }
          } // object
        } // switch
    } // unpack
  
    unpack(o);
  
    var classes = [];
    for (var nname in names)
      if (names[nname])
        classes.push(nname);
    if (classes.length > 0)
      attr['class'] = classes.join(' ');
    
    var s = '';
  
    s += '<' + name;
    for (var id in attr)
      s += ' ' + id.toLowerCase() + '="' + attr[id] + '"';
    if (CLOSING[name])
      s += ' />';
    else
      s += '>' + inner + '</' + name + '>';
  
    return s;
  }

  function encode(s) {
  	if (/["\\\x00-\x1f&<>"']/.test(s))
  	  s = s.replace(/([\x00-\x1f\\"&<>"'])/g, function(a, b) {
  	    switch(b){
  	      case '&':  return '&amp;';
  	      case '<':  return '&lt;';
  	      case '>':  return '&gt;';
  	      case '"':  return '&quot;';
  	      case '\'': return '&#39;';
  	    }
  	    c = b.charCodeAt();
  	    return '&#x00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16) + ";";
  	  });
  	return s;
  };

  tags = function(id, o, f) {
    var s = '';
    if (o)
      if (o.length)
        for (var iid, i = 0; i < o.length; i++) {
          iid = id;
          if (i == 0)
            iid += '.first';
          if (i == o.length - 1)
            iid += '.last';
          s += tag(iid, f ? f(o[i], i) : o[i]);
        }
      else
        for (var v in o)
          if (o[v])
            s += tag(id, f ? f(v, o[v]) : v);
    return { inner: s };
  }

  $ = function(id, f) {
    $[id] = f;
  }
  $.list = function(o) {
    if (o.n) {
      var v = {}, i = 0;
      while (i++ < o.n) v[o.id + ':' + i] = o.f ? o.f(i) : {};
      return { inner: v };
    }
    return tags(o.id, o.vs, o.f);
  }

  build = function(a, b) {
    var node, s, n = 1;
    if (b || a.indexOf('<') == -1) {
      s = tag(a, b);
      var i = a.indexOf('.'), name = i > -1 ? a.slice(0, i) : a;
      while (WRAP[name]) {
        s = tag(WRAP[name], { inner: s });
        name = WRAP[name];
        n++;
      }
    } else
      s = a;

    if (document.e4xNode)
      node = document.createElement('div');
    else
      node = build.node = build.node || document.createElement('div');

    node.innerHTML = s;

    while (n-- > 0)
      node = node.firstChild;
    
    return node;
  }

}