function QuickReply(textareaid)
{
  this.id = textareaid;
/*   this.insertButtons(); */
  this.initShortCuts();
};

QuickReply.prototype.insertButtons = function(e)
{
  var span = make_el('span');
  span.innerHTML = '<button type="button">b</button> <button type="button">i</button> <button type="button">u</button>';
  addEvent(span, 'click', getRef2(this, this.clickEvents), false);
  insert_before(span, get_by_id(this.id));
};

QuickReply.prototype.initShortCuts = function(e)
{
  var span = make_el('span');
  span.innerHTML = '<p class="editor-shortcuts"><strong>Shortcuts:</strong> bold: <dfn title="shortcut for bold">CTRL-b</dfn>,\
                  italic: <dfn title="shortcut for italic">CTRL-i</dfn>,\
                  underline: <dfn title="shortcut for underline">CTRL-u</dfn>,\
                  image: <dfn title="shortcut for inserting image">CTRL-ALT-i</dfn>, \
                  link: <dfn title="shortcut for link">CTRL-l</dfn>,\
                  spoiler: <dfn title="shortcut for spoiler">CTRL-s</dfn> (Pressing shortcut a second time will move the caret out of the inserted bbcode.)</p>';

  var tarea = get_by_id(this.id);
  tarea.parentNode.appendChild(span);
  addEvent(tarea, 'keydown', getRef2(this, this.keyDown), false);
  addEvent(tarea, 'keypress', getRef2(this, this.keyPrevent), false);
  addEvent(tarea, 'keyup', getRef2(this, this.keyPrevent), false);
};

QuickReply.prototype.tags = {
  'b': {openning: '[b]', closing: '[/b]'},
  'i': {openning: '[i]', closing: '[/i]'},
  'u': {openning: '[u]', closing: '[/u]'},
  'img': {openning: '[img]', closing: '[/img]',
          content: function()
	         {
		   return prompt('Please enter image url:', 'http://') || 'put image url here';
		 }
},
  'spoiler': {openning: '[spoiler]', closing: '[/spoiler]'},
  'url': {openning: '[url%0]', closing: '[/url]',
	  params:[
		  function(){
		    var link = prompt('Please enter link:', 'http://');
		    if (!link)
		      return null;
		    if (/^\s*?http:\/\/\s*$/i.test(link))
		      return '=';
		    return '='+link.replace(/^(?:http:\/\/)+/, 'http://');
		  }
		  ]}
};

QuickReply.prototype.keyPrevent = function(e)
{
  if (!e.ctrlKey)
    return;
  switch (e.keyCode) {
  case 66: case 98:
  case 85: case 117:
  case 73: case 105:
  case 83: case 76:
    preventDefault(e);
    break;
  }
};

QuickReply.prototype.keyDown = function(e)
{
  if (!e.ctrlKey)
    return;

  switch (e.keyCode) {
  case 66: case 98:
    preventDefault(e);
    this.insertTag('b');
    break;
  case 85: case 117:
    preventDefault(e);
    this.insertTag('u');
    break;
  case 73: case 105:
    preventDefault(e);
    if (e.altKey)
      this.insertTag('img');
    else
      this.insertTag('i');
    break;
  case 83:
    preventDefault(e);
    this.insertTag('spoiler');
    break;
  case 76:
    preventDefault(e);
    this.insertTag('url');
    break;
  }
};

QuickReply.prototype.clickEvents = function(e)
{
  var srcEl = get_srcEl(e);
  srcEl = srcEl.innerText || srcEl.textContent;
  if (this.tags[srcEl])
    this.insertTag(srcEl);
};

QuickReply.prototype.focus = function()
{
  get_by_id(this.id).focus();
};

QuickReply.prototype.insertTag = function(tagkey)
{
  var tag = this.tags[tagkey];
  var sStart = this.getSelectionStart();
  var sEnd = this.getSelectionEnd();

  /* try and figure if we are in the middle of a tag */
  var txt = get_by_id(this.id).value;
  var oppos = txt.indexOf('[', sStart);
  var clpos = txt.indexOf(']', sStart);
  if (clpos < oppos)
    sStart = txt.indexOf(']', oppos)+1;
  oppos = txt.indexOf('[', sEnd);
  clpos = txt.indexOf(']', sEnd);
  if (clpos < oppos)
    sEnd = txt.indexOf(']', oppos)+1;

  var wrap = this.getWrapperByTag(sStart, sEnd, tag);
  if (wrap) {
    this.setSelectionStart(wrap.endIndx);
    this.setSelectionEnd(wrap.endIndx);
  }
  else {
    var openning = tag.openning;
    var params = tag.params;
    var paramVal = '';
    if (params)
      for (var i = 0, l = params.length; i < l; i++) {
	paramVal = params[i]();
	if (!paramVal)
	  return;
	openning = openning.replace('%'+i, paramVal);
      }
    var nStart = this.insertTagAtPoint(openning, sStart);
    var nEnd = this.insertTagAtPoint(tag.closing, sEnd + nStart-sStart);
    nEnd = sEnd + nStart-sStart;
    this.setSelectionStart(nStart);
    this.setSelectionEnd(nEnd);
  }

  if (tag.content) {
    nStart = this.replace(tag.content(), nStart, nStart);
    this.setSelectionStart(nStart+tag.closing.length);
    this.setSelectionEnd(nStart+tag.closing.length);
  }
  else if (sStart != sEnd) {
    this.cleanUpTags(nEnd+tag.closing);
    this.setSelectionStart(nEnd+tag.closing.length);
    this.setSelectionEnd(nEnd+tag.closing.length);
  }

  this.focus();
};


QuickReply.prototype.replace = function(str, sStart, sEnd)
{
  var tarea = get_by_id(this.id);
  var txt = tarea.value;
  var left = txt.substr(0, sStart);
  var right = txt.substr(sEnd);
  tarea.value = left + str + right;
  return (left+str).length;
};

QuickReply.prototype.cleanUpTags = function()
{
  /* removes extra tags. fixes wrongly nested tags */
};

QuickReply.prototype.insertTagAtPoint = function(tagCode, sStart)
{
  var txt = get_by_id(this.id).value;
  var left = txt.substr(0, sStart);
  var right = txt.substr(sStart);
  get_by_id(this.id).value = left.concat(tagCode, right);
  return (left.length + tagCode.length);
};

QuickReply.prototype.getFreeTags = function(str)
{
  var stack = [];
  stack.length = 0;
  var match, tag = {}, oTag = {};
  var aclm = 0;
  var aculm = 0;

  while ((match = /\[\/?(\w+)[^\]]*\]/.exec(str))) {
    aculm = match.index + match[0].length;
    aclm += aculm;
    if (!(tag = this.tags[match[1]]) || !tag.closing) {
      str = str.substr(aculm);
      continue;
    }

    tag = copy(tag);
    tag.isOpenning = tag.closing != match[0];

    oTag = stack.pop();

    if ((oTag && oTag.isOpenning)
	&& (oTag.closing == tag.closing)
	&& (oTag.isOpenning != tag.isOpenning)) {
	stack.push(oTag);
      str = str.substr(aculm);
      continue;
    }

    if (oTag)
      stack.push(copy(oTag));

    str = str.substr(match.index + match[0].length);
    tag.startIndx = aculm-match[0].length;
    tag.endIndx = aclm;
    stack.push(tag);
  }

  return stack;
};

QuickReply.prototype.getWrapperByTag = function(sStart, sEnd, tag)
{
  var txt = get_by_id(this.id).value;
  var oStack = this.getFreeTags(txt.substr(0, sStart));
  var cStack = this.getFreeTags(txt.substr(sEnd));
  var oTag, cTag;

  while ((oTag = oStack.pop()) && (cTag = cStack.shift()))
    if (oTag.closing == tag.closing && cTag.closing == tag.closing)
      break;

  if (!oTag || !cTag)
    return null;
  var Rs = txt.match(/\r/mg);
  Rs = Rs ? Rs.length : 0;
  oTag.endIndx = cTag.endIndx + sStart;
  return oTag;
};

QuickReply.prototype.getSelectionStart = function()
{
  var textfield = get_by_id(this.id);
  if (document.selection
      && document.selection.createRange) {
    var rng = document.selection.createRange();
    var rng2 = rng.duplicate();
    rng2.moveToElementText(textfield);
    rng2.setEndPoint('EndToStart', rng);
    return rng2.text.length;
  }
  else
    return textfield.selectionStart;
};

QuickReply.prototype.getSelectionEnd = function()
{
  var textfield = get_by_id(this.id);
  if (document.selection
      && document.selection.createRange) {
    var rng = document.selection.createRange();
    var rng2 = rng.duplicate();
    rng2.moveToElementText(textfield);
    rng2.setEndPoint('EndToEnd', rng);
    return rng2.text.length;
  }

  return textfield.selectionEnd;
};

QuickReply.prototype.setSelectionStart = function(pos)
{
  var textfield = get_by_id(this.id);
  if (document.selection
      && document.selection.createRange) {
    var rng = document.selection.createRange();
    var rng2 = rng.duplicate();
    rng2.moveToElementText(textfield);
    rng2.setEndPoint('EndToStart', rng);
    rng2.moveStart('character', pos-this.getSelectionStart());
    rng2.select();
  }
  else
    textfield.selectionStart = pos;
};

QuickReply.prototype.setSelectionEnd = function(pos)
{
  var textfield = get_by_id(this.id);
  if (document.selection
      && document.selection.createRange) {
    var rng = document.selection.createRange();
    var rng2 = rng.duplicate();
    rng2.moveToElementText(textfield);
    rng2.setEndPoint('EndToEnd', rng);
    rng2.moveEnd('character', pos-this.getSelectionEnd());
    rng2.collapse(false);
    rng2.select();
  }
  else
    textfield.selectionEnd = pos;
};
