/*  Prototype JavaScript framework, version 1.4.0
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.4.0',
  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

  emptyFunction: function() {},
  K: function(x) {return x}
}

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.inspect = function(object) {
  try {
    if (object == undefined) return 'undefined';
    if (object == null) return 'null';
    return object.inspect ? object.inspect() : object.toString();
  } catch (e) {
    if (e instanceof RangeError) return '...';
    throw e;
  }
}

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this;
  return function(event) {
    return __method.call(object, event || window.event);
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

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;
}
Object.extend(String.prototype, {
  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(eval);
  },

  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] ? div.childNodes[0].nodeValue : '';
  },

  toQueryParams: function() {
    var pairs = this.match(/^\??(.*)$/)[1].split('&');
    return pairs.inject({}, function(params, pairString) {
      var pair = pairString.split('=');
      params[pair[0]] = pair[1];
      return params;
    });
  },

  toArray: function() {
    return this.split('');
  },

  camelize: function() {
    var oStringList = this.split('-');
    if (oStringList.length == 1) return oStringList[0];

    var camelizedString = this.indexOf('-') == 0
      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
      : oStringList[0];

    for (var i = 1, len = oStringList.length; i < len; i++) {
      var s = oStringList[i];
      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
    }

    return camelizedString;
  },

  inspect: function() {
    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
  }
});

String.prototype.parseQuery = String.prototype.toQueryParams;

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != $continue) throw e;
        }
      });
    } catch (e) {
      if (e != $break) throw e;
    }
  },

  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      result = result && !!(iterator || Prototype.K)(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      if (result = !!(iterator || Prototype.K)(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function (iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },

  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.collect(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value >= (result || value))
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value <= (result || value))
        result = value;
    });
    return result;
  },

  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.collect(Prototype.K);
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      iterator(value = collections.pluck(index));
      return value;
    });
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0; i < iterable.length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);

Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0; i < this.length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != undefined || value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  indexOf: function(object) {
    for (var i = 0; i < this.length; i++)
      if (this[i] == object) return i;
    return -1;
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  shift: function() {
    var result = this[0];
    for (var i = 0; i < this.length - 1; i++)
      this[i] = this[i + 1];
    this.length--;
    return result;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});
var Hash = {
  _each: function(iterator) {
    for (key in this) {
      var value = this[key];
      if (typeof value == 'function') continue;

      var pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  },

  keys: function() {
    return this.pluck('key');
  },

  values: function() {
    return this.pluck('value');
  },

  merge: function(hash) {
    return $H(hash).inject($H(this), function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  toQueryString: function() {
    return this.map(function(pair) {
      return pair.map(encodeURIComponent).join('=');
    }).join('&');
  },

  inspect: function() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }
}

function $H(object) {
  var hash = Object.extend({}, object || {});
  Object.extend(hash, Enumerable);
  Object.extend(hash, Hash);
  return hash;
}
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    do {
      iterator(value);
      value = value.succ();
    } while (this.include(value));
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
      function() {return new XMLHttpRequest()}
    ) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responderToAdd) {
    if (!this.include(responderToAdd))
      this.responders.push(responderToAdd);
  },

  unregister: function(responderToRemove) {
    this.responders = this.responders.without(responderToRemove);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (responder[callback] && typeof responder[callback] == 'function') {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) {}
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate: function() {
    Ajax.activeRequestCount++;
  },

  onComplete: function() {
    Ajax.activeRequestCount--;
  }
});

Ajax.Base = function() {};
Ajax.Base.prototype = {
  setOptions: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      parameters:   ''
    }
    Object.extend(this.options, 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 = Object.extend(new Ajax.Base(), {
  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 {
      this.url = url;
      if (this.options.method == 'get' && parameters.length > 0)
        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

      Ajax.Responders.dispatch('onCreate', this, this.transport);

      this.transport.open(this.options.method, this.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) {
      this.dispatchException(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);
  },

  header: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } catch (e) {}
  },

  evalJSON: function() {
    try {
      return eval(this.header('X-JSON'));
    } catch (e) {}
  },

  evalResponse: function() {
    try {
      return eval(this.transport.responseText);
    } catch (e) {
      this.dispatchException(e);
    }
  },

  respondToReadyState: function(readyState) {
    var event = Ajax.Request.Events[readyState];
    var transport = this.transport, json = this.evalJSON();

    if (event == 'Complete') {
      try {
        (this.options['on' + this.transport.status]
         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(transport, json);
      } catch (e) {
        this.dispatchException(e);
      }

      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
        this.evalResponse();
    }

    try {
      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch('on' + event, this, transport, json);
    } catch (e) {
      this.dispatchException(e);
    }

    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
    if (event == 'Complete')
      this.transport.onreadystatechange = Prototype.emptyFunction;
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  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(transport, object) {
      this.updateContent();
      onComplete(transport, object);
    }).bind(this);

    this.request(url);
  },

  updateContent: function() {
    var receiver = this.responseIsSuccess() ?
      this.containers.success : this.containers.failure;
    var response = this.transport.responseText;

    if (!this.options.evalScripts)
      response = response.stripScripts();

    if (receiver) {
      if (this.options.insertion) {
        new this.options.insertion(receiver, response);
      } else {
        Element.update(receiver, response);
      }
    }

    if (this.responseIsSuccess()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.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 || Prototype.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, parentElement) {
  var children = ($(parentElement) || document.body).getElementsByTagName('*');
  return $A(children).inject([], function(elements, child) {
    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      elements.push(child);
    return elements;
  });
}

/*--------------------------------------------------------------------------*/

if (!window.Element) {
  var Element = new Object();
}

Object.extend(Element, {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      Element[Element.visible(element) ? 'hide' : 'show'](element);
    }
  },

  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);
  },

  update: function(element, html) {
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
  },

  getHeight: function(element) {
    element = $(element);
    return element.offsetHeight;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).include(className);
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).add(className);
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).remove(className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    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);
    }
  },

  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

  scrollTo: function(element) {
    element = $(element);
    var x = element.x ? element.x : element.offsetLeft,
        y = element.y ? element.y : element.offsetTop;
    window.scrollTo(x, y);
  },

  getStyle: function(element, style) {
    element = $(element);
    var value = element.style[style.camelize()];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css.getPropertyValue(style) : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style.camelize()];
      }
    }

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';

    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (name in style)
      element.style[name.camelize()] = style[name];
  },

  getDimensions: function(element) {
    element = $(element);
    if (Element.getStyle(element, 'display') != 'none')
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = '';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = 'none';
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element._overflow = element.style.overflow;
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
  },

  undoClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element.style.overflow = element._overflow;
    element._overflow = undefined;
  }
});

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.stripScripts();

    if (this.adjacency && this.element.insertAdjacentHTML) {
      try {
        this.element.insertAdjacentHTML(this.adjacency, this.content);
      } catch (e) {
        if (this.element.tagName.toLowerCase() == 'tbody') {
          this.insertContent(this.contentFromAnonymousTable());
        } else {
          throw e;
        }
      }
    } else {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.insertContent([this.range.createContextualFragment(this.content)]);
    }

    setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
    var div = document.createElement('div');
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment, this.element);
    }).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },

  insertContent: function(fragments) {
    fragments.reverse(false).each((function(fragment) {
      this.element.insertBefore(fragment, this.element.firstChild);
    }).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.appendChild(fragment);
    }).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment,
        this.element.nextSibling);
    }).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set(this.toArray().concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set(this.select(function(className) {
      return className != classNameToRemove;
    }).join(' '));
  },

  toString: function() {
    return this.toArray().join(' ');
  }
}

Object.extend(Element.ClassNames.prototype, Enumerable);
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 = $(element);
    element.focus();
    if (element.select)
      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) {
    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) {
    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 = '';
    }
  },

  findFirstElement: function(form) {
    return Form.getElements(form).find(function(element) {
      return element.type != 'hidden' && !element.disabled &&
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    Field.activate(Form.findFirstElement(form));
  },

  reset: function(form) {
    $(form).reset();
  }
}

Form.Element = {
  serialize: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter) {
      var key = encodeURIComponent(parameter[0]);
      if (key.length == 0) return;

      if (parameter[1].constructor != Array)
        parameter[1] = [parameter[1]];

      return parameter[1].map(function(value) {
        return key + '=' + encodeURIComponent(value);
      }).join('&');
    }
  },

  getValue: function(element) {
    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) {
    return Form.Element.Serializers[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var value = '', opt, index = element.selectedIndex;
    if (index >= 0) {
      opt = element.options[index];
      value = opt.value;
      if (!value && !('value' in opt))
        value = opt.text;
    }
    return [element.name, value];
  },

  selectMany: function(element) {
    var value = new Array();
    for (var i = 0; i < element.length; i++) {
      var opt = element.options[i];
      if (opt.selected) {
        var optValue = opt.value;
        if (!optValue && !('value' in opt))
          optValue = opt.text;
        value.push(optValue);
      }
    }
    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 = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  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':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        case 'password':
        case 'text':
        case 'textarea':
        case 'select-one':
        case 'select-multiple':
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  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;
      event.cancelBubble = true;
    }
  },

  // 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.match(/Konqueror|Safari|KHTML/)
        || 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.match(/Konqueror|Safari|KHTML/)
        || 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];
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

  offsetParent: function(element) {
    if (element.offsetParent) return element.offsetParent;
    if (element == document.body) return element;

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return element;

    return document.body;
  },

  // 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';
  },

  page: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      valueT -= element.scrollTop  || 0;
      valueL -= element.scrollLeft || 0;
    } while (element = element.parentNode);

    return [valueL, valueT];
  },

  clone: function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
    element = $(element);
    if (element.style.position == 'absolute') return;
    Position.prepare();

    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';;
    element.style.left   = left + 'px';;
    element.style.width  = width + 'px';;
    element.style.height = height + 'px';;
  },

  relativize: function(element) {
    element = $(element);
    if (element.style.position == 'relative') return;
    Position.prepare();

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return [valueL, valueT];
  }
}
function Pokkari() {}

Pokkari.AttachEvent = function(element,event,func)
{
	event = event.toLowerCase();
	if (element.attachEvent) 
	{
		element.attachEvent("on"+event,func);
	}
	else if (element.addEventListener)
	{
		element.addEventListener(event,func,true);
	}
}

Pokkari.Debug = function(message)
{
	Pokkari.DebugWindowWrite(message);
}

Pokkari.Error = function(message)
{
	window.alert(message);
}

Pokkari.HandleException = function(error,message)
{
	var errMsg = error.message || error.description;
	Pokkari.DebugWindowWrite(message+": "+errMsg);
}

Pokkari.ShowDebugWindow = function()
{
	Pokkari.debugWindow = window.open('/scripts/index.html','debugWindow','width=300,height=200,scrollbars=yes,resizable=yes');
	/*
	var element = document.getElementById('pokkariDebugWindow');
	var top = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;
	element.style.top = top;	
	element.style.display = "block";
	*/
}

Pokkari.DebugWindowWrite = function(message)
{
/*
	var element = document.getElementById('pokkariDebugWindow');
	var top = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;

	if (element && element.style.display != "none")
	{
		element.innerHTML = message + "<br />" + element.innerHTML;
		element.style.top = top;
		element.style.display = "block";
	}
*/
	if (Pokkari.debugWindow && Pokkari.debugWindow.document && Pokkari.debugWindow.document.body)
	{
		var element = Pokkari.debugWindow.document.body;
		element.innerHTML = message + "<br />" + element.innerHTML;
	}
}

Pokkari.UrlReplace = function(param,value)
{
	var parts = window.location.href.split("?");
	
	if (parts.length < 2)
		return window.location.href + "?" + param + "=" + escape(value);
	
	var query = parts[1].split("#")[0];
	var pattern = "\\b" + param + "=[^&;]*";
	var replace = param + "=" + escape(value);
	
	var regex = new RegExp(pattern);
	if (regex.test(window.location.href))
		return window.location.href.replace(regex,replace);
	else
		return window.location.href + "&" + param + "=" + escape(value);
}

Pokkari.SetCookiePreference = function(params) {
        if(params.name) {
                var today = new Date();
                var expire = new Date();
                expire.setTime(today.getTime() + 3600000*24*1);
                document.cookie = 'users_prefs_'+params.name+'='+escape(params.value)+";path=/;expires="+expire.toGMTString();
                if(params.reload) {
                        window.location.reload(true);
                }
        } else {
                throw("Cannot set cookie preference without name");
        }
}
// Constructor for PokkariElement
function PokkariElement(params) {
	// Load parameters passed in as associative array
	for (var param in params) {
		this[param] = params[param];
	}

	// void anything returns the undefined value
	if (this.element != void(0) && this.element.constructor == HTMLElement) {
		this.bindToHTMLElement(this.element);
	}

}

// Called after construction -- does nothing here, override to do stuff at
// construction time.
PokkariElement.prototype.onConstruct = function() {}

/**
 * Find siblings of a given class name 
 */

/*
PokkariElement.prototype.getSiblingsByClassName = function(className,element) {

	if(arguments.length == 1) {
		element = this.element;
	}

	alert("Class name: " + className + " Element: " + element);

	var matchedSiblings = new Array();

	alert("Parent node: " + element.parentNode.parentNode);

	for(var i = 0; i < element.parentNode.parentNode.getElementsByTagName('*'); i++) {
		alert("got one: " + element.parentNode.getElementsByTagName('*')[i]);
		if(element.parentNode.getElementsByTagName('*')[i].className.match(className)) {
			matchedSiblings.push(element.parentNode.getElementsByTagName('*')[i]);
		}
	}

	return matchedSiblings;
}
*/

// Constructor.prototype is the proper way to make a method.
PokkariElement.prototype.bindToHTMLElement = function(e) 
{
	// Set the element property
	this.element = e;
	
	// Create a circular reference so we can get back here from the DOM
	this.element.pokkariElement = this;

	// Element index for event handling
	this.pokkariElementIndex = e.pokkariElementIndex;
	
	if (!this.immutable_params) {
		this.setParamsFromAttributes(e);
	}

	this.isBound();
}

PokkariElement.prototype.isBound = function() {
	// noop
}

// Parses pokkariParameters and returns an associative array.
PokkariElement.GetParamsFromAttributes = function(e)
{
	if (e.getAttribute("pokkariParameters")) {
		var attr = e.getAttribute("pokkariParameters");
		var moreParams = eval("var a={"+attr+"}; a;");
		for (var param in moreParams) {
			if(typeof(moreParams[param]) == "string") {
				moreParams[param] = unescape(moreParams[param]);
			}
		}
	}
	
	return moreParams;
}

// Sets the current pokkariElement's parameters according to attributes.
PokkariElement.prototype.setParamsFromAttributes = function(e) 
{
	var params = PokkariElement.GetParamsFromAttributes(e);
	for (var param in params) {
		this[param] = params[param];
	}
}

function createObjectFromXML(topElement) {
/*	var child;
	if (topElement.hasChildNodes()) {
		var obj = new Object();
		var children = topElement.childNodes;
		for (var i=0; i<children.length; i++) {
			child = children[i];
			if (child.nodeType == 3) { // Text node
				return child.nodeValue;
			}
			else if (obj[child.tagName].prototype == Array) {
				obj.push(createObjectFromXML(child));
			}
			else if (obj[child.tagName != void(0)) {
				obj = new Array();
				obj.push(createObjectFromXML(child));
			}
			else {
				obj[child.tagName] = createObjectFromXML(child);
			}
		}
		return obj;
	}
*/  // TODO FIXME Ill conceived -- rethink
}
		
PokkariElement.prototype.fromXML = function(topElement) {
	// TODO FIXME		
}

PokkariElement.prototype.toXML = function() {
	// TODO FIXME
}

PokkariElement.prototype.fromXMLById = function(id) {
	// This will get the <xml> element, we actually need to load from the
	// firstChild of this.
	var xmlElement = document.getElementById(id);
	this.fromXML(xmlElement.firstChild);
}

PokkariElement.prototype.toXMLString = function() {
	var element = this.toXML();
	var name = this.toString().toLowerCase();
	return "<"+name+">"+element.innerHTML+"</"+name+">";
}

PokkariElement.prototype.eventHandler = function(func) {
	var funcCode = "var self = PokkariElement.AllElements[" +
		this.pokkariElementIndex + "].pokkariElement;\n" +
		"if (!e) e = window.event;\n" +
		"var sender = e.target || e.srcElement;\n" + 
		"var code = " + func + "\n" +
		"return code(self,sender,e);";

	return new Function("e",funcCode);
}

// Casts all HTMLElements by using name and attributes.
PokkariElement.CastHTMLElements = function() {
	var element;
	var type;
	var obj;
	var attribute;

	// Look for elements named 'PokkariElement' 
	var pokkariElements = PokkariElement.GetPokkariElements();

	PokkariElement.AllElements = pokkariElements;

	for (var i=0; i<pokkariElements.length; i++) {
		element = pokkariElements[i];
		element.pokkariElementIndex = i;

		attribute = element.getAttribute("pokkariParameters") ?
			element.getAttribute("pokkariParameters") :
			"";

		// Create an object as specified by type 
		if (element.getAttribute("pokkariType")) {
			type = element.getAttribute("pokkariType");
			try {
				obj = eval("new "+type+"()");
			}
			catch (e) { 
				alert("While creating a "+type+": \""+e.message+"\"\n"+attribute);
				continue;
			}
			
			// Now try to bind that to object to the element.
			if (obj != void(0)) {
				try {
					obj.bindToHTMLElement(element);
				}
				catch (e) {
					alert("While binding: "+e.message+"\n"+attribute);
					continue;
				}
			}

			if (obj.onConstruct != void(0) && 
				typeof obj.onConstruct == "function") 
			{
				try {
					obj.onConstruct();
				}
				catch (e) {
					alert("onConstruct: "+e.message);
					continue;
				}
			}
		}
	}
}

// Executes onLoad="" attributes and obj.onLoadHandler functions
PokkariElement.ExecuteOnLoadHandlers = function() {
	var element;

	// Get all the PokkariElements
	var pokkariElements = PokkariElement.GetPokkariElements();
	
	for (var i=0; i<pokkariElements.length; i++) {
		element = pokkariElements[i];

		// Make sure it's actually a pokkariElement..
		if (element.pokkariElement != void(0)) {
		    if (element.pokkariElement.oninit != void(0) && typeof(element.pokkariElement.oninit) == "function") {
		        element.pokkariElement.oninit();
		    }
			// If onLoadHandler exists and is a function, call it.
			if (element.pokkariElement.onLoadHandler != void(0) && typeof(element.pokkariElement.onLoadHandler) == "function") {
				element.pokkariElement.onLoadHandler();
			}
			// If the HTMLElement has onLoad attribute, eval it.
			if (element.getAttribute("onLoad")) {
				element.pokkariElement.onLoadInlineHandler = new Function(
					element.getAttribute("onLoad")
				);
				element.pokkariElement.onLoadInlineHandler();
			}
		}
	}
}

// IE executes attached events in random order, so we need to make sure ours
// run in order..
PokkariElement._OnLoadHandler = function()
{
	PokkariElement.CastHTMLElements();
	PokkariElement.ExecuteOnLoadHandlers();
}

PokkariElement.GetPokkariElements = function()
{
	if (!PokkariElement.AllElements) {

		var collection = new Array();
		var element_types = ["div","table","tr","td","input","textarea","select"];
	
		for (var i=0; i<element_types.length; i++) {
			var elements = document.getElementsByTagName(element_types[i]);
			for (var j=0; j<elements.length; j++) {
				if (elements[j].name == "PokkariElement" ||
					elements[j].getAttribute('name') == "PokkariElement" ||
					elements[j].getAttribute('pokkarielement') == "pokkarielement"
				) {
					collection.push(elements[j]);
				}
			}
		}

		PokkariElement.AllElements = collection;
	}
	
	return PokkariElement.AllElements;
}

PokkariElement.prototype.attachEvent = function(eventName,func)
{
    if (!document.all) {
        eventName = eventName.replace(/^on/,"");        
        this.element.addEventListener(eventName,func,true);
    }
    else {
        this.element.attachEvent(eventName,func);
    }
}

// Set up onLoad 
Pokkari.AttachEvent(window,"load",PokkariElement._OnLoadHandler);

/**
 * Used to add attachEvent support to Mozilla browsers
 */
function PokkariElementAttachEvent(eventName,func)
{
    // Strip on the leading on.
    eventName = eventName.replace(/^on/,"");
        
    this.addEventListener(eventName,func,true);
}

// Add attachEvent support to Mozilla.
if (!document.all)
{
    window.attachEvent = PokkariElementAttachEvent;
    document.attachEvent = PokkariElementAttachEvent;
    if(Element.prototype) {
    	Element.prototype.attachEvent = PokkariElementAttachEvent;
    } else {
	Element.attachEvent = PokkariElementAttachEvent;
    }
}

pokkariChooser = function() {
	this.init(this);
}

pokkariChooser.prototype = new PokkariElement();
pokkariChooser.constructor = pokkariChooser;

pokkariChooser.prototype.init = function() {
	this.topchoices = new Array();
	this.choicepanes = new Array();
}

pokkariChooser.prototype.onConstruct = function() {
	
	this.findChooserTriggers();
	this.findChooserPanes();
}

pokkariChooser.prototype.findChooserTriggers = function() {

	var potentialTriggers = this.element.getElementsByTagName('input');
	for(var i = 0; i < potentialTriggers.length; i++) {
		if(
			potentialTriggers[i].getAttribute('pokkariType') == 'pokkariChooserTriger'
		) {
			potentialTriggers[i].chooser = this;
		}
	}
}

pokkariChooser.prototype.findChooserPanes = function() {
	// All chooser panes must be divs
	var potentialPanes = this.element.getElementsByTagName('div');
	for(var i = 0; i < potentialPanes.length; i++) {
		/*
			We don't really have to cast our chooser panes as anything,
			although at some point we may want to cast them as
			PokkariChooserPanes.  Right now it just doesn't feel all that
			necessary.
		*/
		
	}
}

pokkariChooserTrigger = function() {
	alert("Got a chooser trigger");
	this.init(this);
}

pokkariChooserTrigger.prototype = new PokkariElement();
pokkariChooserTrigger.constructor = pokkariChooserTrigger;

pokkariChooserTrigger.prototype.init = function() {
	this.chooser = void(0);
}

pokkariChooserTrigger.prototype.onConstruct = function() {
	
	this.element.onchange = function() {
		alert("Hi there!");
	}	
	
}
function PokkariXmlRequest(params) {
	if (params)
	{
		this.url = params.url;
		this.method = params.method;
	}

	if (!this.method)
		this.method = "GET";
}

// Set this up as a subclass of PokkariElement
PokkariXmlRequest.prototype = new PokkariElement();
PokkariXmlRequest.prototype.constructor = PokkariXmlRequest;

// Initialize our lookup table.
PokkariXmlRequest.lookupTable = new Object();

PokkariXmlRequest.prototype.send = function(data)
{
	var url = this.url;

	// Fix the URL to make sure it's going to xmlhttprequest skin.
	if (!url.match(/skin=xmlhttprequest/)) {
		if (!url.match(/\?/)) {
			url += "?skin=xmlhttprequest";
		}
		else {
			url += ";skin=xmlhttprequest";
		}
	}
	
	// Save our object so we can get it later.
	this.key = PokkariXmlRequest.StoreObject(this);

	// Create the request object, or throw an error.
	if (window.XMLHttpRequest) {
		this.request = new XMLHttpRequest();
	}
	else if (window.ActiveXObject) {
		this.request = new ActiveXObject("Microsoft.XMLHTTP");
	}

	if (!this.request) {
		throw new Error("Could not create XML-HTTP Request object.");
	}

	// Send the request.
	try {
		// When it callsback, get our object and call our handler
		this.request.onreadystatechange = new Function("PokkariXmlRequest.GetObject("+this.key+").onreadystatechange()");
		this.request.open(this.method,url,true);
		this.request.send(data);
	}
	catch (exception) {
		alert("While sending XML-HTTP request: "+exception.message);
	}
}

// This is our handler called from the new Function in send.
PokkariXmlRequest.prototype.onreadystatechange = function()
{
	if (this.request.readyState == 4) {
		if (this.request.status == 200) {
			// Send this as a parameter incase we override without subclassing
			this.onsuccess(this); 
		}
		else {
			this.onfailure(this);
		}
		// We're done with the request so now delete the key
		PokkariXmlRequest.DeleteObject(this.key);
		delete this.key;
		
		// Call the oncompleted method
		this.oncompleted(this);
	}
	else if (this.request.readyState == 1) {
		this.onloading(this);
	}
}

// This is the default onsuccess method.  It should be overridden
PokkariXmlRequest.prototype.onsuccess = function(object) 
{
	alert("Got back: "+ this.request.responseText);
}

// Called when the request fails.
PokkariXmlRequest.prototype.onfailure = function(object)
{
	//alert(this.request.status + " " + this.request.statusText);
	throw new Error("XML-HTTP Request Server Error: "+this.request.status+" "+this.request.statusText);
}

// Called while the request is loading.  Override to display something if you want.
PokkariXmlRequest.prototype.onloading = function (object) {}

// Called after the request completes.  Override to hide something if you want.
PokkariXmlRequest.prototype.oncompleted = function (object) {}

// Convenience methods so we don't end up saying request.request.whatever;
PokkariXmlRequest.prototype.getResponseXml = function() {
	return this.request.responseXML;
}

PokkariXmlRequest.prototype.getResponseText = function() {
	return this.request.responseText;
}

PokkariXmlRequest.prototype.getStatus = function() {
	return this.request.status;
}

PokkariXmlRequest.prototype.getStatusText = function() {
	return this.request.statusText;
}

// Stores an object in our lookup table.
PokkariXmlRequest.StoreObject = function(object)
{
	var key = PokkariXmlRequest.GenerateKey();
	PokkariXmlRequest.lookupTable[key] = object;
	return key;
}

// Removes an object from the lookupTable.
PokkariXmlRequest.DeleteObject = function(key)
{
	if (key != void(0)) {
		delete PokkariXmlRequest.lookupTable[key];
	}
}

// Get an object from the lookuptable by key.
PokkariXmlRequest.GetObject = function(key)
{
	return PokkariXmlRequest.lookupTable[key];
}

PokkariXmlRequest.GenerateKey = function()
{
	var random = Math.floor(Math.random() * 10000);
	var count = 1;
	
	while (PokkariXmlRequest.lookupTable[random] != void(0)) {
		random = Math.floor(Math.random() * 10000);
		if (count++ > 100) {
			throw new Error("Could not locate a unique key after 100 tries");
		}
	}

	return random;
}


function PokkariPost(params) {

	if (params) {
		this.posts_id			=	params.posts_id;
		this.item_id			=	params.item_id;
		this.item_type			=	params.item_type;
		this.title			=	params.title;
		this.wikiword			=	params.wikiword;
		this.categories_id		=	params.categories_id;
		this.category			=	params.category;
		this.enable_permissions_widget	=	params.enable_permissions_widget;
		this.enable_folders_widget	=	params.enable_folders_widget;
		this.permissions_use_buckets	=	params.permissions_use_buckets;
	}
}

// Set this up a subclass of PokkariElement..
PokkariPost.prototype = new PokkariElement();
PokkariPost.prototype.constructor = PokkariPost;

PokkariPost.prototype.onConstruct = function() {

	if (this.enable_folders_widget) {
		this.folders_widget = document.getElementById('folders_widget');
	}

	if (this.enable_permissions_widget) {
		this.permissions_widget = document.getElementById('permissions_widget');
	}

	if (this.folders_widget && this.enable_permissions_widget) {

		/* TODO - FIXME - Have to check whether we're on autopilot here
		   and only update the permissions widget if we are on autopilot
		*/
		
		var foldSelect = function(e) {
			e.target.pokkariPostElement.updatePotentialPermissions(e.target.options[e.target.selectedIndex].value);
		}

		this.folders_widget.pokkariPostElement = this;
	
		try {
			this.folders_widget.addEventListener('change',foldSelect,true);
		} catch(e) {
			this.folders_widget.addevent('change',foldSelect);
		}
	}
}

PokkariPost.prototype.fromXML = function(domObject) {

		this.posts_id = domObject.getElementsByTagName('id')[0].firstChild.nodeValue;
		this.title = domObject.getElementsByTagName('title')[0].firstChild.nodeValue;
		this.item_type = domObject.getElementsByTagName('item_type')[0].firstChild.nodeValue;
		this.item_id = domObject.getElementsByTagName('item_id')[0].firstChild.nodeValue;
		this.top_element = domObject;

}

PokkariPost.prototype.toXMLString = function() {
	var xml = "<post>\n";
		xml += "<id>"+this.posts_id+"</id>\n";
		xml += "<guid>"+this.posts_guid+"</guid>\n";
		xml += "<item_type>"+this.item_type+"</item_type>\n";
		xml += "<item_id>"+this.item_id+"</item_id>\n";
		xml += "<title>"+this.title+"</title>\n";
		xml += "</post>\n";

	return xml;
}

PokkariPost.prototype.getPermissionsEditable = function(folders_id) {
	
	// Only folders have editable permissions
	if(this.item_type != 'folder') {
		return false;
	}

	if(this.permissions_use_buckets) {
		/*
		   With buckets folders are only editable if their
		   parent is root
		*/

		if(folders_id == 0 || folders_id == void(0)) {
			return true;
		} else {
			return false;
		}
		
	} else {
		// Without buckets folders are always editable
		return true;
	}
	
}

PokkariPost.prototype.updatePotentialPermissions = function(folders_id) {
	/*
	   Now we want to get a new permissions value system via
	   xmlhttprequest so we can show the user what he's getting
	   himself into by moving his post
	*/

	var request = new PokkariXmlRequest();
	request.pokkariPost = this;
	request.url = "/posts/get_permissions?folders_id="+folders_id;
	request.onsuccess = this.receivePotentialPermissions;
	request.onfailure = this.potentialPermissionsFailure;
	request.send();

}

PokkariPost.prototype.receivePotentialPermissions = function(request) {
	/* 
		TODO - FIXME

		We're doing this the wrong way right now.
		The permissions widget should be a PokkariElement subclass
		that simply accepts our XML (or at least a parsed
		version of our XML) as input, and will rewrite itself
		for us.

		We're not doing that right now, but we should RSN.
	*/

	// These are the types of permissions Mango currently supports
	var permTypes = new Array('r','w','a');
	
	// Get our groups
	var groups = request.getResponseXml().getElementsByTagName('group');

	var permissionsEditability = request.pokkariPost.getPermissionsEditable(request.getResponseXml().getElementsByTagName('folders_id')[0].firstChild.nodeValue);

	// Loop through our groups
	for(var i = 0; i < groups.length; i++) {
		
		// Loop through our permissions types and set them in
		// the widget according to what we have in XML
		for(var j = 0; j < permTypes.length; j++) {

			var checkbox = document.getElementById('pm_'+groups[i].getElementsByTagName('guid')[0].firstChild.nodeValue+'_'+permTypes[j]);

			/* enable and disable the permissions widget as
			   appropriate
			*/
			if(permissionsEditability) {
				checkbox.disabled = false;
			} else {
				checkbox.disabled = true;
			}

			if(groups[i].getElementsByTagName(permTypes[j])[0].firstChild == void(0)) {
				checkbox.checked = false;	
			} else if(groups[i].getElementsByTagName(permTypes[j])[0].firstChild.nodeValue == 1) {
				checkbox.checked = true;
			} else {
				checkbox.checked = false;
			}
		}
	}
}

PokkariPost.prototype.failedPotentialPermissions = function(request) {

	alert("Failed to retrieve permissions of selected folder.  Permissions will still be set properly, but no permissions preview is available.");

}

PokkariPost.prototype.processPotentialPermissions = function() {
	alert("Processing potential permissions");
}

PokkariPost.prototype.addMetaHTML = function(html) {
	var element = document.getElementById("post_meta_add_"+this.posts_id);

	if (element != undefined) {
		element.innerHTML += html;
	}
}

/* toggleToolsMenuItem(button,div) (Public) [JD]

Similar to toggleHiddenDiv, but also selects the button, 
deselects all other buttons, and hides all other divs that
are tools related.
*/
PokkariPost.prototype.toggleTool = function(tool) {
	//var top_element = document.getElementById('post_'+this.posts_id);
	var button = document.getElementById('tools_menu_'+tool+'_'+this.posts_id);
	var tool = document.getElementById('container_hidden_'+tool+'_'+this.posts_id);
	var all_buttons = document.getElementsByName('tools_menu');
	var all_tools = document.getElementsByName('tools_tool');

	// Highlight the selected button, show the selected tool.
	if (tool.style.display == "block") {
		button.className = "item";
		tool.style.display = "none";
	}
	else {
		button.className = "item item_selected";
		tool.style.display = "block";
	}
		
	// Unhighlight and hide all appropriate DIVs
	for(var i=0; i<all_buttons.length; i++) {
		if (all_buttons[i].id != button.id) {
			all_buttons[i].className = "item";		
		}

		if (all_tools[i].id != tool.id) {
			all_tools[i].style.display = "none";
		}
	}
}

PokkariPost.getCurrentPost = function(element) {
	while(element.pokkariElement == void(0)) {
		if (element.parentNode) {
			element = element.parentNode;
		}
		else {
			alert("Could not locate pokkariElement");
			return;
		}
	}
	return element.pokkariElement;
}
		
PokkariPost.prototype.validateField = function(field,requirements,billboard) {

	if(!field.value && requirements['required']) {
		this.setError(billboard,'The "' + field.name + '" field is required.');
		return false;
	}

	if(requirements.extension) {
		var extensionValidates = false;
		for(var i = 0; i < requirements.extension.length; i++) {
			if(field.value.match(new RegExp("\." + requirements.extension[i] + "$","i"))) {
				extensionValidates = true;
			}
		}

		if(!extensionValidates) {
			this.setError(billboard,'<p>The file you have selected for uploading ("' + field.value + '") is of a type that we don\'t support for this purpose.  If you\'d like us to support this file type please contact support with your request.</p><p>Files you upload here must have an extension like: <b>' + requirements.extension.join(', ') + '</b>.</p>');
			return false;
		}
	}

	this.setError(billboard,'');
	return true;

}

PokkariPost.prototype.setMessage = function(billboard,message) {
	
	if(message) {
		billboard.innerHTML = message;
		billboard.style.display = 'block';
		fader.fade(billboard,'ffffff','666699');
	} else {
		billboard.style.display = 'none';
	}
	
}

PokkariPost.prototype.setError = function(billboard,message) {
	
	if(message) {
		billboard.innerHTML = message;
		billboard.style.display = 'block';
		fader.fade(billboard,'ffffff','CC6666');
	} else {
		billboard.style.display = 'none';
	}
	
}

PokkariPost.bookmark = function(posts_id,bookmarker,new_text) {
        if(!posts_id) {
                alert("You must provide a posts ID");
                return false;
        }

        var request = new PokkariXmlRequest("/bookmarks/add");
        request.posts_id = posts_id;
        request.bookmarker = bookmarker;
        request.new_text = new_text;
        request.url = "/bookmarks/add/?posts_id="+posts_id;
        request.onsuccess = PokkariPost.bookmarked;
        request.onfailure = PokkariPost.bookmarkFailure;
        request.send();
}

PokkariPost.bookmarked = function(request) {

        if(request.bookmarker && request.new_text) {
                request.bookmarker.innerHTML = request.new_text;
        } else {
                alert("Bookmarking successful");
        }

        if($('mini_bookmarks_list')) {
                PokkariPost.updateBookmarks($('mini_bookmarks_list'),request.posts_id);
        }

}

PokkariPost.bookmarkFailure = function(request) {
        alert("Failure!");
}

PokkariPost.updateBookmarks = function(obj,posts_id) {

        var updater = new Ajax.Updater(
                obj.id,
                '/bookmarks/view/?view=bookmarks_quick&no_wrap=1&posts_id='+posts_id,
                {
                        method  :       'get'
                });
}

PokkariPost.unbookmark = function(posts_id,bookmarker,new_text) {
        if(!posts_id) {
                alert("You must provide a posts ID");
                return false;
        }

        var request = new PokkariXmlRequest("/bookmarks/remove");
        request.posts_id = posts_id;
        request.bookmarker = bookmarker;
        request.new_text = new_text;
        request.url = "/bookmarks/remove/?posts_id="+posts_id;
        request.onsuccess = PokkariPost.unbookmarked;
        request.onfailure = PokkariPost.unbookmarkFailure;
        request.send();
}

PokkariPost.unbookmarked = function(request) {

        if(request.bookmarker) {
                switch(request.bookmarker.tagName) {
                        case 'TR':
                                request.bookmarker.style.display = 'none';
                        break
                        default:
                                if(request.new_text) {
                                        request.bookmarker.innerHTML = request.new_text;
                                } else {
                                        alert("Bookmark removal successful!");
                                }
                        break
                }
        }

        if($('mini_bookmarks_list')) {
                PokkariPost.updateBookmarks($('mini_bookmarks_list'),request.posts_id);
        }                                                                   

}

PokkariPost.unbookmarkFailure = function(request) {
        alert("Failure!");
}

PokkariPost.prototype.show = function() {
	this.element.style.display = "";  // Defualt
}

PokkariPost.prototype.hide = function() {
	this.element.style.display = "none";
}

function PokkariEditable(params) {

	if (params) {
		this.url = params.url;
		this.innerId = params.innerId;
		this.innerElement = params.innerElement;
		this.name = params.name;
	}
}

PokkariEditable.prototype = new PokkariXmlRequest();
PokkariEditable.prototype.constructor = PokkariXmlRequest;

PokkariEditable.prototype._setInnerElement = function()
{
	if (this.innerId) {
		this.innerElement = document.getElementById(this.innerId);
	}
	// Otherwise get the first inner DIV..
	else {
		var subelements = this.element.getElementsByTagName("div");
		this.innerElement = subelements[0];
	}
}

PokkariEditable.prototype.edit = function()
{
	if (!this.innerElement)
		this._setInnerElement();

	if (this.element.style.display == "none")
		this.element.style.display = "block";

	this.element.style.minHeight = "300px";
	this.innerElement.style.minHeight = "300px";

	if (this.innerElement.style.pixelHeight && 
		this.innerElement.style.pixelHeight < 300)
		this.innerElement.style.pixelHeight = 300;
	
	this.editorId = "mce_editor_" + tinyMCE.idCounter;

	tinyMCE.addMCEControl(this.innerElement);
}

PokkariEditable.prototype.getHTML = function()
{
	var editor = tinyMCE.instances[this.editorId];

	if (!editor)
		return void(0);

	tinyMCE._setHTML(editor.getDoc(), editor.getBody().innerHTML);

	var html = editor.getBody().innerHTML;

	return html;
}

PokkariEditable.prototype.save = function()
{
	alert("Feature not implemented");
}

PokkariEditable.prototype.onsuccess = function()
{
}

PokkariEditable.prototype.onloading = function ()
{
	var element = document.getElementById("xmlhttploading");
	var top = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;

	if (element != void(0) && top != void(0)) {
		element.style.top = top;
		element.style.display = "block";
	}
}

PokkariEditable.prototype.oncompleted = function()
{
	var element = document.getElementById("xmlhttploading");
	if (element != void(0)) {
		element.style.display = "none";
	}
}

PokkariEditable.GetEditableElements = function()
{
	var elements = PokkariElement.GetPokkariElements();
	var results = new Array();
	
	for (var i in elements)
	{
		if (elements[i].pokkariElement instanceof PokkariEditable)
			results.push(elements[i]);
	}

	return results;
}

PokkariEditable.EditAll = function()
{
	var elements = PokkariEditable.GetEditableElements();
	for (var i in elements)
	{
		elements[i].pokkariElement.edit();
	}
}

PokkariEditable.SaveAll = function(data)
{
	var elements = PokkariEditable.GetEditableElements();
	var e;
	var post;
	var url;

	for (var i in elements)
	{
		e = elements[i].pokkariElement;
		url = e.url;
		post += escape(e.name) + "=" + escape(e.getHTML()) + "&";
	}

	post += data;

	if (url)
	{
		var request = new PokkariXmlRequest();
		request.url = url;
		request.onsuccess = function() { window.location.reload(); }
		request.onloading = PokkariEditable.prototype.onloading;
		request.oncompleted = PokkariEditable.prototype.oncompleted;

		request.send(post);
	}
}

function PokkariFileReplacer(params) {
	if (params)
	{
	}

}

PokkariFileReplacer.prototype = new PokkariElement();
PokkariFileReplacer.prototype.constructor = PokkariElement;

PokkariFileReplacer.prototype.onLoadHandler = function()
{
	this.button = this.element.getElementsByTagName("button")[0];
	this.text = this.element.getElementsByTagName("input")[0];
	this.file = this.element.getElementsByTagName("input")[1];

	Pokkari.AttachEvent(this.button,"click",
		this.eventHandler(this.onReplaceClick));
}

PokkariFileReplacer.prototype.onReplaceClick = function(self,sender,e)
{
	
	self.text.style.display = "none";
	self.file.style.display = "inline";
	self.button.style.display = "none";

	// I'm handling it thanks.
	return true;
}
/**
 *	PokkariElementTabber class
 *	subclasses @PokkariElement
*/

function  PokkariElementTabber(params) {
	this.tabs = new Object();
}

PokkariElementTabber.prototype = new PokkariElement();
PokkariElementTabber.prototype.constructor = PokkariElementTabber;
PokkariElementTabber.prototype.superclass = PokkariElement;

PokkariElementTabber.prototype.getAnchors = function() {
	
	var collection = new Array();
	
	if(this.element) {
		var lis = this.element.getElementsByTagName('LI');
		
		for(var i = 0; i < lis.length; i++) {
			for(var ii = 0; ii < lis[i].childNodes.length; ii++) {
				if(
					lis[i].childNodes[ii].nodeType == 1 &&
					lis[i].childNodes[ii].getAttribute('tabelementpartner')
				) {
					collection.push(lis[i].childNodes[ii]);
				}
			}
		}
	}

	return collection;
}

PokkariElementTabber.prototype.isBound = function() {

	var anchors = this.getAnchors();

	for(var i = 0; i < anchors.length; i++) {
		anchors[i].PokkariElementTabber = this;
		anchors[i].onclick = new Function("this.PokkariElementTabber.performSwitch(this);");
		if(anchors[i].className.match('active')) {
			this.performSwitch(anchors[i]);
		}
	}

}

PokkariElementTabber.prototype.hideTabElementPartners = function() {
	
	var anchors = this.getAnchors();

	for(var i = 0; i < anchors.length; i++) {
		if(anchors[i].getAttribute('tabelementpartner')) {
			anchors[i].className = anchors[i].className.replace(/(\s+)?active(\s+)?/,'');
			document.getElementById(anchors[i].getAttribute('tabelementpartner')).style.display = 'none';
		}
	}
}

PokkariElementTabber.prototype.setActiveTabElementPartner = function(id,callback) {

	this.hideTabElementPartners();
	
	document.getElementById(id).style.display = 'block';

	if(callback) {
		eval(callback+"()");
	}
	
}

PokkariElementTabber.prototype.performSwitch = function(anchor) {

	if(anchor.getAttribute('tabelementpartner')) {
		this.setActiveTabElementPartner(anchor.getAttribute('tabelementpartner'),anchor.getAttribute('tabelementcallback'));
		anchor.className += " active";
		anchor.blur();
	} else {
		// nooop, at least for now
	}
}

/**
 *	PokkariPlayer class
 * 	subclasses @PokkariElement
 *	For writing of video player inline
 *	$Header: /usr/local/cvsroot/otter/html/scripts/pokkariPlayer.js,v 1.8 2006/07/10 18:22:22 mike Exp $
*/

function PokkariPlayer(params) { 
	this.MAX_WIDTH = 512;
	this.MAX_HEIGHT = 512;
}

PokkariPlayer.prototype = new Object();
PokkariPlayer.prototype.constructor = PokkariPlayer;

PokkariPlayer.prototype.setPrimaryMediaUrl = function(url) {
	this.primary_media_url = url;
}

PokkariPlayer.prototype.getPrimaryMediaUrl = function() {
	return this.primary_media_url;
}

PokkariPlayer.prototype.setWidth = function(width) {
	if(width == -1) { width = 320 }
	this.width = width;
}

PokkariPlayer.prototype.setResizeRatio = function(ratio) {
	this.resizeRatio = ratio;
}

PokkariPlayer.prototype.getResizeRatio = function() {
	return this.resizeRatio;
}

/**
* TODO - FIXME
* You MUST call getWidth() before getHeight() - otherwise you'll get
* the wrong aspect ratio.
* Should probably move this into a single getDimensions() method.
*/
PokkariPlayer.prototype.getWidth = function() {
	if(this.width) {
		if(this.width < this.MAX_WIDTH) {
			return this.width;
		} else {
			this.setResizeRatio(this.MAX_WIDTH / this.width);
			return this.MAX_WIDTH;
		}
	} else {
		return '320';
	}
}

PokkariPlayer.prototype.setHeight = function(height) {

	if(height == -1) { height = 240 }

	this.height = height;
}

PokkariPlayer.prototype.getHeight = function() {
	if(this.height) {
		if(this.getResizeRatio()) {
			return this.height * this.getResizeRatio();
		} else {
			if(this.height < this.MAX_HEIGHT) {
				return this.height + 20;
			} else {
				return this.MAX_HEIGHT + 20;
			}
		}
	} else {
		return '260';
	}
}

PokkariPlayer.prototype.setAutoPlay = function(ap) {
	this.autoPlay = ap;
}

PokkariPlayer.prototype.getAutoPlay = function() {
	if(this.autoPlay) {
		return true;
	} else {
		return false;
	}
}

PokkariPlayer.prototype.setPlayerTarget = function(obj) {
	this.playerTarget = obj;	
}

PokkariPlayer.prototype.getPlayerTarget = function() {
	return this.playerTarget;
}

PokkariPlayer.prototype.getPlayer = function() {
	return $('video_player_object');
}

PokkariPlayer.prototype.getTime = function() {
	throw("Cannot get time from generic embed");
}

PokkariPlayer.prototype.setTime = function() {
	throw("Cannot set time on generic embed");
}

PokkariPlayer.prototype.getDuration = function() {
	throw("Cannot get duration from generic embed");
}

PokkariPlayer.prototype.getStatus = function() {
	throw("Cannot get plugin status from generic embed");
}

PokkariPlayer.prototype.setPermalinkUrl = function(url) {
	this.permalinkUrl = url;
}

PokkariPlayer.prototype.getPermalinkUrl = function() {
	return this.permalinkUrl;
}

PokkariPlayer.prototype.setAdvertisingType = function(t) {
	this.adType = t;
}

PokkariPlayer.prototype.getAdvertisingType = function() {
	return this.adType;
}

PokkariPlayer.prototype.convertTimeToSeconds = function(timecode) {
	var time = timecode.replace(/;\d+$/,"");
	var timeParts = time.split(':');
	var result = timeParts[0]*60*60 + timeParts[1]*60 + timeParts[2];
	return result;
}

PokkariPlayer.prototype.convertSecondsToTime = function(s) {
	var d = new Date(s*1000);
	return d.toGMTString().substr(17,8);
}

PokkariPlayer.prototype.ensureSeconds = function(p) {
	if (isNaN(p)) { return this.convertTimeToSeconds(p); }
	else { return p; }
}

PokkariPlayer.prototype.ensureTime = function(p) {
	if (!isNaN(p)) { return this.convertSecondsToTime(p); }
	else { return p; }
}

PokkariPlayer.prototype.render = function() {

	if(!this.getPrimaryMediaUrl()) {
		throw("Cannot render without primary media URL");
	}
        if(!this.getPlayerTarget()) {
                throw("Cannot render without player target");
        }

	var autoPlay = (this.getAutoPlay()) ? 'true' : 'false';

	var html = '<embed src="' + this.getPrimaryMediaUrl() + '" autoplay="' + autoPlay + '" controller="true" width="' + this.getWidth() + '" height="' + this.getHeight() + '" scale="aspect" EnableJavaScript="true" ></embed>';

	this.getPlayerTarget().style.width = this.getWidth() + "px";
	this.getPlayerTarget().style.height = this.getHeight() + "px";

	this.getPlayerTarget().innerHTML = html;
}

PokkariPlayer.GetInstanceByMimeType = function(type) {

	var obj;

        switch(type) {
                case 'video/quicktime':
			return new PokkariQuicktimePlayer();
                break;
		case 'video/mpg':
			return new PokkariQuicktimePlayer();
		break;
		case 'video/mpeg':
			return new PokkariQuicktimePlayer();
		break;
		case 'video/mp4':
			return new PokkariQuicktimePlayer();
		break;
		case 'video/x-flv':
			return new PokkariFlashPlayer();
		break;
		case 'video/ms-wmv':
			return new PokkariWindowsPlayer();
		break;
		case 'video/x-ms-wmv':
			return new PokkariWindowsPlayer();
		break;
		case 'video/ms-wmv,video/x-ms-wmv':
			return new PokkariWindowsPlayer();
		break;
		case 'application/ogg':
			return new PokkariTheoraPlayer();
		break;
		case 'video/theora':
			return new PokkariTheoraPlayer();
		break;
		default:
			return new PokkariPlayer();
		break;
        }

}

function PokkariWindowsPlayer(params) {

}

PokkariWindowsPlayer.prototype = new PokkariPlayer();
PokkariWindowsPlayer.prototype.constructor = PokkariWindowsPlayer;
PokkariWindowsPlayer.prototype.superclass = PokkariPlayer;

PokkariWindowsPlayer.prototype.getPrimaryMediaUrl = function() {
	if (this.getAdvertisingType() && window.navigator.platform == "Win32") {
		var url = new Url(this.getPermalinkUrl());
		url.setQueryParam("skin","asx");
		return url.getUrl();
	}
	else {
		return PokkariPlayer.prototype.getPrimaryMediaUrl.apply(this,[]);
	}
}

PokkariWindowsPlayer.prototype.render = function() {
	
	if(!this.getPrimaryMediaUrl()) {
		throw("Cannot render without primary media URL");
	}
	if(!this.getPlayerTarget()) {
		throw("Cannot render without player target");
	}

	var autoPlay = (this.getAutoPlay()) ? 'true' : 'false';

	var html = '<object id="video_player_object" width="' + this.getWidth() + '" height="' + this.getHeight() + '" classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701" standby="Loading Microsoft Windows Media Player components...", type="application/x-oleobject">';


	html += '<param name="fileName" value="' + this.getPrimaryMediaUrl() + '">';
	html += '<param name="animationStart" value="true">';

	if (typeof(this.startTime) != "undefined" && this.startTime) {
		html += '<param name="currentPosition" value="'+this.startTime+'">';
		autoPlay = true;
	}

	html += '<param name="AutoStart" value="' + autoPlay + '">';
	html += '<param name="showControls" value="true">';
	html += '<param name="transparentatStart" value="false">';
	html += '<param name="loop" value="false">';

	html += '<embed type="application/x-mplayer2" pluginspage="http://www.microsoft.com/Windows/MediaPlayer/" id="video_player_object" name="video_player_object" displaysize="4" autosize="-1" bgcolor="darkblue" showcontrols="true" showtracker="-1" showdisplay="0" showstatusbar="-1" videoborder3d="-1" width="' + this.getWidth() + '" height="' + this.getHeight() + '" src="' + this.getPrimaryMediaUrl() + '" autostart="' + autoPlay + '" designtimesp="5311" loop="false"';
	if (typeof(this.startTime) != "undefined" && this.startTime) {
		html += ' currentPosition="'+this.startTime+'"';
	}
	
	html += '></embed>';
	html += '</object>';

	this.getPlayerTarget().style.width = this.getWidth() + "px";
	this.getPlayerTarget().style.height = this.getHeight() + "px";

	this.getPlayerTarget().innerHTML = html;
}

PokkariWindowsPlayer.prototype.setTime = function(time) {
	var s = this.ensureSeconds(time);
	var player = this.getPlayer();

	if (player) {	
		player.controls.currentPosition = s;
	}
	else {
		this.startTime = s;
	}
}

PokkariWindowsPlayer.prototype.getTime = function() {
	var player = this.getPlayer();

	if (player) {
		player.controls.currentPosition;
	}
	else if (this.startTime) {
		return this.startTime;
	}
	else {
		return null;
	}
}

PokkariWindowsPlayer.prototype.getDuration = function() {
	var player = this.getPlayer();

	if (player) {
		player.currentMedia.duration;
	}
	else {
		return null;
	}
}

PokkariWindowsPlayer.prototype.getStatus = function() {
	// TODO FIXME Convert status
	//Value 	State 	Description
	//0 	Undefined 	Windows Media Player is in an undefined state.
	//1 	Stopped 	Playback of the current media item is stopped.
	//2 	Paused 	Playback of the current media item is paused. When a media item is paused, resuming playback begins from the same location.
	//3 	Playing 	The current media item is playing.
	//4 	ScanForward 	The current media item is fast forwarding.
	//5 	ScanReverse 	The current media item is fast rewinding.
	//6 	Buffering 	The current media item is getting additional data from the server.
	//7 	Waiting 	Connection is established, but the server is not sending data. Waiting for session to begin.
	//8 	MediaEnded 	Media item has completed playback.
	//9 	Transitioning 	Preparing new media item.
	//10 	Ready 	Ready to begin playing.
	//11 	Reconnecting
	var player = this.getPlayer();

	if (player) {
		return player.playState;
	}
	else {
		return null;
	}
}


function PokkariQuicktimePlayer(params) {

}

PokkariQuicktimePlayer.prototype = new PokkariPlayer();
PokkariQuicktimePlayer.prototype.constructor = PokkariQuicktimePlayer;
PokkariQuicktimePlayer.prototype.superclass = PokkariPlayer;

PokkariQuicktimePlayer.prototype.getPrimaryMediaUrl = function() {
	if (this.getAdvertisingType()) {
		var url = new Url(this.getPermalinkUrl());
		url.setQueryParam("skin","smil");
		return url.getUrl();
	}
	else {
		return PokkariPlayer.prototype.getPrimaryMediaUrl.apply(this,[]);
	}
}

PokkariQuicktimePlayer.prototype.getTime = function() {
	var player = this.getPlayer();

	return player.GetTime() / player.GetTimeScale();
}

PokkariQuicktimePlayer.prototype.setTime = function(time) {
	var player = this.getPlayer();

	time = time * player.GetTimeScale();

	player.SetTime(time);
}

PokkariQuicktimePlayer.prototype.getDuration = function() {
	var player = this.getPlayer();

	return player.GetDuration() / player.GetTimeScale();
}

PokkariQuicktimePlayer.prototype.getPlayer = function() {
	
	return document.video_player_object;
	
}

PokkariQuicktimePlayer.prototype.getStatus = function() {
	return this.getPlayer().GetPluginStatus();
}

PokkariQuicktimePlayer.prototype.render = function() {
	if(!this.getPrimaryMediaUrl()) {
		throw("Cannot render without primary media URL");
	}
	if(!this.getPlayerTarget()) {
		throw("Cannot render without player target");
	}

	var autoPlay = (this.getAutoPlay()) ? 'true' : 'false';

	var html = '<object id="video_player_object" classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="' + this.getWidth() + 
		'" height="' + this.getHeight() + '" codebase="http://www.apple.com/qtactivex/qtplugin.cab">';

	html += '<param name="src" value="' + this.getPrimaryMediaUrl() + '">';
	html += '<param name="autoplay" value="' + autoPlay + '">';
	html += '<param name="controller" value="true">';
	html += '<param name="scale" value="aspect">';

	html += '<embed name="video_player_object" src="' + this.getPrimaryMediaUrl() + '" autoplay="' + autoPlay + 
		'" controller="true" width="' + this.getWidth() + '" height="' + this.getHeight() + 
		'" scale="aspect" EnableJavaScript="true" type="video/quicktime"></embed>';

	html += '</object>';

	this.getPlayerTarget().style.width = this.getWidth() + "px";
	this.getPlayerTarget().style.height = this.getHeight() + "px";

	this.getPlayerTarget().innerHTML = html;

}

function PokkariFlashPlayer(params) {

}

PokkariFlashPlayer.prototype = new PokkariPlayer();
PokkariFlashPlayer.prototype.constructor = PokkariFlashPlayer;
PokkariFlashPlayer.prototype.superclass = PokkariPlayer;

PokkariFlashPlayer.prototype.render = function() {
	
	var autoPlay = (this.getAutoPlay()) ? 'true' : 'false';
	var query = "file=" + escape(this.getPrimaryMediaUrl()) + "&autoStart=" + autoPlay;
	if (this.getAdvertisingType()) {
		query += "&trailerMovie=http://ad.postroller.com/postroller/flash/blip.swf";
	}
	
	var html = '<object id="video_player_object" type="application/x-shockwave-flash" width="' + this.getWidth() + '" height="' + this.getHeight() + '" wmode="transparent" data="/scripts/flash/blipplayer.swf?' + query + '">';
	html += '<param name="movie" value="/scripts/flash/flvplayer.swf?' + query + '">';
	html += '<param name="wmode" value="transparent" />';
	html += '</object>';

	this.getPlayerTarget().style.width = this.getWidth() + "px";
	this.getPlayerTarget().style.height = this.getHeight() + "px";

	this.getPlayerTarget().innerHTML = html;

}

PokkariFlashPlayer.prototype.getTime = function() {
	var player = this.getPlayer();
	
	return player.GetVariable("videoCurrentTime");
}

PokkariFlashPlayer.prototype.setTime = function(value) {
	var player = this.getPlayer();

	player.SetVariable("videoSetCurrentTime",value);
}

PokkariFlashPlayer.prototype.getDuration = function(value) {
	var player = this.getPlayer();

	return player.GetVariable("videoTotalTime");
}

PokkariFlashPlayer.prototype.getStatus = function(value) {
	var player = this.getPlayer();

	// TODO TRANSLATE ME
	return player.GetVariable("videoCurrentStatus");
}

function PokkariTheoraPlayer(params) {
}

PokkariTheoraPlayer.prototype = new PokkariPlayer();
PokkariTheoraPlayer.prototype.constructor = PokkariTheoraPlayer;
PokkariTheoraPlayer.prototype.superclass = PokkariPlayer;  // This isn't necessary is it?

PokkariTheoraPlayer.prototype.render = function() {
	var url = this.getPrimaryMediaUrl().replace("/file/get","/uploadedFiles");
	var width = this.getWidth();
	var height = this.getHeight();

	var html =  '<object classid="clsid:CAFEEFAC-0014-0002-0000-ABCDEFFEDCBA"\n' +
		'width="' + width + '" height="' + height + '"\n' +
	    'codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_4_2-windows-i586.cab#Version=1,4,2,0">\n' +
		'<param name="code" value="com.fluendo.player.Cortado.class" />\n' +
		'<param name="codebase" value="http://blip.tv/scripts/theora" />\n' +
	    '<param name="type" value="application/x-java-applet;jpi-version=1.4.2">\n' +
	    '<param name="scriptable" value="true">\n' +	
		'<param name="url" value="' + url + '" />\n' +
		'<param name="local" value="false" />\n' +
		'<param name="keepaspect" value="true" />\n' +
		'<comment>\n' +		
		'<APPLET code="com.fluendo.player.Cortado.class"\n' +
        'codebase="/scripts/theora"\n' +
        'width="' + width + '" height="' + height + '">\n' +
  		'<PARAM name="url" value="' + url + '"/>\n' +
	  	'<PARAM name="local" value="false"/>\n' +
	  	'<PARAM name="keepaspect" value="true"/>\n' +
		'</APPLET>\n' +
		'</comment>\n' +
		'</object>\n';

	this.getPlayerTarget().innerHTML = html;
}

function PokkariRating(params) {
	PokkariXmlRequest.apply(this,[params]);
}

PokkariRating.prototype = new PokkariXmlRequest();
PokkariRating.prototype.constructor = PokkariRating;

PokkariRating.prototype.onLoadHandler = function()
{
	var star;
	var images = this.element.getElementsByTagName("img");
	this.stars = new Array();
	for (var i=0; i<images.length; i++) {
		if (images[i].className.match(/star/))
			this.stars.push(images[i]);
	}
	
	if (!this.stars || !this.stars.length) {
		throw new Error("Could not locate img children for stars");
	}

	if (!this.disabled) 
	{
		for (var i=0; i<this.stars.length; i++) {
			star = this.stars[i];
			star.rating = i+1;
		
			Pokkari.AttachEvent(star,"click",
				this.eventHandler(this.onStarClick));

			Pokkari.AttachEvent(star,"mouseover",
				this.eventHandler(this.onStarMouseOver));

			Pokkari.AttachEvent(star,"mouseout",
				this.eventHandler(this.onStarMouseOut));

			star.style.cursor = "pointer";
		}
	}

	if (this.messageId) {
		this.messageElement = $(this.messageId);
	}

	if (this.messageElement) {
		this.messageElement.oldHTML = this.messageElement.innerHTML;
	}

	if (typeof(this.messages) == "string") {
		this.messages = this.messages.split(',');
	}

	if (this.voteShowId) {
		this.voteShowElement = $(this.voteShowId);
	}

	if (this.voteHideId) {
		this.voteHideElement = $(this.voteHideId);
	}

	if (!this.imageExtension) {
		this.imageExtension = 'gif';
	}

	if (typeof(this.imageColors) == "string")
		this.imageColors = this.imageColors.split(',');
	else if (!this.imageColors)
		this.imageColors = ['null','blue','red','purple'];
	
	if (typeof(this.imageSides) == "string")
		this.imageSides = this.imageSides.split(',');
	else if (!this.imageSides)
		this.imageSides = ['left','right'];

	if (!this.imageRoot)
		this.imageRoot = '/images/star_';
		
	this.preload();
}

PokkariRating.prototype.onStarClick = function(self,sender,e)
{
	if (self.disabled) { return; }

	self.disabled = true;

	self.url = "/" + self.itemType + "/rate/" + self.itemId + "?rating=" +
		sender.rating + "&entropy=" + Math.floor(Math.random()*10000);

	self.send();
}

PokkariRating.prototype.onStarMouseOver = function(self,sender,e)
{
	if (self.disabled) { return; }

	var myRating = sender.rating;
	var siteRating = self.currentRating || 0;
	var side = null;
	var color = null;
	var element = null;
	
	for(var x=1; x<=myRating; x++) {
		side = self.imageSides[1 - (x % 2)];
		if (self.imageColors.length == 4)
			color = Math.round(siteRating) >= x ? self.imageColors[3] : self.imageColors[2];
		else
			color = self.imageColors[1];
		
		element = self.stars[x-1];
		element.src = PokkariRating.preloadedImages[color+"_"+side].src;
			//self.imageRoot+color+"_"+side+"."+self.imageExtension
	}

	if (self.imageColors.length < 4) {
		for(var x=myRating+1; x<=self.stars.length; x++) {
			side = self.imageSides[1 - (x % 2)];
			color = self.imageColors[0];
		
			element = self.stars[x-1];
			element.src = PokkariRating.preloadedImages[color+"_"+side].src;
		//		self.imageRoot+color+"_"+side+"."+self.imageExtension
		}
	}

	if (self.messageElement)
	{
		if (self.messages) {
			self.messageElement.innerHTML = self.messages[myRating-1];
		}
		else {
			self.messageElement.innerHTML = "Rate this " + myRating;
		}
	}
}

PokkariRating.prototype.onStarMouseOut = function(self,sender,e)
{
	if (self.disabled) { return; }

	var myRating = sender.rating;
	var siteRating = self.currentRating || 0;
	var side = null;
	var color = null;
	var element = null;
	var x
	for(x=1; x<=self.stars.length; x++) {
		side = self.imageSides[1 - (x % 2)];
		color = Math.round(siteRating) >= x ? self.imageColors[1] : self.imageColors[0];
		
		element = self.stars[x-1];
		element.src = PokkariRating.preloadedImages[color+"_"+side].src;
		//self.imageRoot+color+"_"+side+"."+self.imageExtension
	}

	if (self.messageElement)
		self.messageElement.innerHTML = self.messageElement.oldHTML;
}

PokkariRating.prototype.onsuccess = function(request)
{
	var xml = request.getResponseXml();
	var topNode = xml.documentElement;
	var response = topNode.getElementsByTagName("response")[0];
	var error = response.getElementsByTagName("error")[0];
	var notice = response.getElementsByTagName("notice")[0];

	if (error) {
		window.alert("Error: " + error.firstChild.nodeValue);
	}
	else if (notice) {
		window.alert(notice.firstChild.nodeValue);
	}
	else {
		var type = response.getElementsByTagName("type")[0].firstChild.nodeValue;
		var id = response.getElementsByTagName("id")[0].firstChild.nodeValue;
		var myRating = response.getElementsByTagName("myRating")[0].firstChild.nodeValue;
		var siteRating = response.getElementsByTagName("siteRating")[0].firstChild.nodeValue;

		this.disabled = true;
		this.currentRating = siteRating;
		this.onStarMouseOver(this,this.stars[myRating-1],null);

		if (this.messageElement) {
			if (this.voteMessage) {
				this.messageElement.innerHTML = this.voteMessage;
			}
			else {
				this.messageElement.innerHTML = "Thank You For Voting!";
			}
		}

		if (this.voteShowElement) {
			this.voteShowElement.style.display = 'block';
		}
		if (this.voteHideElement) {
			this.voteHideElement.style.display = 'none';
		}
	}
}

PokkariRating.prototype.preload = function()
{
	var img;
	var colors = this.imageColors;
	var sides = this.imageSides;
	var ext = this.imageExtenion;
	var root = this.imageRoot;

	PokkariRating.preload(root,colors,sides,ext);
}

PokkariRating.preload = function(root,colors,sides,ext)
{
	if (!PokkariRating.preloadedImages)
	{
		PokkariRating.preloadedImages = new Object();
		for (var i=0; i<colors.length; i++) {
			for (var j=0; j<sides.length; j++) {
				img = new Image();
				img.src = root + colors[i] + '_' + sides[j] + '.' + ext;
				PokkariRating.preloadedImages[colors[i]+"_"+sides[j]] = img;
			}
		}
	}
}

function PokkariRotatingTip(params) {
}

PokkariRotatingTip.prototype = new PokkariXmlRequest();
PokkariRotatingTip.prototype.constructor = PokkariRotatingTip;

PokkariRotatingTip.prototype.show = function()
{
	this.locateElements();
	this.url = "/tips?ignore=1234";
	this.timer = window.setInterval("document.getElementById('"+this.element.id+"').pokkariElement.ontick();",10000);

	// Non-IE browsers can't do two XmlHttpRequests at once, and since we've probably just fired UploadMonitor, let's 
	// throw in a delay.
	if (document.all)
		this.ontick(this,this);	
	else
		window.setTimeout("document.getElementById('"+this.element.id+"').pokkariElement.ontick();",500);
}

PokkariRotatingTip.prototype.locateElements = function()
{
	var elements = this.element.getElementsByTagName("*");
	for (var i=0; i<elements.length; i++)
	{
		if (elements[i].innerHTML)
		{
			if (elements[i].innerHTML == "title")
				this.titleElement = elements[i];
			else if (elements[i].innerHTML == "body")
				this.bodyElement = elements[i];
		}
	}

	if (!this.titleElement || !this.bodyElement)
		throw new Error("Could not locate title and/or body elements");
}

PokkariRotatingTip.prototype.ontick = function()
{
	// Trick to convince IE to actually update..
	this.url = this.url.substring(0,this.url.length-4) +
			(Math.floor(Math.random()*9000)+1000);
	this.send();
}

PokkariRotatingTip.prototype.onsuccess = function() 
{
	var xml = this.getResponseXml();
	var topNode = xml.documentElement;
	var title = topNode.getElementsByTagName("title")[0].firstChild.nodeValue;
	var body = topNode.getElementsByTagName("body")[0].firstChild.nodeValue;

	this.paint(title,body);
}

PokkariRotatingTip.prototype.paint = function(title,body) 
{
	if (body) 
	{
		if (this.element.style.display != "block")
			this.element.style.display = "block";

		this.titleElement.innerHTML = title;
		this.bodyElement.innerHTML = body;
	}
}


/**
 *	PokkariScriptEditor class
 *	subclasses @PokkariElement
*/

function  PokkariScriptEditor(params) {
	this.fields = new Object();

	this.baseURL = BLIP_SITE_URL + '/posts/?skin=js&has_thumbnail=1';
}

PokkariScriptEditor.prototype = new PokkariElement();
PokkariScriptEditor.prototype.constructor = PokkariScriptEditor;
PokkariScriptEditor.prototype.superclass = PokkariElement;

PokkariScriptEditor.prototype.getFields = function() {
	
	var collection = new Array();

	if(this.element) {
		var inputs = this.element.getElementsByTagName('INPUT');
		for(var i = 0; i < inputs.length; i++) {
			if(inputs[i].getAttribute('scripteditorfield')) {
				collection.push(inputs[i]);
			}
		}
		var selects = this.element.getElementsByTagName('SELECT');
		for(var i = 0; i < selects.length; i++) {
			if(selects[i].getAttribute('scripteditorfield')) {
				collection.push(selects[i]);
			}
		}
	}

	return collection;
}

PokkariScriptEditor.prototype.getCodeRepository = function() {
	
	var textareas = this.element.getElementsByTagName('TEXTAREA');

	for(var i = 0; i < textareas.length; i++) {
		if(textareas[i].getAttribute('scripteditorcode')) {
			return textareas[i];
		}
	}
	
}

PokkariScriptEditor.prototype.isBound = function() {

	this.fields = this.getFields();

	this.codeRepository = this.getCodeRepository();

	for(var i = 0; i < this.fields.length; i++) {
		this.fields[i].PokkariScriptEditor = this;
		this.fields[i].onkeyup = new Function("this.PokkariScriptEditor.changedField(this);");
		this.fields[i].onchange = new Function("this.PokkariScriptEditor.changedField(this);");
	}

	if(document.getElementById('blip_sidebar_container')) {
		this.blip_sidebar_container = document.getElementById('blip_sidebar_container');
	}
}

PokkariScriptEditor.prototype.changedField = function(field) {
	this.updateCodeRepository();
}

PokkariScriptEditor.prototype.updateCodeRepository = function() {
	
	var string = '<script type="text/javascript" src="' + this.getScriptURL() + '"></script>';
	this.codeRepository.value = string;
	
}

PokkariScriptEditor.prototype.updatePreview = function() {

	this.blip_sidebar_container.innerHTML = '';

	var script = document.createElement('script');
	script.type = 'text/javascript';
	script.src = this.getScriptURL(); 
	this.blip_sidebar_container.appendChild(script);
}

PokkariScriptEditor.prototype.getScriptURL = function() {

	var url = '';
	
	for (var i = 0; i < this.fields.length; i++) {
		url += "&" + escape(this.fields[i].name) + "=" + escape(this.fields[i].value);
	}

	return this.baseURL + url;
}
function TemplateEditor(params) {
	this.init(params);
}

TemplateEditor.prototype = new PokkariXmlRequest();
TemplateEditor.prototype.constructor = TemplateEditor;

TemplateEditor.prototype.init = function() {
	
	this.baseURL = "/templates/preview/";
	this.method = "GET";

	this.onsuccess = this.receivePreview;
	this.onfailure = this.receivePreviewFailure;
}

TemplateEditor.prototype.onConstruct = function() {

	if(
		document.getElementById('templatePreviewPane')
	) {
		this.previewPane = document.getElementById('templatePreviewPane');
	}

	if(this.element.toString().match(/HTMLSelectElement/)) {
		try {
			this.element.addEventListener('change',new Function("this.pokkariElement.updatePreview();"),false);
		} catch(e) {
			this.element.attachEvent('keyup',new Function("this.pokkariElement.updatePreview();"));
		}
	}

	if(document.getElementById('share_tags')) {
		document.getElementById('share_tags').TemplateEditor = this.element;
		try {
			document.getElementById('share_tags').addEventListener('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"),false);
		} catch(e) {
			document.getElementById('share_tags').attachEvent('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"));
		}

		this.share_tags = document.getElementById('share_tags');
	}

	if($('playback_style')) {
		$('playback_style').TemplateEditor = this.element;
		try {
			$('playback_style').addEventListener('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"),false);
		} catch(e) {
			$('playback_style').attachEvent('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"));
		}

		this.playback_style = $('playback_style');
	}

	if($('include_format_links')) {
		$('include_format_links').TemplateEditor = this.element;
		try {
			$('include_format_links').addEventListener('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"),false);
		} catch(e) {
			$('include_format_links').attachEvent('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"));
		}

		this.include_format_links = $('include_format_links');
	}

	if($('ctp_text')) {
		$('ctp_text').TemplateEditor = this.element;
		try {
			$('ctp_text').addEventListener('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"),false);
		} catch(e) {
			$('ctp_text').attachEvent('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"));	
		}
		this.ctp_text = $('ctp_text');
	}

	if(this.previewPane.style.display != 'none') {
		this.updatePreview();
	}

	if(this.element.getAttribute('blogid')) {
		this.blogid = this.element.getAttribute('blogid');
	}

	this.updatePreview();

}

TemplateEditor.prototype.togglePreview = function() {
	
	if(this.previewPane) {
		if(this.previewPane.style.display == 'none') {
			this.updatePreview();
		} else {
			this.previewPane.style.display = 'none';
		}
	}
}

TemplateEditor.prototype.updatePreview = function() {
	
	if(this.element.toString().match(/HTMLSelectElement/)) {
		this.url = this.baseURL + "?id=" + this.element.value; 
	} else {
		this.url = this.baseURL + "?template=" + escape(this.element.value);
	}

	if(this.blogid) {
		this.url += '&blogId='+this.blogid; 
	}

	if(this.share_tags) {
		var share_tags = 0;
		if(this.share_tags.checked) {
			share_tags = 1;
		}
		this.url += '&share_tags='+share_tags;
	}

	if(this.playback_style) {
		this.url += '&playback_style='+this.playback_style.options[this.playback_style.selectedIndex].getAttribute('style_name');
	}

	if(this.include_format_links) {
		var ifl = 0;
		if(this.include_format_links.checked) {
			ifl = 1;
		}
		this.url += '&include_format_links='+ifl;
	}

	if(this.ctp_text) {
		this.url += '&ctp_text='+escape(this.ctp_text.value);
	}

	this.send();
}

TemplateEditor.prototype.receivePreview = function() {
	if(this.getResponseXml().getElementsByTagName('parsed')[0]) {
		var preview = this.getResponseXml().getElementsByTagName('parsed')[0].firstChild.nodeValue;
		this.displayPreview({ markup : preview });
	} else {
		if(this.getResponseXml().getElementsByTagName('error')[0]) {
			this.receivePreviewFailure(this.getResponseXml().getElementsByTagName('error')[0].firstChild.nodeValue);
		}
	}
}

TemplateEditor.prototype.receivePreviewFailure = function(error) {

	this.previewPane.innerHTML = "<h1>Preview failed</h1>";
	if(error) {
		this.previewPane.innerHTML += "<p>"+error+"</p>";
	}

}

TemplateEditor.prototype.displayPreview = function(params) {

	if(this.previewPane) {
		this.previewPane.innerHTML = params['markup'];
		if(this.previewPane.style.display == 'none') {
			this.previewPane.style.display = 'block';
		}
	}
	
}
/*
	By Mike Hudack for Pokkari, Inc.
	Tuesday August 30, 2005
*/
function TopicEditor(params) {
	this.init(params);
}

TopicEditor.prototype = new PokkariXmlRequest();
TopicEditor.prototype.constructor = TopicEditor;

TopicEditor.prototype.init = function() {

	this.url = "/topics/suggest/";
	this.method = "GET";

	this.onsuccess = this.receiveSuggestions;
	this.onfailure = this.receiveSuggestionsFailure; 
}

TopicEditor.prototype.onConstruct = function() {
	
	/*
		Let's set our onchange
	*/

	function onkeyup(e) {
		return this.pokkariElement.onKeyUp(e);
	}

	function onkeypress(e) {
		return this.pokkariElement.onKeyPress(e);
	}

	if(this.element) {
		try {
			this.element.addEventListener('keyup',onkeyup,false);
			this.element.addEventListener('keypress',onkeypress,false);
		} catch(e) {
			this.element.attachEvent('keyup',onkeyup);
			this.element.attachEvent('kepress',onkeypress);
		}
	} else { }
	
	/* 
	We need to find if we have an auto-completion zone.  These can be
	siblings.
	*/

	if(document.getElementById('autoCompletionZoneContainer')) {
		this.autoCompletionZoneContainer = document.getElementById('autoCompletionZoneContainer');
	} 

	this.getSuggestions();
}

TopicEditor.prototype.getSuggestions = function() {
	
	this.send();

}

/*
	A nice and complicated method to get the juices flowing.
*/
TopicEditor.prototype.receiveSuggestions = function() {

	// We have fresh suggestions, so clear the old ones
	this.suggestions = new Array();

	for(var i = 0; i < this.getResponseXml().getElementsByTagName('class').length; i++) {
		var suggestionClass = this.getResponseXml().getElementsByTagName('class')[i];
		var className = suggestionClass.getElementsByTagName('className')[0].firstChild.nodeValue;
		if(!this.suggestions[className]) {
			this.suggestions[className] = new Array();
			this.suggestions[className]['suggestions'] = new Array();
			var acz = document.createElement('div');
			var aczObj = new AutoCompletionZone();
			aczObj.EditPalette = this.element;
			aczObj.bindToHTMLElement(acz);
			aczObj.setLabel(className);
			aczObj.onConstruct();
			// first ACZ is primary
			if(i == 0) {
				aczObj.setPrimary(true);
				this.setPrimaryCompletionZone(acz);
			}	
			this.suggestions[className]['autoCompletionZone'] = acz;
			if(this.autoCompletionZoneContainer) {
				this.autoCompletionZoneContainer.appendChild(acz);
			} else {
				this.element.parentNode.appendChild(acz);
			}
		} else {
			this.suggestions[className]['suggestions'].length = -1;	
		}
		for(var ii = 0; ii < suggestionClass.getElementsByTagName('name').length; ii++) {
			var firstLetter = suggestionClass.getElementsByTagName('name')[ii].firstChild.nodeValue.substring(0,1);	
			if(!this.suggestions[className]['suggestions'][firstLetter]) {
				this.suggestions[className]['suggestions'][firstLetter] = new Array();
			}
			this.suggestions[className]['suggestions'][firstLetter].push(suggestionClass.getElementsByTagName('name')[ii].firstChild.nodeValue);
		}
	}

}

TopicEditor.prototype.receiveSuggestionsFailure = function() {
	/*
		Stoics fail silently, without upsetting other people.
	*/
}

TopicEditor.prototype.getCurrentTopics = function() {
	
	var topics = new Array();

	if(this.element.value.match(/,/)) {
		topics = this.element.value.split(",");
	} else {
		topics = this.element.value.split(" ");
	}

	return topics;
}

/**
 * @param {String} topic
 *
 * Sets the currentWorkingTopic to topic (first argument)
*/
TopicEditor.prototype.setCurrentTopic = function(topic) {

	var topics = this.getCurrentTopics();

	if(!this.element.value.match(/,/) && topics.length >= 2) {
		topic = topic.replace(/\W+/,"_");
	}

	var currentWorkingTopic = topics[topics.length - 1];
	var re = eval("/"+currentWorkingTopic+"$/");
	this.element.value = this.element.value.replace(re,topic);

	if(this.element.value.match(/,/) || topics.length <= 1) {
		this.element.value += ', ';
	} else {
		this.element.value += ' ';
	}

	this.clearAutoCompletionZones();

	this.element.focus();
}

TopicEditor.prototype.clearAutoCompletionZones = function() {

	for(var suggestionClass in this.suggestions) {
		if(this.suggestions[suggestionClass]['autoCompletionZone']) {
			this.suggestions[suggestionClass]['autoCompletionZone'].pokkariElement.clearCandidates();
		}
	}
}

TopicEditor.prototype.onKeyPress = function(e) {
	
	if(e.keyCode == 9) {
		try {
			if(this.getPrimaryCompletionZone() && this.getPrimaryCompletionZone().pokkariElement.getTopSuggestion()) {
				this.setCurrentTopic(this.getPrimaryCompletionZone().pokkariElement.getTopSuggestion());
			}
		} catch(e) {
			alert(e);
		}
		e.preventDefault();
	}

	return true;
	
}

TopicEditor.prototype.onKeyUp = function(e) {
	// First, we have to find what we're really working on 

	var topics = this.getCurrentTopics();

	// CWT is last topic discovered, with leading spaces removed
	var currentWorkingTopic = topics[topics.length - 1].replace(/^\s+/,""); 

	this.doSuggest(currentWorkingTopic);
}

TopicEditor.prototype.doSuggest = function(target) {
	
	for(var suggestionClass in this.suggestions) {
		var potentials = this.suggestions[suggestionClass]['suggestions'][target.substring(0,1)];
		var toSuggest = new Array;
		if(potentials != void(0) && potentials.length) {
			for(var i = 0; i < potentials.length; i++) {
				if(potentials[i].indexOf(target) == 0) {
					toSuggest.push(potentials[i]);
				}
			}
		}

		if(toSuggest.length > 7) {
			toSuggest.length = 7;
		}
		
		if(this.suggestions[suggestionClass]['autoCompletionZone']) {
			this.suggestions[suggestionClass]['autoCompletionZone'].pokkariElement.newCandidates(toSuggest);	
		}
	}
}

TopicEditor.prototype.setPrimaryCompletionZone = function(zone) {
	this.primaryCompletionZone = zone;
}

TopicEditor.prototype.getPrimaryCompletionZone = function() {
	return this.primaryCompletionZone;
}
function PokkariUploadMonitor(params) {
	if (params)
	{
		this.form_cookie = params.form_cookie;
	}

	this.filenameSpan = document.getElementById("upload_monitor_filename");
	this.progressDiv = document.getElementById("upload_monitor_progressbar_inner");
	this.timeleftSpan = document.getElementById("upload_monitor_timeleft");
	this.sentSpan = document.getElementById("upload_monitor_sent");
	this.totalSpan = document.getElementById("upload_monitor_total");
	this.rateSpan = document.getElementById("upload_monitor_rate");
	this.predictionRatio = 0.8;
}

// Set this up as a subclass of PokkariXmlRequest
PokkariUploadMonitor.prototype = new PokkariXmlRequest("/ugc/upload/status");
PokkariUploadMonitor.prototype.constructor = PokkariXmlRequest;

// Show the upload monitor and start updating..
PokkariUploadMonitor.prototype.show = function()
{
	// Update the PokkariXmlRequest url with the form_cookie set on construct
	this.url = "/ugc/upload/status?form_cookie="+this.form_cookie + "&1234";

	// Display the widget
	this.element.style.display = "block";

        // if we have a form, hide it
		if($('post_form_table') && !window.navigator.userAgent.match(/KHTML/)) {
			$('post_form_table').style.display = 'none';
			// and we have to scroll to the top or everything gets fubar'd
			window.scroll(0,0);
		}

	var context = this.element.id;

	this.tickCount = 0;
	this.timer = window.setInterval("document.getElementById('"+
		context+"').pokkariElement.ontick()",500);

	//Pokkari.ShowDebugWindow();
	Pokkari.Debug("Starting transfer..");
	this.paint();
}

PokkariUploadMonitor.prototype.onabort = function() 
{
	Pokkari.Debug("Transfer aborted");
	window.clearInterval(this.timer);
	this.element.style.display = "none";
	delete this.currentStatus;
	delete this.lastPrediction;
}

PokkariUploadMonitor.prototype.ontick = function()
{
	// Cancel on abort.  Only works in IE.
	if (this.readyState && this.readyState != document.readyState) {
		this.onabort();
	}
	// Every tenth tick, request an update..
	else if (this.tickCount % 10 == 0)
	{
		// Trick to convince IE to actually update..A
		this.url = this.url.substring(0,this.url.length-4) +
			(Math.floor(Math.random()*9000)+1000);
	
		this.send();
	}
	else if (this.currentStatus && !this.stalled)
	{
		var now = (new Date()).getTime();

		if (this.lastPrediction)
		{
			var elapsed = now - this.lastPrediction;
			this.currentStatus.read +=
				elapsed/1000 *
				this.currentStatus.rate *
				this.predictionRatio

			// Sometimes post processing takes awhile, so don't predict over the total.
			if (this.currentStatus.read > this.currentStatus.total)
				this.currentStatus.read = this.currentStatus.total;

			this.calculateStatus(this.currentStatus);
			this.paint();
		}

		this.lastPrediction = now;	
	}

	this.tickCount++;

	// Set our ready state
	this.readyState = document.readyState;
}

PokkariUploadMonitor.prototype.onsuccess = function(object)
{
	var status = this.getStatusFromResponse();

	delete this.lastPrediction;

	Pokkari.Debug(this.lastRead + " " + status.read);

	this.stalled = this.lastRead ? (status.read == this.lastRead) : false;
	this.lastRead = status.read;
	this.currentStatus = status;

	if (this.stalled)
		Pokkari.Debug("Stalled");

	this.paint();
}

PokkariUploadMonitor.prototype.paint = function()
{
	var status = this.currentStatus;

	if (!status || isNaN(status.remainPercent) || !isFinite(status.remainPercent) || !status.rate)
	{
		this.filenameSpan.innerHTML = "(Beginning Transfer.. Please wait...)";
		this.timeleftSpan.innerHTML = "--";
		this.sentSpan.innerHTML = "--";
		this.totalSpan.innerHTML = "--";
		this.rateSpan.innerHTML = "--";
		return;
	}
	else if (this.stalled)
	{
		this.rateSpan.innerHTML = "Stalled";
		return;
	}
	

	try {
		this.filenameSpan.innerHTML = status.filename;

		Pokkari.Debug(this.progressDiv.style.width);
		if (status.remainPercent > 0 + this.progressDiv.style.width.replace(/\D/g,""))
			this.progressDiv.style.width = status.remainPercent + "%";

		if (status.remainMin > 0) {
			this.timeleftSpan.innerHTML = status.remainMin + " min " + status.remainSec + " sec";
		}
		else {
			this.timeleftSpan.innerHTML = status.remainSec + " sec";
		}
	
		if (status.readM > 1) {
			this.sentSpan.innerHTML = status.readM + " MB";
		}
		else if (status.readK > 1) {
			this.sentSpan.innerHTML = status.readK + " KB";
		}
		else {
			this.sentSpan.innerHTML = status.read + " bytes";
		}
		if (status.totalM > 1) {
			this.totalSpan.innerHTML = status.totalM + " MB";
		}
		else if (status.totalK > 1) {
			this.totalSpan.innerHTML = status.totalK + " KB";
		}
		else {
			this.totalSpan.innerHTML = status.total + " bytes";
		}
		if (status.rateK > 1) {
			this.rateSpan.innerHTML = status.rateK + " KB/s";
		}
		else {
			this.ratesSpan.innerHTML = status.rate + " bytes/s";
		}
	}
	catch (exception)
	{
		Pokkari.HandleException(exception,"In paint");
	}
}

PokkariUploadMonitor.prototype.getStatusFromResponse = function()
{
	try {
		var xml = this.getResponseXml();
		var topNode = xml.documentElement;
		var response = topNode.getElementsByTagName("response")[0];
		var status = new Object();
		status.guid = response.getElementsByTagName("guid")[0].firstChild.nodeValue;
		status.filename = response.getElementsByTagName("filename")[0].firstChild.nodeValue;
		status.start = parseInt(response.getElementsByTagName("start")[0].firstChild.nodeValue);
		status.update = parseInt(response.getElementsByTagName("update")[0].firstChild.nodeValue);
		status.read = parseInt(response.getElementsByTagName("read")[0].firstChild.nodeValue);
		status.total = parseInt(response.getElementsByTagName("total")[0].firstChild.nodeValue);
		status.now = (new Date()).getTime();

		this.calculateStatus(status);

		return status;
	}
	catch (exception)
	{
		Pokkari.HandleException(exception,"In getStatus");
	}
}


PokkariUploadMonitor.prototype.calculateStatus = function(status)
{
	// Calculations..
	try {
		status.elapsed = status.update - status.start;
		status.readK = Math.round(status.read / 102.4) / 10;
		status.totalK = Math.round(status.total / 102.4) / 10;
		status.readM = Math.round(status.readK / 102.4) / 10;
		status.totalM = Math.round(status.totalK / 102.4) / 10;
		status.rate = status.elapsed ? Math.round(status.read / status.elapsed) : 0;
		status.rateK = Math.round(status.rate / 102.4) / 10;
		status.remainPercent = status.total ? Math.round(status.read/status.total*100) : 0;
		status.remainBytes = status.total - status.read;
		status.remainTime = status.rate ? Math.round(status.remainBytes / status.rate) : 99*60*60;
		status.remainMin = Math.floor(status.remainTime / 60);
		status.remainSec = status.remainTime % 60;
	}
	catch (exception)
	{
		Pokkari.HandleException(exception,"In calculate");
	}
	return status;
}
//<div class='validator'
//	name='PokkariElement'
//	pokkariType='PokkariValidator'
//	pokkariParameters='targetId:"target",expression:"parseInt(value) >= 1 && parseInt(value) <= 10",formId:"theForm"'
//>The number was invalid</div>

// Set targetId to the id of the input element
// Set formId to the id of the form element
// Set expression to the expression to be validated -- 'value' is the value
// Set the class of the div to be validator for display:none and red.
// Set mode to 'static' if you're going to use visibility:hidden
function PokkariValidator(params) {
	PokkariElement.apply(this,[params]);
}
PokkariValidator.prototype = new PokkariElement();
PokkariValidator.prototype.constructor = PokkariValidator;

PokkariValidator.AllValidators = new Array();
PokkariValidator.AllValidatorsEventSet = false;
PokkariValidator.ValidateAllOnSubmit = null;
PokkariValidator.Group = 'default';

PokkariValidator.prototype.onLoadHandler = function()
{
	if (this.targetId)
		this.targetElement = document.getElementById(this.targetId);

	if (!this.targetElement)
		throw new Error('Invalid targetElement or targetId specified');

	if (this.expression)
		this.callback = new Function("value","return "+this.expression);
	else if (typeof(this.callback) == "string") {
		var a;
		eval("a = " + this.callback);
		this.callback = a;
	}

	if (!this.callback || typeof(this.callback) != "function")
		throw new Error('Invalid expression or callback function specified');

	if (this.formId)
		this.formElement = document.getElementById(this.formId);

	if (!this.validateEvent)
		this.validateEvent = "blur";

	Pokkari.AttachEvent(this.targetElement,
		this.validateEvent,
		this.eventHandler(this.onValidate));

	if (this.formElement)
	{
		if (!PokkariValidator.AllValidatorsEventSet) 
		{
			PokkariValidator.AllValidatorsEventSet = true;
			if (this.formElement.onsubmit) {
				PokkariValidator.ValidateAllOnSubmit = this.formElement.onsubmit;
			}
			this.formElement.onsubmit = PokkariValidator.ValidateAll;
		}

		PokkariValidator.AllValidators.push(this);
	}
	
	if (!this.mode)
		this.mode = 'dynamic';
}

PokkariValidator.prototype.show = function()
{
	if (this.mode == 'dynamic')
		this.element.style.display = 'block';
	else
		this.element.style.visibility = 'visible';
}

PokkariValidator.prototype.hide = function()
{
	if (this.mode == 'dynamic')
		this.element.style.display = 'none';
	else
		this.element.style.visibility = 'hidden';
}

PokkariValidator.prototype.getTargetValue = function()
{
	return this.targetElement.value;
}

PokkariValidator.prototype.focus = function()
{
	this.targetElement.focus();
}

PokkariValidator.prototype.validate = function(focus)
{
	// If this validator has a group specified, and it's not our current
	// group then return validated.
	if (this.group && this.group != PokkariValidator.Group) {
		return true;
	}

	var value = this.getTargetValue();
	var result = this.callback(value);
	
	if (result)
		this.hide();
	else {
		this.show();
		if (focus) { this.focus(); }
	}	
	return result;
}

PokkariValidator.prototype.onValidate = function(self,sender,e)
{
	return self.validate();
}

PokkariValidator.ValidateAll = function(e)
{
	var result = true;
	
	for (var i=0; i<PokkariValidator.AllValidators.length; i++)
	{
		// We'll focus the first result that fails.
		result = PokkariValidator.AllValidators[i].validate(result) && result;
	}

	if (result && PokkariValidator.ValidateAllOnSubmit) 
	{
		PokkariValidator.ValidateAllOnSubmit(e);
	}

	return result || false; 
}

pokkariWizard = function() {
	this.init(this);
}

pokkariWizard.prototype = new PokkariElement();
pokkariWizard.constructor = pokkariWizard;

pokkariWizard.prototype.init = function() {
	this.pages = new Array();
	this.values = new Object();
	this.history = new Array();
	this.errors = new Array();
}

pokkariWizard.prototype.onConstruct = function() {
	
	// All wizard pages must be divs
	var potentialPages = this.element.getElementsByTagName('div');
	
	for(var i = 0; i < potentialPages.length; i++) {
		/*
			We cast all our pages as PokkariWizardPages
		*/

		if(potentialPages[i].getAttribute('pokkariwizardpage')) {
			this.pages.push(potentialPages[i]);
			potentialPages[i].wizard = this;

			if(potentialPages[i].id == this.firstPageId) {
				potentialPages[i].setAttribute('isCurrentPage','true');
				potentialPages[i].style.display = 'block';
			} else {
				potentialPages[i].style.display = 'none';
			}
		}
	}
}

pokkariWizard.prototype.goNext = function() {
	if(this.currentPage.submit()) {
		this.history.push(this.currentPage);
		this.changePage(this.currentPage.getNextPage());
	}
}

pokkariWizard.prototype.goBack = function() {

	if(this.history.length) {
		var oldPage = this.history.pop();
		this.changePage(oldPage.element.id);
	} else {
		alert("Sorry, no pages to go back.  You're at the start.");
	}
}

pokkariWizard.prototype.changePage = function(page) {
	this.currentPage.element.style.display = "none";
	this.currentPage.onExit();
	document.getElementById(page).style.display = "block";
	this.currentPage = document.getElementById(page).pokkariElement;
	this.currentPage.onEnter();

	if(page != this.firstPageId) {
		document.getElementById('previousButton').style.display = 'block';
	} else {
		document.getElementById('previousButton').style.display = 'none';
	}

	if(page == this.lastPageId) {
		document.getElementById('nextButton').style.display = 'none';
	} else {
		document.getElementById('nextButton').style.display = 'block';
	}
}

// Blog wizard
pokkariWizardBlog = function() {
	this.init();

	this.firstPageId = 'blogTypeSelection';
	this.lastPageId = 'blogSave';
}

pokkariWizardBlog.prototype = new pokkariWizard();
pokkariWizardBlog.constructor = pokkariWizardBlog;
pokkariWizardBlog.superclass = pokkariWizard.prototype;

pokkariWizardBlog.prototype.init = function() {
	pokkariWizardBlog.superclass.init.call(this);
}
function pokkariWizardPage(params) {
	// nooop
}

pokkariWizardPage.prototype = new PokkariElement();
pokkariWizardPage.constructor = pokkariWizardPage;

pokkariWizardPage.prototype.onConstruct = function() {
	if(!this.wizard) {
		this.wizard = this.element.parentNode.pokkariElement;
	}
	if(this.element.getAttribute('isCurrentPage')) {
		this.wizard.currentPage = this;
	}
}

pokkariWizardPage.prototype.submit = function() {
	// First we have to validate
	if(this.validate()) {
		// Now that we're validated, set values
		this.setValues();
		return true;
	} else {
		return false;
	}
}

/*
 * Event for the entering of a page
 * Fired when the page is presented to the user user
*/
pokkariWizardPage.prototype.onEnter = function() {

}

/*
 * Event for the exiting of a page
 * Fired when the page is replaced by another page in the wizard
*/

pokkariWizardPage.prototype.onExit = function() {

}

pokkariWizardPage.prototype.validate = function() {
	/*
		Generally, we'll loop through our fields and make sure
		we have good values for each of them.
	*/

	return true;
}

pokkariWizardPage.prototype.setValues = function() {
	/*
		Generally, we'd set this.wizard.values.foo = 'bar'
		for all our fields.
	*/

	return true;
}

pokkariWizardPage.prototype.getNextPage = function() {
	/*
		We'll evaluate some logic and return the name of
		a page we want to return next
	*/
}

pokkariWizardPageAdminLocationForm = function(params) {

}

pokkariWizardPageAdminLocationForm.prototype = new pokkariWizardPage();
pokkariWizardPageAdminLocationForm.constructor = pokkariWizardPageAdminLocationForm;

pokkariWizardPageAdminLocationForm.prototype.validate = function() {
	
	if(!document.getElementById('blogAdminLocation').value.match(/http(s)?:\/\/(\w+)/)) {
		alert("You must tell us where you go to manage your blog.");
		return false;
	}

	return true;
	
}

pokkariWizardPageAdminLocationForm.prototype.setValues = function() {

	this.wizard.values.blogAdminUrl = document.getElementById('blogAdminLocation').value;

}

pokkariWizardPageAdminLocationForm.prototype.getNextPage = function() {
	return "blogEndpointForm";
}

pokkariWizardPageEndpointForm = function(params) {

}

pokkariWizardPageEndpointForm.prototype = new pokkariWizardPage();
pokkariWizardPageEndpointForm.constructor = pokkariWizardPageEndpointForm;

pokkariWizardPageEndpointForm.prototype.onEnter = function() {

	var request = new PokkariXmlRequest("/blog/discover_api_endpoint");
	request.pokkariWizardPage = this;
	request.url = "/blog/discover_api_endpoint/?blogAdminUrl="+escape(this.wizard.values.blogAdminUrl)+"&api="+this.wizard.values.api+"&blogTypeId="+this.wizard.values.blogTypeId;
	request.onsuccess = this.receiveEndpoint;
	request.onfailure = this.receiveEndpointFailure;
	request.send();

	document.getElementById('blogEndpoint').focus();
}

pokkariWizardPageEndpointForm.prototype.receiveEndpoint = function(response) {

	if(response.getResponseXml().getElementsByTagName('endpointsuggestion')[0]) {
		document.getElementById('blogEndpoint').value = response.getResponseXml().getElementsByTagName('endpointsuggestion')[0].firstChild.nodeValue;
		if(response.pokkariWizardPage.wizard.values.blogTypeName == "Movable Type") {
			document.getElementById('movableTypeHelpText').style.display = 'block';
		}
		else if(response.pokkariWizardPage.wizard.values.blogTypeName == "WordPress") {
			document.getElementById('wordpressHelpText').style.display = 'block';
		}     
	}
}

pokkariWizardPageEndpointForm.prototype.receiveEndpointFailure = function() {
	alert("Receiving endpoint information failed");
}

pokkariWizardPageEndpointForm.prototype.onExit = function() {
	document.getElementById('movableTypeHelpText').style.display = 'none';
	document.getElementById('wordpressHelpText').style.display = 'none';
	document.getElementById('blogEndpoint').value = ''; 
}

pokkariWizardPageEndpointForm.prototype.validate = function() {
	if(!document.getElementById('blogEndpoint').value.match(/^http(s)?:\/\/\w+/)) {
		alert("You must enter an API endpoint.  It should begin with http://");
		return false;
	}

	return true;
}

pokkariWizardPageEndpointForm.prototype.setValues = function() {
	this.wizard.values.endpoint = document.getElementById('blogEndpoint').value;

	return true;
}

pokkariWizardPageEndpointForm.prototype.getNextPage = function() {
	return "blogLoginForm";
}

pokkariWizardPageLoginForm = function(params) {
}

pokkariWizardPageLoginForm.prototype = new pokkariWizardPage();
pokkariWizardPageLoginForm.constructor = new pokkariWizardPageLoginForm;

pokkariWizardPageLoginForm.prototype.onEnter = function() {
	document.getElementById('blogUsername').focus();
}

pokkariWizardPageLoginForm.prototype.validate = function() {
	if(!document.getElementById('blogUsername').value) {
		alert("You must enter a username to continue");
		return false;
	}
	if(!document.getElementById('blogPassword').value) {
		alert("You must enter a password to continue");
		return false;
	}

	// Everything's okay if we got this far
	return true;
}

pokkariWizardPageLoginForm.prototype.setValues = function() {
	
	this.wizard.values.blogUsername = document.getElementById('blogUsername').value;
	this.wizard.values.blogPassword = document.getElementById('blogPassword').value;

}

pokkariWizardPageLoginForm.prototype.getNextPage = function() {

	return "blogChooseBlogForm";
	
}

pokkariWizardPageChooseBlog = function(params) {
}

pokkariWizardPageChooseBlog.prototype = new pokkariWizardPage();
pokkariWizardPageChooseBlog.constructor = pokkariWizardPageChooseBlog;

pokkariWizardPageChooseBlog.prototype.onEnter = function(params) {
	if(
		this.wizard.values.blogUsername &&
		this.wizard.values.blogPassword &&
		this.wizard.values.api &&
		this.wizard.values.endpoint
	) {
		var request = new PokkariXmlRequest("/blog/api_proxy");
		request.pokkariWizardPage = this;
		request.url = "/blog/api_proxy/?api="+this.wizard.values.api+"&username="+escape(this.wizard.values.blogUsername)+"&password="+escape(this.wizard.values.blogPassword)+"&endpoint="+escape(this.wizard.values.endpoint)+"&api_cmd=getUsersBlogs";
		request.onsuccess = this.receiveBlogs;
		request.onfailure = this.receiveBlogsFailure;
		request.send();
	}

	document.getElementById('fetchingBlogText').style.display = 'block';
}

pokkariWizardPageChooseBlog.prototype.onExit = function() {
	document.getElementById('chooseBlogText').style.display = 'none';
	if(document.getElementById('chooseBlogErrorText')) {
		document.getElementById('chooseBlogErrorText').style.display = 'none';
	}
	document.getElementById('fetchingBlogErrorText').style.display = 'none';
}

pokkariWizardPageChooseBlog.prototype.receiveBlogs = function(request) {
	var wizard = request.pokkariWizardPage;

	try {
		if(request.getResponseXml().getElementsByTagName('success')[0].firstChild.nodeValue != 1) {
			return wizard.receiveBlogsFailure(request);
		}
		if(request.getResponseXml().getElementsByTagName('blog').length == 0) {
			return wizard.receiveBlogsFailure(request);
		}
	} catch(e) {
		return wizard.receiveBlogsFailure(request);
	}

	document.getElementById('chooseBlogText').style.display = 'block';
	document.getElementById('fetchingBlogText').style.display = 'none';

	var blogs = request.getResponseXml().getElementsByTagName('blog');
	for(var i = 0; i < blogs.length; i++) {
		var option = document.createElement('option');
		option.text = request.getResponseXml().getElementsByTagName('name')[i].firstChild.nodeValue;
		option.value = request.getResponseXml().getElementsByTagName('id')[i].firstChild.nodeValue;

		option.setAttribute("url",request.getResponseXml().getElementsByTagName('url')[i].firstChild.nodeValue);
		try {
			option.setAttribute("isadmin",request.getResponseXml().getElementsByTagName('isadmin')[i].firstChild.nodeValue);
		} catch(e) { 
			// Do nothing, isadmin isn't always set
		}
		document.getElementById('chooseBlogList').appendChild(option);
	}

	document.getElementById('chooseBlogTable').style.display = '';
}

pokkariWizardPageChooseBlog.prototype.receiveBlogsFailure = function(request) {
	var wizard = request.pokkariWizardPage;
	
	document.getElementById('fetchingBlogErrorText').style.display = 'block';
	document.getElementById('fetchingBlogText').style.display = 'none';

	var errorMessage = document.createElement('p');
	if(request.getResponseXml().getElementsByTagName('error').length != 0) {
		errorMessage.innerHTML = request.getResponseXml().getElementsByTagName('error')[0].firstChild.nodeValue;
	} else {
		errorMessage.innerHTML = "Your blogging system didn't give us a list of blogs you have permission to post to.  This is probably because the API Endpoint URL you gave us is wrong.  It's possible, though, that it's because you gave us the wrong username and password.";
	}
	errorMessage.className = "error";
	errorMessage.id = "chooseBlogErrorText";

	wizard.element.appendChild(errorMessage);

}

pokkariWizardPageChooseBlog.prototype.validate = function() {
	
	var blogChooser 	= document.getElementById('chooseBlogList');
	if(!blogChooser.value) {
		return false;
	}

	return true;
}

pokkariWizardPageChooseBlog.prototype.setValues = function() {

	var blogChooser = document.getElementById('chooseBlogList');
	var chosenBlog	= blogChooser.options[blogChooser.selectedIndex];

	this.wizard.values.blogUrl 		= chosenBlog.getAttribute("url");
	this.wizard.values.blogName 	= chosenBlog.text;
	this.wizard.values.blogId		= blogChooser.value;

	return true;

}

pokkariWizardPageChooseBlog.prototype.getNextPage = function() {
	return "blogSave";
}

pokkariWizardPageSaveBlog = function(params) {

}

pokkariWizardPageSaveBlog.prototype = new pokkariWizardPage();
pokkariWizardPageSaveBlog.constructor = pokkariWizardPageSaveBlog;

pokkariWizardPageSaveBlog.prototype.onEnter = function() {

	/*
		We prevent re-saving by removing the back button.  If an error
		is encountered that can be saved by going back in the history
		we'll ink to the goBack() function directly.
	*/
	document.getElementById('previousButton').style.display = 'none';

	try {
		this.startSaveOperation();
	} catch(e) {
		alert("An error was encountered saving your blog: "+e);
	}
}

pokkariWizardPageSaveBlog.prototype.onExit = function() {

	document.getElementById('previousButton').style.display = '';

	document.getElementById('savingBlogText').style.display = 'block';
	document.getElementById('savedBlogErrorText').style.display = 'none';
	document.getElementById('savedBlogText').style.display = 'none';
	document.getElementById('saveBlogErrorText').style.display = 'none';

}

pokkariWizardPageSaveBlog.prototype.startSaveOperation = function() {

	var request = new PokkariXmlRequest({
		url : "/blog/save",
		method : 'GET'
		});
	request.pokkariWizardPage = this;
	request.onsuccess = this.receiveSaveOperationSuccess;
	request.onfailure = this.receiveSaveOperationFailure;
	
	/*
		We need to build our data key-value chain 
	*/

	var chain = "";
	for(key in this.wizard.values) {
		chain += "&"+key+"="+escape(this.wizard.values[key]);
	}
	chain = chain.replace(/^&/,"?");
	
	request.url += chain;
	
	request.send();
}

pokkariWizardPageSaveBlog.prototype.receiveSaveOperationSuccess = function(request) {
	var wizard = request.pokkariWizardPage;

	try {
		if(request.getResponseXml().getElementsByTagName('success')[0].firstChild.nodeValue != 1) {
			return wizard.receiveSaveOperationFailure(request);
		}
	} catch(e) {
		return wizard.receiveSaveOperationFailure(request);
	}

	document.getElementById('savingBlogText').style.display = 'none';
	document.getElementById('savedBlogText').style.display = 'block';
}

pokkariWizardPageSaveBlog.prototype.receiveSaveOperationFailure = function(request) {
	var wizard = request.pokkariWizardPage;

	document.getElementById('savingBlogText').style.display = 'none';
	document.getElementById('savedBlogErrorText').style.display = 'block';

	var errorMessage = document.createElement('p');
	if(request.getResponseXml().getElementsByTagName('error').length != 0) {
		errorMessage.innerHTML = request.getResponseXml().getElementsByTagName('error')[0].firstChild.nodeValue;
	} else { 
		errorMessage.innerHTML = "Sorry, we encountered an unknown error saving your blog to our database.  Please wait a few minutes and try again.  If you keep getting this error please contact support for assistance.";
	}
	errorMessage.className = "error";
	errorMessage.id = "saveBlogErrorText";

	wizard.element.appendChild(errorMessage);
}

pokkariWizardPageBlogType = function(params) {

}

pokkariWizardPageBlogType.prototype = new pokkariWizardPage();
pokkariWizardPageBlogType.constructor = pokkariWizardPageBlogType;

pokkariWizardPageBlogType.prototype.onEnter = function() {
	this.wizard.values.blogTypeId 		= void(0);
	this.wizard.values.blogTypeName 	= void(0);
	this.wizard.values.endpoint			= void(0);
	this.wizard.values.api				= void(0);
}

pokkariWizardPageBlogType.prototype.validate = function() {
	if(!document.getElementById('blogType').value) {
		alert("You must select a blog type to continue"); 
		return false;
	}

	// Everything validated okay
	return true;
}

pokkariWizardPageBlogType.prototype.setValues = function() {

	var bType = document.getElementById('blogType');
	var selectedType = bType.options[bType.selectedIndex];

	this.wizard.values.blogTypeId 	= selectedType.value;
	this.wizard.values.blogTypeName = selectedType.text;
	
	if(selectedType.getAttribute("endpoint")) {
		this.wizard.values.endpoint = selectedType.getAttribute("endpoint");
	}

	if(selectedType.getAttribute("api")) {
		this.wizard.values.api = selectedType.getAttribute("api");
	}

	return true;

}

pokkariWizardPageBlogType.prototype.getNextPage = function() {

	if(this.wizard.values.endpoint != void(0)) {
		return "blogLoginForm";
	} else {
		return "blogAdminLocationForm";
	}
}
function findPosX(obj)
{
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

function findPosY(obj)
{
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;
	return curtop;
}

function setListPos(id,id2) {
	e = document.getElementById(id);
	//window.alert("x:"+findPosX(e) + " y:"+findPosY(e));
	p = document.getElementById(id2);
	p.style.left = (findPosX(e)-113)+"px";
	p.style.top = findPosY(e)+"px";
	p.style.visibility = "visible";
}

function setSecondaryPos(id,id2,flush_left) {
        e = $(id);
        p = $(id2);
        if(flush_left) {
                p.style.left = "15px";
        } else {
                p.style.left = (findPosX(e)+125)+"px";
        }
        p.style.top = (findPosY(e)-23)+"px";
        p.style.visibility = "visible";

        if((p.offsetWidth + findPosX(p) + 125) > self.screen.availWidth) {
                p.style.left = self.screen.availWidth - (p.offsetWidth * 2) - 125 + "px";
        }

}
function rate_over(type,id,myRating,siteRating) {
	for(var x=1; x<=myRating; x++) {
		var side = x % 2 == 0 ? "right" : "left";
		var color = Math.round(siteRating) >= x ? "purple" : "red";
		
		var element = document.getElementById("Star_"+type+id+"_"+x);
		element.src="/images/star_"+color+"_"+side+".gif";
	}
}

function rate_out(type,id,myRating,siteRating) {
	for(var x=1; x<=myRating; x++) {
		var side = x % 2 == 0 ? "right" : "left";
		var color = Math.round(siteRating) >= x ? "blue" : "null";
		
		var element = document.getElementById("Star_"+type+id+"_"+x);
		element.src="/images/star_"+color+"_"+side+".gif";
	}
}

function rate_disable(type,id) {
	for(var x=1; x<=10; x++) {
		var element = document.getElementById("Rate_"+type+id+"_"+x);
		element.href = "javascript:void(0)";
		element.disableme = true;
	}
}

function rate_post(type,id,myRating,siteRating) {
	var request = new PokkariXmlRequest();

	request.url = "/"+type+"/rate/"+id+"?rating="+myRating;
	request.onsuccess = rate_success;
	request.onfailure = rate_failure;
	request.onloading = rate_loading;
	request.oncompleted = rate_completed;
	request.send();
/*
	var url = "/"+type+"/rate/"+id+"?rating="+myRating+"&skin=xmlhttprequest";
	if (window.XMLHttpRequest) {
		rate_req = new XMLHttpRequest();
		rate_req.onreadystatechange = rate_req_change;
		rate_req.open("GET",url,true);
		rate_req.send(null);
	}
	else if (window.ActiveXObject) {
		rate_req = new ActiveXObject("Microsoft.XMLHTTP");
		if (rate_req) {
			rate_req.onreadystatechange = rate_req_change;
			rate_req.open("GET",url,true);
			rate_req.send();
		}
	}
*/
}
/*
function rate_req_change() {
	if (rate_req.readyState == 4) {
		if (rate_req.status == 200) {
			var xml = rate_req.responseXML;
			var topNode = xml.documentElement;
			var response = topNode.getElementsByTagName("response")[0];
			var error = response.getElementsByTagName("error")[0];
			var notice = response.getElementsByTagName("notice")[0];

			if (error) {
				window.alert("Error: " + error.firstChild.nodeValue);
			}
			else if (notice) {
				window.alert(notice.firstChild.nodeValue);
			}
			else {
				var type = response.getElementsByTagName("type")[0].firstChild.nodeValue;
				var id = response.getElementsByTagName("id")[0].firstChild.nodeValue;
				var myRating = response.getElementsByTagName("myRating")[0].firstChild.nodeValue;
				var siteRating = response.getElementsByTagName("siteRating")[0].firstChild.nodeValue;

				rate_disable(type,id);
				rate_over(type,id,myRating,siteRating);
				var r = document.getElementById("Rating_"+type+id);
				r.innerHTML = siteRating;
				var v = document.getElementById("Votes_"+type+id);
				v.innerHTML = parseInt(v.innerHTML) + 1;

			}	
		}
		else {
			alert("There is a problem retrieving the XML data:\n" +
			rate_req.statusText);
		}
	}
}
*/

function rate_success(request)
{
	var xml = request.getResponseXml();
	var topNode = xml.documentElement;
	var response = topNode.getElementsByTagName("response")[0];
	var error = response.getElementsByTagName("error")[0];
	var notice = response.getElementsByTagName("notice")[0];

	if (error) {
		window.alert("Error: " + error.firstChild.nodeValue);
	}
	else if (notice) {
		window.alert(notice.firstChild.nodeValue);
	}
	else {
		var type = response.getElementsByTagName("type")[0].firstChild.nodeValue;
		var id = response.getElementsByTagName("id")[0].firstChild.nodeValue;
		var myRating = response.getElementsByTagName("myRating")[0].firstChild.nodeValue;
		var siteRating = response.getElementsByTagName("siteRating")[0].firstChild.nodeValue;

		rate_disable(type,id);
		rate_over(type,id,myRating,siteRating);
		var r = document.getElementById("Rating_"+type+id);
		r.innerHTML = siteRating;
		var v = document.getElementById("Votes_"+type+id);
		v.innerHTML = parseInt(v.innerHTML) + 1;
	}
}

function rate_failure(request)
{
	alert("There is a problem retrieving the XML data:\n" +
		request.getStatusText());
}

function rate_loading(request)
{
	var element = document.getElementById("xmlhttploading");
	var top = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;

	if (element != void(0) && top != void(0)) {
		element.style.top = top;
		element.style.display = "block";
	}
}

function rate_completed(request)
{
	var element = document.getElementById("xmlhttploading");
	if (element != void(0)) {
		element.style.display = "none";
	}
}
function queryChanger(params) {
	// noop
}

queryChanger.prototype = new PokkariElement();
queryChanger.prototype.constructor = queryChanger;

queryChanger.prototype.onConstruct = function() {

	this.element.onchange = function() {
		this.pokkariElement.onChange();
	}
}

queryChanger.prototype.onChange = function() {
	this.changeQueryParameter({ key : this.key, value : this.element.value, remove: this.removeKeys });
}

queryChanger.prototype.changeQueryParameter = function(params) {
	var href = window.location.href;

	if(href.match(/\?/)) {
		var paramFinder = new RegExp(params.key+"=[^;&]*");
		if(href.match(paramFinder)) {
			href = href.replace(paramFinder,params.key+"="+params.value);
		} else {
			href += "&"+params.key+"="+params.value;
		}

		if (params.remove) {
			if (typeof(params.remove) == "string")
				params.remove = params.remove.split(/,/);

			for (var i=0; i<params.remove.length; i++) {
				var removeFinder = new RegExp(params.remove[i]+"=[^&;]*[&;]?");
				href = href.replace(removeFinder,'');
			}
		}
	} else {
		href += "?"+params.key+"="+params.value;
	}

	window.location.href = href;
}

AutoCompletionZone = function() {
	this.init(this);
}

AutoCompletionZone.prototype = new PokkariElement();
AutoCompletionZone.constructor = AutoCompletionZone;

AutoCompletionZone.prototype.onConstruct = function() {

	/*
		Our EditPalette may have been instantiated before us 
	*/
	if(this.element.EditPalette) {
		this.EditPalette = this.element.EditPalette;
	}

	this.element.className = "autoCompletionZone";
}

AutoCompletionZone.prototype.init = function() {
	// nooop
}

AutoCompletionZone.prototype.setLabel = function(labelText) {
	var label = document.createElement('label'); 
	label.innerHTML = labelText;
	this.element.appendChild(label);
}

AutoCompletionZone.prototype.clearCandidates = function() {

	while(this.element.getElementsByTagName('a').length) {
		this.element.getElementsByTagName('a')[0].parentNode.removeChild(this.element.getElementsByTagName('a')[0]);
	}

	this.setTopSuggestion('');

}

AutoCompletionZone.prototype.newCandidates = function(candidates) {

	this.clearCandidates();

	for(var i = 0; i < candidates.length; i++) {
		var tagElement = document.createElement('a');
		tagElement.innerHTML = candidates[i];
		tagElement.className = 'suggestion';

		tagElement.href = 'javascript:void(0);';

		tagElement.onclick = new Function("this.parentNode.pokkariElement.acceptSuggestion(this.innerHTML)");

		/*
			The first suggestion in the primary AutoCompletionZone
			is used for tag copmletion
		*/
		if(this.isPrimary() && i == 0) {
			tagElement.style.color = 'red';
			this.setTopSuggestion(candidates[i]);
		}

		this.element.appendChild(tagElement);
	}

}

AutoCompletionZone.prototype.acceptSuggestion = function(suggestion) {
	if(this.EditPalette) {
		this.EditPalette.pokkariElement.setCurrentTopic(suggestion);
		this.EditPalette.focus();
	} else { }
}

AutoCompletionZone.prototype.setPrimary = function(primary) {
	this.primary = primary;
}

AutoCompletionZone.prototype.isPrimary = function() {
	return this.primary;
}

AutoCompletionZone.prototype.setTopSuggestion = function(topSuggestion) {
	this.topSuggestion = topSuggestion;
}

AutoCompletionZone.prototype.getTopSuggestion = function() {
	return this.topSuggestion;
}
function sideBox(params) {
	
	if(params) {

	}
}

sideBox.prototype = new PokkariElement();
sideBox.prototype.constructor = sideBox;

sideBox.prototype.onConstruct = function() {

	if(document.getElementById('side0_auto')) {
		if(
			this.element.parentNode.id != 'side0' &&
			this.element.parentNode.id != 'side0_auto'	
		) {
			// move to the sidebar if we're not already there
			document.getElementById('side0_auto').appendChild(this.element);
			this.element.style.display = '';
		}
	}
	
}
function Url(url) {
	this.setUrl(url);
}

Url.prototype.getUrl = function() {
	if (this.urlDirty) {
		this.updateUrl();
	}

	return this.url;
}

Url.prototype.setUrl = function(url) {
	this.url = url;
	this.query = this.parseQuery();
	this.urlDirty = false;
}

Url.prototype.getQuery = function() {
	return Url.GetQuery(this.url);
}

Url.prototype.parseQuery = function() {
	return Url.ParseQuery(this.getQuery());
}

Url.prototype.getQueryParam = function(name) {
	return this.query[name];
}

Url.prototype.setQueryParam = function(name,value) {
	this.query[name] = value;
	this.urlDirty = true;
}

Url.prototype.updateUrl = function() {
	this.url = Url.ReplaceQuery(this.url,Url.MakeQuery(this.query));
	this.urlDirty = false;
}

Url.GetQuery = function(url) {
	if (typeof(url) == "undefined" || !url) 
		return null;
	
	var parts = url.split("?");
	var query = parts[1];
	if (query) {
		return query.split("#")[0];
	}

	return null;
}

Url.ParseQuery = function(query) {
	var result = new Object();
	
	if (typeof(query) != "undefined" && query) {
		// Split it into name/value pairs
		var crumbs = query.split(/[&;]/);
		for (var i=0; i<crumbs.length; i++) {
			// Split the name and value
			var crumb = crumbs[i].split("=");
			
			// No equals means that it's a keyword
			if (crumb.length < 2) {
				if (result.keywords) { 
					result.keywords += ","; 
				} 
				else {
					result.keywords = "";
				}

				result.keywords += unescape(crumb[0]);
			}
			else {
				result[unescape(crumb[0])] = unescape(crumb[1]);
			}
		}

		return result;
	}

	return result;
}

Url.MakeQuery = function(object) {
	if (typeof(object) == "undefined" || !object)
		return null;

	var result = "";
	
	for (var i in object) {
		if (typeof(object[i]) == "number" || typeof(object[i]) == "string") {
			if (result) { 
				result += "&"; 
			}

			result += escape(i) + "=" + escape(object[i]);
		}
	}

	return result;
}

Url.ReplaceQuery = function(url,query) {
	var parts = url.split("?");
	var u = parts[0];
	var a;
	
	if (parts[1]) {
		parts = parts[1].split("#");
		a = parts[1];
	}

	if (query) {
		u += "?" + query;
	}

	if (a) {
		u += "#" + a;
	}

	return u;
}

// RATING RELATED FUNCTIONS

// FOLDING TREE MENU
function initTree() {
	/* Original Code by sean@frontierit.com */
	if (!document.getElementById) return;
	
	var aTrees = document.getElementsByTagName('UL');
	
	if (aTrees.length > 0) {
		for (var i = 0; i < aTrees.length; i++) {
			if (aTrees[i].className == "ftm") {
				ftm(aTrees[i]);
			}
		}
	}
}

function ftm(menu) {

	var docs  = menu.getElementsByTagName('LI');

	for (var i = 0; i < docs.length; i++) {
        var oHref = document.createElement("IMG");
        oHref.src = "image/x.gif";
        oHref.style.display = 'inline';
        oHref.style.marginRight = '3px';
        // oHref.style.paddingBottom = '4px';

		if (docs[i].getElementsByTagName('UL').length > 0 ) {

			oHref.src = "image/plus.gif";
			oHref.style.cursor = 'hand';
			
			oHref.onmousedown = function() {
				if (this.parentNode.childNodes[2].style.display == '' || 
					this.parentNode.childNodes[2].style.display == 'none' ) {
					this.parentNode.childNodes[2].style.display = 'block';
					this.src = "image/minus.gif";
				
				} else {
					this.parentNode.childNodes[2].style.display = 'none';
					this.src = "image/plus.gif";
				}
			}
		}
		docs[i].insertBefore(oHref,docs[i].firstChild);
	}
	
	/* Expand Folding Tree Menu to current url if it exists in the tree */
	for (var i = 0; i < docs.length; i++) {

		if (docs[i].childNodes[1].nodeName == 'A' &&
			docs[i].childNodes[1].href == location ) {
			docs[i].firstChild.src = "image/selected.gif";
			var q = docs[i].parentNode;
			
			while (q.className != 'ftm') {
				q.firstChild.src = "image/minus.gif";
	          	q.style.display = 'block';
	          	q = q.parentNode;
    	    }
		} 
	}
}

window.onload = initTree;


// COMMENT QUOTING
    function showCommentReplyForm(attachedTo,id,subject,author) {
        try {
			// Grabs the comments form at the bottom of the page
			// and inserts it in there
            var commentsFormContainer = document.getElementById("commentForm_Container");
            var newCommentsFormContainer = document.getElementById("commentForm_Container_Reply_"+id);
            newCommentsFormContainer.innerHTML = commentsFormContainer.innerHTML;
            newCommentsFormContainer.style.display = "block";
	    	var quotedText = getFormattedQuoteBlock(author);
            // We get our underlying HTMLFormElement
            var newCommentsForm = newCommentsFormContainer.firstChild.action ? newCommentsFormContainer.firstChild : newCommentsFormContainer.firstChild.nextSibling;
            // Let's see if we can't walk the DOM to set our quoted text
			var textareas = newCommentsForm.getElementsByTagName("*");
            var textareas = newCommentsForm.getElementsByTagName("textarea");
            for(i = 0; i < textareas.length; i++) {
                if(textareas[i].getAttribute("name") == "body") {
                    textareas[i].value = quotedText;
                }
            }
            var inputs = newCommentsForm.getElementsByTagName("input");
            for(i = 0; i < inputs.length; i++) {
                if(inputs[i].getAttribute("name") == "reply") {
                    inputs[i].value = id;
                }
                else if(inputs[i].getAttribute("name") == "subject") {
                    inputs[i].value = "Re: "+subject    
                }
            }   
        } catch(e) {
            // We'll redirect to the normal form
            alert("Exception: "+e);
            window.location.href = "/?s=c;attached_to="+attachedTo+";reply="+id+";subject=RE: "+subject+";cmd=post";
        }
    }
    function getFormattedQuoteBlock(author) {
	var quotedText  = (document.all) ? document.selection.createRange().text : window.getSelection();
	if(author) {
		quotedText = author+" wrote:\n<blockquote>"+quotedText+"</blockquote>";
	}
	else {
		quotedText = "<blockquote>\n"+quotedText+"\n</blockquote>";
	}
	return quotedText;
    }


// OTHER FUNCTIONS
	function toggleNavBox(header,name) {
		// Get references to the objects
		img = header.firstChild.firstChild;
		box = header.nextSibling.style ? header.nextSibling : header.nextSibling.nextSibling;
		// Update the values
		if (box.style.display == 'none') {
			img.src = '/skin/otter/nav.down.gif';
			box.style.display = 'block';
			document.cookie ='show_'+name+'="yes"';
		} else {
			img.src = '/skin/otter/nav.right.gif';
			box.style.display = 'none';
			document.cookie = 'show_'+name+'="no"';
		}
	}
	function getCookie(name) {
		var dc = document.cookie;
		var prefix = name + "=";
		var begin = dc.indexOf("; " + prefix);
		if (begin == -1) {
			begin = dc.indexOf(prefix);
		if (begin != 0) return null;
		} else
			begin += 2;
		var end = document.cookie.indexOf(";", begin);
		if (end == -1)
			end = dc.length;
		return unescape(dc.substring(begin + prefix.length, end));
	}

/* toggleHiddenDiv(buttonId, divId) (Public) [JD]

Hide or unhide a div layer using an onClick.  The innerHTML of the
buttonId's element is changed so that it will start with << or end
with >> depending on the state of the hidden div.
*/
function toggleHiddenDiv(buttonId, divId, startColor, destinationColor, hideInnerHTML, showInnerHTML)
{
	var button;
	if(buttonId) {
		button = document.getElementById(buttonId);
	}

	if (buttonId && button == undefined) {
		window.alert('Cannot locate '+buttonId);
		return false;
	}

	var div = document.getElementById(divId);
	if (div == undefined) {
		window.alert('Cannot locate '+divId);
	}

	if(buttonId) {
		var s = button.innerHTML;
		s = s.replace("&nbsp;","");
		s = s.replace("&gt;","");
		s = s.replace("&lt;","");
		s = s.replace("<","");
		s = s.replace(">","");
	}

	if(!startColor) {
		startColor = '99FF33'
	}
	if(!destinationColor) {
		destinationColor = 'ffffff';
	}

	if (div.style.display != 'block') {
		if(buttonId) {
			if(button.tagName.toLowerCase() == "img") {
					button.src = showInnerHTML;
			} else {
				if (hideInnerHTML) {
					button.innerHTML = unescape(showInnerHTML);
				} else {
					button.innerHTML = "&lt;&nbsp;" + s;
				}
			}
		}
		div.style.display = 'block';
		if (typeof(fader) != "undefined") 
			fader.fade(div,startColor,destinationColor);
	}
	else {
		if(buttonId) {
			if(button.tagName.toLowerCase() == "img") {
				button.src = hideInnerHTML;
			} else {
				if (showInnerHTML) {
					button.innerHTML = unescape(hideInnerHTML);
				} else {
					button.innerHTML = s + "&nbsp;&gt;";
				}
			}
		}
		div.style.display = 'none';
	}

	return true;
}

/* addFormWarning(warningText) (Public) [JD]

Adds text to the warnings div of a cotnainer_form

*/
function addFormWarning(warningText) {
	var element = document.getElementById("form_warnings");

	if (element != undefined) {
		var newWarning = document.createElement('div');
		newWarning.className = 'warnings_generic';
		newWarning.innerHTML = '<span class="warning_text">WARNING:</span> ' + warningText;
		element.appendChild(newWarning);
	}
}

/* addFormOnSubmit(js) (Public) [JD]

Adds text to the form elements onSubmit clause

*/
function addFormOnSubmit(js) {
	var element = document.getElementById("form_post");

	if (element != undefined) {
		element.onSubmit = element.onSubmit == undefined ? js : element.onSubmit+js;
	}
}

function PokkariPostField(params) {
}


PokkariPostField.prototype = new PokkariXmlRequest();
PokkariPostField.prototype.constructor = PokkariPostField;

PokkariPostField.prototype.onConstruct = function() {

	// Get the value of the field if we didn't get one explicitly
	if(!this.value) {
		this.previousValue = this.element.innerHTML;
	}

	// Make the field editable
	if(this.can_edit) {
		this.editableState();
	}
}

PokkariPostField.prototype.getValue = function() {
	var value;
	if(this.value) {
		value = this.value;
	} else {
		value = this.previousValue;
	}
	
	if(value == this.nullValue) {
		return "";
	} else {
		return value;
	}
}

PokkariPostField.prototype.setValue = function(v) {
	this.value = v;
}

PokkariPostField.prototype.getHumanValue = function() {
	if(this.humanValue) {
		return this.humanValue;
	} else {
		return this.getValue();
	}
}

PokkariPostField.prototype.setHumanValue = function(hv) {
	this.humanValue = hv;
}

PokkariPostField.prototype.getPreviousValue = function() {
	return this.previousValue;
}

PokkariPostField.prototype.setPreviousValue = function(pv) {
	this.previousValue = pv;
}

PokkariPostField.prototype.getWidgetType = function() {
	return this.widgetType;
}

PokkariPostField.prototype.setWidgetType = function(wt) {
	this.widgetType = wt;
}

PokkariPostField.prototype.getWidgetWidth = function() {
	if(this.widgetWidth) {
		return this.widgetWidth;
	} else {
		return 30;
	}
}

PokkariPostField.prototype.setWidgetWidth = function(wx) {
	this.widgetWidth = wx;
}

PokkariPostField.prototype.getWidgetHeight = function() {
	return this.widgetHeight;
}

PokkariPostField.prototype.setWidgetHeight = function(wh) {
	this.widgetHeight = wh;
}

PokkariPostField.prototype.setAcceptableValues = function(av) {
	this.acceptableValues = av;
}

PokkariPostField.prototype.getAcceptableValues = function() {
	return this.acceptableValues;
}

PokkariPostField.prototype.getFieldName = function() {
	return this.fieldName;
}

PokkariPostField.prototype.setFieldName = function(fn) {
	this.fieldName = fn;
}

PokkariPostField.prototype.getItemType = function() {
	return this.item_type;
}

PokkariPostField.prototype.setItemType = function(it) {
	this.item_type = it;
}

PokkariPostField.prototype.getItemId = function() {
	return this.item_id;
}

PokkariPostField.prototype.setItemId = function(id) {
	this.item_id = id;
}

PokkariPostField.prototype.editableState = function() {

	if(this.state == "saving" || this.state == "edit") {
		this.element.innerHTML = this.getHumanValue();
	}

	this.element.className += ' inline_editable';
	if(!this.element.title) {
		this.element.title = "Click to edit";
	}
	this.element.onclick = function() { this.pokkariElement.editState(); };

	this.state = "normal";
}

PokkariPostField.prototype.savingState = function() {
	this.state = "saving";

	this.element.innerHTML = "<i>Saving...</i>";
}

PokkariPostField.prototype.editState = function() {
	
	this.element.className = this.element.className.replace(/(\s+)?inline_editable/,'');
	
	this.element.innerHTML = "";
	this.element.onclick = "";
	this.element.title = "";

	this.element.appendChild(this.getWidget());
	if(this.getWidgetType() == "textarea") {
		this.element.appendChild(document.createElement('br'));
	}
	this.element.appendChild(this.getSaveButton());
	this.element.appendChild(this.getAbandonButton());

	if($("field_editor_" + this.getFieldName())) {
		$("field_editor_" + this.getFieldName()).focus();
	}

	this.state = "edit";

}

PokkariPostField.prototype.getWidget = function() {

	var widget;
	switch(this.getWidgetType()) {
		case 'textarea':
			widget		= document.createElement('textarea');
			if(this.getWidgetWidth().match(/px/)) {
				widget.style.width = this.getWidgetWidth();
			} else {
				widget.cols	= this.getWidgetWidth();
			}
			if(this.getWidgetHeight()) {
				if(this.getWidgetHeight().match(/px/)) {
					widget.style.height = this.getWidgetHeight();
				} else {
					widget.rows = this.getWidgetHeight();
				}
			}
			widget.name	= this.getFieldName();
			widget.value	= this.getValue();
		break;
		case 'select':
			widget	= document.createElement('select');

			if(!this.getAcceptableValues()) {
				throw("Cannot construct select widget without acceptable values");
			}
			
			if(this.getWidgetHeight()) {
				if(!this.getWidgetHeight().match(/px/)) {
					widget.size = this.getWidgetHeight();
				}
			}

			for(var i = 0; i < this.getAcceptableValues().length; i++) {
				var av = this.getAcceptableValues()[i];
				var option = document.createElement('option');
				option.text = av.text;
				option.value = av.value;
				if(av.value == this.getValue()) {
					option.setAttribute("selected","selected");
				}
				widget.appendChild(option);
			}

			widget.name = this.getFieldName();
		break;
		case 'topic':
			/*
				NOTE: You need to setWidth() with a pixel
				value for a topic widget.
			*/
			// var container = document.createElement('div');

			widget		= document.createElement('input');
			if(this.getWidgetWidth().match(/px/)) {
				widget.style.width = this.getWidgetWidth();
			}

			widget.name	= this.getFieldName();
			widget.value	= this.getValue();

			widget.setAttribute("pokkarielement","pokkarielement");
			widget.setAttribute("pokkariType","TopicEditor");
			widget.setAttribute("autocomplete","off");

			/*

			widget.className = "field_editor";
			widget.id = "field_editor_" + this.getFieldName();

			var autoCompleter = document.createElement('div');
			if(this.getWidgetWidth().match(/px/)) {
				autoCompleter.style.width = this.getWidgetWidth();
			}
			autoCompleter.id = "autoCompletionZoneContainer";
			autoCompleter.style.border = "1px solid red;";

			alert("I'm instantiating my object");
			var obj = new TopicEditor({autoCompletionZoneContainer : autoCompleter});
			obj.bindToHTMLElement(widget);
			obj.onConstruct();

			container.appendChild(widget);
			container.appendChild(autoCompleter);

			return container;
			*/
		break;
		default:
			widget 		= document.createElement('input');
			if(this.getWidgetWidth().match(/px/)) {
				widget.style.width = this.getWidgetWidth();
			} else {
				widget.size 	= this.getWidgetWidth(); 
			}
			widget.name	= this.getFieldName();
			widget.value	= this.getValue();
		break;
	}

	widget.className = "field_editor";
	widget.id       = "field_editor_" + this.getFieldName();

	return widget;
}

PokkariPostField.prototype.getSaveButton = function() {
	
	var saveButton = document.createElement("button");
	saveButton.innerHTML = "Save";
	saveButton.onclick = function() { this.parentNode.pokkariElement.save(); };
	saveButton.title = "Save your changes";

	return saveButton;
}

PokkariPostField.prototype.getAbandonButton = function() {

	var abandonButton = document.createElement("button");
	abandonButton.innerHTML = "Cancel";
	// abandonButton.onclick = function() { alert("TK"); };
	abandonButton.onclick = function() { this.parentNode.pokkariElement.editableState(); };

	abandonButton.title = "Abandon your changes";

	return abandonButton;

}

PokkariPostField.prototype.getWidgetValue = function() {
	return $("field_editor_" + this.getFieldName()).value;
}

PokkariPostField.prototype.save = function() {
	/* 
		Double-check that the user has permission to do this
		(Additional permissions validation will happen server-side)
	*/

	if(!this.can_edit) {
		alert("Sorry, you don't seem to have permission to edit this field.");
		throw("User does not have permission to perform this operation");
	}

	this.setValue($("field_editor_" + this.getFieldName()).value);

	if(this.getWidgetType() == "select") {
		var hv = $("field_editor_" + this.getFieldName()).options[$("field_editor_" + this.getFieldName()).selectedIndex].text;
		this.setHumanValue(hv);
	}

	this.url = "/" + this.getItemType() + "/post/";

	/*
		We should be sending this through as data in an arg to send()
		Seems like when we do that, though, it shows up as a string
		under the key POSTDATA.
	*/
	
	this.url += "?id="+this.getItemId()+"&item_type="+this.getItemType()+"&"+this.fieldName+"="+this.value+"&post=post"

	this.url += '&skin=xmlhttprequest';

	this.method = "POST";
	this.send();

	this.savingState();

}

PokkariPostField.prototype.onsuccess = function() {

	this.editableState();
}

PokkariPostField.prototype.onfailure = function() {
	alert("We have an error");
	alert(this.getResponseText());
}
/**
 * AccordionControl is an expanding/collasing control.  It finds any child controls with coorisponding CSS class
 * names matching tabClassName and contentClassName and makes them into summary/detail views.  Each tab and content
 * child element should be a block element (such as div).   Clicking the tab element will cause the content element
 * to expand or contract.
 * @constructor
 * @param {String} tabClassName CSS Class name that will signal a summary block element
 * @param {String} contentClassName CSS Class name that will signal a detail block element
 * @param {Boolean} singleSelect If set, only one detail block element will be expanded at a time
 */
function AccordionControl(params)
{
    PokkariElement.apply(this,[params]);
}

AccordionControl.prototype = new PokkariElement();
AccordionControl.prototype.constructor = AccordionControl;

/**
 * Initializer
 * @private
 */
AccordionControl.prototype.oninit = function()
{
    if (!this.tabClassName)
        throw new Error("Missing init parameter: tabClassName");
    
    if (!this.contentClassName)
        throw new Error("Missing init parameter: contentClassName");
 
    this.tabControls = new Array();
    this.contentControls = new Array();
    this.attachChildControls(this.element);

    // TODO Add cookie save support
    
    if (this.tabControls.length != this.contentControls.length)
        throw new Error("Unequal number of tab and content controls");        
}

/**
 * Called from oninit to find summary and detail controls, and attach events.  If the summary control contains an image
 * it will be assumed to be accordionCollapsed.gif (FIXME)
 * @private
 * @param {Object} src Element from which to get the summary/detail controls.  Called from oninit.  Elsewhere?
 */
AccordionControl.prototype.attachChildControls = function(src)
{
    var divs = src.getElementsByTagName("div");
    
    for (var i=0; i<divs.length; i++)
    {
        if (divs[i].className && (
			divs[i].className == this.tabClassName ||
			divs[i].className == this.tabExpandedClassName))
        {
            divs[i].tabNumber = this.tabControls.length;
            divs[i].unselectable = true;
			divs[i].selected = (divs[i].className == this.tabExpandedClassName);
            
            var imgs = divs[i].getElementsByTagName("img");
            if (imgs && imgs.length > 0)
                divs[i].tabIcon = imgs[0];
            
            this.tabControls.push(divs[i]);
            this.attachTabEvents(divs[i]);
        }
        else if (divs[i].className == this.contentClassName)
        {
            this.contentControls.push(divs[i]);
        }
    }
}

/**
 * Called from attachChildControls to attach an onclick event to the summary controls
 * @param {Object} tabControl Summary element to attach events to
 * @private
 */
AccordionControl.prototype.attachTabEvents = function(tabControl)
{
    tabControl.attachEvent("onclick",this.eventHandler(this.onToggleTab))
}

/**
 * Event handler, finds the appropriate summary control by inspecting the sender or sender's parents and calls toggleTab.
 * @param {Element} self Context of this object (use instead of 'this')
 * @param {Element} sender Object who trigger the event (not necessary the object with the event attached)
 * @param {Object} e Event arguments object (see IE window.event)
 * @returns True, no further event handling will take place.
 */
AccordionControl.prototype.onToggleTab = function(self,sender,e)
{
    self.toggleTab(self.findTabFromSender(sender));
        
    return true;
}

/**
 * Finds the appropriate summary control by looking at an element and its' parents.
 * @private
 */
AccordionControl.prototype.findTabFromSender = function(sender)
{
    var node = sender;
    while (node && typeof(node.tabNumber) == "undefined")
    {
        node = node.parentNode;
    }
    
    return node;
}

/**
 * Toggles the visibility for the detail portion of this summary/detail pair.  If tabControl implements a onExpand
 * method, it will be called before displaying the details.  Similarly if tabControl onCollapse exists, it will
 * be called prior to hiding details.  Use onExpand to implement callback loading.
 * @param {Object} tabControl Summary element for which the coorisponding detail element will be shown or hidden.
 */
AccordionControl.prototype.toggleTab = function(tabControl)
{
    var tabNumber = tabControl.tabNumber;
    var contentControl = this.contentControls[tabNumber];
    var selected = tabControl.selected;

    if (this.singleSelect)    
        this.collapseAll();

    if (!selected)
    {
        tabControl.selected = true;
        if (tabControl.onExpand)
            tabControl.onExpand(this,tabControl,null);
        
        if (tabControl.tabIcon) 
            tabControl.tabIcon.src = '/images/accordionExpanded.gif';

		if (this.tabExpandedClassName) {
			tabControl.className = this.tabClassName;
		}
            
       	contentControl.style.display = "block";

	if(typeof(Pokkari.SetCookiePreference) == "function" && tabControl.id) {
		Pokkari.SetCookiePreference({
			name : 'acs_' + tabControl.id + '_expanded',
			value : 1
		});
		Pokkari.SetCookiePreference({
			name : 'acs_' + tabControl.id + '_collapsed',
			value : 0
		});
	}
    }
    else
    {
        tabControl.selected = false;
        if (tabControl.onCollapse)
            tabControl.onCollapse(this,tabControl,null);
            
        if (tabControl.tabIcon) 
            tabControl.tabIcon.src = '/images/accordionCollapsed.gif';
        
		if (this.tabExpandedClassName) {
			tabControl.className = this.tabExpandedClassName;
		}
            
       	contentControl.style.display = "none";

	if(typeof(Pokkari.SetCookiePreference) == "function" && tabControl.id) {
		Pokkari.SetCookiePreference({
			name : 'acs_' + tabControl.id + '_collapsed',
			value : 1
		});
		Pokkari.SetCookiePreference({
			name : 'acs_' + tabControl.id + '_expanded',
			value : 0
		});
	}
    }   
}

/**
 * Collapse all controls, hiding all details.
 */
AccordionControl.prototype.collapseAll = function()
{
    for (var i=0; i<this.contentControls.length; i++)
    {
        this.contentControls[i].style.display = "none";
        this.tabControls[i].selected = false;
    }
}
function PokkariCaptcha() {
	this.initialize();
	this.render();
}

PokkariCaptcha.prototype = {
	initialize: function() {
		this.uuid = new UUID();
	},
	
	render: function() {
		var html = "<input type='hidden' name='captchas_id' value='" + this.uuid + "' />\n" +
			"<img src='/captchas/" + this.uuid + "' />\n";

		document.write(html);
	}	
}
/*

uuid.js - Version 0.1
JavaScript Class to create a UUID like identifier

Copyright (C) 2006, Erik Giberti (AF-Design), All rights reserved.

This program is free software; you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by the Free Software 
Foundation; either version 2 of the License, or (at your option) any later 
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, Inc., 59 Temple 
Place, Suite 330, Boston, MA 02111-1307 USA

The latest version of this file can be downloaded from
http://www.af-design.com/resources/javascript_uuid.php

HISTORY:
6/5/06 - Initial Release

*/


// on creation of a UUID object, set it's initial value
function UUID(){
	this.id = this.createUUID();
}



// When asked what this Object is, lie and return it's value
UUID.prototype.valueOf = function(){ return this.id; }
UUID.prototype.toString = function(){ return this.id; }



//
// INSTANCE SPECIFIC METHODS
//



UUID.prototype.createUUID = function(){
	// JavaScript Version of UUID implementation.
	//
	// Copyright 2006 Erik Giberti, all rights reserved.
	//
	// Loose interpretation of the specification DCE 1.1: Remote Procedure Call
	// described at http://www.opengroup.org/onlinepubs/009629399/apdxa.htm#tagtcjh_37
	// since JavaScript doesn't allow access to internal systems, the last 48 bits 
	// of the node section is made up using a series of random numbers (6 octets long).
	//  
	var dg = UUID.timeInMs(new Date(1582, 10, 15, 0, 0, 0, 0));
	var dc = UUID.timeInMs(new Date());
	var t = dc - dg;
	var h = '-';
	var tl = UUID.getIntegerBits(t,0,31);
	var tm = UUID.getIntegerBits(t,32,47);
	var thv = UUID.getIntegerBits(t,48,59) + '1'; // version 1, security version is 2
	var csar = UUID.getIntegerBits(UUID.randrange(0,4095),0,7);
	var csl = UUID.getIntegerBits(UUID.randrange(0,4095),0,7);

	// since detection of anything about the machine/browser is far to buggy, 
	// include some more random numbers here
	// if nic or at least an IP can be obtained reliably, that should be put in
	// here instead.
	var n = UUID.getIntegerBits(UUID.randrange(0,8191),0,7) + 
			UUID.getIntegerBits(UUID.randrange(0,8191),8,15) + 
			UUID.getIntegerBits(UUID.randrange(0,8191),0,7) + 
			UUID.getIntegerBits(UUID.randrange(0,8191),8,15) + 
			UUID.getIntegerBits(UUID.randrange(0,8191),0,15); // this last number is two octets long
	return tl + h + tm + h + thv + h + csar + csl + h + n; 
}



//
// GENERAL METHODS (Not instance specific)
//



// Pull out only certain bits from a very large integer, used to get the time
// code information for the first part of a UUID. Will return zero's if there 
// aren't enough bits to shift where it needs to.
UUID.getIntegerBits = function(val,start,end){
	var base16 = UUID.returnBase(val,16);
	var quadArray = new Array();
	var quadString = '';
	var i = 0;
	for(i=0;i<base16.length;i++){
		quadArray.push(base16.substring(i,i+1));	
	}
	for(i=Math.floor(start/4);i<=Math.floor(end/4);i++){
		if(!quadArray[i] || quadArray[i] == '') quadString += '0';
		else quadString += quadArray[i];
	}
	return quadString;
}

// Numeric Base Conversion algorithm from irt.org
// In base 16: 0=0, 5=5, 10=A, 15=F
UUID.returnBase = function(number, base){
	//
	// Copyright 1996-2006 irt.org, All Rights Reserved.	
	//
	// Downloaded from: http://www.irt.org/script/146.htm	
	// modified to work in this class by Erik Giberti
	var convert = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
    if (number < base) var output = convert[number];
    else {
        var MSD = '' + Math.floor(number / base);
        var LSD = number - MSD*base;
        if (MSD >= base) var output = this.returnBase(MSD,base) + convert[LSD];
        else var output = convert[MSD] + convert[LSD];
    }
    return output;
}

// This is approximate but should get the job done for general use.
// It gets an approximation of the provided date in milliseconds. WARNING:
// some implementations of JavaScript will choke with these large numbers
// and so the absolute value is used to avoid issues where the implementation
// begin's at the negative value.
UUID.timeInMs = function(d){
	var ms_per_second = 100; // constant
	var ms_per_minute = 6000; // ms_per second * 60;
	var ms_per_hour   = 360000; // ms_per_minute * 60;
	var ms_per_day    = 8640000; // ms_per_hour * 24;
	var ms_per_month  = 207360000; // ms_per_day * 30;
	var ms_per_year   = 75686400000; // ms_per_day * 365;
	return Math.abs((d.getUTCFullYear() * ms_per_year) + (d.getUTCMonth() * ms_per_month) + (d.getUTCDate() * ms_per_day) + (d.getUTCHours() * ms_per_hour) + (d.getUTCMinutes() * ms_per_minute) + (d.getUTCSeconds() * ms_per_second) + d.getUTCMilliseconds());
}

// pick a random number within a range of numbers
// int c randrange(int a, int b); where a <= c <= b
UUID.randrange = function(min,max){
	var num = Math.round(Math.random() * max);
	if(num < min){ 
		num = min;
	} else if (num > max) {
		num = max;
	}
	return num;
}

// end of UUID class file
