/*  Prototype JavaScript framework, version 1.5.0
 *  (c) 2005-2007 Sam Stephenson
 *
 *  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.5.0',
  BrowserFeatures: {
    XPath: !!document.evaluate
  },

  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 (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Object.extend(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;
    }
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({}, object);
  }
});

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, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}

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, length = arguments.length; i < 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() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.callback(this);
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
}
String.interpret = function(value){
  return value == null ? '' : String(value);
}

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = count === undefined ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return this;
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : this;
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  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(function(script) { return eval(script) });
  },

  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.length > 1 ?
      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return {};

    return match[1].split(separator || '&').inject({}, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var name = decodeURIComponent(pair[0]);
        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;

        if (hash[name] !== undefined) {
          if (hash[name].constructor != Array)
            hash[name] = [hash[name]];
          if (value) hash[name].push(value);
        }
        else hash[name] = value;
      }
      return hash;
    });
  },

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

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

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

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function(){
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.replace(/\\/g, '\\\\');
    if (useDoubleQuotes)
      return '"' + escapedString.replace(/"/g, '\\"') + '"';
    else
      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (typeof replacement == 'function') return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
}

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

var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern  = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    return this.template.gsub(this.pattern, function(match) {
      var before = match[1];
      if (before == '\\') return match[2];
      return before + String.interpret(object[match[3]]);
    });
  }
}

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;
    }
    return this;
  },

  eachSlice: function(number, iterator) {
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.map(iterator);
  },

  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 = false;
    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 || Prototype.K)(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;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = fillWith === undefined ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  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.map(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 (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value < result)
        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.map(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.map();
  },

  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) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  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, length = iterable.length; i < length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse)
  Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < 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 != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value && 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, length = this.length; i < length; i++)
      if (this[i] == object) return i;
    return -1;
  },

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

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function() {
    return this.inject([], function(array, value) {
      return array.include(value) ? array : array.concat([value]);
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});

Array.prototype.toArray = Array.prototype.clone;

function $w(string){
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if(window.opera){
  Array.prototype.concat = function(){
    var array = [];
    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for(var i = 0, length = arguments.length; i < length; i++) {
      if(arguments[i].constructor == Array) {
        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  }
}
var Hash = function(obj) {
  Object.extend(this, obj || {});
};

Object.extend(Hash, {
  toQueryString: function(obj) {
    var parts = [];

	  this.prototype._each.call(obj, function(pair) {
      if (!pair.key) return;

      if (pair.value && pair.value.constructor == Array) {
        var values = pair.value.compact();
        if (values.length < 2) pair.value = values.reduce();
        else {
        	key = encodeURIComponent(pair.key);
          values.each(function(value) {
            value = value != undefined ? encodeURIComponent(value) : '';
            parts.push(key + '=' + encodeURIComponent(value));
          });
          return;
        }
      }
      if (pair.value == undefined) pair[1] = '';
      parts.push(pair.map(encodeURIComponent).join('='));
	  });

    return parts.join('&');
  }
});

Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
  _each: function(iterator) {
    for (var key in this) {
      var value = this[key];
      if (value && value == Hash.prototype[key]) 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(this, function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  remove: function() {
    var result;
    for(var i = 0, length = arguments.length; i < length; i++) {
      var value = this[arguments[i]];
      if (value !== undefined){
        if (result === undefined) result = value;
        else {
          if (result.constructor != Array) result = [result];
          result.push(value)
        }
      }
      delete this[arguments[i]];
    }
    return result;
  },

  toQueryString: function() {
    return Hash.toQueryString(this);
  },

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

function $H(object) {
  if (object && object.constructor == Hash) return object;
  return new Hash(object);
};
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;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  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 XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

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

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

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

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (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,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   ''
    }
    Object.extend(this.options, options || {});

    this.options.method = this.options.method.toLowerCase();
    if (typeof this.options.parameters == 'string')
      this.options.parameters = this.options.parameters.toQueryParams();
  }
}

Ajax.Request = Class.create();
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  _complete: false,

  initialize: function(url, options) {
    this.transport = Ajax.getTransport();
    this.setOptions(options);
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = this.options.parameters;

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    params = Hash.toQueryString(params);
    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='

    // when GET, append parameters to URL
    if (this.method == 'get' && params)
      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;

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

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous)
        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      var body = this.method == 'post' ? (this.options.postBody || params) : null;

      this.transport.send(body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (typeof extras.push == 'function')
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
		if (typeof(headers[name]) != "function")
			this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    return !this.transport.status
        || (this.transport.status >= 200 && this.transport.status < 300);
  },

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

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

      if ((this.getHeader('Content-type') || 'text/javascript').strip().
        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
          this.evalResponse();
    }

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

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

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

  evalJSON: function() {
    try {
      var json = this.getHeader('X-JSON');
      return json ? eval('(' + json + ')') : null;
    } catch (e) { return null }
  },

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

  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.container = {
      success: (container.success || 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, param) {
      this.updateContent();
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },

  updateContent: function() {
    var receiver = this.container[this.success() ? 'success' : 'failure'];
    var response = this.transport.responseText;

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

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

    if (this.success()) {
      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.options.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);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (typeof element == 'string')
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(query.snapshotItem(i));
    return results;
  };
}

document.getElementsByClassName = function(className, parentElement) {
  if (Prototype.BrowserFeatures.XPath) {
    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    return document._getElementsByXPath(q, parentElement);
  } else {
    var children = ($(parentElement) || document.body).getElementsByTagName('*');
    var elements = [], child;
    for (var i = 0, length = children.length; i < length; i++) {
      child = children[i];
      if (Element.hasClassName(child, className))
        elements.push(Element.extend(child));
    }
    return elements;
  }
};

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

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

Element.extend = function(element) {
  if (!element || _nativeExtensions || element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;

    if (element.tagName == 'FORM')
      Object.extend(methods, Form.Methods);
    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
      Object.extend(methods, Form.Element.Methods);

    Object.extend(methods, Element.Methods.Simulated);

    for (var property in methods) {
      var value = methods[property];
      if (typeof value == 'function' && !(property in element))
        element[property] = cache.findOrStore(value);
    }
  }

  element._extended = true;
  return element;
};

Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
};

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

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, html) {
    html = typeof html == 'undefined' ? '' : html.toString();
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  replace: function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    if (element.outerHTML) {
      element.outerHTML = html.stripScripts();
    } else {
      var range = element.ownerDocument.createRange();
      range.selectNodeContents(element);
      element.parentNode.replaceChild(
        range.createContextualFragment(html.stripScripts()), element);
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $A($(element).getElementsByTagName('*'));
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (typeof selector == 'string')
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    return Selector.findElement($(element).ancestors(), expression, index);
  },

  down: function(element, expression, index) {
    return Selector.findElement($(element).descendants(), expression, index);
  },

  previous: function(element, expression, index) {
    return Selector.findElement($(element).previousSiblings(), expression, index);
  },

  next: function(element, expression, index) {
    return Selector.findElement($(element).nextSiblings(), expression, index);
  },

  getElementsBySelector: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  getElementsByClassName: function(element, className) {
    return document.getElementsByClassName(className, element);
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (document.all && !window.opera) {
      var t = Element._attributeTranslations;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name])  name = t.names[name];
      var attribute = element.attributes[name];
      if(attribute) return attribute.nodeValue;
    }
    return element.getAttribute(name);
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

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

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    if (elementClassName.length == 0) return false;
    if (elementClassName == className ||
        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      return true;
    return false;
  },

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

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

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
    return element;
  },

  observe: function() {
    Event.observe.apply(Event, arguments);
    return $A(arguments).first();
  },

  stopObserving: function() {
    Event.stopObserving.apply(Event, arguments);
    return $A(arguments).first();
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

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

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);
    while (element = element.parentNode)
      if (element == ancestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = Position.cumulativeOffset(element);
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    if (['float','cssFloat'].include(style))
      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
    style = style.camelize();
    var value = element.style[style];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css[style] : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style];
      }
    }

    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
      value = element['offset'+style.capitalize()] + 'px';

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
    if(style == 'opacity') {
      if(value) return parseFloat(value);
      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if(value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }
    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (var name in style) {
      var value = style[name];
      if(name == 'opacity') {
        if (value == 1) {
          value = (/Gecko/.test(navigator.userAgent) &&
            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else if(value === '') {
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else {
          if(value < 0.00001) value = 0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
              'alpha(opacity='+value*100+')';
        }
      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
      element.style[name.camelize()] = value;
    }
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      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;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    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;
      }
    }
    return element;
  },

  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 = '';
    }
    return element;
  },

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

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  }
};

Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});

Element._attributeTranslations = {};

Element._attributeTranslations.names = {
  colspan:   "colSpan",
  rowspan:   "rowSpan",
  valign:    "vAlign",
  datetime:  "dateTime",
  accesskey: "accessKey",
  tabindex:  "tabIndex",
  enctype:   "encType",
  maxlength: "maxLength",
  readonly:  "readOnly",
  longdesc:  "longDesc"
};

Element._attributeTranslations.values = {
  _getAttr: function(element, attribute) {
    return element.getAttribute(attribute, 2);
  },

  _flag: function(element, attribute) {
    return $(element).hasAttribute(attribute) ? attribute : null;
  },

  style: function(element) {
    return element.style.cssText.toLowerCase();
  },

  title: function(element) {
    var node = element.getAttributeNode('title');
    return node.specified ? node.nodeValue : null;
  }
};

Object.extend(Element._attributeTranslations.values, {
  href: Element._attributeTranslations.values._getAttr,
  src:  Element._attributeTranslations.values._getAttr,
  disabled: Element._attributeTranslations.values._flag,
  checked:  Element._attributeTranslations.values._flag,
  readonly: Element._attributeTranslations.values._flag,
  multiple: Element._attributeTranslations.values._flag
});

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    var t = Element._attributeTranslations;
    attribute = t.names[attribute] || attribute;
    return $(element).getAttributeNode(attribute).specified;
  }
};

// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
  Element.Methods.update = function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    var tagName = element.tagName.toUpperCase();
    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
      var div = document.createElement('div');
      switch (tagName) {
        case 'THEAD':
        case 'TBODY':
          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
          depth = 2;
          break;
        case 'TR':
          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
          depth = 3;
          break;
        case 'TD':
          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
          depth = 4;
      }
      $A(element.childNodes).each(function(node){
        element.removeChild(node)
      });
      depth.times(function(){ div = div.firstChild });

      $A(div.childNodes).each(
        function(node){ element.appendChild(node) });
    } else {
      element.innerHTML = html.stripScripts();
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  }
};

Object.extend(Element, Element.Methods);

var _nativeExtensions = false;

if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
    var className = 'HTML' + tag + 'Element';
    if(window[className]) return;
    var klass = window[className] = {};
    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  });

Element.addMethods = function(methods) {
  Object.extend(Element.Methods, methods || {});

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    var cache = Element.extend.cache;
    for (var property in methods) {
      var value = methods[property];
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = cache.findOrStore(value);
    }
  }

  if (typeof HTMLElement != 'undefined') {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
    copy(Form.Methods, HTMLFormElement.prototype);
    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
      copy(Form.Element.Methods, klass.prototype);
    });
    _nativeExtensions = true;
  }
}

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) {
        var tagName = this.element.tagName.toUpperCase();
        if (['TBODY', 'TR'].include(tagName)) {
          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($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

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

Object.extend(Element.ClassNames.prototype, Enumerable);
var Selector = Class.create();
Selector.prototype = {
  initialize: function(expression) {
    this.params = {classNames: []};
    this.expression = expression.toString().strip();
    this.parseExpression();
    this.compileMatcher();
  },

  parseExpression: function() {
    function abort(message) { throw 'Parse error in selector: ' + message; }

    if (this.expression == '')  abort('empty expression');

    var params = this.params, expr = this.expression, match, modifier, clause, rest;
    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
      params.attributes = params.attributes || [];
      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
      expr = match[1];
    }

    if (expr == '*') return this.params.wildcard = true;

    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
      modifier = match[1], clause = match[2], rest = match[3];
      switch (modifier) {
        case '#':       params.id = clause; break;
        case '.':       params.classNames.push(clause); break;
        case '':
        case undefined: params.tagName = clause.toUpperCase(); break;
        default:        abort(expr.inspect());
      }
      expr = rest;
    }

    if (expr.length > 0) abort(expr.inspect());
  },

  buildMatchExpression: function() {
    var params = this.params, conditions = [], clause;

    if (params.wildcard)
      conditions.push('true');
    if (clause = params.id)
      conditions.push('element.readAttribute("id") == ' + clause.inspect());
    if (clause = params.tagName)
      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
    if ((clause = params.classNames).length > 0)
      for (var i = 0, length = clause.length; i < length; i++)
        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
    if (clause = params.attributes) {
      clause.each(function(attribute) {
        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
        var splitValueBy = function(delimiter) {
          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
        }

        switch (attribute.operator) {
          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
          case '|=':      conditions.push(
                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
                          ); break;
          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
          case '':
          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
        }
      });
    }

    return conditions.join(' && ');
  },

  compileMatcher: function() {
    this.match = new Function('element', 'if (!element.tagName) return false; \
      element = $(element); \
      return ' + this.buildMatchExpression());
  },

  findElements: function(scope) {
    var element;

    if (element = $(this.params.id))
      if (this.match(element))
        if (!scope || Element.childOf(element, scope))
          return [element];

    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');

    var results = [];
    for (var i = 0, length = scope.length; i < length; i++)
      if (this.match(element = scope[i]))
        results.push(Element.extend(element));

    return results;
  },

  toString: function() {
    return this.expression;
  }
}

Object.extend(Selector, {
  matchElements: function(elements, expression) {
    var selector = new Selector(expression);
    return elements.select(selector.match.bind(selector)).map(Element.extend);
  },

  findElement: function(elements, expression, index) {
    if (typeof expression == 'number') index = expression, expression = false;
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    return expressions.map(function(expression) {
      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
        var selector = new Selector(expr);
        return results.inject([], function(elements, result) {
          return elements.concat(selector.findElements(result || element));
        });
      });
    }).flatten();
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, getHash) {
    var data = elements.inject({}, function(result, element) {
      if (!element.disabled && element.name) {
        var key = element.name, value = $(element).getValue();
        if (value != undefined) {
          if (result[key]) {
            if (result[key].constructor != Array) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return getHash ? data : Hash.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, getHash) {
    return Form.serializeElements(Form.getElements(form), getHash);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.blur();
      element.disabled = 'true';
    });
    return form;
  },

  enable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.disabled = '';
    });
    return form;
  },

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

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  }
}

Object.extend(Form, Form.Methods);

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

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
}

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = {};
        pair[element.name] = value;
        return Hash.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    element.focus();
    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
      !['button', 'reset', 'submit'].include(element.type) ) )
      element.select();
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.blur();
    element.disabled = false;
    return element;
  }
}

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;

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

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
      default:
        return Form.Element.Serializers.textarea(element);
    }
  },

  inputSelector: function(element) {
    return element.checked ? element.value : null;
  },

  textarea: function(element) {
    return element.value;
  },

  select: function(element) {
    return this[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
}

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

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();
    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
      ? this.lastValue != value : String(this.lastValue) != String(value));
    if (changed) {
      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() {
    Form.getElements(this.element).each(this.registerCallback.bind(this));
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          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,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,

  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, length = Event.observers.length; i < length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    Event._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    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) {
      try {
        element.detachEvent('on' + name, observer);
      } catch (e) {}
    }
  }
});

/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
  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) {
        if(element.tagName=='BODY') break;
        var 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;
  },

  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 {
      if (!window.opera || element.tagName=='BODY') {
        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];
  }
}

Element.addMethods();
// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007

// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';
  if(this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if(this.slice(0,1) == '#') {  
      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if(this.length==7) color = this.toLowerCase();  
    }  
  }  
  return(color.length==7 ? color : (arguments[0] || this));  
}

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

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
}

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
}

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
  return element;
}

Element.getOpacity = function(element){
  return $(element).getStyle('opacity');
}

Element.setOpacity = function(element, value){
  return $(element).setStyle({opacity:value});
}

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
}

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

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

Array.prototype.call = function() {
  var args = arguments;
  this.each(function(f){ f.apply(this, args) });
}

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

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  tagifyText: function(element) {
    if(typeof Builder == 'undefined')
      throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
      
    var tagifyStyle = 'position:relative';
    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if(child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            Builder.node('span',{style: tagifyStyle},
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if(((typeof element == 'object') || 
        (typeof element == 'function')) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || {});
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || {});
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.Transitions = {
  linear: Prototype.K,
  sinoidal: function(pos) {
    return (-Math.cos(pos*Math.PI)/2) + 0.5;
  },
  reverse: function(pos) {
    return 1-pos;
  },
  flicker: function(pos) {
    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
  },
  wobble: function(pos) {
    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
  },
  pulse: function(pos, pulses) { 
    pulses = pulses || 5; 
    return (
      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
      );
  },
  none: function(pos) {
    return 0;
  },
  full: function(pos) {
    return 1;
  }
};

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = (typeof effect.options.queue == 'string') ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if(!this.interval) 
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if(this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++) 
      if(this.effects[i]) this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if(typeof queueName != 'string') return queueName;
    
    if(!this.instances[queueName])
      this.instances[queueName] = new Effect.ScopedQueue();
      
    return this.instances[queueName];
  }
}
Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        60.0,  // max. 60fps due to Effect.Queue implementation
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn + (this.options.duration*1000);
    this.event('beforeStart');
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if(timePos >= this.startOn) {
      if(timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if(this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
      var frame = Math.round(pos * this.options.fps * this.options.duration);
      if(frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  render: function(pos) {
    if(this.state == 'idle') {
      this.state = 'running';
      this.event('beforeSetup');
      if(this.setup) this.setup();
      this.event('afterSetup');
    }
    if(this.state == 'running') {
      if(this.options.transition) pos = this.options.transition(pos);
      pos *= (this.options.to-this.options.from);
      pos += this.options.from;
      this.position = pos;
      this.event('beforeUpdate');
      if(this.update) this.update(pos);
      this.event('afterUpdate');
    }
  },
  cancel: function() {
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if(this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if(typeof this[property] != 'function') data[property] = this[property];
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if(effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Event = Class.create();
Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
  initialize: function() {
    var options = Object.extend({
      duration: 0
    }, arguments[0] || {});
    this.start(options);
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || {});
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Bug in Opera: Opera returns the "real" position of a static element or
    // relative element that does not have top/left explicitly set.
    // ==> Always set top and left for position relative elements in your stylesheets 
    // (to 0 if you do not need them) 
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if(this.options.mode == 'absolute') {
      // absolute movement, so we need to calc deltaX and deltaY
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
      top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};

Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
  initialize: function(element, percent) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || {});
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = {};
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if(fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if(this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if(/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if(!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if(this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = {};
    if(this.options.scaleX) d.width = Math.round(width) + 'px';
    if(this.options.scaleY) d.height = Math.round(height) + 'px';
    if(this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if(this.elementPositioning == 'absolute') {
        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if(this.options.scaleY) d.top = -topd + 'px';
        if(this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = {};
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if(!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if(!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    this.start(arguments[1] || {});
  },
  setup: function() {
    Position.prepare();
    var offsets = Position.cumulativeOffset(this.element);
    if(this.options.offset) offsets[1] += this.options.offset;
    var max = window.innerHeight ? 
      window.height - window.innerHeight :
      document.body.scrollHeight - 
        (document.documentElement.clientHeight ? 
          document.documentElement.clientHeight : document.body.clientHeight);
    this.scrollStart = Position.deltaY;
    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
  },
  update: function(position) {
    Position.prepare();
    window.scrollTo(Position.deltaX, 
      this.scrollStart + (position*this.delta));
  }
});

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
  from: element.getOpacity() || 1.0,
  to:   0.0,
  afterFinishInternal: function(effect) { 
    if(effect.options.to!=0) return;
    effect.element.hide().setStyle({opacity: oldOpacity}); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element)
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || {})
   );
}

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || {})
  );
}

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || {}));
}

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      })
    }
  }, arguments[1] || {}));
}

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        } 
      }, arguments[1] || {}));
}

Effect.Shake = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element, 
      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
}

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || {})
  );
}

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
      effect.element.down().undoPositioned();
    }
   }, arguments[1] || {})
  );
}

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, { 
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping(); 
    }
  });
}

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
             }
           }, options)
      )
    }
  });
}

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
}

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || {};
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
}

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || {}));
};

Effect.Morph = Class.create();
Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: {}
    }, arguments[1] || {});
    if (typeof options.style == 'string') {
      if(options.style.indexOf(':') == -1) {
        var cssText = '', selector = '.' + options.style;
        $A(document.styleSheets).reverse().each(function(styleSheet) {
          if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
          else if (styleSheet.rules) cssRules = styleSheet.rules;
          $A(cssRules).reverse().each(function(rule) {
            if (selector == rule.selectorText) {
              cssText = rule.style.cssText;
              throw $break;
            }
          });
          if (cssText) throw $break;
        });
        this.style = cssText.parseStyle();
        options.afterFinishInternal = function(effect){
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            if(transform.style != 'opacity')
              effect.element.style[transform.style.camelize()] = '';
          });
        }
      } else this.style = options.style.parseStyle();
    } else this.style = $H(options.style)
    this.start(options);
  },
  setup: function(){
    function parseColor(color){
      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0].underscore().dasherize(), value = pair[1], unit = null;

      if(value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if(property == 'opacity') {
        value = parseFloat(value);
        if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if(Element.CSS_LENGTH.test(value)) 
        var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;

      var originalValue = this.element.getStyle(property);
      return $H({ 
        style: property, 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      });
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      )
    });
  },
  update: function(position) {
    var style = $H(), value = null;
    this.transforms.each(function(transform){
      value = transform.unit=='color' ?
        $R(0,2).inject('#',function(m,v,i){
          return m+(Math.round(transform.originalValue[i]+
            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
        transform.originalValue + Math.round(
          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
      style[transform.style] = value;
    });
    this.element.setStyle(style);
  }
});

Effect.Transform = Class.create();
Object.extend(Effect.Transform.prototype, {
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || {};
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      var data = $H(track).values().first();
      this.tracks.push($H({
        ids:     $H(track).keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var elements = [$(track.ids) || $$(track.ids)].flatten();
        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.prototype.parseStyle = function(){
  var element = Element.extend(document.createElement('div'));
  element.innerHTML = '<div style="' + this + '"></div>';
  var style = element.down().style, styleRules = $H();
  
  Element.CSS_PROPERTIES.each(function(property){
    if(style[property]) styleRules[property] = style[property]; 
  });
  if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
    styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
  }
  return styleRules;
};

Element.morph = function(element, style) {
  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
  return element;
};

['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
  function(f) { Element.Methods[f] = Element[f]; }
);

Element.Methods.visualEffect = function(element, effect, options) {
  s = effect.gsub(/_/, '-').camelize();
  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
  new Effect[effect_class](element, options);
  return $(element);
};

Element.addMethods();
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,options) {
	var url = new Url(window.location.href);
	url.setQueryParam(param,value);
	if(options && options.reset_pagination) {
		url.setQueryParam('page',1);
	}
	return url.getUrl();
}

Pokkari.UrlReplaceExtended = function(params,options,baseurl) {
	var url;
	if (baseurl) {
		url = baseurl;
	}
	else {
		url = new Url(window.location.href);
	}
	for(var param in params) {
		if (param != 'toJSONString') {
			url.setQueryParam(param,params[param]);
		}
	}
	if(options && options.reset_pagination) {
		url.setQueryParam('page',1);
	}
	return url.getUrl();
}

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

Pokkari.includedScripts = {};

Pokkari.includes = function(scriptName) 
{
	Pokkari.includedScripts[scriptName] = true;
}

Pokkari.requires = function(scriptName) 
{
	var url = '/scripts/' + scriptName + '.js';	
	
	if (!Pokkari.includedScripts[scriptName])
	{
		Pokkari.includedScripts[scriptName] = true;

		if (document.readyState && document.readyState != "completed")
		{
			document.write("<script type='text/javascript' src='"+url+"'></script>");
		}
		else 
		{
			var script = document.createElement("script");
			script.type = 'text/javascript';
			script.src = url;
			var body = document.body || document.documentElement;	
			body.appendChild(script);
		}
	}

	return true;
}

Pokkari.defaultThumbnailUrl = "/skin/blipnew/placeholder_video.gif";

Pokkari.OpenWindow = function(opts) {

        if(!window.my_modal_window) {
                window.my_modal_window = new YAHOO.widget.Panel('my_modal_window',
                {
                        width : "550px",
                        fixedcenter: true,
                        constraintoviewport: true,
                        underlay: 'none',
                        close:true,
                        visible: false,
                        draggable:true
                });

        }
        
        window.my_modal_window.setHeader(opts.title);

        if(opts['url']) {
		window.my_modal_window.setBody('<img src="/html/skin/blipnew/icons/animLoading.gif" />');
                window.my_modal_window.body.id = 'my_modal_window_body';
        } else {
                window.my_modal_window.setBody(opts['body_content']);
        }

        window.my_modal_window.render(document.body);
        window.my_modal_window.show();

	var onComplete = new Function(); 

	if(opts['rewrite_form_targets']) {
		onComplete = function(r) {
			window.my_modal_window.body.id = "my_modal_form_target";
			var forms = window.my_modal_window.body.getElementsByTagName('form');
			for(var i = 0; i < forms.length; i++) {
				forms[i].onsubmit = function() {
					return Pokkari.SubmitFormToDiv({form : this, target : 'my_modal_form_target'});
					}
			}
		}
	}

        if(opts['url']) {
                var modal_window_updater = new Ajax.Updater(
                        "my_modal_window_body",
                        opts['url'],
                        {
                                method : 'get',
				evalScripts : true,
				onComplete : onComplete
                        });
        }
}

Pokkari.SubmitFormToDiv = function(opts) {

	if(typeof(opts['method']) == "undefined") {
		if(opts['form'].method) {
			opts['method'] = opts['form'].method;
		} else {
			opts['method'] = 'get';
		}
	}

	if(typeof(opts['action']) == "undefined") {
		opts['action'] = opts['form'].action;
	}

	opts['parameters'] = Form.serialize(opts['form']);
	opts['parameters'] += '&no_wrap=1';

	$(opts['target']).innerHTML = "<i>Processing...</i>";

	var submitter = new Ajax.Updater(
		opts['target'],
		opts['action'],
		{
			method : opts['method'],
			parameters : opts['parameters']
		}
	);
	
	return false;
}

Pokkari.includes("pokkari");

// Flash Player Version Detection - Rev 1.5
// Detect Client Browser type
// Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved.
var isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;

function ControlVersion()
{
	var version;
	var axo;
	var e;

	// NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry

	try {
		// version will be set for 7.X or greater players
		axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
		version = axo.GetVariable("$version");
	} catch (e) {
	}

	if (!version)
	{
		try {
			// version will be set for 6.X players only
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
			
			// installed player is some revision of 6.0
			// GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
			// so we have to be careful. 
			
			// default to the first public version
			version = "WIN 6,0,21,0";

			// throws if AllowScripAccess does not exist (introduced in 6.0r47)		
			axo.AllowScriptAccess = "always";

			// safe to call for 6.0r47 or greater
			version = axo.GetVariable("$version");

		} catch (e) {
		}
	}

	if (!version)
	{
		try {
			// version will be set for 4.X or 5.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
			version = axo.GetVariable("$version");
		} catch (e) {
		}
	}

	if (!version)
	{
		try {
			// version will be set for 3.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
			version = "WIN 3,0,18,0";
		} catch (e) {
		}
	}

	if (!version)
	{
		try {
			// version will be set for 2.X player
			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
			version = "WIN 2,0,0,11";
		} catch (e) {
			version = -1;
		}
	}
	
	return version;
}

// JavaScript helper required to detect Flash Player PlugIn version information
function GetSwfVer(){
	// NS/Opera version >= 3 check for Flash plugin in plugin array
	var flashVer = -1;
	
	if (navigator.plugins != null && navigator.plugins.length > 0) {
		if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
			var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
			var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;			
			var descArray = flashDescription.split(" ");
			var tempArrayMajor = descArray[2].split(".");
			var versionMajor = tempArrayMajor[0];
			var versionMinor = tempArrayMajor[1];
			if ( descArray[3] != "" ) {
				tempArrayMinor = descArray[3].split("r");
			} else {
				tempArrayMinor = descArray[4].split("r");
			}
			var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
			var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
		}
	}
	// MSN/WebTV 2.6 supports Flash 4
	else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
	// WebTV 2.5 supports Flash 3
	else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
	// older WebTV supports Flash 2
	else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
	else if ( isIE && isWin && !isOpera ) {
		flashVer = ControlVersion();
	}	
	return flashVer;
}

// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
{
	versionStr = GetSwfVer();
	if (versionStr == -1 ) {
		return false;
	} else if (versionStr != 0) {
		if(isIE && isWin && !isOpera) {
			// Given "WIN 2,0,0,11"
			tempArray         = versionStr.split(" "); 	// ["WIN", "2,0,0,11"]
			tempString        = tempArray[1];			// "2,0,0,11"
			versionArray      = tempString.split(",");	// ['2', '0', '0', '11']
		} else {
			versionArray      = versionStr.split(".");
		}
		var versionMajor      = versionArray[0];
		var versionMinor      = versionArray[1];
		var versionRevision   = versionArray[2];

        	// is the major.revision >= requested major.revision AND the minor version >= requested minor
		if (versionMajor > parseFloat(reqMajorVer)) {
			return true;
		} else if (versionMajor == parseFloat(reqMajorVer)) {
			if (versionMinor > parseFloat(reqMinorVer))
				return true;
			else if (versionMinor == parseFloat(reqMinorVer)) {
				if (versionRevision >= parseFloat(reqRevision))
					return true;
			}
		}
		return false;
	}
}

function AC_AddExtension(src, ext)
{
  if (src.indexOf('?') != -1)
    return src.replace(/\?/, ext+'?'); 
  else
    return src + ext;
}

function AC_Generateobj(objAttrs, params, embedAttrs) 
{ 
    var str = '';
    if (isIE && isWin && !isOpera)
    {
  		str += '<object ';
  		for (var i in objAttrs)
  			str += i + '="' + objAttrs[i] + '" ';
  		for (var i in params)
  			str += '><param name="' + i + '" value="' + params[i] + '" /> ';
  		str += '></object>';
    } else {
  		str += '<embed ';
  		for (var i in embedAttrs)
  			str += i + '="' + embedAttrs[i] + '" ';
  		str += '> </embed>';
    }

    document.write(str);
}

function AC_FL_RunContent(){
  var ret = 
    AC_GetArgs
    (  arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
     , "application/x-shockwave-flash"
    );
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
  var ret = new Object();
  ret.embedAttrs = new Object();
  ret.params = new Object();
  ret.objAttrs = new Object();
  for (var i=0; i < args.length; i=i+2){
    var currArg = args[i].toLowerCase();    

    switch (currArg){	
      case "classid":
        break;
      case "pluginspage":
        ret.embedAttrs[args[i]] = args[i+1];
        break;
      case "src":
      case "movie":	
        args[i+1] = AC_AddExtension(args[i+1], ext);
        ret.embedAttrs["src"] = args[i+1];
        ret.params[srcParamName] = args[i+1];
        break;
      case "onafterupdate":
      case "onbeforeupdate":
      case "onblur":
      case "oncellchange":
      case "onclick":
      case "ondblClick":
      case "ondrag":
      case "ondragend":
      case "ondragenter":
      case "ondragleave":
      case "ondragover":
      case "ondrop":
      case "onfinish":
      case "onfocus":
      case "onhelp":
      case "onmousedown":
      case "onmouseup":
      case "onmouseover":
      case "onmousemove":
      case "onmouseout":
      case "onkeypress":
      case "onkeydown":
      case "onkeyup":
      case "onload":
      case "onlosecapture":
      case "onpropertychange":
      case "onreadystatechange":
      case "onrowsdelete":
      case "onrowenter":
      case "onrowexit":
      case "onrowsinserted":
      case "onstart":
      case "onscroll":
      case "onbeforeeditfocus":
      case "onactivate":
      case "onbeforedeactivate":
      case "ondeactivate":
      case "type":
      case "codebase":
      case "id":
        ret.objAttrs[args[i]] = args[i+1];
        break;
      case "width":
      case "height":
      case "align":
      case "vspace": 
      case "hspace":
      case "class":
      case "title":
      case "accesskey":
      case "name":
      case "tabindex":
        ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
        break;
      default:
        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
    }
  }
  ret.objAttrs["classid"] = classid;
  if (mimeType) ret.embedAttrs["type"] = mimeType;
  return ret;
}


Pokkari.includes("pokkariDom");

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

	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,context) {
	var funcCode = "var self = PokkariElement.AllElements[" +
		this.pokkariElementIndex + "].pokkariElement;\n" +
		"if (!e) e = window.event;\n" +
		"var sender = e && (e.target || e.srcElement);\n" + 
		"var context = " + 
			(typeof(context) == undefined ? "null" : "'"+context+"'") +
		";\n" +
		"var code = " + func + "\n" +
		"return code(self,sender,e,arguments,context);";

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

PokkariElement.destroy = function(element)
{
	if (typeof(element) == "string")
		element = document.getElementById(element);
	
	if (!element)
		return null;

	element.pokkariElement = null;
	PokkariElement.AllElements[element.pokkariElementIndex] = null;
	element.pokkariElementIndex = null;
}

PokkariElement.cast = function(element,type,params)
{
	if (typeof(element) == "string")
		element = document.getElementById(element);
	
	if (!element)
		return null;

	PokkariElement.AllElements.push(element);
	element.pokkariElementIndex = PokkariElement.AllElements.length - 1;
	
	var obj;	
	obj = eval("new "+type+"()");

	if (!obj)
		return null;
		
	obj.bindToHTMLElement(element);

	for (var key in params) {
		if (typeof(obj[key]) == "undefined")
			obj[key] = params[key];
	}

	if (typeof(obj.onConstruct) == "function") {
		obj.onConstruct();
	}
	
	if (document.readyState == "completed") {
		if (typeof(obj.onLoadHandler) == "function") {
			obj.onLoadHandler();
		}
	}

	return obj;
}

// 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();

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

		// If the element already has a pokkariElementIndex then it was already cast.
		if (typeof(element.pokkariElementIndex)=="undefined")
		{
			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.AllElementsFound) {

		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"
				) {
					PokkariElement.AllElements.push(elements[j]);
				}
			}
		}

		PokkariElement.AllElementsFound = true;
	}
	
	return PokkariElement.AllElements;
}

/* TODO - FIXME - This should really be moved elsewhere post-haste */
PokkariElement.prototype.setValidationProfile = function(p) {
	this.validationProfile = p;
}

PokkariElement.prototype.getValidationProfile = function() {
	if(typeof(this.validationProfile) != "undefined") {
		return this.validationProfile;
	} else {
		if(document.validation_profile) {
			this.setValidationProfile(document.validation_profile);
			return this.getValidationProfile();
		}
	}
}

PokkariElement.prototype.validateSubmission = function() {
	
	var errCount = 0;
	var vp = this.getValidationProfile();

	try {
		for(var field in vp) {
			if($(field)) {
				if(!this.validateField(
					$(field),
					this.getValidationProfile()[field],
					this.getValidationProfile()[field]['billboard']
				)) {
					errCount++;
				}
			}
		}
	} catch(e) { }

	if(errCount > 0) {
		PokkariUI.displayBlockingError({
			title: "You didn't complete the entire form",
			text: "Sorry, but it looks like you didn't fill out the form properly.  We've marked the places in the form where we found problems.  Please give it a look and try submitting again." 
		});
		return false;
	} else {
		return true;
	}
}
		
PokkariElement.prototype.validateField = function(field,requirements,billboard) {

	if(typeof(requirements) != 'object') {
		requirements = this.getValidationProfile()[field.id];
	}
	if(!billboard || billboard == "undefined") {
		billboard = requirements['billboard'];
	}

	if ((field.type == "checkbox") &&
	    (requirements['required'] == true) &&
		(field.checked == false)) {
		if(requirements['requiredMessage']) {
			this.setError(billboard,requirements['requiredMessage']);
		} else {
			this.setError(billboard,'The "' + field.name + '" checkbox must be checked.');
		}
		return false;
	}

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

	if(field.value && requirements['requiredExp']) {
		var re = new RegExp(requirements.requiredExp,"i");
		var result = re.test(field.value);
		if(!result) {
			this.setError(billboard,requirements.requiredExpMessage);
			return false;
		}
	}

	if (requirements['function']) {
		var fn = requirements['function'];
		var result = fn.apply(this,new Array(field.value));
		if (!result) {
			this.setError(billboard,requirements.requiredFnMessage);
			return false;
		}
	}

	if(field.value && requirements['extension']) {
		var extensionValidates = false;

		for(var i = 0; i < requirements.extension.length; i++) {

		    if(requirements.extension[i] == "*") {
			extensionValidates = true;
			break;
		    }
		    
		    if(field.value.match(new RegExp("\\." + requirements.extension[i] + "$","i"))) {
			extensionValidates = true;
			break;
		    }
		    
		}

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

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

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

PokkariElement.AllElements = [];
PokkariElement.AllElementsFound = false;

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() {
}

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

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

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

		return true;
}

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("/bookmarks/view/?posts_id=" +
					request.posts_id + "&page=" +
					PokkariPost.getBookmarksLastPageAfterBookmark('mini_bookmarks_list') +
					"&no_wrap=1&view=bookmarks_quick"
				);
        }

}

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

PokkariPost.updateBookmarks = function(url,elementid,formid) {
    if (!elementid)
		elementid = 'mini_bookmarks_list';

	if (!url)
		url = "/bookmarks/view/?no_wrap=1&view=bookmarks_quick";

	// Have to do this before animateLoad!
	var postBody = formid ? Form.serialize(formid) : null;
	
	PokkariUI.animateLoad(elementid);
	var updater = new Ajax.Updater(elementid,url,{
		method: formid ? 'post' : 'get',
		onComplete: function() {
			var as = $('pagination').getElementsByTagName('a');
				for(var i = 0; i < as.length; i++) {
					as[i].onclick = function() {
					PokkariPost.updateBookmarks(this.href + ";no_wrap=1",elementid);
					return false;
				}
			}
		},
		postBody: postBody
	});
}

  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("/bookmarks/view/?posts_id=" +
					request.posts_id + "&page=" +
					PokkariPost.getBookmarksCurrentPageAfterUnbookmark('mini_bookmarks_list') +
					"&no_wrap=1&view=bookmarks_quick"
				);
		}

}

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

PokkariPost.getBookmarkCount = function(objid) {
	var obj;

	if (!objid) return 0;
	obj = $(objid);
	if (!obj) return 0;

	var rows = obj.getElementsByTagName("tr");
	if (!rows || !rows.length) return 0;

	return rows.length - 1;
}

PokkariPost.getBookmarksLastPage = function(objid) {
	var obj;

	if (!objid) return 1;
	obj = $(objid);
	if (!obj) return 1;

	var spans = obj.getElementsByTagName("span");
	if (!spans || !spans.length) return 1;

	for (var i=0; i<spans.length; i++) {
		if (spans[i].className == "pagination_total_pages") {
			var n = parseInt(spans[i].innerText);
			if (!isNaN(n))
				return n;
		}
	}

	return 1;
}

PokkariPost.getBookmarksLastPageAfterBookmark = function(objid) {
	var lastPage = PokkariPost.getBookmarksLastPage(objid);
	var bookmarks = PokkariPost.getBookmarkCount(objid);

	if (bookmarks == 15)
		return lastPage + 1;
	else
		return lastPage;
}

PokkariPost.getBookmarksCurrentPage = function(objid) {
	var obj;

	if (!objid) return 1;
	obj = $(objid);
	if (!obj) return 1;

	var spans = obj.getElementsByTagName("span");
	if (!spans || !spans.length) return 1;

	for (var i=0; i<spans.length; i++) {
		if (spans[i].className == "pagination_current_page") {
			var n = parseInt(spans[i].innerText);
			if (!isNaN(n))
				return n;
		}
	}

	return 1;
}

PokkariPost.getBookmarksCurrentPageAfterUnbookmark = function(objid) {
	var currentPage = PokkariPost.getBookmarksCurrentPage(objid);
	var bookmarks = PokkariPost.getBookmarkCount(objid);

	if (bookmarks == 1 && currentPage > 1)
		return currentPage - 1;
	else
		return currentPage;
}

PokkariPost.updateBookmarksOrder = function() {
	PokkariPost.updateBookmarks('/bookmarks/update_order','mini_bookmarks_list','update_bookmark_order');
}

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

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

function PokkariDiscussion() {

	this.baseURL = '/comment/post/';
	this.url = '';
	this.comments = new Array();
	this.form = '';

}

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

PokkariDiscussion.prototype.onConstruct = function() {
	this.readCurrentComments();
}

PokkariDiscussion.prototype.readCurrentComments = function() {
	var divs = document.getElementsByClassName('comment',this.element);
	for(var i = 0; i < divs.length; i++) {
		if(divs[i].className == "comment") {
			this.comments.push(divs[i]);
		}
	}
}

PokkariDiscussion.prototype.addComment = function(c) {
	var div = document.createElement('div');
	div.className = "comment";
	div.id = "comment_" + c.id;

	var body = document.createElement('div');
	body.className = "comment_body";
	body.innerHTML = c.body;

	var meta = document.createElement('div');
	meta.className = "comment_meta";
	if(c.meta) {
		meta.innerHTML = c.meta;
	} 
	else {
		if (c.login == "Anonymous") {
			meta.innerHTML = '&#151;&nbsp; Anonymous User posted just now';
		}
		else {
			meta.innerHTML = '&#151;&nbsp;' + c.login + ' from <a href="' + c.userlink + '">' + c.displayname + '</a>' + ' posted just now';
		}
	}

	div.appendChild(body);
	div.appendChild(meta);

	this.comments.push(div);

	div.style.filter = "alpha(opacity=0)";
	this.element.appendChild(div);

	if(typeof(YAHOO.util.Anim) != 'undefined') {
		var fadeIn = new YAHOO.util.Anim(div, { opacity: {to: 1} }, 1);
		fadeIn.animate();
		if($('no_comments')) {
			var fadeOut = new YAHOO.util.Anim($('no_comments'), { opacity: {to: 0} }, 1);
			fadeOut.animate();
			$('no_comments').style.display = 'none';
		}
	} else {
		if($('no_comments')) {
			$('no_comments').style.display = 'none';
		}
	}
}

PokkariDiscussion.prototype.postComment = function(obj) {

	this.form = obj.form;
	
	var submission = Form.serialize(obj.form);
	submission += '&skin=xmlhttprequest';

	if($('comment_submit')) {
		$('comment_submit').value = "Posting your comment...";
		$('comment_submit').disabled = true;
	}

	this.url =  this.baseURL + "?" + submission;
	// this.method = "POST";
	this.send(submission);
}

PokkariDiscussion.prototype.resetForm = function() {
	if(this.form) {
		this.resetFormCaptcha();
		this.resetFormSubmit();
		for(var i = 0; i < document.getElementsByClassName('view_comment_input',this.form).length; i++) {
			document.getElementsByClassName('view_comment_input',this.form)[i].value = '';
		}
	}
}

PokkariDiscussion.prototype.resetFormSubmit = function() {
	if(this.form) {
		document.getElementsByClassName('comment_submit',this.form)[0].value = "Post Your Comment";
		document.getElementsByClassName('comment_submit',this.form)[0].disabled = false;
	}
}

PokkariDiscussion.prototype.resetFormCaptcha = function() {
	$('captcha_includer').innerHTML = '';
	$('captchas_response').value = '';
	PokkariCaptcha.appendChildTo($('captcha_includer'));
}

PokkariDiscussion.prototype.onsuccess = function() {
	if(this.getResponseXml()) {
		var r = this.getResponseXml();
		if(r.getElementsByTagName('notice').length > 0) {
			alert(r.getElementsByTagName('notice')[0].firstChild.nodeValue);
			this.resetFormCaptcha();
			this.resetFormSubmit();
		} else {
		this.addComment({
			body : r.getElementsByTagName('body')[0].firstChild.nodeValue, 
			id : r.getElementsByTagName('id')[0].firstChild.nodeValue, 
			displayname : r.getElementsByTagName('displayname')[0].firstChild.nodeValue, 
			login : r.getElementsByTagName('login')[0].firstChild.nodeValue,
			userlink : r.getElementsByTagName('userlink')[0].firstChild.nodeValue
		});

		this.resetForm();
		}
	}
}

PokkariDiscussion.prototype.onfailure = function() {
	alert("There was a problem posting your comment.  Please try again in a moment.");
	this.resetFormCaptcha();
	this.resetFormSubmit();
}
function PokkariForm(params) {

}

// Set this up a subclass of PokkariElement..
PokkariForm.prototype = new PokkariElement();
PokkariForm.prototype.constructor = PokkariForm;
/**
 *	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') ||
						lis[i].childNodes[ii].getAttribute('ontabactivation')
					)
				) {
					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); this.PokkariElementTabber.memorializeInCookies(this.getAttribute('tabelementpartner'));");
		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';
		} else if(anchors[i].getAttribute('ontabactivation')) {
			anchors[i].className = anchors[i].className.replace(/(\s+)?active(\s+)?/,'');
			anchors[i].parentNode.className = anchors[i].parentNode.className.replace(/(\s+)?active(\s+)?/,'');
		}
	}
}

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

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

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

PokkariElementTabber.prototype.performSwitch = function(anchor) {

	this.hideTabElementPartners();
	
	if(anchor.getAttribute('tabelementpartner')) {
		this.setActiveTabElementPartner(anchor.getAttribute('tabelementpartner'),anchor.getAttribute('tabelementcallback'));
	} else if(anchor.getAttribute('ontabactivation')) { 
		eval(anchor.getAttribute('ontabactivation')); 
	}

	anchor.className += " active";
	anchor.parentNode.className += " active";
	anchor.blur();

}

PokkariElementTabber.prototype.memorializeInCookies = function(selectedId) {

	if(typeof(Pokkari.SetCookiePreference) == "function") {
		var anchors = this.getAnchors();
		if(this.name) {
			Pokkari.SetCookiePreference({
				name : 'ts_' + this.name + '_hs',
				value : 1
			});
		}
		for(var i = 0; i < anchors.length; i++) {
			if(anchors[i].getAttribute('tabelementpartner') == selectedId) {
				Pokkari.SetCookiePreference({
					name : 'tab_' + anchors[i].getAttribute('tabelementpartner') + '_s',
					value : 1
				});
			} else {
				Pokkari.SetCookiePreference({
					name : 'tab_' + anchors[i].getAttribute('tabelementpartner') + '_s',
					value : 0
				});
			}
		}
	}
}

/**
 *	PokkariPlayer class
 * 	subclasses @PokkariElement
 *	For writing of video player inline
 *	$Header: /usr/local/cvsroot/otter/html/scripts/pokkariJavascript.js,v 1.116 2009/07/29 20:23:49 allan Exp $
*/

if (typeof(PokkariPlayerOptions) == "undefined") {
	var PokkariPlayerOptions = {
		maxWidth: 480,
		maxHeight: 600,
		useShowPlayer: true,
		showPlayerOptions: {
			smallPlayerMode: true
		},
		useDocumentWrite: false,
		useScanScoutShim: false,
		baristaEndpoint: "http://barista.blip.tv:9393/barista/resolve",
		forceAspectWidth: false
	};
}

function PokkariPlayer(params) {
	this.wmode = "transparent";
}

PokkariPlayer.VERSION = '$Revision: 1.116 $';

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

PokkariPlayer.setMaxWidth = function(m) {
	PokkariPlayerOptions.maxWidth = m;
}

PokkariPlayer.setMaxHeight = function(m) {
	PokkariPlayerOptions.maxHeight = m;
}

PokkariPlayer.initializeLegacyOptions = function() {
	if (typeof(PokkariPlayer.MAX_WIDTH) != "undefined")
		PokkariPlayerOptions.maxWidth = PokkariPlayer.MAX_WIDTH;

	if (typeof(PokkariPlayer.MAX_HEIGHT) != "undefined")
		PokkariPlayerOptions.maxHeight = PokkariPlayer.MAX_HEIGHT;

	if (typeof(PokkariPlayer.USE_SHOWPLAYER) != "undefined")
		PokkariPlayerOptions.useShowPlayer = PokkariPlayer.USE_SHOWPLAYER;

	if (typeof(PokkariPlayer.SHOWPLAYER_OPTIONS) != "undefined")
		PokkariPlayerOptions.showPlayerOptions = PokkariPlayer.SHOWPLAYER_OPTIONS;

	if (typeof(PokkariPlayer.USE_DOCUMENT_WRITE) != "undefined")
		PokkariPlayerOptions.useDocumentWrite = PokkariPlayer.USE_DOCUMENT_WRITE;

	if (typeof(PokkariPlayer.USE_SS_SHIM) != "undefined")
		PokkariPlayerOptions.useScanScoutShim = PokkariPlayer.USE_SS_SHIM;

	if (typeof(PokkariPlayer.BARISTA_ENDPOINT) != "undefined")
		PokkariPlayerOptions.baristaEndpoint = PokkariPlayer.BARISTA_ENDPOINT;
}

PokkariPlayer.eventContext = new Object();

PokkariPlayer.prototype.storeContext = function() {
	var key = (new Date()).getTime() + "-" + Math.floor(Math.random() * 1000);

	// TODO -- Write a method that periodically removes context past a certain age...

	PokkariPlayer.eventContext[key] = this;

	return key;
}

PokkariPlayer.retreiveContext = function(key) {
	var self = PokkariPlayer.eventContext[key];

	delete PokkariPlayer.eventContext[key];

	return self;
}

PokkariPlayer.prototype.destroy = function() {
}

PokkariPlayer.prototype.setSiteUrl = function(url) {
	this.site_url = url;
}

PokkariPlayer.prototype.getSiteUrl = function() {
	if(this.site_url) {
		return this.site_url;
	} else {
		var url = new Url(window.location.href);
		return "http://" + url.getServer();
	}
}

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

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

PokkariPlayer.prototype.setPostsId = function(id) {
	this.posts_id = id;
}

PokkariPlayer.prototype.getPostsId = function() {
	return this.posts_id;
}

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

PokkariPlayer.prototype.calculateResizedDimensions = function(width,height,targetWidth,targetHeight) {
	if (width>targetWidth || height>targetHeight) {
		var thisAspect = width/height;
		var targetAspect = targetWidth/targetHeight;
		var newWidth;
		var newHeight;

		if (thisAspect>=targetAspect || PokkariPlayerOptions.forceAspectWidth) {
			newWidth = targetWidth;
			newHeight = Math.floor(height * (targetWidth/width));
			if (this.controlsHeight) {
				newHeight += this.controlsHeight;
			}
		}
		else {
			newHeight = targetHeight;
			newWidth = Math.floor(width * (targetHeight/height));
		}

		return { width: newWidth, height: newHeight };
	}
	else {
		return { width: width, height: height };
	}
}

PokkariPlayer.prototype.getDimensions = function() {
	var width = Math.floor(this.width || 320);
	var height = Math.floor(this.height || 240);

	if (this.controlsHeight)
		height = height + this.controlsHeight;

	var dimensions = this.calculateResizedDimensions(width,height,PokkariPlayerOptions.maxWidth,PokkariPlayerOptions.maxHeight);
	return dimensions;
}


/**
* 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() {
	var dimensions = this.getDimensions();

	return dimensions.width;
}

PokkariPlayer.prototype.getPlayerWidth = function() {
	return this.getWidth();
}

PokkariPlayer.prototype.setHeight = function(height) {

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

	this.height = parseInt(height);
}

PokkariPlayer.prototype.getHeight = function() {
	var dimensions = this.getDimensions();

	return dimensions.height;
}

PokkariPlayer.prototype.getPlayerHeight = function() {
	return this.getHeight();
}

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() {
	var embedId = this.getEmbedId();
	var objectId = this.getObjectId();

	return document.getElementById(embedId) ||
		document.getElementById(objectId);
}

PokkariPlayer.prototype.generateId = function(stub) {
	var name = stub;
	var i = 0;
	while (document.getElementById(name) && i<100)
		name = stub + (i++);

	return name;
}

PokkariPlayer.prototype.getObjectId = function() {
	if (!this.objectId) {
		this.objectId = this.generateId("video_player_object");
	}

	return this.objectId;
}

PokkariPlayer.prototype.getEmbedId = function() {
	if (!this.embedId) {
		this.embedId = this.generateId("video_player_embed");
	}

	return this.embedId;
}

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

	if (typeof(player) != "undefined" && player) {
		if (typeof(player.object) != "undefined" && typeof(player.object.CurrentPosition) != "undefined") {
			return player.object.CurrentPosition;
		}
		if (typeof(player.controls) != "undefined") {
			return player.controls.currentPosition;
		}
		else if (typeof(player.GetTime) != "undefined") {
			return player.GetTime() / player.GetTimeScale();
		}
		else if (typeof(player.GetVariable) != "undefined") {
			return player.GetVariable("videoCurrentTime");
		}
	}

	return this.getRenderedTime();
}

PokkariPlayer.prototype.getRenderedTime = function() {
	if (this.renderTime) {
		var t = new Date();

		var time = (t.getTime() - this.renderTime.getTime()) / 1000;

		return time;
	}

	return null;
}

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

PokkariPlayer.prototype.getDuration = function() {
	// TODO FIXME Ad player specific implementation.
	return this.duration;
}

PokkariPlayer.prototype.setDuration = function(value) {
	this.duration = value;
}

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

	if (typeof(player) != "undefined" && player) {
		if (typeof(player.playState) != "undefined") {
			return PokkariWindowsPlayer.prototype.getStatus.apply(this);
		}
		else if (typeof(player.GetPluginStatus) != "undefined") {
			return PokkariQuicktimePlayer.prototype.getStatus.apply(this);
		}
		else if (typeof(player.GetVariable) != "undefined") {
			return PokkariFlashPlayer.prototype.getStatus.apply(this);
		}
	}

	var time = this.getTime();
	var duration = this.getDuration();

	if (time && duration) {
		return (time < duration) ? "Playing" : "Played";
	}

	return null;
}

PokkariPlayer.prototype.setStatus = function(value) {
	var player = this.getPlayer();

	if (typeof(player) != "undefined" && player) {
		if (typeof(player.playState) != "undefined") {
			return PokkariWindowsPlayer.prototype.setStatus.apply(this,arguments);
		}
		else if (typeof(player.GetPluginStatus) != "undefined") {
			return PokkariQuicktimePlayer.prototype.setStatus.apply(this,arguments);
		}
		else if (typeof(player.GetVariable) != "undefined") {
			return PokkariFlashPlayer.prototype.setStatus.apply(this,arguments);
		}
	}

	return null;
}

PokkariPlayer.prototype.pause = function() {
	this.setStatus("Paused");
}

PokkariPlayer.prototype.play = function() {
	this.setStatus("Playing");
}

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.isAdProvidedBy = function(provider) {
	if (typeof(this.adType) == "string") {
		provider = provider.toLowerCase();
		var parts = this.adType.toLowerCase().split(/\s*,\s*/);
		for (var i=0; i<parts.length; i++) {
			if (parts[i] && parts[i] == provider)
				return true;
		}
	}

	return false;
}

PokkariPlayer.prototype.setPrerollAnimationUrl = function(value) {
	this.prerollAnimationUrl = value;
}

PokkariPlayer.prototype.getPrerollAnimationUrl = function() {
	return this.prerollAnimationUrl;
}

PokkariPlayer.prototype.setPostrollAnimationUrl = function(value) {
	this.postrollAnimationUrl = value;
}

PokkariPlayer.prototype.getPostrollAnimationUrl = function() {
	return this.postrollAnimationUrl;
}

PokkariPlayer.prototype.getPostsTitle = function() {
	return this.postsTitle;
}

PokkariPlayer.prototype.setPostsTitle = function(value) {
	this.postsTitle = value;
}

PokkariPlayer.prototype.getUsersId = function() {
	return this.usersId;
}

PokkariPlayer.prototype.setUsersId = function(value) {
	this.usersId = value;
}

PokkariPlayer.prototype.getUsersLogin = function() {
	return this.usersLogin;
}

PokkariPlayer.prototype.setUsersLogin = function(value) {
	this.usersLogin = value;
}

PokkariPlayer.prototype.getUsersFeedUrl = function() {
	return this.usersFeedUrl;
}

PokkariPlayer.prototype.setUsersFeedUrl = function(value) {
	this.usersFeedUrl = value;
}

PokkariPlayer.prototype.getTopics = function() {
	return this.topics;
}

PokkariPlayer.prototype.setTopics = function(value) {
	this.topics = value;
}

PokkariPlayer.prototype.getGuid = function() {
	return this.guid;
}

PokkariPlayer.prototype.setGuid = function(value) {
	this.guid = value;
}

PokkariPlayer.prototype.setThumbnail = function(value) {
	this.thumbnail = value;
}

PokkariPlayer.prototype.getThumbnail = function() {
	return this.thumbnail;
}

PokkariPlayer.prototype.getDescription = function() {
	return this.description;
}

PokkariPlayer.prototype.setDescription = function(value) {
	this.description = value;
}

PokkariPlayer.prototype.getWmode = function(value) {
	return this.wmode;
}

PokkariPlayer.prototype.setWmode = function(value) {
	this.wmode = value;
}

PokkariPlayer.prototype.setContentRating = function(value) {
	this.contentRating = value;
}

PokkariPlayer.prototype.getContentRating = function() {
	return this.contentRating;
}

PokkariPlayer.prototype.initializeFromPost = function(post) {
	this.setPrimaryMediaUrl(post.media.url);
    this.setPermalinkUrl(post.url);
    this.setAdvertisingType(post.advertising);
    this.setPostsId(post.postsId);
    this.setUsersId(post.usersId);
    this.setUsersLogin(post.login);
    this.setPostsTitle(post.title);
    this.setGuid(post.postsGuid);
	this.setDescription(post.description);
	this.setContentRating(post.contentRating);
    if (post.topics) {
    	this.setTopics(post.topics.join(', '));
    }

    if (post.media.width && post.media.height) {
    	this.setWidth(post.media.width);
    	this.setHeight(post.media.height);
    }
    else {
    	this.setWidth(320);
    	this.setHeight(240);
    }
}

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.getDocumentWidth = function(d) {
	if (!d) d = document;

	if (d.body && typeof(d.body.clientWidth != "undefined"))
		return d.body.clientWidth;
	else if (d.documentElement && typeof(d.documentElement.clientWidth != "undefined"))
		return d.documentElement.clientWidth;
	else if (typeof(window.innerWidth) != "undefined")
		return window.innerWidth;
}

PokkariPlayer.prototype.getDocumentHeight = function(d) {
	if (!d) d = document;

	if (d.body && typeof(d.body.clientHeight != "undefined"))
		return d.body.clientHeight;
	else if (d.documentElement && typeof(d.documentElement.clientHeight != "undefined"))
		return d.documentElement.clientHeight;
	else if (typeof(window.innerHeight) != "undefined")
		return window.innerHeight;
}

PokkariPlayer.prototype.openFullscreen = function() {
	var width = window.screen.availWidth;
	var height = window.screen.availHeight;

	this.getPlayerTarget().innerHTML = "<b>Full-screen mode, make sure pop-ups aren't blocked</b>";

	this.fullWindow = window.open("","fullWindow","top=0,left=0,status=0,toolbar=0,titlebar=0,resizable=0,location=0,fullscreen=1,directories=0,width="+width+",height="+height);
	this.fullWindow.document.open("text/html","replace");
	this.fullWindow.document.write("<html><head><title>" + document.title +
		" (Full Screen)</title></head><body style='padding:0; margin:0; background-color: black;'>" +
		"<table width='100%' height='100%'><tr><td align='center' valign='middle'>" +
		"<div id='video_player' style='margin:auto'></div>" +
		"</td></tr></table></body></html>"
	);
	this.fullWindow.document.close();
	this.fullWindow.document.title = document.title + " (Full Screen)";
	var div = this.fullWindow.document.getElementById('video_player');
	this.setPlayerTarget(div);

	this.setFullscreenSize(this.fullWindow.document);
	this.setAutoPlay(true);

	this.render();
}

PokkariPlayer.prototype.setFullscreenSize = function(d)
{
	var width = this.getWidth();
	var height = this.getHeight();
	var doc_width = this.getDocumentWidth(d);
	var doc_height = this.getDocumentHeight(d);
	var aspect = width/height;

	// Safari won't let me set height = 100%, so gotta make up some number...
	if (doc_height < 100) {
		doc_height = window.screen.availHeight - 30;
	}

	if (!d) d = document;

	width = doc_width - (20 * aspect);
	height = Math.round(width / aspect);

	if (width < height || height > doc_height) {
		height = doc_height - 20;
		width = Math.round(height*aspect);
	}

	PokkariPlayerOptions.maxWidth = width;
	PokkariPlayerOptions.maxHeight = height;
	this.setWidth(width);
	this.setHeight(height);
}

PokkariPlayer.prototype.getHtml = 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.getPlayerWidth() + '" height="' + this.getPlayerHeight() + '" scale="aspect" EnableJavaScript="true" ></embed>';

	return html;
}

PokkariPlayer.prototype.render = function() {
	var html = this.getHtml();

	/*
        if (this.isAdProvidedBy("adjacent_blip")) {
		html += this.getBlipHtml();
	}
	else if (this.isAdProvidedBy("adjacent_scanscout") && !this.isAdProvidedBy("overlay_google")) {
		html += this.getScanScoutHtml();
       }*/

	if (PokkariPlayerOptions.useDocumentWrite) {
		document.write(html);
		var context = this.storeContext();
		var func = new Function("PokkariPlayer.retreiveContext('"+context+"').onRendered()");
		if (window.attachEvent) {
			window.attachEvent("onload",func);
		}
		else if (window.addEventListener) {
			window.addEventListener("load",func,true);
		}
	}
	else {
		this.getPlayerTarget().style.width = this.getPlayerWidth() + "px";
		this.getPlayerTarget().style.height = this.getPlayerHeight() + "px";
		this.getPlayerTarget().innerHTML = html;
		this.onRendered();
	}
}

PokkariPlayer.prototype.getScanScoutHtml = function() {
	this.hasScanScoutHtml = true;

	var html = '' +
	'<div id="ss_ads" style="height:60px">' +
	'</div>';

	return html;
}

PokkariPlayer.prototype.getBlipHtml = function() {
	var html = '<div id="blip_adPresentation" style="width:320px; display:none; padding:auto; margin:auto; text-align:center"></div>';

	this.hasBlipHtml = true;

	return html;
}

PokkariPlayer.prototype.getBlipHtmlCallback = function(params) {
	var element = document.getElementById("blip_adPresentation");
	var obj = params[0];
	element.innerHTML = obj.html;
	//this.adjustSizeForBlip();
	element.style.display = "block";

	// RE-adjust for variable height banners
	var context = this.storeContext();
	window.setTimeout("PokkariPlayer.retreiveContext('"+context+"').adjustSizeForBlip()",100);
}

PokkariPlayer.prototype.adjustSizeForScanScout = function() {
	var height = parseInt(this.getHeight());

	var newHeight = height + 80;

	this.getPlayerTarget().style.height = newHeight + "px";
}

PokkariPlayer.prototype.adjustSizeForBlip = function() {
	var element = document.getElementById("blip_adPresentation");
	var width = parseInt(this.getWidth());
	var height = parseInt(this.getHeight());
	var bannerHeight = element && element.offsetHeight ? element.offsetHeight : 30;
	var newWidth = (width > 320) ? width : 320;
	var newHeight = height + bannerHeight + 30;
	this.getPlayerTarget().style.width = newWidth + "px";
	this.getPlayerTarget().style.height = newHeight + "px";
}

PokkariPlayer.prototype.ss_play = function() {
	// We're outside of context here.   Use ss' context.
	var self = ss_videoPlayer;
	return self.play();
}

PokkariPlayer.prototype.ss_pause = function() {
	// We're outside of context here.   Use ss' context.
	var self = ss_videoPlayer;
	return self.pause();
}

PokkariPlayer.prototype.onRendered = function() {
	if (this.isAdProvidedBy("adjacent_scanscout") && this.hasScanScoutHtml) {
		if (self.ss_isLoaded == true) {
			this.adjustSizeForScanScout();

			self.ss_externalId = this.getGuid();
			self.ss_contentTitle = this.getPostsTitle();
			self.ss_contentDescription = this.getDescription();
			self.ss_contentKeywords = this.getTopics();
			self.ss_contentCategories = 'Default Category'; // TODO FIXME
			self.ss_partnerId = 970;
			self.ss_resumeVideoFunction = this.ss_play;
			self.ss_pauseVideoFunction = this.ss_pause;
			self.ss_getVideoPlayPositionFunction = ss_getPlayPositionPokkari;
			self.ss_getPlayStateFunction = ss_getPlayStatePokkari;
			self.ss_videoPlayer = this;
			self.ss_videoPlayerWidth = this.getWidth();
			self.ss_videoPlayerHeight = this.getHeight();
			self.ss_videoControlsHeight = this.controlsHeight;
			self.ss_videoAdMargin = 0;
			self.ss_adType = 'ticker';
			self.ss_playerId = this.getPlayer().id;
			self.ss_playerType = 'flashint';
			self.ss_contentURL = this.getPrimaryMediaUrl();
			self.ss_contentRating = this.getContentRating();

			if (self.ss_videoPlayerWidth == null ||
				self.ss_videoPlayerWidth == 0
			) {
				self.ss_videoPlayerWidth = 320;
			}

			if (self.ss_videoPlayerHeight == null ||
				self.ss_videoPlayerHeight == 0
			) {
				self.ss_videoPlayerHeight = 240 + self.ss_videoControlsHeight;
			}
			ss_start();
		}
	}
	else if (this.hasBlipHtml) {
		var barista = this.getBaristaUrl("adjacent","","blip","code","getBlipHtmlCallback");

		if (document.all && document.readyState && document.readyState != "complete") {
			document.write("<script type='text/javascript' src='" + barista + "'></script>");
		}
		else {
			var script = document.createElement("script");
			script.type = 'text/javascript';
			script.src = barista;
			var body = document.body || document.documentElement;
			body.appendChild(script);
		}
	}

	this.renderTime = new Date();
}


PokkariPlayer.prototype.getBaristaUrl = function(phase,format,source,flavor,callback,err_callback) {
	var url = new Url(window.location.href);
	var server = url.getServer();
	var parts = server.split('.');
	var domain = parts[parts.length-2] + "." + parts[parts.length-1];

	var barista = (PokkariPlayerOptions.baristaEndpoint || "http://barista." + domain + ":9393/barista/resolve/") +
		"?post_id=" + this.getPostsId() + "&format=" + format + "&phase=" + phase +
		"&source=" + source + "&flavor=" + flavor;

	if (typeof(callback) != "undefined") {
		var key = this.storeContext();

		barista += "&callback=" + escape("PokkariPlayer.retreiveContext('" + key + "')." + callback);

		if (typeof(err_callback) != "undefined") {
			barista += "&err_callback=" + escape("PokkariPlayer.retreiveContext('" + key + "')." + err_callback);
		}
		else {
			barista += "&err_callback=void";
		}
	}

	return barista;
}

PokkariPlayer.ChangeInstanceByMimeType = function(obj,type) {
	var newobj = PokkariPlayer.GetInstanceByMimeType(type);

	if (obj && newobj) {
		for (var i in obj) {
			if (typeof(obj[i]) == "function") {
				delete obj[i];
			}
		}
		for (var i in newobj) {
			if (typeof(newobj[i]) == "function") {
				obj[i] = newobj[i];
			}
		}
	}

	return obj;
}

PokkariPlayer.GetInstanceByMimeType = function(type) {
	PokkariPlayer.initializeLegacyOptions();
	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-dv':
			return new PokkariQuicktimePlayer();
		break;
		case 'video/x-flv':
			return PokkariPlayerOptions.useShowPlayer ? new PokkariShowPlayer() : new PokkariFlashPlayer();
		break;
		case 'video/x-flv,video/flv':
			return PokkariPlayerOptions.useShowPlayer ? new PokkariShowPlayer() : new PokkariFlashPlayer();
		break;
		case 'video/flv':
			return PokkariPlayerOptions.useShowPlayer ? new PokkariShowPlayer() : new PokkariFlashPlayer();
		break;
		case 'video/f4v,video/mp4':
			return new PokkariShowPlayer();
		break;
		case 'video/f4v':
			return new PokkariShowPlayer();
		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 'video/msvideo':
			return new PokkariWindowsPlayer();
		break;
		case 'application/ogg':
			return new PokkariTheoraPlayer();
		break;
		case 'video/theora':
			return new PokkariTheoraPlayer();
		break;
		case 'video/vnd.objectvideo':
			return new PokkariQuicktimePlayer();
		break;
		case 'image/jpeg':
			return new PokkariImagePlayer();
		break;
		case 'image/png':
			return new PokkariImagePlayer();
		break;
		case 'image/bmp':
			return new PokkariImagePlayer();
		break;
		case 'image/gif':
			return new PokkariImagePlayer();
		break;
		case 'audio/mpeg':
			return new PokkariMp3Player();
		break;
		case 'application/x-pando':
			return new PokkariPandoPlayer();
		break;
		case 'application/pando':
			return new PokkariPandoPlayer();
		break;
		default:
			return new PokkariPlayer();
		break;
        }

	return new PokkariPlayer();
}

function PokkariWindowsPlayer(params) {
	this.controlsHeight = document.all ? 26 : 40;
}

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

PokkariWindowsPlayer.prototype.getPrimaryMediaUrl = function() {

	if (window.navigator.platform == "Win32" &&
		(this.isAdProvidedBy("postroll_postroller") ||
		this.getPrerollAnimationUrl() ||
		this.getPostrollAnimationUrl()))
	{

		var filename = /\/file\/get\/(.*?)\?/;
		var results = filename.exec(PokkariPlayer.prototype.getPrimaryMediaUrl.apply(this,[]));
		var url = new Url(this.getPermalinkUrl());
		url.setQueryParam("skin","asx");
		url.setQueryParam("filename",results[1]);
		url.setQueryParam("preurl",this.getPrerollAnimationUrl());
		url.setQueryParam("posturl",this.getPostrollAnimationUrl());
		return url.getUrl();
	}
	else {
		return PokkariPlayer.prototype.getPrimaryMediaUrl.apply(this,[]);
	}
}

PokkariWindowsPlayer.prototype.getHtml = 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 objectId = this.getObjectId();
	var embedId = this.getEmbedId();

	var html = '<object id="' + objectId + '" width="' + this.getPlayerWidth() + '" height="' + this.getPlayerHeight() + '" 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="' + embedId + '" name="'+ embedId + '" displaysize="4" autosize="-1" bgcolor="darkblue" showcontrols="true" showtracker="-1" showdisplay="0" showstatusbar="-1" videoborder3d="-1" width="' + this.getPlayerWidth() + '" height="' + this.getPlayerHeight() + '" src="' + this.getPrimaryMediaUrl() + '" autostart="' + autoPlay + '" designtimesp="5311" loop="false"';
	if (typeof(this.startTime) != "undefined" && this.startTime) {
		html += ' currentPosition="'+this.startTime+'"';
	}

	html += '></embed>';
	html += '</object>';

	return html;
}

/*
PokkariWindowsPlayer.prototype.render = function() {
	var html = this.getHtml();

	this.getPlayerTarget().style.width = this.getPlayerWidth() + "px";
	this.getPlayerTarget().style.height = this.getPlayerHeight() + "px";

	this.getPlayerTarget().innerHTML = html;

	this.onRendered();
}
*/

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

	if (typeof(player) != "undefined") {
		if (typeof(player.object) != "undefined")
			player.object.CurrentPosition = s;
		else if (typeof(player.controls) != "undefined")
			player.controls.currentPosition = s;
	}
}

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

	if (typeof(player) != "undefined") {
		if (typeof(player.object) != "undefined")
			return player.object.CurrentPosition;
		else if (typeof(player.controls) != "undefined")
			return player.controls.currentPosition;
	}

	return null;
}

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

	if (typeof(player) != "undefined") {
		if (typeof(player.currentMedia) != "undefined")
			return player.currentMedia.duration;
		else if (typeof(player.object) != "undefined")
			return player.object.Duration;
	}

	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 (typeof(player) != "undefined") {
		var state;

		if (typeof(player.object) != "undefined")
			state = player.object.PlayState;
		else if (typeof(player.playState) != "undefined")
			state = player.playState;

		if (state == 1 || state == 0)
			return "Paused";
		else if (state == 2)
			return "Playing";
		else if (state == 5)
			return "Loading";
		else if (state == 6)
			return "Waiting";
		else if (state == 7)
			return "Played";
		else if (state == 9)
			return "Complete";
		else
			return "Undefined";
	}
	else {
		return "Unsupported";
	}
}

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

	if (typeof(player) != "undefined") {
		try {
			if (value == "Playing") {
				if (typeof(player.play) != "undefined") {
					player.play();
					return value;
				}
				if (typeof(player.controls) != "undefined" &&
					typeof(player.controls.play) != "undefined"
				) {
					player.controls.play();
					return value;
				}
				if (typeof(player.object) != "undefined" &&
					typeof(player.object.controls) != "undefined" &&
					typeof(player.object.controls.play) != "undefined"
				) {
					player.object.controls.play();
					return value;
				}
			}
			else if (value == "Paused") {
				if (typeof(player.pause) != "undefined") {
					player.pause();
					return value;
				}
				if (typeof(player.controls) != "undefined" &&
					typeof(player.controls.pause) != "undefined"
				) {
					player.controls.pause();
					return value;
				}
				if (typeof(player.object) != "undefined" &&
					typeof(player.object.controls) != "undefined" &&
					typeof(player.object.controls.pause) != "undefined"
				) {
					player.object.controls.pause();
					return value;
				}
			}
		}
		catch (err) {}
	}

	return null;
}

function PokkariQuicktimePlayer(params) {
	// Backwards Compatibility
	if (this.MAX_WIDTH)
		PokkariPlayerOptions.maxWidth=this.MAX_WIDTH;

	this.controlsHeight = 20;
}

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

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.getStatus = function() {
	var player = this.getPlayer();

	if (player && typeof(player.GetPluginStatus) != "undefined") {
		var status = player.GetPluginStatus();

		if (status == "Complete" || status == "Completed" || status == "Playable") {

			var time = player.GetTime();
			var totalTime = player.GetDuration();
			var rate = player.GetRate();

			if (time >= totalTime)
				return "Played";
			else if (rate > 0)
				return "Playing";
			else if (rate == 0)
				return "Paused";
			else
				return status;
		}
		else
			return status;
	}

	return "Undefined";
}

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

	if (player) {
		if (value == "Playing" && typeof(player.Play) != "undefined") {
			player.Play();
			return value;
		}
		else if (value == "Paused" && typeof(player.Stop) != "undefined") {
			player.Stop();
			return value;
		}
	}

	return null;
}

PokkariQuicktimePlayer.prototype.getHtml = 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 objectId = this.getObjectId();
	var embedId = this.getEmbedId();

	var html = '<object id="' + objectId + '" classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" width="' + this.getPlayerWidth() +
		'" height="' + this.getPlayerHeight() + '" 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="uimode" value="full">';
	html += '<param name="scale" value="aspect">';

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

	html += '</object>';

	return html;
}

/*
PokkariQuicktimePlayer.prototype.render = function() {
	var html = this.getHtml();

	this.getPlayerTarget().style.width = this.getPlayerWidth() + "px";
	this.getPlayerTarget().style.height = this.getPlayerHeight() + "px";

	this.getPlayerTarget().innerHTML = html;

	this.onRendered();
}
*/

function PokkariImagePlayer(params) {
}

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

PokkariImagePlayer.prototype.getHtml = function() {
	var html = '<img src="' + this.getPrimaryMediaUrl() + '" width="' + this.getWidth() + '" height="' + this.getHeight() + '" />';

	return html;
}

PokkariImagePlayer.prototype.render = function() {
	if (!this.duration) {
		this.duration = 15;
	}

	PokkariPlayer.prototype.render.apply(this);
}

//PokkariImagePlayer.prototype.getStatus = function() {
//	return "Played";
//}

function PokkariFlashPlayer(params) {
	// Backwards Compatibility
	if (this.MAX_WIDTH)
		PokkariPlayerOptions.maxWidth=this.MAX_WIDTH;
}

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

PokkariFlashPlayer.prototype.getPlayerHeight = function() {
	var height = PokkariPlayer.prototype.getPlayerHeight.apply(this);

	return parseInt(height)+20;
}

PokkariFlashPlayer.prototype.getHtml = function() {
	var autoPlay = (this.getAutoPlay()) ? '1' : '0';
	var query = "file=" + escape(this.getPrimaryMediaUrl());

	if (this.getAutoPlay()) {
		query += "&autoStart=1";
	}

    if (this.getUsersId() == 12877) {
		if (this.getPostsId() >= 123933 && this.getPostsId() <= 126990) {
			query += "&trailerMovie=http://www.blip.tv/scripts/flash/starrring.swf";
		}
		else if (this.getPostsId() >= 131118 && this.getPostsId() <= 132751) {
			query += "&trailerMovie=http://www.blip.tv/scripts/flash/starrring-video1.swf";
		}
		else if (this.getPostsId() == 134504) {
			query += "&trailerMovie=http://www.blip.tv/scripts/flash/starrring-video2.swf";
		}
		else if (this.getPostsId() > 134504 && this.getPostsId() <= 196949) {
			query += "&trailerMovie=http://www.blip.tv/scripts/flash/starrring-paltalk-slate.swf";
		}
	}
	else if (this.isAdProvidedBy("postroll_blip")) {
		query += "&trailerMovie=" + escape(this.getBaristaUrl("post","flv","blip","url") + "&ext=.swf");

		if (this.getUsersId() == 1 || this.getUsersId() == 48245) {
			query += "&trailerResize=0";
		}
	}
	else if (this.isAdProvidedBy("postroll_author")) {
		query += "&trailerMovie=" + escape(this.getBaristaUrl("post","flv","author","post"));
	}
	else if (this.isAdProvidedBy("postroll_postroller")) {
		query += "&trailerMovie=http://1048.btrll.com/ad/12060%3Fvid%3D" + this.getPostsId() + "%26sid%3D" + this.getUsersId() +
			"%26kw%3D" + escape(this.getTopics()) + "%26type%3D.swf";
	}
	else if (this.isAdProvidedBy("postroll_immense")) {
		query += "&adProvider=immense";
	}
	else if (this.isAdProvidedBy("postroll_google")) {
		query += "&adProvider=google&guid=" + this.getPostsId();
	}
	else if (this.getPostrollAnimationUrl()) {
		query += "&trailerMovie=" + this.getPostrollAnimationUrl();
	}

	if (this.getPrerollAnimationUrl()) {
		query += "&thumbNail=" + this.getPrerollAnimationUrl();
	}
	if (this.getUsersId()) {
		query += "&userId=" + escape(this.getUsersId());
	}
	if (this.getUsersLogin()) {
		query += "&userLogin=" + escape(this.getUsersLogin());
	}
	if(this.getPostsTitle()) {
		query += "&postsTitle=" + escape(this.getPostsTitle());
	}
	if(this.getPostsId()) {
		query += "&postsId=" + escape(this.getPostsId());
	}
	if(this.getTopics()) {
		query += "&topics=" + escape(this.getTopics());
	}
	if (this.getThumbnail()) {
		query += "&thumbNail=" + escape(this.getThumbnail());
	}

	if (document.all) {
		query += "&cacheBuster=" + Math.floor(Math.random() * 10000);
	}

	var objectId = this.getObjectId();
	var embedId = this.getEmbedId();

	var html = '<object id="' + objectId + '" type="application/x-shockwave-flash" width="' + this.getPlayerWidth() + '" height="' + this.getPlayerHeight() + '"';

	if (this.getWmode()) {
		html += ' wmode="' + this.getWmode() + '"';
	}

	html += ' data="' + this.getSiteUrl() + '/scripts/flash/blipplayer.swf?' + query + '">';
	html += '<param name="movie" value="' + this.getSiteUrl() +
		'/scripts/flash/blipplayer.swf?' + query + '">';
	html += '<param name="flashvars" value="' + query + '" />';
	html += '<param name="allowScriptAccess" value="always" />';

	if (this.getWmode()) {
		html += '<param name="wmode" value="' + this.getWmode() + '" />';
	}
	html += '</object>';

	return html;
}

PokkariFlashPlayer.prototype.render = function() {

	var hasProductInstall = DetectFlashVer(6, 0, 65);
	var hasRequestedVersion = DetectFlashVer(8, 0, 0);

//	if(hasProductInstall && hasRequestedVersion) {
	if (true) {
		PokkariPlayer.prototype.render.apply(this);
	} 
	else {
		var html = '<div class="flash_error_msg">'
		+ '<div class="user_info_title">Ooops</div>'
		+ '<p>If you\'d like to see this video you\'ll need to install <a href="http://www.adobe.com/go/getflashplayer/">Macromedia Flash Player 8</a> (please note that we require Flash 8, and an installation of an earlier version like Flash 7 will not work).</p>'
		+ '<a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" /></a>'
		+ '<p>If you feel you\'ve reached this message in error, please <a href="mailto:support@blip.tv">let us know</a>.</p>'
		+ '</div></div>';
		if (PokkariPlayerOptions.useDocumentWrite) {
			document.write(html);
		}
		else {
			this.getPlayerTarget().innerHTML = html;
		}
	}
}

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

	try {
		if (player && typeof(player.GetVariable) != "undefined") {
			return player.GetVariable("_root.videoCurrentTime");
		}
	}
	catch (err) {}

	return null;
}

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

	try {
		if (player && typeof(player.SetVariable) != "undefined") {
			player.SetVariable("_root.videoSetCurrentTime",value);
		}
	}
	catch (err) {}
}

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

	try {
		if (player && typeof(player.GetVariable) != "undefined") {
			return player.GetVariable("_root.videoTotalTime");
		}
	}
	catch (err) {}

	return null;
}

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

	try {
		if (player && typeof(player.GetVariable) != "undefined") {
			var status = player.GetVariable("_root.currentVideoStatus");

			return status || "Undefined";
		}
	}
	catch (err) {}

	return "Undefined";
}

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

	try {
		if (player && typeof(player.SetVariable) != "undefined") {
			player.SetVariable("_root.setCurrentVideoStatus",value);
			return value;
		}
	}
	catch (err) {}

	return null;
}

function PokkariTheoraPlayer(params) {
	this.controlsHeight = 20;
}

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

PokkariTheoraPlayer.prototype.getHtml = function() {
		var url = this.getPrimaryMediaUrl();
	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="/cortado.jar" />\n' +
		'<param name="archive" value="/cortado.jar" />\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="/cortado.jar"\n' +
		'archive="/cortado.jar"\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';

	return html;
}
/*
PokkariTheoraPlayer.prototype.render = function() {
	var html = this.getHtml();

	this.getPlayerTarget().innerHTML = html;

	this.onRendered();
}
*/

function PokkariMp3Player(params) {
}

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

PokkariMp3Player.prototype.getHtml = function() {
	var autoPlay = (this.getAutoPlay()) ? 'true' : 'false';
	var query = "song_url=" + escape(this.getPrimaryMediaUrl()) + "&autoload=" + autoPlay + "&song_title=" + escape(this.getPostsTitle());

	var objectId = this.getObjectId();
	var embedId = this.getEmbedId();

	var html = '<object id="' + objectId + '" type="application/x-shockwave-flash" width="' + this.getPlayerWidth() + '" height="' + this.getPlayerHeight() + '" wmode="transparent" data="' + this.getSiteUrl() + '/scripts/flash/blipmp3player.swf?' + query + '">';
	html += '<param name="movie" value="' + this.getSiteUrl() + '/scripts/flash/blipmp3player.swf?' + query + '">';
	html += '<param name="flashvars" value="' + query + '" />';
	html += '<param name="wmode" value="transparent" />';
	html += '</object>';

	return html;
}

PokkariMp3Player.prototype.render = function() {
	var html = this.getHtml();

	if (PokkariPlayerOptions.useDocumentWrite) {
		document.write(html);
	}
	else {
		this.getPlayerTarget().innerHTML = html;
		this.onRendered();
	}

}

function PokkariShowPlayer() {
	this.controlsHeight = 30;
}
PokkariShowPlayer.prototype = new PokkariPlayer();
PokkariShowPlayer.prototype.constructor = PokkariShowPlayer;
PokkariShowPlayer.prototype.superclass = PokkariPlayer;

PokkariShowPlayer.prototype.render = function() {
	PokkariPlayer.prototype.render.apply(this,[]);

	if (typeof(window.video_player_object) == "undefined") {
		// The showplayer erroneously refers to the player like this..
		window.video_player_object = this.getPlayer();
	}
}

PokkariShowPlayer.prototype.getFileUrl = function() {
	var file = this.getSiteUrl() + "/rss/flash/" + this.getPostsId();
	var media = this.getPrimaryMediaUrl();

	var result = Url.ReplaceQuery(file,Url.GetQuery(media));

	return result;
}

PokkariShowPlayer.prototype.getHtml = function() {
	var playerUrl = (PokkariPlayerOptions.showPlayerOptions && PokkariPlayerOptions.showPlayerOptions.playerUrl) ||
		(this.getSiteUrl() + "/scripts/flash/showplayer.swf");

	var url = new Url(playerUrl);

	url.setQueryParam("file", this.getFileUrl());
	url.setQueryParam("enablejs","true");
	url.setQueryParam("showplayerpath",playerUrl);

	if (this.getAutoPlay()) {
		url.setQueryParam("autostart","true");
	}
	else if (this.getThumbnail()) {
		url.setQueryParam("thumb",this.getThumbnail());
	}

	if (this.getUsersFeedUrl()) {
		url.setQueryParam("feedurl",this.getUsersFeedUrl());
	}

	this.includeOptions(url,PokkariPlayerOptions.showPlayerOptions);

	var width = this.getPlayerWidth();
	var height = this.getPlayerHeight();
	var objectId = this.getObjectId();
	var embedId = this.getEmbedId();
	var wmode = "opaque";

	if (PokkariPlayerOptions.useScanScoutShim) {
		wmode = (navigator.userAgent.indexOf("Safari") > -1 &&
			this.isAdProvidedBy("adjacent_scanscout") &&
			this.hasScanScoutHtml) ? "opaque" : "window";
	}

	var html='\n<object id="' + objectId +'" type="application/x-shockwave-flash" ' +
		'width="' + width + '" ' +
		'height="' + height + '" ' +
		'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' +
		'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" ' +
		'allowfullscreen="true" allowscriptaccess="always">\n' +
		'\t<param name="allowfullscreen" value="true" />\n'+
		'\t<param name="allowscriptaccess" value="always" />\n'+
		'\t<param name="wmode" value="'+wmode+'" />\n' +
		'\t<param name="movie" value="' + url.getUrl() + '" />\n' +
		'\t<param name="quality" value="best" />\n' +
		'\t<embed width="' + width + '" height="' + height + '"\n' +
		'\t\ttype="application/x-shockwave-flash" wmode="'+wmode+'"\n' +
		'\t\tallowscriptaccess="always" allowfullscreen="true"\n' +
		'\t\tpluginspage="http://www.macromedia.com/go/getflashplayer"\n' +
		'\t\tsrc="' + url.getUrl() + '" id="' + embedId +'" />' +
		'</object>\n';

	return html;
}

PokkariShowPlayer.prototype.getTime = function() {
	return PokkariShowPlayer.currentTime || 0;
}

PokkariShowPlayer.prototype.getStatus = function() {
	switch (PokkariShowPlayer.currentState) {
		case 1:
			return "Complete";
		case 3:
			return "Complete";
		case 2:
			return "Playing";
		case 0:
			return "Paused";
		default:
			return "Playing";
	}
}

PokkariShowPlayer.prototype.setStatus = function(value) {
	var player = this.getPlayer();

	if (value == "Playing") {
		player.sendEvent("play");
	}
	else if (value == "Paused") {
		player.sendEvent("pause");
	}
}

PokkariShowPlayer.prototype.ss_pause = function() {
	// We're outside of context here.   Use ss' context.
	var self = ss_videoPlayer;
	self.pause();

	// Need to give ss time to build and display EAU
	if (PokkariPlayerOptions.useScanScoutShim) {
		var context = self.storeContext();
		window.setTimeout("PokkariPlayer.retreiveContext('"+context+"').hidePlayerForScanScout();",100);
	}
}

PokkariShowPlayer.prototype.ss_play = function() {
	// We're outside of context here.   Use ss' context.
	var self = ss_videoPlayer;
	self.play();

	if (PokkariPlayerOptions.useScanScoutShim)
		self.showPlayerForScanScout();
}

PokkariShowPlayer.prototype.hidePlayerForScanScout = function() {
	var eau = document.getElementById("EAU");
	var player = this.getPlayer();

	if (eau && navigator.userAgent.indexOf("Safari") == -1) {
			player.style.visibility = "hidden";
	}
}

PokkariShowPlayer.prototype.showPlayerForScanScout = function() {
	var player = this.getPlayer();

	if (navigator.userAgent.indexOf("Safari") == -1) {
		player.style.visibility = "visible";
	}
}

PokkariShowPlayer.prototype.includeOptions = function(url,options) {
	if (typeof(options) == "string") {
		options = Url.ParseQuery(options);
	}

	if (typeof(options) == "object") {
		for (var i in options) {
			if (/^string|number|boolean$/.test(typeof(options[i]))) {
				url.setQueryParam(i,options[i]);
			}
		}
	}
}

function getUpdate(type, arg1, arg2) {
	switch (type) {
		case "time":
			PokkariShowPlayer.currentTime = arg1;
		break;
		case "state":
			PokkariShowPlayer.currentState = arg1;
		break;
	}
}

function PokkariPandoPlayer() {
}

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

PokkariPandoPlayer.prototype.getHtml = function() {
	var html = "\n<div class='Pando' style='border: 1px solid #ddd; padding: 30px;'><div class='PandoLogo' style='text-align:center; margin-bottom:25px;'>" +
		"<a href='http://www.pando.com/'><img src='" + this.getSiteUrl() + "/skin/blipnew/partners/pando.png' border='0' /></a>" +
		"</div>\n<div class='PandoText' style='width:450px; margin:auto'>" + 
		"<div class='PandoButton' style='float:left; margin-right: 10px'><a href='" + this.getPrimaryMediaUrl() + "'";
		
	if (!this.hasPando()) {
		html += " target='_blank'";
	}

	html += "><img src='http://rss.pando.com/themes/rssvideo/pando_dl_button2.png' border='0' /></a></div>\n" +
		"This video is available via <a href='http://www.pando.com/'>Pando</a>.  Use the button on the " +
		"left begin your download.</div>\n<div class='Clear'> </div>\n</div>\n";

	return html;
}

PokkariPandoPlayer.prototype.hasPando = function() {
	return PokkariPandoPlayer.hasPando && true;
}

PokkariPandoPlayer.prototype.setPandoUrl = function(url) {
	this.pandoUrl = url;
}

PokkariPandoPlayer.prototype.getPrimaryMediaUrl = function() {
	var url = this.pandoUrl || PokkariPlayer.prototype.getPrimaryMediaUrl.apply(this,[]);
	return PokkariPandoPlayer.getDownloadUrl(url);
}

PokkariPandoPlayer.initialize = function(callback) {
	if (typeof(PokkariPandoPlayer.hasPando) == "undefined") {
		if (/hasPando=/.test(document.cookie)) {
			PokkariPandoPlayer.hasPando = document.cookie.replace(/.*hasPando=([^;]+).*/,"$1");
		}
		else {
			if (callback) {
				PokkariPandoPlayer.initializeCallbackCallback = callback;
			}

			document.write("<script type='text/javascript' src='http://cache.pando.com/soapservices/PandoAPI.js'></script>");

			// I know this seems weird, since we're already in javascript, but we want to make sure these things happen in order...
			document.write("<script>PandoAPI.hasPando(PokkariPandoPlayer.initializeCallback);</script>");

			// Set a 3 second backup timer so the callback for sure fires.
			PokkariPandoPlayer.backupTimer = window.setTimeout("PokkariPandoPlayer.initializeCallback(false);",3000);
		}
	}
}

PokkariPandoPlayer.initializeCallback = function(hasPando) {
	if (PokkariPandoPlayer.backupTimer) {
		window.clearTimeout(PokkariPandoPlayer.backupTimer);
	}

	PokkariPandoPlayer.hasPando = hasPando && true;

	var date = new Date();
	date.setTime(date.getTime()+(365*24*60*60*1000));
	document.cookie = "hasPando=" + PokkariPandoPlayer.hasPando + "; domain=.blip.tv; path=/; expires=" + date.toGMTString();

	if (PokkariPandoPlayer.initializeCallbackCallback) {
		PokkariPandoPlayer.initializeCallbackCallback();
	}
}

PokkariPandoPlayer.getDownloadUrl = function(url) {
	if (PokkariPandoPlayer.hasPando) {
		return PokkariPandoPlayer.getPandoDownloadUrl(url);
	}
	else {
		return PokkariPandoPlayer.getPandoInstallDownloadUrl(url);
	}
}

PokkariPandoPlayer.getPandoDownloadUrl = function(url) {
	return "pando:download?" + url;
}

PokkariPandoPlayer.getPandoInstallDownloadUrl = function(url) {
	if (/^http:\/\/cache.pando.com\//.test(url)) {
		var u = new Url(url);
		var pkg = u.getQueryParam('id');
		var key = u.getQueryParam('key');

		return "http://www.pando.com/link/preinstall?package=" + pkg + "&key=" + key + "&referer=" + window.location.href;
	}
	else {
		return "http://www.pando.com/download?packageURL=" + escape(url);
	}
}

PokkariPandoPlayer.subscribe = function(url,title) {
	window.location.href = PokkariPandoPlayer.getSubscribeUrl(url,title);
}

PokkariPandoPlayer.getSubscribeUrl = function(url,title) {
	if (PokkariPandoPlayer.hasPando) {
		return PokkariPandoPlayer.getPandoSubscribeUrl(url,title);
	}
	else {
		return PokkariPandoPlayer.getPandoInstallSubscribeUrl(url,title);
	}
}

PokkariPandoPlayer.getPandoSubscribeUrl = function(url,title) {
	return "pando:subscribe?" + url;
}

PokkariPandoPlayer.getPandoInstallSubscribeUrl = function(url,title) {
	return "http://www.pando.com/subscribe?rss=" + escape(url) + "&title=" + escape(title);
}

PokkariPandoPlayer.prototype.render = function() {
	if (typeof(PokkariPandoPlayer.hasPando) == "undefined") {
		var self = this;
		PokkariPandoPlayer.initialize(function() { self.render(); });
	}
	else {
		PokkariPlayer.prototype.render.apply(this,[]);
	}
}
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_';
	
	// Defined modes: half (lrlrlrlrlr), whole (llllllllll), scale (lllllrrrrr)
	if (this.mode) {
		this.mode = this.mode.toLowerCase();
	}
	else {
		this.mode = 'half';
	}

	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.getSide = function(i) 
{
	if (this.mode == 'scale') {
		return (i<5) ? this.imageSides[0] : this.imageSides[1];
	}
	else if (this.mode == 'half') {
		return this.imageSides[(i % 2)];
	}
	else {
		return this.imageSides[0];
	}
}

PokkariRating.prototype.setStarColor = function(i,c) 
{
	var side = this.getSide(i);
	var color = this.imageColors[c];

	if (this.stars[i])
		this.stars[i].src = PokkariRating.preloadedImages[color+"_"+side].src;
}

PokkariRating.prototype.renderScaleOver = function(rating) 
{
	if (rating == null)
	{
		for(var i=0; i<10; i++)
			this.setStarColor(i,0);
	}
	else if (rating<6)
	{
		for(var i=1; i<rating; i++)
			this.setStarColor(i-1,0);
			
		for (var i=rating; i<=5; i++) 
			this.setStarColor(i-1,1);
			
		for (var i=6; i<=10; i++)
			this.setStarColor(i-1,0);
	}
    else 
	{
		for(var i=1; i<=5; i++)
			this.setStarColor(i-1,0);

		for (var i=6; i<=rating; i++)
			this.setStarColor(i-1,1);

		for (var i=rating+1; i<=10; i++)
			this.setStarColor(i-1,0);
	}
}

PokkariRating.prototype.renderScaleOut = function() 
{
	var rating = this.currentRating;

	if (rating != null)
		rating = Math.round(this.currentRating);

	this.renderScaleOver(rating);
}

PokkariRating.prototype.renderNormalOver = function(rating) 
{
	var myRating = rating;
	var siteRating = this.currentRating || 0;
	var color = null;
	var element = null;
	
	for(var x=1; x<=myRating; x++) {
		if (this.imageColors.length == 4)
			this.setStarColor(x-1,Math.round(siteRating) >= x ? 3 : 2);
		else
			this.setStarColor(x-1,1);
		
		//element = this.stars[x-1];
		//element.src = PokkariRating.preloadedImages[color+"_"+side].src;
	}

	if (this.imageColors.length < 4) {
		for(var x=myRating+1; x<=this.stars.length; x++) {
			//side = this.getSide(x);
			//color = this.imageColors[0];
		
			//element = this.stars[x-1];
			//element.src = PokkariRating.preloadedImages[color+"_"+side].src;
			this.setStarColor(x-1,0);
		}
	}
}

PokkariRating.prototype.renderNormalOut = function(rating)
{
	var siteRating = this.currentRating || 0;
	var side = null;
	var color = null;
	var element = null;
	var x;
	for(x=1; x<=this.stars.length; x++) 
	{
		this.setStarColor(x-1,Math.round(siteRating) >= x ? 1 : 0);
		//side = this.getSide(x);
		//color = Math.round(siteRating) >= x ? this.imageColors[1] : this.imageColors[0];
		
		//element = this.stars[x-1];
		//element.src = PokkariRating.preloadedImages[color+"_"+side].src;
	}
}

PokkariRating.prototype.renderMessageOver = function(rating) 
{
	if (this.messages) 
	{
		this.messageElement.innerHTML = this.messages[rating-1];
	}
}

PokkariRating.prototype.renderMessageOut = function() 
{
	this.messageElement.innerHTML = this.messageElement.oldHTML;
}

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

	if (self.mode == 'scale') 
	{
		self.renderScaleOver(sender.rating);
	}
	else 
	{
		self.renderNormalOver(sender.rating);
	}
	
	if (self.messageElement)
	{
		self.renderMessageOver(sender.rating);
	}
}

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

	if (self.mode == 'scale')
	{
		self.renderScaleOut();
	}
	else 
	{
		self.renderNormalOut();
	}
	
	if (self.messageElement)
	{
		self.renderMessageOut();
	}
}

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.myRating = myRating;
		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';
		}

		if (typeof(this.onVoted) == "function")
		{
			this.onVoted(this,request,null);
		}
		else if (typeof(this.onVoted) == "string")
		{
			var func = new Function("self","sender","e",this.onVoted);
			func(this,request,null);
		}
	}
}

PokkariRating.prototype.preload = function()
{
	var img;
	var colors = this.imageColors;
	var sides = this.imageSides;
	var ext = this.imageExtension;
	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.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.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 body = topNode.getElementsByTagName("body")[0].firstChild.nodeValue;

	this.paint(body);
}

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

		this.element.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() + "&output_method=dom"; 
	
	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.onChange = function () {
	alert("Hellooooo!");
}

TemplateEditor.prototype.onConstruct = function() {

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

	if(this.element.tagName.toLowerCase().match(/select/)) {
		try {
			this.element.addEventListener('change',new Function("this.pokkariElement.updatePreview(); this.pokkariElement.onTemplateChange();"),false);
		} catch(e) {
			this.element.onchange = new Function("this.pokkariElement.updatePreview(); this.pokkariElement.onTemplateChange();");
		}
	}

	if(document.getElementById('share_tags')) {
		document.getElementById('share_tags').TemplateEditor = this.element;
		try {
			$('share_tags').addEventListener('change',new Function("this.TemplateEditor.pokkariElement.updatePreview();"),false);
		} catch(e) {
			$('share_tags').onchange = 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').onchange = 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').onchange = 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').onchange = new Function("this.TemplateEditor.pokkariElement.updatePreview();");	
		}
		this.ctp_text = $('ctp_text');
	}

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

	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.onTemplateChange = function() {
	if(this.element.tagName.toLowerCase().match(/select/)) {
		var isLegacy = this.element.options[this.element.selectedIndex].getAttribute("legacy");

		var warnings = document.getElementsByClassName('legacy_warning');
		for(var i = 0; i < warnings.length; i++) {
			if(isLegacy) {
				warnings[i].style.display = '';
			} else {
				warnings[i].style.display = 'none';
			}
		}

		var advancedHelp = document.getElementsByClassName('advanced_help');

		for(var i = 0; i < advancedHelp.length; i++) {
			if(isLegacy) {
				advancedHelp[i].style.display = 'none';	
			} else {
				advancedHelp[i].style.display = '';
			}
		}

		var advancedOptions = document.getElementsByClassName('advanced_option');

		for(var i = 0; i < advancedOptions.length; i++) {
			if(isLegacy) {
				advancedOptions[i].disabled = true;
			} else {
				advancedOptions[i].disabled = false;
			}
		}
	}
}

TemplateEditor.prototype.updatePreview = function() {

	if(this.element.tagName.toLowerCase().match(/select/)) {
		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);
	}

	if(this.preferred_file_type_name) {
		this.url += '&preferred_file_type_name='+escape(this.preferred_file_type_name.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]) {
			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) {
		if(this.suggestions[suggestionClass] && this.suggestions[suggestionClass]['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;
}
//<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 (this.focus && 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; 
}

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);
		if (element) {
			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);
		if (r)
			r.innerHTML = siteRating;
		var v = document.getElementById("Votes_"+type+id);
		if (v)
			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() {
	if (this.multipleKeys) 
	{
		var changes = Url.ParseQuery(this.element.value);
		var removes = this.removeKeys && this.removeKeys.split(/\s*,\s*/);

		this.changeMultipleParameters(changes,removes);
	}
	else 
	{
		this.changeQueryParameter({ key : this.key, value : this.element.value, remove: this.removeKeys });
	}
}

queryChanger.prototype.changeMultipleParameters = function(changes,removes)
{
	var url = new Url(window.location.href);

	if (removes)
	{
		for(var i=0; i<removes.length; i++)
		{
			url.removeQueryParam(removes[i]);
		}
	}

	if (changes)
	{
		for(var i in changes)
		{
			if (typeof(changes[i]) != "function")
				url.setQueryParam(i,changes[i]);
		}
	}

	if (this.changeRoot)
		window.location.href = Url.ReplaceQuery(this.changeRoot,Url.MakeQuery(url.query));
	else
		window.location.href = url.getUrl();
}

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

	if (params.remove) 
	{ 
		var removes = params.remove.split(/\s*,\s*/);
		for (var i=0; i<removes.length; i++)
			url.removeQueryParam(removes[i]);
	}

	if (params.key)
	{
		url.setQueryParam(params.key,params.value);
	}
	
	window.location.href = url.getUrl();
}

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) {
	if (value == '') {
		delete this.query[name];
	}
	else {
		this.query[name] = value;
	}
	this.urlDirty = true;
}

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

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

Url.prototype.getServer = function() {
    return Url.GetServer(this.url);
}

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

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" || typeof(object[i]) == "boolean") {
			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;
}

Url.ChangeQueryParam = function(url,param,value)
{
	url = new Url(url);
	url.setQueryParam(param,value);

	return url.getUrl();
}

Url.ChangeLocationQueryParam = function(param,value)
{
	window.location.href = Url.ChangeQueryParam(window.location.href,param,value);
}

Url.GetServer = function(url)
{
    var server = url.replace(/.*?:\/\/([\w\.\-]+).*/,"$1");

    return server;
}
// 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;
	}
}

var PokkariCaptcha = Class.create();
PokkariCaptcha.prototype = {
	initialize: function() {
		this.uuid = new UUID();
	},

	getHTML: function() {
		return "<input type='hidden' name='captchas_guid' value='" + this.uuid + "' />\n" +
			"<img src='/captchas/" + this.uuid + "' />\n";
	},
	
	write: function() {
		var html = this.getHTML();
		document.write(html);
	},

	captchaIsAppended: function(element) {
		// TODO FIXME Naive approach, use a className to make sure.
		var imgs = element.getElementsByTagName("img");
		return (imgs && imgs.length);
	},

	appendChildTo: function(element) {
		if (element && element.appendChild) {
			if (!this.captchaIsAppended(element)) {
				var hidden = document.createElement("input");
				hidden.type = "hidden";
				hidden.name = "captchas_guid";
				hidden.value = this.uuid;
				element.appendChild(hidden);

				var img = document.createElement('img');
				img.style.width = "172px";
				img.style.height = "64px";

				// Give IE time to figure out its DOM handling before showing the image.
				if (document.all) {
					img.src = "/blank.gif";
					window.captchaImage = img;
					window.setTimeout("window.captchaImage.src = '/captchas/"+this.uuid+"'; window.captchaImage = null;",50);
				}
				else {
					img.src = "/captchas/" + this.uuid;
				}

				element.appendChild(img);
			}
		}
	}
}

PokkariCaptcha.include = function() {
	var captcha = new PokkariCaptcha();
	captcha.write();
}

PokkariCaptcha.appendChildTo = function(element) {
	var captcha = new PokkariCaptcha();
	captcha.appendChildTo(element);
}
/*

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
Pokkari.includes("pokkariRelatedPosts");

Pokkari.requires("pokkariDom");
Pokkari.requires("prototype");
Pokkari.requires("safexss");

function PokkariRelatedPosts() {}

PokkariRelatedPosts.defaultThumbnailUrl = '/skin/blipnew/placeholder_video.gif';

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

Object.extend(PokkariRelatedPosts.prototype, 
{
	onLoadHandler: function() 
	{
		this.sendRequest();
	},

	sendRequest: function(url)
	{
		if (!url) { url = this.url; }
		this.url = url;

		this.element.innerHTML = "Loading...";

		this.request = new Safexss.Request(url, {
			onSuccess: this.eventHandler(this.request_success)
		});
	},
	
	request_success: function(self,sender,e,args)
	{
		var result = args[0];
		self.data = result.responseData;
		self.render();
	},
	
	render: function()
	{
		var posts = this.data;
	
		if (!posts.length)
		{
			this.element.innerHTML = "No matching posts found."; 
		}
		else {
			this.element.innerHTML = "";
			for(var i=0; i<posts.length; i++) {
				this.renderPost(posts[i]);
			}

			var url = new Url(this.url);
			url.removeQueryParam("skin");
			url.removeQueryParam("version");
			var div = document.createElement("div");
			div.style.textAlign = "right";
			div.innerHTML = "<a href='" + url.getUrl() + "'>Show More</a> &nbsp;";
			this.element.appendChild(div);
		}
	},

	renderPost: function(post)
	{
		var html = "";

		if (!post.thumbnailUrl) {
			post.thumbnailUrl = PokkariRelatedPosts.defaultThumbnailUrl;
		}

		var showName = post.showName || post.login;
		
		html += "<a href='" + post.url + "'><img src='" + post.thumbnailUrl + "' /></a>\n";
		html += "<div class='related_text'>\n";
		html += "\t<div class='related_title'><a href='" + post.url + "'>" + post.title + "</a></div>\n";
		html += "\t<div class='related_user'><a href='/users/view/" + escape(post.login) + "'>" + showName + "</a></div>\n";
		html += "\t<div class='related_description'><a href='" + post.url + "'>" + post.description + "</a></div>\n";
		html += "</div>\n";
		
		var div = document.createElement("div");
		div.className = "related_container";
		div.innerHTML = html;

		this.element.appendChild(div);
	}
});

var Safexss = {
	Version: '0.1',
  	prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
}

if((typeof Prototype=='undefined') || Safexss.prototypeVersion < 1.3)
      throw("Safexss requires the Prototype JavaScript framework >= 1.3");

Safexss.includeScript = function (url) {
	var script = document.createElement("script");
	script.type = 'text/javascript';
	script.src = url;
	var body = document.body || document.documentElement;
	body.appendChild(script);
	return script;
}

Safexss.Transport = Class.create();
Safexss.Transport.prototype = {
    initialize: function(options) {
        this.onreadystatechange = null;
        this.readyState = 0;
        this.responseText = "";
        this.responseXml = null;
        this.status = 0;
        this.statusText = "";
        this.translateUrl = Safexss.Transport.translateUrl;
        Object.extend(this,options||{});

        this.responseHeaders = {};
        // Set up context callback
        Safexss.Context.register(this);
    },
    abort: function() {
        throw new Error("Not implemented");
    },
    getAllResponseHeaders: function() {
        var result = "";
        for (var header in this.responseHeaders)
        {
            var pair = this.responseHeaders[header];
            result += pair[0] + ": " + pair[1] + "\n";
        }

        return result;
    },
    getResponseHeader: function(header) {
		var responseHeader = this.responseHeaders[header.toLowerCase()];

        return responseHeader && responseHeader[1];
    },
    setRequestHeader: function(header,value) {},
    setResponseHeader: function(header,value) {
        this.responseHeaders[header.toLowerCase()]=[header,value];
    },
    importAllResponseHeaders: function(headers) {
        for (var header in headers)
            this.setResponseHeader(header,headers[header]);
    },
    open: function(method,url,async,user,pass) {
        if (method.toLowerCase() != "get")
            throw new Error("Safexss.Transport only supports GET requests");

        // Create and initialize script object
    	this.script = document.createElement("script");
	    this.script.type = 'text/javascript';

	    this.script.src = this.translateUrl(url);
    },
    send: function(content) {
        // Append script object
        var body = document.body || document.documentElement;
        body.appendChild(this.script);

        // Update readyState
        this.changeReadyState(1);
    },
    dispatch: function(data,headers) {
        this.status = 200;
        this.statusText = "OK";
        this.responseData = data;
        if (typeof(data) == "string" || typeof(data) == "number")
        {
            this.responseText = data;
            this.responseXml = this.getDocument();
            this.setResponseHeader("Content-Type",(this.responseXml == null ||
                this.responseXml.documentElement == null ||
                this.responseXml.documentElement.tagName == "parsererror") ?
                    "text/plain" : "text/xml");
        }
        else
        {
            this.responseText = data.toJSONString();
            this.setResponseHeader("Content-Type","text/javascript");
        }

        if (typeof(headers) == "object")
            this.importAllResponseHeaders(headers);

        this.changeReadyState(4);
    },
    getDocument: function() {
        var text = this.responseText;
        return Try.these(
            function() {
                var doc = new DOMParser().parseFromString(text,"text/xml");
                return doc;
            },
            function() {
                var doc = new ActiveXObject("Microsoft.XMLDOM");
                doc.loadXML(text);
                return doc;
            }
        ) || false;
    },
    changeReadyState: function(state) {
        this.readyState = state;
        if (typeof(this.onreadystatechange) == "function")
            this.onreadystatechange();
    },
    dispatchError: function(code,text,headers) {
        this.status = code;
        this.statusText = text;
        if (typeof(headers)=="object")
            this.importAllResponseHeaders(headers);

        this.changeReadyState(4);
    }
}

Safexss.Transport.translateUrl = function(url) {
    return url + ((url.match(/\?/)) ? "&" : "?") +
        "callback=Safexss.callbacks%5B"+this.context+"%5D.dispatch";
}

Safexss.callbacks = $H({});

Safexss.Context = {
	register: function(transport) {
		for (var i=0; i<10; i++) {
			var context = this.generateContext();
			if (!Safexss.callbacks[context]) {
				transport.context = context;
				Safexss.callbacks[context] = transport;

				return context;
			}
		}
		throw new Error("Could not locate free space for context");
	},
    unregister: function(context) {
        Safexss.callbacks[context] = null;
        delete Safexss.callbacks[context];
    },
    transport: function(context) {
        if (Safexss.callbacks[context])
            return Safexss.callbacks[context];
        else
            throw new Error("No transport found for context");
    },
	dispatch: function(context, data) {
	    this.transport(context).dispatch(data);
	},
    dispatchError: function(context, code, text) {
        this.transport(context).dispatchError(code,text);
    },
	generateContext: function() {
        return Math.floor(Math.random()*900000)+100000;
	}
}

Safexss.Request = new Class.create();
Object.extend(Safexss.Request.prototype,Ajax.Request.prototype);
Safexss.Request.prototype.initialize = function(url, options) {
    this.transport = new Safexss.Transport();
    this.setOptions(Object.extend({ method:"get" },options||{}));
    this.request(url);
}
function PokkariJsonRequest(params) {
	if (params)
	{
		this.url = params.url;
	}

	this.method = "GET";
}

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

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

	// Fix the URL to make sure it's going to xmlhttprequest skin.
	if (!url.match(/skin=json/)) {
		if (!url.match(/\?/)) {
			url += "?skin=json";
		}
		else {
			url += "&skin=json";
		}
	}

	if (!url.match(/no-cache=1/)) {
		url += "&no-cache=1";
	}
	
	// Save our object so we can get it later.
	this.key = PokkariXmlRequest.StoreObject(this);

	// Create the request object, or throw an error.
	this.request = new Safexss.Transport();

	if (!this.request) {
		throw new Error("Could not create Safexss.Transport 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();
	}
	catch (exception) {
		alert("While sending JSON request: "+exception.message);
	}
}

PokkariJsonRequest.prototype.getResponseData = function() {
	return this.request.responseData;
}


function PokkariSwitch() {
	this.imgBase = new Array(2);
	this.imgOver = new Array(2);
	this.altText = new Array(2);
	this.state = -1;
}

PokkariSwitch.prototype = new PokkariElement;
PokkariSwitch.prototype.constructor = PokkariSwitch;

PokkariSwitch.prototype.onLoadHandler = function()
{
	this.image = this.element.getElementsByTagName("img")[0];

	Pokkari.AttachEvent(this.image,"click",
		this.eventHandler(this.onHandleClick));
	Pokkari.AttachEvent(this.image,"mouseover",
		this.eventHandler(this.onHandleMouseOver));
	Pokkari.AttachEvent(this.image,"mouseout",
		this.eventHandler(this.onHandleMouseOut));
}

PokkariSwitch.prototype.onHandleClick = function(self,sender,e)
{
	
	var currentState = self.currentState(sender);
	alert("State = " + currentState + " - " + sender.src);
	alert("Span innerhtml = " + self.innerHTML + " img inner = " + sender.innerHTML);
	self.changeState(sender);
	return true;
}

PokkariSwitch.prototype.onHandleMouseOver = function(self,sender,e) {
	sender.src = self.imgOver[self.currentState(sender)];
	return false;
}

PokkariSwitch.prototype.onHandleMouseOut = function(self,sender,e) {
	sender.src = self.imgBase[self.currentState(sender)];
	return false;
}

PokkariSwitch.prototype.currentState = function(sender) {
	if (this.state == -1) {
		if (sender.src.indexOf(this.imgBase[0]) == -1) { 
			this.state = 1;
		}
		else {
			this.state = 0;
		}
	}
	return this.state;
}

PokkariSwitch.prototype.changeState = function(image) {
	this.state = (this.state + 1) % 2;
	image.alt = this.altText[this.state];
	image.src = this.imgBase[this.state];
}

function PokkariPlaylistToggle(params) {
	if (params) {
		this.posts_id                   =       params.posts_id;
	}

	var baseUrl = Pokkari.skinUrl || '/skin/blipnew';

	this.imgBase[0] = baseUrl + "/view_plus.gif";
	this.imgOver[0] = baseUrl + "/view_plus_over.gif";
	this.imgBase[1] = baseUrl + "/view_minus.gif";
	this.imgOver[1] = baseUrl + "/view_minus_over.gif";
	this.altText[0] = "Add to playlist";
	this.altText[1] = "Remove from playlist";
}

PokkariPlaylistToggle.prototype = new PokkariSwitch;
PokkariPlaylistToggle.prototype.constructor = PokkariPlaylistToggle;


PokkariPlaylistToggle.prototype.onHandleClick = function(self,sender,e) {
	
	var currentState = self.currentState(sender);
	if (currentState == 0) {
		self.doBookmarkOp(self,sender,"add",self.posts_id);
	}
	else {
		self.doBookmarkOp(self,sender,"remove",self.posts_id);
	}
	return true;
}

PokkariPlaylistToggle.prototype.doBookmarkOp = function(self,image,op,post_id) {
	var request = new PokkariXmlRequest("/bookmarks/" + op);
	request.posts_id = post_id;
	request.bookmarker = self;
	request.url = "/bookmarks/" + op + "/?posts_id=" + post_id;
	request.onsuccess = PokkariPlaylistToggle.doneOperation;
	request.onfailure = PokkariPlaylistToggle.failedOperation;
	request.send();
}

PokkariPlaylistToggle.doneOperation = function(request) {
   request.bookmarker.changeState(request.bookmarker.image);
   var message = (request.bookmarker.currentState(request.bookmarker.image) == 0 ?
   				  "Removed from" : "Added to") + " playlist";
   PokkariUI.popMessage(request.bookmarker.image,message);
}

PokkariPlaylistToggle.failedOperation = function(request) {
	alert("Operation failed");
}

function PokkariSpinner() {
	this.peers = new Array();
	this.peerPanes = new Array();
	this.helpers = new Array();
}

PokkariSpinner.prototype = new PokkariElement;
PokkariSpinner.prototype.constructor = PokkariSpinner;

PokkariSpinner.openImageUrl = '/skin/blipnew/bulletArrowDown.gif';
PokkariSpinner.closedImageUrl = '/skin/blipnew/bulletArrowRight.gif';

PokkariSpinner.prototype.onLoadHandler = function()
{
	this.image = this.element.getElementsByTagName("img")[0];
	this.anchor = this.element.getElementsByTagName("a")[0];

	Pokkari.AttachEvent(this.image,"click",
		this.eventHandler(this.onHandleClick));
	Pokkari.AttachEvent(this.anchor,"click",
		this.eventHandler(this.onHandleClick));
}

PokkariSpinner.prototype.onHandleClick = function(self,sender,e)
{
	var panelElement = $(self.panel);
	if (panelElement.style.display == 'block') {
		self.hidePanel();
	}
	else {
		self.showPanel();
	}
}

PokkariSpinner.prototype.showPanel = function() {
	for(var i=0;i < this.peers.length;i++) {
		$(this.peers[i]).pokkariElement.hidePanel();
	}
	for(i=0;i < this.peerPanes.length;i++) {
		$(this.peerPanes[i]).style.display = 'none';
	}
	for(i=0;i < this.helpers.length;i++) {
		$(this.helpers[i]).style.display = 'none';
	}
	$(this.panel).style.display = 'block';
	this.openSpinner();
	this.registerState();
}

PokkariSpinner.prototype.hidePanel = function() {
	var panelElement = $(this.panel);
	panelElement.style.display = 'none';
	for(i=0;i < this.helpers.length;i++) {
		$(this.helpers[i]).style.display = 'block';
	}
	this.closeSpinner();
	this.registerState();
}

PokkariSpinner.prototype.openSpinner = function() {
	if (this.image) {
		this.image.style.backgroundImage = "url('" + PokkariSpinner.openImageUrl + "')";
	}
	if (this.opentitle) { this.setTitle(this.opentitle); }
	if (this.onopen) { eval(this.onopen); }
}

PokkariSpinner.prototype.closeSpinner = function() {
	if (this.image) {
		this.image.style.backgroundImage = "url('" + PokkariSpinner.closedImageUrl + "')";
	}
	if (this.closedtitle) { this.setTitle(this.closedtitle); }
}

PokkariSpinner.prototype.setTitle = function(newtitle) {
	 this.element.getElementsByTagName("a").item(0).innerHTML = newtitle;
}

PokkariSpinner.prototype.addPeer = function(peerid) {
	this.peers[this.peers.length] = peerid;
}

PokkariSpinner.prototype.addPeerPane = function(paneid) {
	this.peerPanes[this.peerPanes.length] = paneid;
}

PokkariSpinner.prototype.addHelper = function(helperid) {
	this.helpers[this.helpers.length] = helperid;
	Pokkari.AttachEvent($(helperid),"click",
		this.eventHandler(this.onHandleClick));
}

PokkariSpinner.prototype.registerState = function() {
	var state = ( $(this.panel).style.display == 'block' );
	var id = this.element.id;
	if (window.storageManager) {
		window.storageManager.setValue("blip_ui_" + id + "_state",state);
	}
}

PokkariSpinner.prototype.setOpenHandler = function(name) {
	this.onopen = name;
}

function PokkariSearchbar(params) {
}

PokkariSearchbar.prototype = new PokkariElement;
PokkariSearchbar.prototype.constructor = PokkariSearchbar;

PokkariSearchbar.navigate = function(params) {
	window.new_url = Pokkari.UrlReplaceExtended(params,{reset_pagination:1});

	if (/(?:language_code=|pagelen=(?!12)|media_type=|file_type=|license=|view=list|nsfw=)/.test(window.new_url) ||
		/users_prefs_show_nsfw=1/.test(document.cookie)
	) {
		if (/sort=/.test(window.new_url)) {
			window.new_url = window.new_url.replace(/(\/|s=)(?:popular|recent|random)/,'$1posts');
		}
		else {
			window.new_url = window.new_url.replace(/(\/|s=)(popular|recent|random)\/?(\?|\&)/,'$1posts$3sort=$2&').replace('sort=popular','sort=popularity').replace('sort=recent','sort=date');
		}
	}
	else if (!/\/shows\/?\??/.test(window.new_url)) {
		window.new_url =
			(/sort=popularity/.test(window.new_url) && '/popular') ||
			(/sort=date/.test(window.new_url) && '/recent') ||
			(/sort=random/.test(window.new_url) && '/random') ||
			'/popular';
	}

	setTimeout('window.location.href = window.new_url',0);
}

PokkariSearchbar.loadExtraPanel = function(url) {
	$('searchbar_control_pagelayout').pokkariElement.hidePanel();
	$('searchbar_extra').style.display = 'block';
	PokkariUI.animateLoad('searchbar_extra');
//	$('searchbar_extra').innerHTML = '<p><i>Loading...</i></p>';
	var catsAjax = new Ajax.Updater('searchbar_extra',url);
	window.storageManager.setValue('blip_ui_searchbar_extra_panel_contenturl',url);
	return false;
}

PokkariSearchbar.hideExtraPanel = function() {
	$('searchbar_extra').style.display = 'none';
    window.storageManager.setValue('blip_ui_searchbar_extra_panel_contenturl','');
	return false;
}

PokkariSearchbar.doSearch = function() {
	var text = $('searchbar_input').value;
	if (text == '') {
		alert("Please enter something to search for before clicking 'Search'");
	}
	else {
		PokkariSearchbar.navigate({search:text,page:1,cmd:'',topic_name:'',category:''});
	}
}

PokkariSearchbar.switchUrl = function(base) {
	var url = new Url(Pokkari.UrlReplaceExtended({page:1,cmd:'',s:''}));
	window.new_url = base + "?" + url.getQuery(url);
	setTimeout('window.location.href = window.new_url',0);
}

PokkariSearchbar.restoreState = function() {
	var storage = window.storageManager;
	if (storage) {
		var showPageLayoutPanel = storage.getValue('blip_ui_searchbar_control_pagelayout_state');
		var extraPanelContentURL = storage.getValue('blip_ui_searchbar_extra_panel_contenturl');

		if (showPageLayoutPanel) {
			$('searchbar_control_pagelayout').pokkariElement.showPanel();
		}
		if (extraPanelContentURL &&
			(showPageLayoutPanel != true)) {
			var new_url_base = ((window.location.href.indexOf("/shows") != -1) ?
						    	"shows" :
								"posts/view" );
			var nexturl = Pokkari.UrlReplaceExtended({url_base:new_url_base},
													 {},
													 new Url(extraPanelContentURL));
			PokkariSearchbar.loadExtraPanel(nexturl);
		}
	}
}
function PokkariPulldown() {
	this.items = new Array();
}

PokkariPulldown.prototype = new PokkariElement;
PokkariPulldown.prototype.constructor = PokkariPulldown;

PokkariPulldown.prototype.onLoadHandler = function() {
	this.initMenu();
}

PokkariPulldown.prototype.initMenu = function() {
	if (document.all && document.getElementById) {
		var node = this.element;
		node.onmouseover = function() { this.className+=" over"; }
		node.onmouseout = function() { this.className=this.className.replace(" over",""); }
	}
}

PokkariPulldown.prototype.addItem = function(title,key,action) {
	var theMenu = this.element.getElementsByTagName("ul")[0];
	var theItem = document.createElement("li");
	var theAnchor = document.createElement("a");
 	var theTitle = document.createTextNode(title);
	if (document.all && document.getElementById) {
		theAnchor.setAttribute("href","javascript:" + action);
	}
	else {
		theAnchor.setAttribute("href","javascript:void(0)");
		theAnchor.setAttribute("onclick",action);
	}
	theAnchor.appendChild(theTitle);
	theItem.appendChild(theAnchor);
	theMenu.appendChild(theItem);
	this.items[key] = title;
}

PokkariPulldown.prototype.setTitle = function(key) {
	var theElement = this.element;
	var newTitle;
	if (typeof(this.items[key]) == "undefined") {
		newTitle = this.title;
	}
	else {
		newTitle = this.items[key];
	}
	var theMenuTitle = this.element.getElementsByTagName("b")[0];
	var oldNode = theElement.getElementsByTagName("b")[0];
	var newNode = document.createElement("b");
	var newText = document.createTextNode(newTitle);
	newNode.appendChild(newText);
	theElement.replaceChild(newNode,oldNode);
}
function PokkariStorage() {
	this.storage = null;
	try { this.writeFlash(); }
	catch(err) { }
}

PokkariStorage.prototype = new PokkariElement;
PokkariStorage.prototype.constructor = PokkariStorage;

PokkariStorage.prototype.writeFlash = function() {
	document.writeln('<div style="display: block;">' +
			 '<object id="pokkaristorage" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="1" height="1" align="middle">' +
			'<param name="allowScriptAccess" value="sameDomain" \/>' +
			'<param name="movie" value="/scripts/flash/pokkariStorage.swf" \/>' +
			'<param name="quality" value="high" \/>' +
			'<param name="bgcolor" value="#ffffff" \/>' +
			'<embed src="/scripts/flash/pokkariStorage.swf" quality="high" bgcolor="#ffffff" width="1" height="1" id="pokkaristorage" name="pokkaristorage" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" \/>' +
			'<\/object>' +
			'<\/div>');
}

PokkariStorage.prototype.findStorage = function() {
	if (navigator.appName.indexOf("Microsoft") != -1) { return window['pokkaristorage'] }
	else { return document['pokkaristorage'] }
}

PokkariStorage.prototype.getValue = function(key) {
	try {
		if (this.storage == null) { this.storage = this.findStorage(); }
		var value = this.storage.getStoredProperty(key);
		if (value == "null") { return; }
		if (this.storage.isObject(key)) { return value.parseJSON(); }
		else { return value; }
	}
	catch(err) {}
}

PokkariStorage.prototype.getValueDefaulted = function(key,dfltval) {
	var val = this.getValue(key);
	if (typeof(val) == "undefined") { return dfltval; }
	else { return val; }
}

PokkariStorage.prototype.setValue = function(key,value) {
	try {
		if (this.storage == null) { this.storage = this.findStorage(); }
		var isObject = ((typeof(value) == 'object') ? 1 : 0 );
		if (isObject) { value = value.toJSONString(); }
		return this.storage.setStoredProperty(key,value,isObject);
	}
	catch(err) {}
}

function PokkariUI() {
}

PokkariUI.prototype = new PokkariElement;
PokkariUI.prototype.constructor = PokkariUI;

PokkariUI.animateLoad = function(id) {
	$(id).innerHTML = '<p><img src="/skin/blipnew/icons/animLoading.gif" width="16" height="16" alt="" /><\/p>';
}

PokkariUI.displayBlockingError = function(obj) {

	alert(obj.text);

	return false;
}

PokkariUI.confirmNavigation = function(anchor,obj) {

	window.navigation_confirmer = new YAHOO.widget.SimpleDialog("navigation_confirmer",
		{
			width: "300px",
			fixedcenter: true,
			visible: false,
			draggable: false,
			close: false,
			modal:true,
			text: obj.text,
			constraintoviewport: true,
			icon: YAHOO.widget.SimpleDialog.ICON_WARN,
			buttons: [
				{ text : "No, I'll do it later", handler : function() { this.hide(); } },
				{ text : "Yes, do it now", handler : function() { window.location.href = anchor.href; this.hide(); }  }
			]
		});

	window.navigation_confirmer.setHeader(obj.title);

	window.navigation_confirmer.render(document.body);
	window.navigation_confirmer.show();

	return false;
}

PokkariUI.popMessage = function(obj,text) {
	var rootdiv = document.getElementsByTagName("div").item(0);
	var msgdiv = document.createElement("div");
	msgdiv.appendChild(document.createTextNode(text));
	msgdiv.setAttribute("id",'popmsg');
	msgdiv.style.position = "absolute";
	msgdiv.style.top = (findPosY(obj) - 24) + "px";
	msgdiv.style.left = findPosX(obj) + "px";
	msgdiv.style.opacity = "0.0";
	rootdiv.appendChild(msgdiv);

	new Effect.Opacity('popmsg',
	    { duration: 0.3,
		  transition: Effect.Transitions.linear,
		  from: 0.0, to: 0.8 });
	top.setTimeout('PokkariUI.vanishMessage()',1300);
}

PokkariUI.vanishMessage = function() {
	new Effect.Opacity('popmsg',
						{ duration: 0.2,
				          transition: Effect.Transitions.linear,
						  from: 0.8, to: 0.0 });
	top.setTimeout("var div=$('popmsg');div.parentNode.removeChild(div)",300);
}

PokkariUI.toggleVisibility = function(id) {
	$(id).style.display = ( ($(id).style.display == 'block') ?  'none' : 'block' );
}

PokkariUI.revealHelp = function(obj,id) {
	$(id).style.display = ( ($(id).style.display == 'block') ? 'none' : 'block' );
	obj.blur();
}

PokkariUI.flipName = function(id,name1,name2) {
	var obj = $(id);
	if (obj.innerHTML == name1) {
		obj.innerHTML = name2;
	}
	else {
		obj.innerHTML = name1;
	}
}

PokkariUI.zebra = function(id) {
	var table = $(id);
	if (table) {
		var tbody = table.getElementsByTagName("tbody").item(0);
		var rows = tbody.getElementsByTagName("tr");
		for(var index = 0;index < rows.length;index++) {
			if (index % 2 == 1) { rows[index].className += ' odd'; }
		}
	}
}

PokkariUI.insertEmail = function(id,name,domain) {
    $(id).innerHTML = '<a href="mailto:' + name + '@' + domain + '">' +  name + '@' + domain + '</a>';
}

PokkariUI.initializeSelect = function(id,value) {
    var options = $(id).options;
    value = value.replace("&amp;","&");
    for(var i = 0;i < options.length;i++) {
       if (value == options[i].value) {
       	options[i].selected = true;
	break;
       }
    }
}


function FlashProxy(uid) {
	this.uid = uid;
}

FlashProxy.callJS = function() {
	var functionToCall = eval(arguments[0]);
	var argArray = new Array();
	for (var i = 1; i < arguments.length; ++i) {
		argArray.push(arguments[i]);
	}
	functionToCall.apply(functionToCall, argArray);
}

/**
 * Call a function in your Flash content.  Arguments should be:
 * 1. ActionScript function name to call,
 * 2. any number of additional arguments of type object,
 *    array, string, number, boolean, date, null, or undefined. 
 */

FlashProxy.prototype.call = function()
{

    if (arguments.length == 0)
    {
        throw new Exception("Flash Proxy Exception",
                            "The first argument should be the function name followed by any number of additional arguments.");
    }

    var qs = 'lcId=' + escape(this.uid) + '&functionName=' + escape(arguments[0]);

    if (arguments.length > 1)
    {
        var justArgs = new Array();
        for (var i = 1; i < arguments.length; ++i)
        {
            justArgs.push(arguments[i]);
        }
        qs += ('&' + this.flashSerializer.serialize(justArgs));
    }

    var divName = '_flash_proxy_' + this.uid;
    if(!document.getElementById(divName))
    {
        var newTarget = document.createElement("div");
        newTarget.id = divName;
        document.body.appendChild(newTarget);
    }
    var target = document.getElementById(divName);
    var ft = new FlashTag(this.proxySwfName, 1, 1);
    ft.setVersion('6,0,65,0');
    ft.setFlashvars(qs);
    target.innerHTML = ft.toString();
}

