/*==========================================================================*\
|| ######################################################################## ||
|| # ILance 3.0.0 Gold - Enterprise Edition                               # ||
|| # -------------------------------------------------------------------- # ||
|| # Copyright ©2000–2007 ILance Inc. All Rights Reserved.	          # ||
|| # This file may not be redistributed in whole or significant part. 	  # ||
|| # ----------------- ILANCE IS NOT FREE SOFTWARE ---------------------- # ||
|| #	 http://www.ilance.ca | http://www.ilance.ca/?cmd=eula.html	  # ||
|| # -------------------------------------------------------------------- # ||
|| ######################################################################## ||
|| # -------------------------------------------------------------------- # ||
|| # CVS:	$RCSfile: ajax.js,v $
|| # CVS:	$Version: 3.0.0 $
|| # CVS:  $Revision: 1.1.1.1 $
|| # CVS:	   $Date: 2006/12/04 13:22:42 $
|| # CVS:    $Author: root $
|| # -------------------------------------------------------------------- # ||
|| ######################################################################## ||
\*==========================================================================*/
var Prototype =
{
    Version: '1.3.1',
    emptyFunction: function() {}
}

var Class =
{
    create: function()
    {
        return function()
        {
            this.initialize.apply(this, arguments);
        }
    }
}

var Abstract = new Object();

Object.extend = function(destination, source)
{
    for (property in source)
    {
        destination[property] = source[property];
    }
    return destination;
}

Object.prototype.extend = function(object)
{
    return Object.extend.apply(this, [this, object]);
}

Function.prototype.bind = function(object)
{
    var __method = this;
    return function()
    {
        __method.apply(object, arguments);
    }
}

Function.prototype.bindAsEventListener = function(object)
{
    var __method = this;
    return function(event)
    {
        __method.call(object, event || window.event);
    }
}

Number.prototype.toColorPart = function()
{
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
}

var Try =
{
    these: function()
    {
        var returnValue;

        for (var i = 0; i < arguments.length; i++)
        {
            var lambda = arguments[i];
            try
            {
                returnValue = lambda();
                break;
            }
            catch (e) {}
        }
        return returnValue;
    }
}

var PeriodicalExecuter = Class.create();

PeriodicalExecuter.prototype =
{
    initialize: function(callback, frequency)
    {
        this.callback = callback;
        this.frequency = frequency;
        this.currentlyExecuting = false;
        this.registerCallback();
    },

    registerCallback: function()
    {
        setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
    },
    
    onTimerEvent: function()
    {
        if (!this.currentlyExecuting)
        {
            try
            {
                this.currentlyExecuting = true;
                this.callback();
            }
            finally
            {
                this.currentlyExecuting = false;
            }
        }
    }
}

function $()
{
    var elements = new Array();

    for (var i = 0; i < arguments.length; i++)
    {
        var element = arguments[i];
        if (typeof element == 'string')
            element = document.getElementById(element);
        if (arguments.length == 1)
            return element;
        elements.push(element);
    }
    return elements;
}

if (!Array.prototype.push)
{
    Array.prototype.push = function()
    {
        var startLength = this.length;
        for (var i = 0; i < arguments.length; i++)
        this[startLength + i] = arguments[i];
        return this.length;
    }
}

if (!Function.prototype.apply)
{
    // Based on code from http://www.youngpup.net/
    Function.prototype.apply = function(object, parameters)
    {
        var parameterStrings = new Array();
        if (!object)     object = window;
        if (!parameters) parameters = new Array();

        for (var i = 0; i < parameters.length; i++)
            parameterStrings[i] = 'parameters[' + i + ']';
    
            object.__apply__ = this;
            var result = eval('object.__apply__(' +
            parameterStrings.join(', ') + ')');
            object.__apply__ = null;
            return result;
    }
}

String.prototype.extend(
{
    stripTags: function()
    {
        return this.replace(/<\/?[^>]+>/gi, '');
    },

    escapeHTML: function()
    {
        var div = document.createElement('div');
        var text = document.createTextNode(this);
        div.appendChild(text);
        return div.innerHTML;
    },

    unescapeHTML: function()
    {
        var div = document.createElement('div');
        div.innerHTML = this.stripTags();
        return div.childNodes[0].nodeValue;
    }
});

var Ajax =
{
    getTransport: function()
    {
        return Try.these(
        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
        function() {return new ActiveXObject('Microsoft.XMLHTTP')},
        function() {return new XMLHttpRequest()}
    ) || false;
    }
}

Ajax.Base = function() {};

Ajax.Base.prototype =
{
    setOptions: function(options)
    {
        this.options =
        {
            method:       'post',
            asynchronous: true,
            parameters:   ''
        }.extend(options || {});
    },

    responseIsSuccess: function()
    {
        return this.transport.status == undefined
        || this.transport.status == 0 
        || (this.transport.status >= 200 && this.transport.status < 300);
    },

    responseIsFailure: function()
    {
        return !this.responseIsSuccess();
    }
}

Ajax.Request = Class.create();

Ajax.Request.Events = 
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = (new Ajax.Base()).extend({
    initialize: function(url, options)
    {
        this.transport = Ajax.getTransport();
        this.setOptions(options);
        this.request(url);
    },

    request: function(url)
    {
        var parameters = this.options.parameters || '';
        if (parameters.length > 0) parameters += '&_=';

        try
        {
            if (this.options.method == 'get')
            url += '?' + parameters;
            this.transport.open(this.options.method, url,
            this.options.asynchronous);

            if (this.options.asynchronous)
            {
                this.transport.onreadystatechange = this.onStateChange.bind(this);
                setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
            }
            this.setRequestHeaders();

            var body = this.options.postBody ? this.options.postBody : parameters;
            this.transport.send(this.options.method == 'post' ? body : null);
        } catch (e) {}
    },

    setRequestHeaders: function()
    {
        var requestHeaders =
        ['X-Requested-With', 'XMLHttpRequest',
         'X-Prototype-Version', Prototype.Version];

        if (this.options.method == 'post')
        {
            requestHeaders.push('Content-type',
            'application/x-www-form-urlencoded');

            /* Force "Connection: close" for Mozilla browsers to work around
            * a bug where XMLHttpReqeuest sends an incorrect Content-length
            * header. See Mozilla Bugzilla #246651.
            */
            if (this.transport.overrideMimeType)
                requestHeaders.push('Connection', 'close');
        }

        if (this.options.requestHeaders)
            requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);

        for (var i = 0; i < requestHeaders.length; i += 2)
            this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
    },

    onStateChange: function()
    {
        var readyState = this.transport.readyState;
        if (readyState != 1)
            this.respondToReadyState(this.transport.readyState);
    },

    respondToReadyState: function(readyState)
    {
        var event = Ajax.Request.Events[readyState];

        if (event == 'Complete')
        (this.options['on' + this.transport.status]
           || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
           || Prototype.emptyFunction)(this.transport);

        (this.options['on' + event] || Prototype.emptyFunction)(this.transport);

        /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
        if (event == 'Complete')
            this.transport.onreadystatechange = Prototype.emptyFunction;
    }
});

Ajax.Updater = Class.create();
Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';

Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend(
{
    initialize: function(container, url, options) {
    this.containers =
    {
        success: container.success ? $(container.success) : $(container),
        failure: container.failure ? $(container.failure) :
            (container.success ? null : $(container))
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function()
    {
        this.updateContent();
        onComplete(this.transport);
    }).bind(this);

    this.request(url);
  },

  updateContent: function()
  {
        var receiver = this.responseIsSuccess() ?
        this.containers.success : this.containers.failure;

        var match    = new RegExp(Ajax.Updater.ScriptFragment, 'img');
        var response = this.transport.responseText.replace(match, '');
        var scripts  = this.transport.responseText.match(match);

        if (receiver)
        {
            if (this.options.insertion)
            {
                new this.options.insertion(receiver, response);
            }
            else
            {
                receiver.innerHTML = response;
            }
        }

        if (this.responseIsSuccess())
        {
          if (this.onComplete)
            setTimeout((function() {this.onComplete(
              this.transport)}).bind(this), 10);
        }

        if (this.options.evalScripts && scripts)
        {
          match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
          setTimeout((function()
          {
            for (var i = 0; i < scripts.length; i++)
              eval(scripts[i].match(match)[1]);
          }).bind(this), 10);
        }
    }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend(
{
    initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = 1;

    this.updater = {};
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function()
  {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function()
  {
    this.updater.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(request)
  {
    if (this.options.decay)
    {
      this.decay = (request.responseText == this.lastText ? 
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this), 
      this.decay * this.frequency * 1000);
  },

  onTimerEvent: function()
  {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});

document.getElementsByClassName = function(className)
{
    var children = document.getElementsByTagName('*') || document.all;
    var elements = new Array();
  
    for (var i = 0; i < children.length; i++)
    {
        var child = children[i];
        var classNames = child.className.split(' ');
        for (var j = 0; j < classNames.length; j++)
        {
            if (classNames[j] == className)
            {
                elements.push(child);
                break;
            }
        }
  }
  return elements;
}

if (!window.Element)
{
    var Element = new Object();
}

Object.extend(Element,
{
  toggle: function()
  {
    for (var i = 0; i < arguments.length; i++)
    {
      var element = $(arguments[i]);
      element.style.display = 
        (element.style.display == 'none' ? '' : 'none');
    }
  },

  hide: function()
  {
    for (var i = 0; i < arguments.length; i++)
    {
      var element = $(arguments[i]);
      element.style.display = 'none';
    }
  },

  show: function()
  {
    for (var i = 0; i < arguments.length; i++)
    {
      var element = $(arguments[i]);
      element.style.display = '';
    }
  },

  remove: function(element)
  {
    element = $(element);
    element.parentNode.removeChild(element);
  },
   
  getHeight: function(element)
  {
    element = $(element);
    return element.offsetHeight; 
  },

  hasClassName: function(element, className)
  {
    element = $(element);
    if (!element)
      return;
    var a = element.className.split(' ');
    for (var i = 0; i < a.length; i++)
    {
      if (a[i] == className)
        return true;
    }
    return false;
  },

  addClassName: function(element, className)
  {
    element = $(element);
    Element.removeClassName(element, className);
    element.className += ' ' + className;
  },

  removeClassName: function(element, className)
  {
    element = $(element);
    if (!element)
      return;
    var newClassName = '';
    var a = element.className.split(' ');
    for (var i = 0; i < a.length; i++)
    {
      if (a[i] != className)
      {
        if (i > 0)
          newClassName += ' ';
        newClassName += a[i];
      }
    }
    element.className = newClassName;
  },
  
  // removes whitespace-only text node children
  cleanWhitespace: function(element)
  {
    var element = $(element);
    for (var i = 0; i < element.childNodes.length; i++)
    {
      var node = element.childNodes[i];
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 
        Element.remove(node);
    }
  }
});

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency)
{
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype =
{
  initialize: function(element, content)
  {
    this.element = $(element);
    this.content = content;
    
    if (this.adjacency && this.element.insertAdjacentHTML)
    {
      this.element.insertAdjacentHTML(this.adjacency, this.content);
    }
    else
    {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.fragment = this.range.createContextualFragment(this.content);
      this.insertContent();
    }
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },
  
  insertContent: function() {
    this.element.parentNode.insertBefore(this.fragment, this.element);
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },
  
  insertContent: function() {  
    this.element.insertBefore(this.fragment, this.element.firstChild);
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },
  
  insertContent: function() {
    this.element.appendChild(this.fragment);
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },
  
  insertContent: function() {
    this.element.parentNode.insertBefore(this.fragment, 
      this.element.nextSibling);
  }
});

var Field = {
  clear: function() {
    for (var i = 0; i < arguments.length; i++)
      $(arguments[i]).value = '';
  },

  focus: function(element) {
    $(element).focus();
  },
  
  present: function() {
    for (var i = 0; i < arguments.length; i++)
      if ($(arguments[i]).value == '') return false;
    return true;
  },
  
  select: function(element) {
    $(element).select();
  },
   
  activate: function(element) {
    $(element).focus();
    $(element).select();
  }
}

/*--------------------------------------------------------------------------*/

var Form = {
  serialize: function(form) {
    var elements = Form.getElements($(form));
    var queryComponents = new Array();
    
    for (var i = 0; i < elements.length; i++) {
      var queryComponent = Form.Element.serialize(elements[i]);
      if (queryComponent)
        queryComponents.push(queryComponent);
    }
    
    return queryComponents.join('&');
  },
  
  getElements: function(form) {
    var form = $(form);
    var elements = new Array();

    for (tagName in Form.Element.Serializers) {
      var tagElements = form.getElementsByTagName(tagName);
      for (var j = 0; j < tagElements.length; j++)
        elements.push(tagElements[j]);
    }
    return elements;
  },
  
  getInputs: function(form, typeName, name) {
    var form = $(form);
    var inputs = form.getElementsByTagName('input');
    
    if (!typeName && !name)
      return inputs;
      
    var matchingInputs = new Array();
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) ||
          (name && input.name != name)) 
        continue;
      matchingInputs.push(input);
    }

    return matchingInputs;
  },

  disable: function(form) {
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.blur();
      element.disabled = 'true';
    }
  },

  enable: function(form) {
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.disabled = '';
    }
  },

  focusFirstElement: function(form) {
    var form = $(form);
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      if (element.type != 'hidden' && !element.disabled) {
        Field.activate(element);
        break;
      }
    }
  },

  reset: function(form) {
    $(form).reset();
  }
}

Form.Element = {
  serialize: function(element) {
    var element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);
    
    if (parameter)
      return encodeURIComponent(parameter[0]) + '=' + 
        encodeURIComponent(parameter[1]);                   
  },
  
  getValue: function(element) {
    var element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);
    
    if (parameter) 
      return parameter[1];
  }
}

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'submit':
      case 'hidden':
      case 'password':
      case 'text':
        return Form.Element.Serializers.textarea(element);
      case 'checkbox':  
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
    }
    return false;
  },

  inputSelector: function(element) {
    if (element.checked)
      return [element.name, element.value];
  },

  textarea: function(element) {
    return [element.name, element.value];
  },

  select: function(element) {
    var value = '';
    if (element.type == 'select-one') {
      var index = element.selectedIndex;
      if (index >= 0)
        value = element.options[index].value || element.options[index].text;
    } else {
      value = new Array();
      for (var i = 0; i < element.length; i++) {
        var opt = element.options[i];
        if (opt.selected)
          value.push(opt.value || opt.text);
      }
    }
    return [element.name, value];
  }
}

/*--------------------------------------------------------------------------*/

var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
    this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;
    
    this.lastValue = this.getValue();
    this.registerCallback();
  },
  
  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },
  
  onTimerEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;
    
    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },
  
  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },
  
  registerFormCallbacks: function() {
    var elements = Form.getElements(this.element);
    for (var i = 0; i < elements.length; i++)
      this.registerCallback(elements[i]);
  },
  
  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':  
        case 'radio':
          element.target = this;
          element.prev_onclick = element.onclick || Prototype.emptyFunction;
          element.onclick = function() {
            this.prev_onclick(); 
            this.target.onElementEvent();
          }
          break;
        case 'password':
        case 'text':
        case 'textarea':
        case 'select-one':
        case 'select-multiple':
          element.target = this;
          element.prev_onchange = element.onchange || Prototype.emptyFunction;
          element.onchange = function() {
            this.prev_onchange(); 
            this.target.onElementEvent();
          }
          break;
      }
    }    
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
  getValue: function() {
    return Form.serialize(this.element);
  }
});


if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX + 
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY + 
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) { 
      event.preventDefault(); 
      event.stopPropagation(); 
    } else {
      event.returnValue = false;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,
  
  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },
  
  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0; i < Event.observers.length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.attachEvent))
      name = 'keydown';
    
    this._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.detachEvent))
      name = 'keydown';
    
    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      element.detachEvent('on' + name, observer);
    }
  }
});

/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);

var Position = {

  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false, 

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset 
                || document.documentElement.scrollLeft 
                || document.body.scrollLeft 
                || 0;
    this.deltaY =  window.pageYOffset 
                || document.documentElement.scrollTop 
                || document.body.scrollTop 
                || 0;
  },

  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0; 
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = this.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] && 
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = this.realOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = this.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] && 
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {  
    if (!mode) return 0;  
    if (mode == 'vertical') 
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 
        element.offsetWidth;
  },

  clone: function(source, target) {
    source = $(source);
    target = $(target);
    target.style.position = 'absolute';
    var offsets = this.cumulativeOffset(source);
    target.style.top    = offsets[1] + 'px';
    target.style.left   = offsets[0] + 'px';
    target.style.width  = source.offsetWidth + 'px';
    target.style.height = source.offsetHeight + 'px';
  }
}

/* AJAX Clientside functions
/*--------------------------------------------------------------------------*/
function findChild(parent, name)
{
	if (!parent.hasChildNodes())
		return null;

	for (i=0; i<parent.childNodes.length; i++)
		if (parent.childNodes.item(i).getAttribute && parent.childNodes.item(i).getAttribute("name") == name)
			return parent.childNodes.item(i);
}

function ajax_reportok(elementID, resp)
{
	// unlock this field for processing by ajax editor
	document.getElementById(elementID).setAttribute("processing", 0);
		
	if (resp.responseText != '')
	{
		// report error in AJAX PHP handler
		alert("Request error:\n" + resp.responseText);
		
		// apply "state mark" effect
		if (document.getElementById(elementID).getAttribute('ajax_effect') == '1')
		{
			document.getElementById(elementID).style.border = "2px solid red";
		}
		else if (document.getElementById(elementID).getAttribute('ajax_effect') == '-1')
		{
			document.getElementById(elementID).style.border = "0px";
		}
		else
		{
			document.getElementById(elementID).style.color = "red";
		}
	}
	else
	{
		if (document.getElementById(elementID).getAttribute('ajax_effect') == '1')
		{
			document.getElementById(elementID).style.border = "2px solid green";
		}
		else if (document.getElementById(elementID).getAttribute('ajax_effect') == '-1')
		{
			document.getElementById(elementID).style.border = "0px";
		}
		else
		{
			document.getElementById(elementID).style.color = "green";
		}
	}
}

function ajax_reporterror(elementID)
{
	// unlock this field for processing by ajax editor	
	document.getElementById(elementID).setAttribute("processing", 0);	
	alert(phrase['_could_not_process_ajax_request_fields_not_updated_will_be_marked_in_red']);
	
	if (document.getElementById(elementID).getAttribute('ajax_effect') == '1')
	{
		document.getElementById(elementID).style.border = "2px solid green";
	}
	else
	{
		document.getElementById(elementID).style.color = "green";
	}
}


function AjaxWidgetlessBase()
{
	this.databaseID = -1;
	this.ajaxHandlerURL = "ajax.php";
	this.arr_ajax_queries = new Array();

	this.init = function init(_ajaxHandlerURL, _databaseID)
	{
		this.ajaxHandlerURL = _ajaxHandlerURL;
		this.databaseID = _databaseID;
	}

	this.ajax_edit_do = function ajax_edit_do(editor)
	{
		// we must check if we have editing locked for that item
		// lock is done to prevent race condition (HTTP requests are not guaranteed to be received in same order as sent so database could get updated with old content without locking)
		editedObject = editor;
		if (editedObject.getAttribute("processing") == 1)
		{
			alert(phrase['_there_is_an_update_request_currently_pending_please_wait']);
			return;
		}

		newWidget = this.AjaxWidgetlessEdit(editedObject);
		if (newWidget)
		{
			// set "processing" attrib
			newWidget[0].setAttribute('processing', 1);

			// preserve events and ID
			newWidget[0].onclick = editedObject.onclick;
			newWidget[0].ondblclick = editedObject.ondblclick;
			newWidget[0].id = editedObject.id;

			// replace old widget
			editedObject.parentNode.replaceChild(newWidget[0], editedObject);
			if (this.afterShowEditorWidget)
				this.afterShowNewWidget(newWidget[0]);
			
			// do ajax query
			var id = editedObject.getAttribute("id");
			var url = this.ajaxHandlerURL;
			var pars = 'id=' + escape(id) + '&val=' + escape(newWidget[1]) + '&dbid=' + escape(this.databaseID);
			
			var code = "this.arr_ajax_queries[this.arr_ajax_queries.length] = new Ajax.Request(url, {method: 'get', parameters: pars, onFailure:  function() {ajax_reporterror(\""+id+"\")}, onSuccess:  function(resp) {ajax_reportok(\""+id+"\", resp)} });";
			eval(code);
		}
	}
}

function AjaxWidgetlessCheckbox(checkedImg_, uncheckedImg_, imagesPath_)
{
	subw_ = new AjaxWidgetlessBase();
	subw_.checkedImg = checkedImg_;
	subw_.uncheckedImg = uncheckedImg_;
	subw_.imagesPath = imagesPath_;

	subw_.AjaxWidgetlessEdit = function AjaxWidgetlessEdit(element)
	{
		val = 0;
		imgsrc = this.imagesPath;
		if (element.getAttribute("src").indexOf(this.uncheckedImg) > -1)
		{
			imgsrc += this.checkedImg;
			val = 1;
		}
		else
			imgsrc += this.uncheckedImg;

		_w = document.createElement('img');
		_w.src = imgsrc;
		_w.setAttribute('src', imgsrc);
		_w.setAttribute('ajax_effect', '-1');
        //_w.style.border = "2px solid blue";

		return Array(_w, val);
	}

	return subw_;
}

function AjaxBase()
{
	EditorState = new Object();
		
	this.databaseID = -1;
	this.ajaxHandlerURL = "ajax.php";

	this.editingMode = false;
	this.editedObject = -1;
	this.editorSite = -1;
	this.editorWidget = -1;

	this.init = function init(_ajaxHandlerURL, _databaseID)
	{
		this.ajaxHandlerURL = _ajaxHandlerURL;
		this.databaseID = _databaseID;
	}

	this.ajax_edit_do = function ajax_edit_do(editor)
	{
		// allow only one ajax editor run at a time...
		if (EditorState && EditorState.editingMode === true)
		{
			alert(phrase['_only_one_editor_can_be_active_at_a_time_please_save_or_cancel']);
			return;
		}

		this.editedObject = findChild(editor, 't')
		
		// we must check if we have editing locked for that item
		// lock is done to prevent race condition (HTTP requests are not guaranteed to be received in same order as sent so database could get updated with old content without locking)
		if (this.editedObject.getAttribute("processing") == 1)
		{
			alert(phrase['_there_is_an_update_request_currently_pending_please_wait']);
			return;
		}

		this.editingMode = true;
		this.editorSite = editor;
		this.editorWidget = this.createEditorWidget();
		EditorState = this;

		this.editorSite.replaceChild(this.editorWidget, this.editedObject);
		this.afterShowEditorWidget(this.editorWidget);
	}

	// in standard CALLBACK-FUNCTIONS below we get access to the EditorState object refered as "this" variable.

	this.ajax_edit_blur = function ajax_edit_blur()
	{
		// check if user made some changes (edit) to object
		changed = false;
		if (this.getEditedValue && this.getEditedValue())
		{
			changed = true;
		}

		if (this.beforeAjaxBlur)
			this.beforeAjaxBlur();

		this.editorSite.replaceChild(this.editedObject, this.editorWidget);
		
		// change back to non-editing mode check if we need to update
		this.editingMode = false;
		if (!changed)
			return;
		
		// lock this field for edit again until ajax request is processed
		this.editedObject.setAttribute("processing", 1);
		
		id = this.editedObject.getAttribute("id");
		var url = this.ajaxHandlerURL;
		
		var pars = 'id=' + escape(id) + '&val=' + escape(this.getEditedValue(true)) + '&dbid=' + escape(this.databaseID);
		var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onFailure:  function() {ajax_reporterror(id)}, onSuccess:  function(resp) {ajax_reportok(id, resp)} });	
	}

	this.ajax_edit_keypress = function ajax_edit_keypress()
	{
		if (window.event && window.event.keyCode == 13)
		{
			this.ajax_edit_blur();
		}
	}

}

function AjaxEditbox(_minEditWidth, _maxEditWidth)
{
	sub_ = new AjaxBase();

	if (!_minEditWidth) _minEditWidth = 200;
	if (!_maxEditWidth) _maxEditWidth = 300;
	sub_.minEditWidth = _minEditWidth;
	sub_.maxEditWidth = _maxEditWidth;
	
	// here we create widget that enables user to edit in-place.
	sub_.createEditorWidget = function createEditorWidget()
	{
		_w = document.createElement('input');
		_w.setAttribute('type', 'text');
		_w.setAttribute('value', this.editedObject.innerHTML);
		_w.style.width = Math.min(this.maxEditWidth, Math.max(this.minEditWidth, this.editedObject.offsetWidth+5))+"px";

		return _w;
	}

	// here you should apply event-chandlers to widget we just have created
	sub_.afterShowEditorWidget = function afterShowEditorWidget(widgetElement)
	{
		widgetElement.focus();
		widgetElement.onblur = function() {EditorState.ajax_edit_blur();};
		widgetElement.onkeypress =  function() {EditorState.ajax_edit_keypress();};
	}

	// this function should update HTML element value, before we'll be hiding editor in ajax_edit_blur(...)
	sub_.beforeAjaxBlur = function beforeAjaxBlur()
	{
		this.editedObject.innerHTML = this.editorWidget.value;
	}

	// returns current (edited by user) value of item. Of value doesnt changed - return undefined.
	sub_.getEditedValue = function getEditedValue(returnValAlways)
	{
		if (this.editedObject.innerHTML == this.editorWidget.value && !returnValAlways)
			return;
		else
			return this.editorWidget.value;
	}

	return sub_;
}