//
// Extensions
//

function el_trim(str)
{
  return str.replace(/(^\s+)|(\s+$)/g, "");
}

var el_whitespaces = " \n\r\t";

function el_isspace(chr)
{
  return el_whitespaces.indexOf(chr) >= 0;
}

function el_isdigit(chr)
{
  return chr.charCodeAt(0) >= String("0").charCodeAt(0) &&
         chr.charCodeAt(0) <= String("9").charCodeAt(0);
}

function el_int_error(str, minval, maxval)
{
  var len = str.length;
  var i = 0;
 
  for(; i < len && el_isspace(str.charAt(i)); i++);

  if(i == len)
  {
    return 0;
  }

  var begin = i;
  var chr;

  for(; i < len && (el_isdigit(chr = str.charAt(i)) || 
      i == begin && (chr == "+" || chr == "-")); i++);

  var end = i;
  for(; i < len && el_isspace(str.charAt(i)); i++);

  if(i != len)
  {
    return i;
  }

  if(minval !== undefined)
  {
    var intval = parseInt(str.substring(begin, end));

    if(intval < minval)
    {
      return begin;
    }

    if(maxval !== undefined && intval > maxval)
    {
      return begin;
    }
  }

  return -1;
}

//
// Simple stream
//

El_InputStringStream.prototype = 
{ 
  string : "",
  position : 0,
  eof : false,
  fail : false,

  dump : function() 
  { 
    with(this)
    {
      return "El_InputStringStream: " + position + " " + eof + " " + fail + 
             " '" + string + "'";
    }
  },

  get_char : function() 
  { 
    with(this)
    {
      if(position >= string.length)
      {
        eof = fail = true;
      }

      return eof ? null : string.charAt(position++); 
    }
  },

  putback : function() 
  { 
    with(this)
    {
      if(!eof && position > 0)
      {
        position--;
      }
    }
  }

};

function El_InputStringStream(string)
{
  this.string = string;
  this.position = 0;
  this.eof = false;
  this.fail = false;
}

//
// Range
//

El_Range.prototype = 
{
  start : 0,
  end : 0,

  dump : function()
  {
    return this.start + ":" + this.end;
  }
};

function El_Range(start, end)
{
  this.start = start === undefined ? 0 : start;
  this.end = end === undefined ? start : end;
}

//
// Selection
//

function el_set_selection_(range, focus)
{
  if(focus != false)
  {
    this.focus();
  }

  if(range.start < 0 || range.end < 0)
  {
    var str = this.value.replace(/\r/g, "");

    range = new El_Range(range.start < 0 ? str.length : range.start, 
                         range.end < 0 ? str.length : range.end);
  }

  if(this.selectionStart !== undefined)
  {
    this.selectionStart = range.start;
    this.selectionEnd = range.end;
  }
  else if(this.createTextRange)
  {
    var text_range = this.createTextRange();
    text_range.collapse(true);
    text_range.moveStart("character", range.start);
    text_range.moveEnd("character", range.end - range.start);
    text_range.select();     
  }
}

function el_get_selection_()
{    
  var result = new El_Range();
    
  with(this)
  {
    if(this.selectionStart !== undefined)
    {
      result.start = this.selectionStart;
      result.end = this.selectionEnd;
    }
    else
    {
      result.begin = -1;
      result.end = -1;
  
      var caretPos = document.selection.createRange();
  
      if(caretPos)
      {
        var bookmark = "~~@NG!~~";
        var orig = this.value.replace(/\r/g, "");
 
        caretPos.text = bookmark;
        var str = this.value.replace(/\r/g, "");
  
        result.start = str.search(bookmark);

        result.end = 
          result.start + orig.length - str.length + bookmark.length;
  
        this.value = orig;
  
        var range = this.createTextRange();
        range.collapse(true);
        range.moveStart("character", result.start);
        range.moveEnd("character", result.end - result.start);
        range.select();
      }
    }
  }
  
  return result;
}
  
function el_insert_(text, range)
{
  with(this)
  {
    if(range !== undefined)
    {
      el_set_selection(range);
    }

    var sel = el_get_selection();

    if(this.selectionStart !== undefined)
    {
      var top = scrollTop;
      var left = scrollLeft;
      var val = value.replace(/\r/g, "");
      value = val.substr(0, sel.start) + text + val.substr(sel.end);
      
      scrollTop = top;
      scrollLeft = left;
    }
    else
    {
      var caretPos = document.selection.createRange();
 
      if(caretPos)
      {
        caretPos.text = text;
      }
    }

    var new_sel = new El_Range(sel.start + text.length);
    el_set_selection(new_sel);
  }
}

//
// Enrich objects
//

function el_enrich(object)
{
  if(object !== null)
  {
    if(object.nodeName == "TEXTAREA" || 
       object.nodeName == "INPUT" && object.type == "text")
    {
      el_enrich_edit(object);
    }
  }
  
  return object;
}

function el_enrich_edit(object)
{
  object.el_set_selection = el_set_selection_;
  object.el_get_selection = el_get_selection_;
  object.el_insert = el_insert_;
}

function el_mime_url_encode(text)
{
  return encodeURIComponent(text).replace(/\+/g, "%2B").replace(/\//g, "%2F").
         replace(/'/g, "%27").replace(/"/g, "%22");
}

function el_compact(text)
{
  var result = "";
  var whitespace_last = true;

  for(var i = 0; i < text.length; i++)
  {
    var chr = text.charAt(i);

    if(el_isspace(chr))
    {
      if(!whitespace_last)
      {
        result += " ";
        whitespace_last = true;
      }
    }
    else
    {
      result += chr;
      whitespace_last = false;
    }
  }

  return el_trim(result);
}

var js_spec_chars = "\b\f\n\r\t\'\"\\";

var js_spec_chars_escape = 
[
  "\\b",
  "\\f",
  "\\n",
  "\\r",
  "\\t",
  "\\'",
  "\\\"",
  "\\\\"
];

function el_js_escape(text)
{
  var result = "";

  for(var i = 0; i < text.length; i++)
  {
    var chr = text.charAt(i);
    var pos = js_spec_chars.indexOf(chr);

    if(pos < 0)
    {
      result += chr;
    }
    else
    {
      result += js_spec_chars_escape[pos];
    }
  }

  return result;
}

function el_mime_url_decode(text)
{
  return decodeURIComponent(text.replace(/\+/g, "%20"));
}

function el_xml_encode(text, enforce_numeric)
{
  var enc = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").
            replace(/"/g, "&quot;").replace(/'/g, "&apos;");

  if(enforce_numeric === undefined || !enforce_numeric)
  {
    return enc;
  }

  var res = "";
  for(var i = 0; i < enc.length; ++i)
  {
    var code = enc.charCodeAt(i);
    res += code < 0x80 ? enc.charAt(i) : ("&#x" + code.toString(16) + ";");
  }

  return res;
}

var entities = 
[
  { ent:"&amp;", chr:"&" },
  { ent:"&lt;", chr:"<" },
  { ent:"&gt;", chr:">" },
  { ent:"&quot;", chr:"\"" },
  { ent:"&apos;", chr:"'" }
];

function el_xml_decode(text)
{
  var str = text;
  var new_text = "";

  while(true)
  {
    var pos = str.search(/&#?[a-zA-Z0-9]+;/);

    if(pos < 0)
    {
      new_text += str;
      break;
    }

    var end = str.indexOf(';', pos + 1);

    new_text += str.substr(0, pos);
    var ent = str.substr(pos, end - pos + 1).toLowerCase();
    str = str.substr(end + 1);

    if(ent.substr(1, 2) == "#x")
    {
      new_text += String.fromCharCode(
                    parseInt(ent.replace(/&#x([a-f0-9]+);/, "$1"), 16));

      continue;
    }    

    if(ent.substr(1, 1) == "#")
    {
      var s = String.fromCharCode(
                parseInt(ent.replace(/&#([0-9]+);/, "$1"), 10));

      new_text += s;
      continue;
    }

    var i = 0;
    for(; i < entities.length && entities[i].ent != ent; ++i);
    
    if(i < entities.length)
    {
      new_text += entities[i].chr;
    }
    else
    {
      new_text += ent;
    }
  }

  return new_text;
}

function el_get_selection()
{
  if(window.getSelection)
  {
    return window.getSelection();
  }
  else if(document.getSelection)
  {
    return document.getSelection();
  }
  else if(document.selection)
  {
    return document.selection.createRange().text;
  }

  return "";
}

function el_attach_event(obj, event, handler) 
{
  if(obj.addEventListener) 
  {
    obj.addEventListener(event, handler, false);
  } 
  else
  {
    if(obj.attachEvent) 
    {
      obj.attachEvent('on' + event, handler);
    }
  }
}

function el_detach_event(obj, event, handler) 
{
  if(obj.removeEventListener) 
  {
    obj.removeEventListener(event, handler, false);
  } 
  else 
  {
    if(obj.detachEvent) 
    {
      obj.detachEvent('on' + event, handler);
    }
  }
}

function el_target(e)
{
  return e.target ? e.target : 
    (e.srcElement ? e.srcElement : window.event.srcElement);
}

function el_child_node(parent, child_name, index)
{
  if(index === undefined)
  {
    index = 0;
  }

  if(!parent || !parent.childNodes)
  {
    return null;
  }

  child_name = child_name.toLowerCase();
  var childs = parent.childNodes;

  for(var i = 0; i < childs.length; i++)
  {
    var child = childs[i];

    if(child.nodeName.toLowerCase() == child_name)
    {
      if(index-- == 0)
      {
        return child;
      }
    }
  }

  return null;
}

function el_child_text(parent, child_name)
{
  try
  {
    return el_text(el_child_node(parent, child_name));
  }
  catch(e)
  {
    return "";
  }
}

function el_text(obj)
{
  try
  {
    return obj.firstChild.data;
  }
  catch(e)
  {
    return "";
  }
}

function el_post_url(url, form_id, target)
{
  if(!form_id)
  {
    form_id = "post";
  }

  var form = document.getElementById(form_id);

  if(form == null)
  {
    alert("el_post_url: form with id '" + form_id + "' not found");
    return;
  }

  var parts = url.split("?", 2);

  form.action = parts[0];

  while(form.hasChildNodes())
  {
    form.removeChild(form.firstChild);
  }

  if(parts.length > 1)
  {
    var name_values = parts[1].split("&");

    for(var i = 0; i < name_values.length; i++)
    {
      var nv = name_values[i].split("=", 2);

      var element = document.createElement("input");
      element.type = "hidden";
      element.name = el_mime_url_decode(nv[0]);

      if(nv.length > 1)
      {
        element.value = el_mime_url_decode(nv[1]);
      }

      form.appendChild(element);
    }
  }

  var tg = form.target;

  if(target)
  {
    form.target = target;
  }

  form.submit();

  if(target)
  {
    form.target = tg;
  }
}

function el_window_rect()
{
  var d = document;
  var b = d.body;

  var r = (d.doctype || d.documentMode > 7 || d.firstChild.tagName=='!') ? 
          d.documentElement : b;

  return { x:(r.scrollLeft || b.scrollLeft), y:(r.scrollTop || b.scrollTop), 
           w:r.clientWidth, h:r.clientHeight
         };
}

function el_node_rect(n)
{
  if(n == null)
  {
    return null;
  }

  var r = { x:n.offsetLeft, y:n.offsetTop, w:n.offsetWidth, h:n.offsetHeight };

  while(n.offsetParent)
  {
    n = n.offsetParent;
    r.x += n.offsetLeft;
    r.y += n.offsetTop;
  }

  return r;
}

function el_visible(n, p)
{
  if(n == null) 
  {
    return false;
  }

  var w = el_window_rect();
  var r = el_node_rect(n);

  var v = (r.w + w.w + Math.min(r.x, w.x) - Math.max(r.x + r.w, w.x + w.w)) * 
    (r.h + w.h + Math.min(r.y, w.y) - Math.max(r.y + r.h, w.y + w.h));

  return v >= (r.w * r.h * p);
}

function el_array_find(arr, obj)
{
  for(var i = 0; i < arr.length; i++)
  {
    if(arr[i] == obj) 
    {
      return i;  
    }
  }

  return -1;
}

function el_by_id(id)
{
  return document.getElementById(id);
}

