function ObjectExtender(members) {
  function addEvent(obj, evType, func) {
    var handler = function(ev) {
      var ret;
      if (!('pageX' in ev) && ('clientX' in ev))
        ev.pageX = ev.clientX + window.pageXOffset;
      if (!('pageY' in ev) && ('clientY' in ev))
        ev.pageY = ev.clientY + window.pageYOffset;
      if (ev.target)
        ret = func.call(ev.target, ev);
      else if (ev.srcElement)
        ret = func.call(ev.srcElement, ev);
      else
        ret = func.call(this, ev);
      if ((ret === false) && ev && ev.preventDefault)
        ev.preventDefault();
      return ret;
    };
    if (obj.addEventListener)
      obj.addEventListener(evType, handler, false);
    else if (obj.attachEvent)
      obj.attachEvent('on'+evType, handler);
  }

  function extend(obj, members) {
    var onExtend = null;
    for (var member in members)
      if (member.substring(0, 2) != 'on') {
        if (!obj[member])
          obj[member] = members[member];
      } else if (member.toLowerCase() == 'onextend')
        onExtend = members[member];
      else
        addEvent(obj, member.substring(2).toLowerCase(), members[member]);
    if (onExtend)
      onExtend.call(obj);
    return obj;
  }

  var extender = function(obj) {
    return extend(obj, arguments.callee.Members);
  };
  extender.Members = members;
  return extender;
}

(new ObjectExtender({
XMLHttpRequest: function() {
  try {
    return new ActiveXObject("Msxml2.XMLHTTP");
  } catch(e) {
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
},

noFocus: 0,

onLoad: function() {
  Document(document);
  document.oldTitle = document.title;
  bouchot = Bouchot(document.getElementById("bouchot"));
},

onBlur: function() {
  window.noFocus = 1;
},

onFocus: function() {
  window.noFocus = 0;
  if (document.oldTitle)
    document.title = document.oldTitle;
}
}))(window);

if (!('pageXOffset' in window)) {
  (new ObjectExtender({
  pageXOffset: 0,
  pageYOffset: 0,
  onScroll: function() {
    this.pageXOffset = document.body.scrollLeft || document.documentElement.scrollLeft;
    this.pageYOffset = document.body.scrollTop || document.documentElement.scrollTop;
  }
  }))(window);
}

Document = new ObjectExtender({
onExtend: function() {
  var head = document.getElementsByTagName('head')[0];
  this._styleNode = document.createElement('style');
  this._styleNode.setAttribute('type', 'text/css');
  this._styleNode.setAttribute('id', 'styleNode');
  head.appendChild(this._styleNode);
},

importNode: function(node, recurse) {
  switch (node.nodeType) {
    case 1:
      var clone = this.createElement(node.nodeName);
      if (node.attributes && node.attributes.length > 0)
        for (var i = 0; i < node.attributes.length; i++) {
          var name = node.attributes[i].nodeName;
          var value = node.getAttribute(node.attributes[i].nodeName);
          if (name == 'class')
            clone.className = value;
          else
            clone.setAttribute(name, value);
        }
      if (recurse && node.firstChild)
        for (var child = node.firstChild; child; child = child.nextSibling)
          clone.appendChild(this.importNode(child, recurse));
      return clone;
    case 3:
      return this.createTextNode(node.nodeValue);
  }
},

getStyle: function(selector) {
  if (this.styleSheets)
    for (var i = 0; i < this.styleSheets.length; i++) {
      var rules;
      if (!((rules = this.styleSheets[i].cssRules) ||
            (rules = this.styleSheets[i].rules)))
        break;
      for (var j = 0; j < rules.length; j++)
        if (rules[j].selectorText == selector)
          return rules[j].style.cssText;
    }
  return '';
},

setExtraStyle: function(style) {
  if (this._styleNode.styleSheet)
    this._styleNode.styleSheet.cssText = style;
  else if (this._styleNode.firstChild)
    this._styleNode.replaceChild(document.createTextNode(style), this._styleNode.firstChild);
  else
    this._styleNode.appendChild(document.createTextNode(style));
},

clearExtraStyle: function() {
  this.setExtraStyle('');
}
});

var Field = new ObjectExtender(
{
getSelectionRange: function() {
  if (document.selection) {
    var range = document.selection.createRange();
    if (range.parentElement() == this) {
      var range2 = this.createTextRange();
      range2.collapse(true);
      range2.setEndPoint('EndToEnd', range);
      return [range2.text.length - range.text.length, range2.text.length];
    }
    return [this.value.length, this.value.length];
  } else
    return [this.selectionStart, this.selectionEnd];
},

setSelectionRange: function(start, end) {
  if (this.createTextRange) {
    var range = this.createTextRange();
    range.collapse(true);
    range.moveStart('character', start);
    range.moveEnd('character', end - start);
    range.select();
  }
},

replaceSelection: function(newText) {
  var temp = this.value;
  var range = this.getSelectionRange();
  var before = temp.substring(0, range[0]);
  if (before.length > 0 && !before.substring(before.length - 1).match(/\W/))
    before += ' ';
  if (temp.substring(range[1], range[1] + 1).match(/\W/))
    range[1]++;
  this.value = before + newText + ' ' + temp.substring(range[1], temp.length);
  this.focus();
  var newPos = before.length + newText.length + 1;
  this.setSelectionRange(newPos, newPos);
},

encloseSelection: function(before, after) {
  var temp = this.value;
  var range = this.getSelectionRange();
  this.value = temp.substring(0, range[0]) + before + temp.substring(range[0], range[1]) + after + temp.substring(range[1], temp.length);
  this.setSelectionRange(range[0] + before.length, range[1] + before.length);
},

onKeyDown: function(ev) {
  if (ev.altKey) {
    if ((ev.keyCode == 66) || (ev.keyCode == 73) || (ev.keyCode == 85) || (ev.keyCode == 77) || (ev.keyCode == 83) || ev.keyCode == 84) {
      var c = String.fromCharCode(ev.keyCode + 32);
      if (c == 't') c += 't';
      this.encloseSelection('<' + c + '>', '</' + c + '>');
      return false;
    }
  }
},

onBlur: function() {
  window.noFocus = 1; // IE is stupid
},

onFocus: function() {
  window.noFocus = 0; // IE is stupid
}
});

var Bouchot = new ObjectExtender({
onExtend: function() {
  for (var node = this.firstChild, next = null; node; node = next) {
    next = node.nextSibling;
    if (node.nodeType == 1)
      Post(node);
    else
      this.removeChild(node);
  }
  this.form = Form(document.forms[0]);
  Field(this.form.content).focus();
  this.setTimer();
  /* Find out whether position: fixed is supported */
  var tmpNode = document.createElement('div');
  tmpNode.style.width = '1px';
  tmpNode.style.height = '1px';
  tmpNode.style.right = '-1px';
  tmpNode.style.position = 'fixed';
  document.body.appendChild(tmpNode);
  var offset = tmpNode.offsetLeft;
  tmpNode.style.position = 'absolute';
  this.supportFixed = tmpNode.offsetLeft == offset;
  document.body.removeChild(tmpNode);
},

addPost: function(post) {
  while (this.childNodes.length >= Number(this.className.substring(1)))
    this.removeChild(this.firstChild);
  this.appendChild(post);
  Post(post);
},

setTimer: function() {
  function update() {
    if (this._updateTimer === null) return;
    this._updateTimer = null;
    var url = this.form.action.replace('post', 'update');
    var after = this.lastPostTime();
    if (after)
      url += '&after=' + after;
    this.updateWithRequest('GET', url);
  }
  var bouchot = this;
  this._updateTimer = window.setTimeout(function() { update.call(bouchot); }, 5000);
},

lastPostTime: function() {
  var node = this.lastChild;
  while (node && !node.id)
    node = node.previousSibling;
  if (node)
    return node.id.substring(1);
  return null;
},

updateWithRequest: function(method, url, data) {
  function update(xmlhttp) {
    if (xmlhttp.readyState == 4) {
      if (xmlhttp.status == 200) {
        rootNode = xmlhttp.responseXML.documentElement;
        if (noFocus &&rootNode.firstChild)
          document.title = 'New message!';
        for(var node = rootNode.firstChild; node; node = node.nextSibling)
          if (node.nodeType == 1)
            this.addPost(document.importNode(node, true));
      }
      this.setTimer();
    }
  }
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.open(method, url, true);
  var bouchot = this;
  xmlhttp.onreadystatechange=function() { update.call(bouchot, xmlhttp); };
  if (method.toLowerCase() == 'post') {
    xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    if ('ua' in this)
      xmlhttp.setRequestHeader('User-Agent', this.ua);
  }
  xmlhttp.send(data);
}
});

var Form = new ObjectExtender({
onSubmit: function(ev) {
  if (bouchot.form.content.value.substring(0, 5) == '/set ') {
    if (bouchot.form.content.value.substring(5, 8) == 'ua ')
      bouchot.ua = bouchot.form.content.value.substring(8);
    bouchot.form.reset();
    return false;
  }
  if (bouchot._updateTimer === null) {
    window.setTimeout(arguments.callee, 200);
    return false;
  }
  window.clearTimeout(bouchot._updateTimer);
  bouchot._updateTimer = null;
  var after = bouchot.lastPostTime();
  url = bouchot.form.action;
  url += '&update=';
  if (after)
    url += after;
  bouchot.updateWithRequest('POST', url, "content="+encodeURIComponent(bouchot.form.content.value));
  bouchot.form.reset();
  return false;
}
});

var ClockRef = new ObjectExtender({
onExtend: function() {
  this.clock = normalizeClock(this.firstChild.nodeValue);
  this.className = 'c' + this.clock + ' d' + this.clock.substring(0,6) +
                   ' e' + this.clock.substring(0,4);
},

onMouseOver: function(ev) {
  var classes = '.c' + this.clock;
  switch (this.clock.length) {
  case 4:
    classes += ', .e' + this.clock.substring(0,4);
    break;
  case 6:
    classes += ', span.c' + this.clock.substring(0,4);
    classes += ', .d' + this.clock;
    break;
  case 8:
    classes += ', span.c' + this.clock.substring(0,4);
    classes += ', span.c' + this.clock.substring(0,6);
  }
  document.setExtraStyle(classes + ' { ' + document.getStyle('.highlight') + ' }');
  var c = 'c' + this.clock;
  for (var post = bouchot.firstChild; post; post = post.nextSibling) {
    for (var y = 0, obj = post; obj.offsetParent; obj = obj.offsetParent)
      y += obj.offsetTop;
    if (post.className.indexOf(c) != -1) {
      if ((y < window.pageYOffset) || (this._highlight &&
          (y >= window.pageYOffset + this._highlight.topPosition) &&
          (y <= window.pageYOffset + this._highlight.topPosition + this._highlight.clientHeight))) {
        if (!this._highlight) {
          this._highlight = document.createElement('ul');
          this._highlight.setAttribute('id', 'highlight');
          this._highlight.style.position = bouchot.supportFixed ? 'fixed' : 'absolute';
          document.body.appendChild(this._highlight);
          this._highlight.topPosition = this._highlight.offsetTop;
          if (!bouchot.supportFixed) {
            this._highlight.style.top = (window.pageYOffset + this._highlight.topPosition) + 'px';
            this._highlight.style.width = '100%';
            this._highlight.style.borderLeft = this._highlight.currentStyle.left + ' transparent';
            this._highlight.style.borderRight = this._highlight.currentStyle.right + ' transparent';
          }
        }
        this._highlight.appendChild(post.cloneNode(true));
      }
    }
    if ((!this._highlight && (y >= window.pageYOffset)) || (this._highlight &&
        (y > window.pageYOffset + this._highlight.topPosition + this._highlight.clientHeight)))
        break;
  }
},

onMouseOut: function() {
  if (this._highlight) {
    document.body.removeChild(this._highlight);
    this._highlight = null;
  }
  document.clearExtraStyle();
}
});

var Clock = new ObjectExtender({
onExtend: function() {
  this.clockText = this.firstChild.nodeValue;
  if (this.parentNode.previousSibling) {
    previousClock = this.parentNode.previousSibling.firstChild;
    if (previousClock.clockText == this.clockText) {
      previousClock.clockText += '¹';
      this.clockText += '²';
    } else if (previousClock.clockText.substring(0, 8) == this.clockText) {
      var qualifier = previousClock.clockText.substring(8);
      switch (qualifier.substring(0, 1)) {
      case '^':
      case ':':
        this.clockText += qualifier.substring(0, 1) + (Number(qualifier.substring(1)) + 1);
	break;
      case '²':
        this.clockText += '³';
	break;
      case '³':
        this.clockText += '^4';
      }
    }
  }
  this.clock = normalizeClock(this.clockText);
  if (this.clock.length == 6)
    this.clock += '01';
  this.parentNode.className = 'c' + this.clock + ' c' + this.clock.substring(0,6) +
                              ' c' + this.clock.substring(0,4);
},

onClick: function() {
  bouchot.form.content.replaceSelection(this.clockText);
},

onMouseOver: function() {
  document.setExtraStyle('.c' + this.clock + ', span.c' + this.clock.substring(0,6) + ', span.c' + this.clock.substring(0,4) + ' { ' + document.getStyle('.highlight') + ' }');
},

onMouseOut: function() {
  document.clearExtraStyle();
}
});

var Post = new ObjectExtender({
onExtend: function() {
  Clock(this.firstChild);
  var nodes = this.getElementsByTagName('span');
  var i = 1;
  if ((nodes.length > 1) && ((nodes[i].className == 'name') || ((nodes[i].firstChild.nodeValue.indexOf(' ') == -1) && (nodes[i].firstChild.nodeValue == nodes[i].getAttribute('title')))))
    Nick(nodes[i++]);
  for (; i < nodes.length; i++)
    if (! nodes[i].className)
      ClockRef(nodes[i]);
  nodes = this.getElementsByTagName('a');
  for (i =0; i < nodes.length; i++)
    if (nodes[i].className == 'totoz')
      Totoz(nodes[i]);
}
});

var Nick = new ObjectExtender({
onClick: function(ev) {
  bouchot.form.content.replaceSelection(this.firstChild.nodeValue + '<');
}
});

var TotozImg = new ObjectExtender({
onExtend: function() {
  this.style.position = 'absolute';
},

moveTo: function(x, y) {
  this._x = x;
  this._y = y;
  var maxX = window.pageXOffset + document.documentElement.clientWidth;
      maxY = window.pageYOffset + document.documentElement.clientHeight;
  if (!document.documentElement.clientWidth) {
    maxX += document.body.clientWidth;
    maxY += document.body.clientHeight;
  }
  if (x + 16 + this.clientWidth < maxX)
    x += 16;
  else
    x = maxX - this.clientWidth;
  if (y + 16 + this.clientHeight < maxY)
    y += 16;
  else
    y -= this.clientHeight + 8;
  this.style.top = y + 'px';
  this.style.left = x + 'px';
},

onLoad: function(ev) {
  this.moveTo(this._x, this._y);
}
});

var Totoz = new ObjectExtender({
onMouseOver: function(ev) {
  this._img = document.createElement('img');
  this._img.setAttribute('src', this.getAttribute('href'));
  TotozImg(this._img).moveTo(ev.pageX, ev.pageY);
  document.body.appendChild(this._img);
},

onMouseOut: function(ev) {
  document.body.removeChild(this._img);
},

onMouseMove: function(ev) {
  if (this._img)
    this._img.moveTo(ev.pageX, ev.pageY);
}
});

var re_horloge = new RegExp('^([0-9]{2}):?([0-9]{2})(?::?([0-9]{2})(.*)?)?$');
var exponents = "⁰¹²³⁴⁵⁶⁷⁸⁹";
var exponents_to_num = {};
for (var i = 0; i < 10; i++)
  exponents_to_num[exponents.substring(i, i + 1)] = i;


function normalizeClock(clock) {
  var match = re_horloge.exec(clock);
  if (!match) return null;
  var result = match[1] + match[2];
  if (match[3])
    result += match[3];
  if (match[4])
    switch (match[4].length) {
    case 1:
      result += '0' + exponents_to_num[match[4]];
      break;
    case 2:
      c = match[4].substring(0, 1);
      if ((c == '^') || (c == ':'))
        result += '0' + match[4].substring(1);
      else
        result += exponents_to_num[c].toString()
                  + exponents_to_num[match[4].substring(1)];
      break;
    default:
      result += match[4].substring(1);
    }
  return result;
}
