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

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

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();// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)

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){
  element = $(element);
  var opacity;
  if (opacity = element.getStyle('opacity'))  
    return parseFloat(opacity);  
  if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))  
    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
  return 1.0;  
}

Element.setOpacity = function(element, value){  
  element= $(element);  
  if (value == 1){
    element.setStyle({ opacity: 
      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
      0.999999 : 1.0 });
    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
      element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
  } else {  
    if(value < 0.00001) value = 0;  
    element.setStyle({opacity: value});
    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
      element.setStyle(
        { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
            'alpha(opacity='+value*100+')' });  
  }
  return element;
}  
 
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), 40);
  },
  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();
    this.effects.invoke('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:        25.0,  // max. 25fps 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() {
    return '#<Effect:' + $H(this).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'
    });
  }
});

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 = {
      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] || {})
  );
}

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] || {});
    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.options.style.parseStyle().map(function(property){
      var originalValue = this.element.getStyle(property[0]);
      return $H({ 
        style: property[0], 
        originalValue: property[1].unit=='color' ? 
          parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: property[1].unit=='color' ? 
          parseColor(property[1].value) : property[1].value,
        unit: property[1].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 = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', 
  'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', 
  'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
  'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
  'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
  'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
  'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
  'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
  'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
  'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
  'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
  'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
  'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
  'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
  'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
  'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
  '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]; 
  });
  
  var result = $H();
  
  styleRules.each(function(pair){
    var property = pair[0], value = pair[1], unit = null;
    
    if(value.parseColor('#zzzzzz') != '#zzzzzz') {
      value = value.parseColor();
      unit  = 'color';
    } else if(Element.CSS_LENGTH.test(value)) 
      var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
    
    result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
  }.bind(this));
  
  return result;
};

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();// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)


if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = {}
Autocompleter.Base = function() {};
Autocompleter.Base.prototype = {
  baseInitialize: function(element, update, options) {
    this.element     = $(element); 
    this.update      = $(update);  
    this.hasFocus    = false; 
    this.changed     = false; 
    this.active      = false; 
    this.index       = 0;     
    this.entryCount  = 0;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || {};

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow || 
      function(element, update){ 
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false, 
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide || 
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string') 
      this.options.tokens = new Array(this.options.tokens);

    this.observer = null;
    
    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix && 
      (navigator.appVersion.indexOf('MSIE')>0) &&
      (navigator.userAgent.indexOf('Opera')<0) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update, 
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },
  
  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
      }
     else 
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer = 
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex) 
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },
  
  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },
  
  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;     
  }, 
  
  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ? 
          Element.addClassName(this.getEntry(i),"selected") : 
          Element.removeClassName(this.getEntry(i),"selected");
        
      if(this.hasFocus) { 
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },
  
  markPrevious: function() {
    if(this.index > 0) this.index--
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },
  
  markNext: function() {
    if(this.index < this.entryCount-1) this.index++
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },
  
  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },
  
  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },
  
  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    
    var lastTokenPos = this.findLastToken();
    if (lastTokenPos != -1) {
      var newValue = this.element.value.substr(0, lastTokenPos + 1);
      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value;
    } else {
      this.element.value = value;
    }
    this.element.focus();
    
    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount = 
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else { 
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;
      
      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;   
    if(this.getToken().length>=this.options.minChars) {
      this.startIndicator();
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
  },

  getToken: function() {
    var tokenPos = this.findLastToken();
    if (tokenPos != -1)
      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
    else
      var ret = this.element.value;

    return /\n/.test(ret) ? '' : ret;
  },

  findLastToken: function() {
    var lastTokenPos = -1;

    for (var i=0; i<this.options.tokens.length; i++) {
      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
      if (thisTokenPos > lastTokenPos)
        lastTokenPos = thisTokenPos;
    }
    return lastTokenPos;
  }
}

Ajax.Autocompleter = Class.create();
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    entry = encodeURIComponent(this.options.paramName) + '=' + 
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams) 
      this.options.parameters += '&' + this.options.defaultParams;

    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }

});


Autocompleter.Local = Class.create();
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || {});
  }
});


Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
}

Ajax.InPlaceEditor = Class.create();
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
Ajax.InPlaceEditor.prototype = {
  initialize: function(element, url, options) {
    this.url = url;
    this.element = $(element);

    this.options = Object.extend({
      paramName: "value",
      okButton: true,
      okText: "ok",
      cancelLink: true,
      cancelText: "cancel",
      savingText: "Saving...",
      clickToEditText: "Click to edit",
      okText: "ok",
      rows: 1,
      onComplete: function(transport, element) {
        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
      },
      onFailure: function(transport) {
        alert("Error communicating with the server: " + transport.responseText.stripTags());
      },
      callback: function(form) {
        return Form.serialize(form);
      },
      handleLineBreaks: true,
      loadingText: 'Loading...',
      savingClassName: 'inplaceeditor-saving',
      loadingClassName: 'inplaceeditor-loading',
      formClassName: 'inplaceeditor-form',
      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
      highlightendcolor: "#FFFFFF",
      externalControl: null,
      submitOnBlur: false,
      ajaxOptions: {},
      evalScripts: false
    }, options || {});

    if(!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + "-inplaceeditor";
      if ($(this.options.formId)) {
        // there's already a form with that name, don't specify an id
        this.options.formId = null;
      }
    }
    
    if (this.options.externalControl) {
      this.options.externalControl = $(this.options.externalControl);
    }
    
    this.originalBackground = Element.getStyle(this.element, 'background-color');
    if (!this.originalBackground) {
      this.originalBackground = "transparent";
    }
    
    this.element.title = this.options.clickToEditText;
    
    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
    Event.observe(this.element, 'click', this.onclickListener);
    Event.observe(this.element, 'mouseover', this.mouseoverListener);
    Event.observe(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.observe(this.options.externalControl, 'click', this.onclickListener);
      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  },
  enterEditMode: function(evt) {
    if (this.saving) return;
    if (this.editing) return;
    this.editing = true;
    this.onEnterEditMode();
    if (this.options.externalControl) {
      Element.hide(this.options.externalControl);
    }
    Element.hide(this.element);
    this.createForm();
    this.element.parentNode.insertBefore(this.form, this.element);
    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
    // stop the event to avoid a page refresh in Safari
    if (evt) {
      Event.stop(evt);
    }
    return false;
  },
  createForm: function() {
    this.form = document.createElement("form");
    this.form.id = this.options.formId;
    Element.addClassName(this.form, this.options.formClassName)
    this.form.onsubmit = this.onSubmit.bind(this);

    this.createEditField();

    if (this.options.textarea) {
      var br = document.createElement("br");
      this.form.appendChild(br);
    }

    if (this.options.okButton) {
      okButton = document.createElement("input");
      okButton.type = "submit";
      okButton.value = this.options.okText;
      okButton.className = 'editor_ok_button';
      this.form.appendChild(okButton);
    }

    if (this.options.cancelLink) {
      cancelLink = document.createElement("a");
      cancelLink.href = "#";
      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
      cancelLink.onclick = this.onclickCancel.bind(this);
      cancelLink.className = 'editor_cancel';      
      this.form.appendChild(cancelLink);
    }
  },
  hasHTMLLineBreaks: function(string) {
    if (!this.options.handleLineBreaks) return false;
    return string.match(/<br/i) || string.match(/<p>/i);
  },
  convertHTMLLineBreaks: function(string) {
    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
  },
  createEditField: function() {
    var text;
    if(this.options.loadTextURL) {
      text = this.options.loadingText;
    } else {
      text = this.getText();
    }

    var obj = this;
    
    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
      this.options.textarea = false;
      var textField = document.createElement("input");
      textField.obj = this;
      textField.type = "text";
      textField.name = this.options.paramName;
      textField.value = text;
      textField.style.backgroundColor = this.options.highlightcolor;
      textField.className = 'editor_field';
      var size = this.options.size || this.options.cols || 0;
      if (size != 0) textField.size = size;
      if (this.options.submitOnBlur)
        textField.onblur = this.onSubmit.bind(this);
      this.editField = textField;
    } else {
      this.options.textarea = true;
      var textArea = document.createElement("textarea");
      textArea.obj = this;
      textArea.name = this.options.paramName;
      textArea.value = this.convertHTMLLineBreaks(text);
      textArea.rows = this.options.rows;
      textArea.cols = this.options.cols || 40;
      textArea.className = 'editor_field';      
      if (this.options.submitOnBlur)
        textArea.onblur = this.onSubmit.bind(this);
      this.editField = textArea;
    }
    
    if(this.options.loadTextURL) {
      this.loadExternalText();
    }
    this.form.appendChild(this.editField);
  },
  getText: function() {
    return this.element.innerHTML;
  },
  loadExternalText: function() {
    Element.addClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = true;
    new Ajax.Request(
      this.options.loadTextURL,
      Object.extend({
        asynchronous: true,
        onComplete: this.onLoadedExternalText.bind(this)
      }, this.options.ajaxOptions)
    );
  },
  onLoadedExternalText: function(transport) {
    Element.removeClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = false;
    this.editField.value = transport.responseText.stripTags();
    Field.scrollFreeActivate(this.editField);
  },
  onclickCancel: function() {
    this.onComplete();
    this.leaveEditMode();
    return false;
  },
  onFailure: function(transport) {
    this.options.onFailure(transport);
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
      this.oldInnerHTML = null;
    }
    return false;
  },
  onSubmit: function() {
    // onLoading resets these so we need to save them away for the Ajax call
    var form = this.form;
    var value = this.editField.value;
    
    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
    // to be displayed indefinitely
    this.onLoading();
    
    if (this.options.evalScripts) {
      new Ajax.Request(
        this.url, Object.extend({
          parameters: this.options.callback(form, value),
          onComplete: this.onComplete.bind(this),
          onFailure: this.onFailure.bind(this),
          asynchronous:true, 
          evalScripts:true
        }, this.options.ajaxOptions));
    } else  {
      new Ajax.Updater(
        { success: this.element,
          // don't update on failure (this could be an option)
          failure: null }, 
        this.url, Object.extend({
          parameters: this.options.callback(form, value),
          onComplete: this.onComplete.bind(this),
          onFailure: this.onFailure.bind(this)
        }, this.options.ajaxOptions));
    }
    // stop the event to avoid a page refresh in Safari
    if (arguments.length > 1) {
      Event.stop(arguments[0]);
    }
    return false;
  },
  onLoading: function() {
    this.saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  showSaving: function() {
    this.oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    Element.addClassName(this.element, this.options.savingClassName);
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
  },
  removeForm: function() {
    if(this.form) {
      if (this.form.parentNode) Element.remove(this.form);
      this.form = null;
    }
  },
  enterHover: function() {
    if (this.saving) return;
    this.element.style.backgroundColor = this.options.highlightcolor;
    if (this.effect) {
      this.effect.cancel();
    }
    Element.addClassName(this.element, this.options.hoverClassName)
  },
  leaveHover: function() {
    if (this.options.backgroundColor) {
      this.element.style.backgroundColor = this.oldBackground;
    }
    Element.removeClassName(this.element, this.options.hoverClassName)
    if (this.saving) return;
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.originalBackground
    });
  },
  leaveEditMode: function() {
    Element.removeClassName(this.element, this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
    if (this.options.externalControl) {
      Element.show(this.options.externalControl);
    }
    this.editing = false;
    this.saving = false;
    this.oldInnerHTML = null;
    this.onLeaveEditMode();
  },
  onComplete: function(transport) {
    this.leaveEditMode();
    this.options.onComplete.bind(this)(transport, this.element);
  },
  onEnterEditMode: function() {},
  onLeaveEditMode: function() {},
  dispose: function() {
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
    }
    this.leaveEditMode();
    Event.stopObserving(this.element, 'click', this.onclickListener);
    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  }
};

Ajax.InPlaceCollectionEditor = Class.create();
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
  createEditField: function() {
    if (!this.cached_selectTag) {
      var selectTag = document.createElement("select");
      var collection = this.options.collection || [];
      var optionTag;
      collection.each(function(e,i) {
        optionTag = document.createElement("option");
        optionTag.value = (e instanceof Array) ? e[0] : e;
        if((typeof this.options.value == 'undefined') && 
          ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
        if(this.options.value==optionTag.value) optionTag.selected = true;
        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
        selectTag.appendChild(optionTag);
      }.bind(this));
      this.cached_selectTag = selectTag;
    }

    this.editField = this.cached_selectTag;
    if(this.options.loadTextURL) this.loadExternalText();
    this.form.appendChild(this.editField);
    this.options.callback = function(form, value) {
      return "value=" + encodeURIComponent(value);
    }
  }
});


Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element); 
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
};

function puts(msg) {console.log(msg)}


function icomment(cid, form) {
  // this part isn't working, hmm...  $A($form()) or somesuch wierdness.  seems to not be able to find the form name? aah, this() is getting passed in...
  
  // works, just need to fix complete_comment to update the area below the particular inline post...
  //$(cid+'bb_add_li').innerHTML = ""
  //complete_comment(request)
  new Ajax.Request('/comment/add/' + cid, {asynchronous:true, evalScripts:true, onComplete:function(request){Element.hide(cid+'bb_add_li'); Element.clear('comment_body_'+cid);}, onFailure:function(request){comment_failure(request);}, onLoading:function(request){$(cid+'bb_add_li').innerHTML = "<p>Adding comment <img src='/s/images/wait.gif' /></p>"}, parameters:Form.serialize(form)}); 
  return false;
}

function commentform(twisty, cid, num_comments, load_item) {
  // show inline add form, and set ajax to show other comments below the comment form...  reply form == ?
  target = "c_" + cid
  
  // if it's not open already...
  if ($(target).innerHTML.length < 10) {
    txt = "<form action='/comment/add/" + cid + "' method='post' onsubmit='icomment(" + cid + ", this); return false'>	<input type='hidden' name='inline' value='1' /><input type='hidden' name='unique' value='" + cid + "'><input type='hidden' name='comment[kind]' value='journal' /><input name='comment[item_id]' type='hidden' value='" + cid + "' />	<h2>Add comment</h2><textarea cols='52' id='comment_body_"+cid+"' name='comment[body]' onKeyPress='grow(this,event, 11, 3)' rows='3' style='width: 82%'></textarea></p>	<p><input id='submit' type='submit' value='Submit Comment' /></form><div id='"+cid+"bb_add_li'></div>"

    // inline preview is being a pain in the ass.. for now, just show twiddler
    //<ul class='comment_list'>	<li class='comment' style='display: none' id='rinterm'>
  	//	<div class='comment_by'>
  	//  	<%= image_tag(User.image(@me.id))+"<br />" if @me.has_image? -%>
  	//		<%= @me %> right now
  	//	</div>
  	//	<div class="comment_main">
  	//		<h2 id='rinterm_wait'><img src='/s/images/wait.gif' height='17' width='18'> Adding your reply:</h2>
  	//		<h3 id='rinterm_subject'></h3>
  	//		<div id='rinterm_body'></div>
  	//	</div>
  	//</li>

    // trying to include the bb_add_li div  just below the form, maybe it'll work out...
  
    //comment_controller adds to end of #{@p.unique}bb_add_li
    // was named comment_list"+cid+"
    // fuck, this loads previously made comments... where will NEW comments go?  need a <ul>...
    // currently, clicking on the '5 comments' loads a new page..
  
    // this should be abstracted better?  Or just abstracted in design, hack however in code? :)
  
  
    // this is expected to identify an ELEMENT in a list of comments... but where are the comments?, when are they loaded?  irritatingly getting complicated :( 
    if (num_comments != 0) {      
      txt += "<ul class='comment_list' id='comment_list"+cid+"'><img src='/s/images/wait.gif' /></div>"
      
      // then the result of this replaces comment_list+CID
      new Ajax.Request('/journal/load_comments/' + cid, {asynchronous:true, evalScripts:true});
    }
  
    // only do if comment form not expanded. but STILL do if num_comments is zero.
    // could try and integrate with load_comments, but leave separate for now, as thats complicated  
    if (load_item == 1 && $('journal_brief_'+cid).innerHTML.length < 1201) {
      $('journal_brief_'+cid).innerHTML+= " <img alt='Wait' id='userfind_spinner' src='/s/images/wait.gif' />"
      // scrolling it into place appears to be having problems :(
      // gets off when other display.effect things have taken effect already... moves to the OLD location on the page...
      // new Effect.ScrollToFullView('journal_brief_'+cid, {duration: 0.9, offset: 236}, {sync: true});
      new Ajax.Request('/journal/load_full/' + cid, {asynchronous:true, evalScripts:true, onLoaded:function(request){Field.focus('comment_body_'+cid)}});
    }
  
    $(target).innerHTML = txt
  }

  if ($(target).style.display == 'none') { 
    twisty.getElementsByTagName("span")[0].innerHTML = '&darr;'
    new Effect.BlindDown(target, { duration:0.2, fps:50})//, queue:'end' 

  } else {
    twisty.getElementsByTagName("span")[0].innerHTML = '&rarr;'        
    new Effect.BlindUp(target, { duration:0.2, fps:50 })    
  }
    
  return false
}




blind_len = { duration:0.7, fps:50} // used by aph apparently?
isIE = (navigator.userAgent.indexOf('MSIE') >= 0) // make sure it's not v7?
autosave_interval = 40000 // 60k = 1 minute

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

function d() {
  $A(arguments).each( function(i) {console.log(i)})
}

function check_empty_title(area) { // if we return true, it quits
  if ($F(area) !='') return true;
  return confirm('Are you sure?  You have no title chosen.'); 
}

function toggles() {
  $A(arguments).each( function(i) {Element.toggle(i)})
  return false
}
function etoggles() {
  $A(arguments).each( function(i) {Effect.toggle(i)})
  //$A(arguments).each(Effect.toggle) // doesn't work, hmm
  return false
}

var last_autosave_len = 250; // empty values have a certain amount of space.  actual=296 @ item
function autosave(form, post_to) {
	$('draft').value='1'
	serialized_form = Form.serialize($(form))
	//console.log("last autosave len = ", last_autosave_len, " len now: ", serialized_form.length)
	if (last_autosave_len + 100 > serialized_form.length) return
	last_autosave_len = serialized_form.length;
	
	new Ajax.Updater('autosave', post_to, {asynchronous:true, evalScripts:true, onLoading:function(request){Element.show('autosave'); $('autosave').innerHTML='Autosaving...'}, onFailure:function(request){$('autosave').innerHTML='Unable to autosave'}, parameters:serialized_form});
}


var ljust = false
var hidden = 0
var ljust = 0
function resize_edge() {
  //if (ljust == 0) return;
	//current=ie4 && !window.opera? iecompattest().scrollLeft+iecompattest().clientWidth : window.pageXOffset+window.innerWidth
	
	c = document.body; //$('outer'); //was document.body
	m = $('container_container')

	middle = m.offsetWidth
	owidth = $('outer').offsetWidth
	
	one_edge = (owidth-middle)/2
	// ugh, really want it 1/2 margin, 1/2 padding :) ==  134 -> 67 == a bit left of perfectly centered
	
	//amt = ljust/2
	// why don't I just do this for all of them?
	//if (ljust > 165) amt=0;
	amt = 0
	
	c.style.backgroundPosition = one_edge - amt + 'px 0px'
}


function old_resize_edge() {
  if (ljust == 0) return;
	current=ie4 && !window.opera? iecompattest().scrollLeft+iecompattest().clientWidth : window.pageXOffset+window.innerWidth
  
  // dduh, have to change this based on whether the right side is visible or not, just ilke before?
	if (current < max_width+ljust*2) {		
		if (hidden==0) {
		  //console.log("hiding")
			hidden = 1
			//$('right_edge').style.width=0
			Element.hide('right_edge')
		}
		return // don't do anything if it's already hidden
	}
	
	if (hidden==1) {
		hidden=0
		//$('right_edge').style.width=ljust+'px'
		Element.show('right_edge')
	}
	
	// make the bg tiling not visible... move as we resize
	half=(current - $('container_container').offsetWidth)/2
	$('outer').style.backgroundPosition = (current-ljust)/2 +'px 0px'
}


function l() {
  return
  console.log(arguments)
}

var DEVELOPMENT_MODE = false;  
  
if (typeof console == "undefined" && DEVELOPMENT_MODE == true) {  
   
  // Create an unordered list to display log messages  
  var logsOutput = document.createElement('ul');  
  document.getElementsByTagName('body')[0].appendChild(logsOutput);  
    
  // Define console.log() function  
  console = {  
    log: function(msg) {  
      logsOutput.innerHTML += '<li>' + msg + '</li>';  
    }  
  };  
} else if (typeof console == "undefined" && DEVELOPMENT_MODE == false) { // 
  console = {  
    log: function() {} 
    // Do nothing  
  };  
}  


var current_bg = false;
var current_edge = false;

function close_current_tab(main, tab) {
  if (main == 'edge') {
    if (current_edge && tab != current_edge) twistnow($('h'+current_edge), current_edge);
    if (tab == current_edge) {current_edge = false} else {current_edge = tab}
  } else {
    if (current_bg && tab != current_bg) twistnow($('h'+current_bg), current_bg);
    if (tab == current_bg) {current_bg = false} else {current_bg = tab}
  }
  Element.hide('bg_custom_add')
}

function btwist(area, main, twisty) {
  //console.log("btwist", area, main, twisty, "current:", current_tab)
  if (main == 'edge') { // currently changing the outer edge
    prefix = 'e_'; 
    set_color_across = false
  } else {
    prefix = 'c_'
    // also need to update the actual field... nope, sitll doesn't work
    //if (set_tile_across) {$('cdemo').style.backgroundImage = $('cdemo_parent').style.backgroundImage;}
  }
  tab = prefix + area

  close_current_tab(main, tab)  
  //console.log('opened:', current_edge, 'self:', tab)
  
  // close any others open in our main area.  boy this was a bitch to factor


  //console.log("now currents edge:", current_edge, "current bg:", current_bg)

  // put images from innerhtml in.  when it is clicked, how will we know what area it was? in function == ideal
  // otherwise check parent tree of image id somehow?
  if (area != 'plain' && !shown_images[tab] ) {
    $(tab).innerHTML = images[area]
	  shown_images[tab] = 1
  }
  
  // doesn't close when you click it again == annoying...
  if (area == 'custom') { 
    // if going to be opened, then show bg_add.  if it's going to be closed, hide it.
    if ($(tab).style.display == 'none') {Element.show('bg_custom_add')} // to be opened
    else {Element.hide('bg_custom_add')}
  }
  
  //console.log("current main:", current_main, "area:", area, "main:", main)
  if (main == 'main') {
    if (area== 'plain') { Element.show('color_across') } else { Element.hide('color_across') }
    if (area== 'tiling') { Element.show('tile_across') } else { Element.hide('tile_across') }    
  }
  // not sure why this one works this way, but the other doesn't :)
  //if (current_main == 'tiling') { Element.show('tile_across') }
  //else { Element.hide('tile_across') }
  
  return twistnow(twisty, tab)
}




var current_main = '';

function bg_preview(to,color,image,ljust, area) { 
  // hide bg list & old opened images section
  //console.log($('bg_id_' + to).parentNode == 'c_tiled', 'ljust: ', ljust)  // check for tiled area
  Element.hide('color_across')
  if (!ljust) ljust=0
  //console.log("left_spacing: " + ljust)
  $('left_spacing').innerHTML = ljust
  $('left_spacing2').innerHTML = ljust
    
  // close any others in our main area, including ourselves unless we're a color?
  if (area == 'edge_bgs' || area == 'e_custom') {
    if (current_edge) twistoff($('h'+current_edge), current_edge)
    current_edge = false
  } else {
    //d("main bg.  current_bg:", current_bg)
    if (current_bg) twistoff($('h'+current_bg), current_bg)
    current_bg = false
  }
  
  //$('color3').value = '#FF0'
  //console.log('bg_preview color3 is', $('color3').value, $('color3'), $F('color3').value)
  //d("preview.  area:", area, "to:", to, "color:", color, "image:", image)

  // changing the EDGE area
	if (area == 'edge_bgs' || area == 'e_custom') {
	  // if set_tile_across is set, we need to fix some stuff
	  if (set_tile_across) {
	    set_tile_across = false;// since we're changing edge, turn off tile across
	    // put back demo, so parent doesn't tile across
	    $('cdemo').style.backgroundImage = $('cdemo_parent').style.backgroundImage
	  }
	  
	  $('edge_bg_num').value = to
	  $('cdemo_parent').style.backgroundImage = 'url(' + image + ')' // overrides any background colors usually...
	  $('cdemo_parent').style.paddingLeft = '100px'
	  $('cdemo_parent').style.paddingRight = '100px'
	  
	  $('cdemo').style.borderLeft = $('cdemo').style.borderRight = '0'
    // hack, doesn't fix in all cases... some odd spacing problems below the preview window
    $('cdemo').style.paddingBottom = '12px'
	  //console.log($('cdemo_parent').style.backgroundImage, $('cdemo_parent').style.marginLeft)
	  
  // changing the MAIN area
	} else {
	  
	  // fix edit link to point to proper bg
  	$('background_color').style.backgroundColor = color  // background treated as this color div
	  Element.show('bg_bginfo')  // un-hide background treated as this color...
	  
    $('color0').value = color
    cs0_change_update($('color0').value, true);  
  
	  $('bg_num').value = to
    $('cdemo').style.backgroundImage = 'url(' + image + ')'

    // update ljust padding to add 6 px, and default to 6?
    // so tiling images will have a paddingleft of 12 px, right?  used above to keep 'tile across' visible
    if (!ljust || ljust == 0) ljust=6
    $('cdemo').style.paddingLeft=ljust+6+"px"
    
    // put the main image on the sides, and pump up left padding to ljust, width of 'right' =ljust.  ljust always 7!
    if (ljust > 6) {
      twistoff($('he_plain'), 'e_plain')
      
      //wipe the edge_bg just in case. neeed when custom ljust set for edge, then use ljust main
      $('cdemo_parent').style.backgroundImage = ''
      $('cdemo_parent').style.paddingLeft = '0'
	    $('cdemo_parent').style.paddingRight = '0'      
          
      current_main = 'ljust';
      Element.show('he_centered'); Element.hide('he_categories'); Element.hide('tile_across')
      
      $('cdemo').style.paddingLeft = ljust + 7 + 'px'
      $('cdemo').style.borderLeft = '0' 
      $('cdemo').style.borderRight = '0'
    }
    else { // this is a tiling background in the main area
      current_main = 'tiling';
      Element.show('tile_across')
      Element.hide('he_centered'); Element.show('he_categories')      
      
      $('cdemo').style.backgroundPosition = '0 0'
      
      // but if we currently have a tiling edge bg, need to leave this... nope
      //$('cdemo_parent').style.paddingLeft = '0'
	    
	    if (set_tile_across) {
	      $('cdemo_parent').style.backgroundImage = $('cdemo').style.backgroundImage
	    }
    }
	} // edge_bgs
}

var set_tile_across = false
function tile_across() {
  //if (set_tile_across) {alert('already across');return false}
  close_current_tab('edge',false) 
  
  set_tile_across = true
  $('edge_bg_num').value = $('bg_num').value
  $('bg_num').value = '' // better than 0?
  $('color0').value = '' // otherwise we can't tell this apart from a set inner color...
  
  // does this end up staying as a reference to itself, so changing one changes the other???
  //console.log('demo bg was', $('cdemo').style.backgroundImage, $('edge_bg_num').value)
  $('cdemo_parent').style.backgroundImage = $('cdemo').style.backgroundImage
  //console.log("parent now is", $('cdemo_parent').style.backgroundImage)
  
  $('cdemo').style.padding = '8px'
  //$('cdemo').style.paddingBottom = '15px'
  $('cdemo').style.backgroundImage = ''
  $('cdemo').style.backgroundColor = ''
  $('cdemo').style.border = ''
  return false
}

var set_color_across = false
function color_across() {
  close_current_tab('edge',false)
  set_color_across = true
  cs3_change_update($('color0').value, 2)
  return false
}

function cs0_change_update(new_color, manual) {
  // calls automatically when we refresh, which messes things up.  how to skip?  || current_tab != 'c_plain'
  //console.log("change update with manual", manual, current_edge, current_bg)
  if (!current_bg && !current_edge && !manual) return; 
      
  if (!manual || manual == 2) {
    $('cdemo').style.backgroundImage = ''
    $('cdemo').style.backgroundColor = new_color
    $('bg_num').value = ''

    // ok, bug = change edge bg to non-color, then change main bg to color == sets edge back to color
    // now: sorta better, but changing main to tiled resets edge color to #CCC still.
    //console.log("current_main is", current_main)
    if (current_main == 'ljust') {//} || current_main == 'tiling') {
      //d("Resetting bgcolor from ljust settings")
      //Element.hide('cright')
      //$('cdemo_parent').style.backgroundImage = ''
      $('cdemo_parent').style.paddingRight = '0'
      $('cdemo_parent').style.paddingLeft = '0'
      $('cdemo').style.padding = '8px'
      $('cdemo').style.paddingBottom = '12px'
          
      // put back borders and show edge color chooser
      Element.hide('he_centered'); Element.show('he_categories')
      
      // replaces tiling outer edge with a blank color if we had a tiling edge before...
      //console.log("color3 value is", typeof $('color3').value, $('color3').value)
      
      if (typeof $('color3').value == 'undefined') $('color3').value = '#FFFFFF'
      cs3_change_update($('color3').value )
      
      //color_selects[3].setrgb(color)
      //console.log("border is now", $('cdemo').style.borderLeft)
    }
    // else if (current_main == 'tiling') {  
    //}
  }
  
  if (current_bg) { // in bg tab, not 'edge' tab.
    current_main = 'color';
    Element.hide('bg_bginfo')
  }
  
  $('color0').value = new_color
	$('link_contrast').innerHTML = test_colors(new_color, $('color2').value)		
	$('vlink_contrast').innerHTML = test_colors(new_color, $('color4').value)		
	$('font_contrast').innerHTML = test_colors(new_color, $('color1').value)		

	if (set_color_across) cs3_change_update($('color0').value, '2')
}


function cs3_change_update(new_color, manual) {
  //console.log("change cs3 update with manual", manual, current_edge, current_bg, new_color, new_color.length) 
  if (!current_bg && !current_edge && !manual) return;
  if (new_color.length > 7) {new_color = "#CCCCCC"; color_selects[3].setrgb(new_color);}
  
  //if (!current_tab || (current_tab != 'e_plain' && current_tab != 'c_plain')) return;  
  if (!manual || manual == 2) {
    //console.log("continuing with cs3 change_update", new_color)
    $('edge_bg_num').value = ''
    if (set_color_across)
    $('cdemo_parent').style.backgroundImage = ''	
	  $('cdemo').style.borderLeft = "20px solid " + new_color;
	  $('cdemo').style.borderRight = "20px solid " + new_color;
    $('cdemo').style.padding = '8px'
    $('cdemo').style.paddingBottom = '12px'
      
  	// hide parent margin
    $('cdemo_parent').style.paddingLeft = '0'
    $('cdemo_parent').style.paddingRight = '0'	
    
    //console.log("border is now", $('cdemo').style.borderLeft, $('cdemo'))
  }

  $('color3').value = new_color
}

function cs1_change_update(new_color) {
  $('demo_color').style.color = new_color
  $('color1').value = new_color
	$('font_contrast').innerHTML = test_colors($('color0').value, new_color)
}
function cs2_change_update(new_color) {
  $('demo_link').style.color = new_color
  $('color2').value = new_color
	$('link_contrast').innerHTML = test_colors($('color0').value, new_color)
}

function cs4_change_update(new_color) {
  $('demo_vlink').style.color = new_color
  $('color4').value = new_color
	$('vlink_contrast').innerHTML = test_colors($('color0').value, new_color)	
}

function cs5_change_update(new_color) {
  $('color5').value = new_color;
}


function showhide() {
  var a = arguments;
  Element.show(a[0])
  if (a[1]) Element.hide(a[1]);
  if (a[2]) Element.hide(a[2]);
  if (a[3]) Element.hide(a[3]);
  if (a[4]) Element.hide(a[4]);
  if (a[5]) Element.hide(a[5]);
  if (a[6]) Element.hide(a[6]);  
  return false  
}

function hideshow() {
  var a = arguments;
  Element.hide(a[0])
  if (a[1]) Element.show(a[1]);
  if (a[2]) Element.show(a[2]);
  if (a[3]) Element.show(a[3]);
  if (a[4]) Element.show(a[4]);
  if (a[5]) Element.show(a[5]);
  if (a[6]) Element.show(a[6]);  
  return false
}

function toggle() {
  var a = arguments;
  Effect.Toggle(a[0])
  if (a[1]) Effect.Toggle(a[1]);
  if (a[2]) Effect.Toggle(a[2]);
  if (a[3]) Effect.Toggle(a[3]);
  if (a[4]) Effect.Toggle(a[4]);
  return false  
}



function mood_ch(s) {
  s = $F('mood')
  //console.log("mood", s)  
  
  if (s == 'custom') {
    Element.hide('mood_img')    
    Element.show('mood_custom')
    Field.focus('mood_cust')
    return
  }
  
  v='/s/images/smile/'+s
  if (s== '') {v=v+'.png'} else {v =v+'.gif'}
  $('mood_img').src=v
  Element.hide('mood_custom')
  Element.show('mood_img')
  //console.log("The", animal, "jumped over", count, "tall buildings");
}

function include_print(script_filename, skip_ad_check) {
  if (!hide_ads) hide_ads = false
  if (!skip_ad_check) skip_ad_check = false
  if (!skip_ad_check && hide_ads) return;

  document.write('<' + 'script');
  document.write(' language="javascript"');
  document.write(' type="text/javascript"');
  document.write(' src="' + script_filename + '">');
  document.write('</' + 'script' + '>');
}

function include_dom(script_filename, skip_ad_check) {
  if (!no_ads) no_ads = false
  if (!skip_ad_check) skip_ad_check = false
  if (!skip_ad_check && no_ads) return;
  
  var html_doc = document.getElementsByTagName('head').item(0);
  var js = document.createElement('script');
  js.setAttribute('language', 'javascript');
  js.setAttribute('type', 'text/javascript');
  js.setAttribute('src', script_filename);
  html_doc.appendChild(js);
  return false;
}

var skip_edge_hide = 0
function tab_active(tid, color, ljust) {
	if (active) {
		$('t_' + active).className='tab';
		Element.hide('c_' + active);
	}
	
	//alert(skip_edge_hide)
	//hide edge color when working with tiling bgs
	if (cs3 && skip_edge_hide == 0) {
	  //puts('showing cs3:' + cs3)
  	if (tid == 'tiled') {
      Element.hide('bg_edge')
    } else {
      Element.show('bg_edge')      
    }
  }
  
	$('t_' + tid).className='tab activetab';
	
	if (!shown_images[tid]) $('c_'+tid).innerHTML = images[tid]
	
	shown_images[tid] = 1
	Element.show('c_' + tid);
	active=tid
	
	if (tid == 'current') {
	  bg_move(1, current_color, current_ljust)
	}
	
	return false
}

function test_colors(fore,back,fix) {
  //http://www.snook.ca/technical/colour_contrast/colour.html
	var brightnessThreshold = 100; // was 125
	var colorThreshold = 400; // was 500

  // we're given values with # on them, so skip first character
	br = parseInt(back.substr(1,2),16);
	bg = parseInt(back.substr(3,2),16);
	bb = parseInt(back.substr(5,2),16);
	fr = parseInt(fore.substr(1,2),16);
	fg = parseInt(fore.substr(3,2),16);
	fb = parseInt(fore.substr(5,2),16);

  //alert(br + " from back: " + back)
	var bY=((br * 299) + (bg * 587) + (bb * 114)) / 1000;
	var fY=((fr * 299) + (fg * 587) + (fb * 114)) / 1000;
	var brightnessDifference = Math.floor(Math.abs(bY-fY));

  var colorDifference = (Math.max (fr, br) - Math.min (fr, br)) + (Math.max (fg, bg) - Math.min (fg, bg)) + (Math.max (fb, bb) - Math.min (fb, bb));

	$("bDiff").innerHTML = brightnessDifference;
	$("cDiff").innerHTML = colorDifference;
	
	//only disable form if both are bad.
	if ((brightnessDifference < brightnessThreshold) && (colorDifference < colorThreshold)) {
	  $('color_submit').disabled = true
	  $('color_submit').value = "Please correct brightness or color"
	  //Element.show('let_me_anyways')
	  
	  return "Fix: Poor Color <" + colorThreshold + " and Brightness <" + brightnessThreshold;

  } else {
	  $('color_submit').disabled = false
	  $('color_submit').value = "Submit"
	  //Element.hide('let_me_anyways')
	  
  	//if ((brightnessDifference >= brightnessThreshold) && (colorDifference >= colorThreshold))	{} else 
  	if (brightnessDifference < brightnessThreshold) {
  	  return "Fix: Poor Brightness Contrast, <" + brightnessThreshold;

  	} else if (colorDifference < colorThreshold){
  	  return "Fix: Poor Color Contrast, <" + colorThreshold

  	}
  }
  
  return ""
}


function verify_reward(amount, title, prefix, cost) {
  if (!title) title = 'Reward'
  if (!prefix) prefix = ''
  if (!cost) cost = amt
  
  fcost = prefix+'fcost'
  s = $(prefix + 'submit')
  //alert(amount)
  
  $(prefix + 'afford_amt').innerHTML= Math.floor(points/cost);
  total = amount*cost
  $(fcost).innerHTML = total
  val_item = $(prefix + 'item_id')
  if (!val_item) return; // no list shown by default == fails...
  val = val_item.value
  
  if (amount > 30) {
    s.disabled = true;    
    s.value = "Add " + title + " - 30 is the max comments to reward at once"
    
  } else if (total > points) {// grr, why do we precalculate total?
    s.disabled = true;
    s.value = "Add " + title + " - You don't have that many points"
    
  } else if (val == '0' || val == '') {
    s.disabled = true;
    s.value = "Add " + title + " - Please choose an item"   
    
  } else {    
    s.disabled = false;
    s.value = "Add " + title
  }

}

var lineno = 1
function toggle_lines() {toggle_lines_default();} // poor man's alias
function toggle_lines_default() {
  if (lineno==0) {
   document.getElementsByClassName('n').each( function(i) {Element.show(i); i.style.color = '#bbb';})
  } else if (lineno == 1) {
   document.getElementsByClassName('n').each( function(i) {Element.show(i); i.style.color = '#000';})
  } else {
   document.getElementsByClassName('n').each( function(i) {Toggle.display(i)})
  }
  
  lineno += 1
  if (lineno == 3) lineno = 0
}

function image_preview(title, image_url) {
  if (title == "Choose an image ->") return;
  // load the image, then update it's width property to be 200 if it's too large?
  // better yet, load @ 200 (expect too big), then update if it's smaller?  But it will only return 200 then
  $('image_preview').innerHTML = "<img id='img_preview'  src='"+image_url+"' style='clear: both; margin-top: 10px'/><br />Type to show this image:<br /> [image "+title+"]"
  setTimeout( "image_preview_check()", 100)
}

function image_preview_check() {
  i=$('img_preview')
  if (i.width != 0) {
    if (i.width > 175) i.width=175
  } else {
    setTimeout( "image_preview_check()", 200)
  }
}

author_shown = 0
function twist_author() {
  poems = $('base_poems')
  b = $('base_bottom')
  d = $('full_description')  
  
  if (author_shown == 0) { 
    //b.parentNode.removeChild(b);
    //insertAfter(b, $('base_top_container'));
    //new Insertion.After($('base_top_container'), b)// snippet only :()

    Element.hide(d)
    Element.hide(poems)
    // move their full desc to the base_bottom
    b.innerHTML = d.innerHTML
    Element.show(b)    
    d.innerHTML = ''
    author_shown = 1
    new Effect.BlindDown(poems, { duration:0.7, fps:50})
    
  } else {
    
    Element.hide(b)
    d.innerHTML = b.innerHTML
    author_shown = 0
    new Effect.BlindDown(d, { duration:0.7, fps:50})
    
    //twisty.getElementsByTagName("span")[0].innerHTML = '&rarr;'
    //new Effect.BlindUp(t, { duration:0.2, fps:50 })    
  }
  return false;
}

function twistnow(twisty, target, options) {
  //console.log("twistnow:", twisty.innerHTML, "target", target)
  
  if ($(target).style.display == 'none') { 
    twisty.getElementsByTagName("span")[0].innerHTML = '&darr;'
    Element.show(target)

  } else {
    twisty.getElementsByTagName("span")[0].innerHTML = '&rarr;'
    Element.hide(target)    
  }
  
  return false;  
}

function twistoff(twisty, target, options) {
  twisty.getElementsByTagName("span")[0].innerHTML = '&rarr;'
  Element.hide(target)    
  return false;  
}

function twist(twisty, target, options) { 
  //console.log("twist:", twisty.innerHTML, "target", target)  
  
  //console.log("twisty:", twisty.innerHTML)
  // some odd firefox bug, the display can be something other than "none", which gets really irritating!
  // trying to flip around the cases, to default to opening it...
  if ($(target).style.display == "") { 
     twisty.getElementsByTagName("span")[0].innerHTML = '&rarr;'
    new Effect.BlindUp(target, { duration:0.2, fps:50 }, options)    
  } else {
    twisty.getElementsByTagName("span")[0].innerHTML = '&darr;'
    new Effect.BlindDown(target, { duration:0.2, fps:50}, options)//, queue:'end' 
  }
  
  //setTimeout("$('r'+<%= i %>).focus()", 1000);
    
  //Effect.toggle(target, 'blind', { duration:0.2, fps:50, queue:'end' }) // 1.5+ only
    
  return false;
}


function after(nid) {
  setTimeout("$('"+nid+"').focus();", 380)
}

function twistt(target) {
  if ($(target).style.display == 'none') { 
    new Effect.BlindDown(target, { duration:0.2, fps:50})//, queue:'end' 
  } else {
    new Effect.BlindUp(target, { duration:0.2, fps:50 })    
  }
  return false
}

var rateclear
var ratebase = 0

Effect.ScrollToFullView = Class.create();
var offsets
Object.extend(Object.extend(Effect.ScrollToFullView.prototype,Effect.ScrollTo.prototype, offsets), {  setup: function() {
    Position.prepare();
    this.scrollStart = Position.deltaY;
    
    if (!offsets) offsets = Position.cumulativeOffset(this.element); 
    // maybe store this then?  then in im( have it jump to im, then new Position.prepare();Effect.ScrollToFullView(element, {duration: 0.6, offset: 10},  Position.deltaY);
    var rand = 0;
    if(this.options.offset) rand = this.options.offset;

    var element_kante_oben = offsets[1];
    var element_hoehe = this.element.offsetHeight;

    // Lï¿½nge und Position des dargestellten Ausschnitts relativ zumgesamten Dokument
    var screen_bottom, screen_height, screen_scrolled;
    if (isIE){
      screen_height = document.documentElement.clientHeight;
      screen_scrolled = document.documentElement.scrollTop;
    } else {
      screen_height = window.innerHeight;
      screen_scrolled = window.pageYOffset;
    }
    screen_bottom = screen_scrolled + screen_height;

    // Obere Kante des Elements sichtbar und Position des Elements +seine Hhe < screen_bottom
    // => Element wird schon komplett dargestellt
    if (element_kante_oben > screen_scrolled && element_kante_oben +element_hoehe + rand < screen_bottom ) {
      this.delta = 0;
      return;
    }

    var new_top;
    // Wenn Element her als Bildschirmhe, dann bis zur oberenKante des Elements scrollen
    if (element_hoehe + rand > screen_height) {
      new_top = element_kante_oben - rand;
    } else {
      // Mu nach oben gescrollt werden?
      if (element_kante_oben < screen_scrolled) {
        // nach oben scrollen
        new_top = element_kante_oben - rand;
      } else {
        // nach unten scrollen
        new_top = element_kante_oben + element_hoehe + rand - screen_height;
      }
    }
    
    var max = !isIE
      ? window.height - screen_height : document.body.scrollHeight - screen_height;
    this.delta  = (new_top > max ? max : new_top) - this.scrollStart;
    
    //set the 'duration' to be 1 second per 500 pixels scrolled.
    //puts(this.options.duration)
    this.options.duration = 1*(this.delta/500)
    //puts(this.options.duration)
  }
});

function single_space(fix) {
  b = $(fix).value.replace(/^<p>|<\/p>$/g, "").replace(/<br>|<br \/>/g,"\n").replace(/<p>/g,"\n\n").replace(/<\/p>/g,"").replace(/\n\n/g,"\n")
  $(fix).value = b
  if ($('fix_lines')) {
    $('fix_lines').checked = false;
  }
}

var tinyShown = [];
function toggleEditorMode(sEditorID) {
    fix = sEditorID
    e = $(fix)
    //puts(e)
    //if (e.rows < 34) e.rows = 34 // make it nice and big
    //if (e.cols < 69) e.cols = 69 // make it nice and big, done in _rich_editor now
    
    //try {
        if( tinyShown[sEditorID] ) {// removing control
            tinyMCE.execCommand("mceRemoveControl", false, sEditorID);
            tinyShown[sEditorID] = false;
            if ( !e.value.match(/<div|<p/i) ) {
              b = e.value.replace(/\n/g,"").replace(/<br[^>]*>\s*/g,"\n")
              e.value = b
            } else { // make sure </p> and <br> have \n after
              b = e.value.replace(/<\/p>[\s\r\n]*/g,"</p>\n").replace(/<br[^>]*>[\s\r\n]*/g,"<br>\n")
              e.value = b	
              Element.toggle('rich_warn_' + sEditorID)
            }
            //puts(e.value)
  					Element.toggle('rich_info_' + sEditorID)
        }
        else { // adding control.  replace \n with <br>\n if no <br> and no <p
            if ( !e.value.match(/<br|<p/i) ) {
						  b = e.value.replace(/\r/g,"").replace(/\n/g,'<br>').replace(/  /,' &nbsp;')
						  e.value = b
            }
						
						try {
						  Element.toggle('rich_info_' + sEditorID)
						  Element.hide('rich_warn_' + sEditorID)
						} catch(er) {}
            tinyMCE.execCommand("mceAddControl", false, sEditorID);
            tinyShown[sEditorID] = true;
        }
    //} catch(e) {
        //error handling
		//		alert("Sorry, rich editing doesn't work with your browser. "+ e);//" error" + e + " , " + e.message)
    //}
}

function addSpellcheck(sEditorID, options) {
	if (!options['height']) options['height']=280;
  if (!tinyShown[sEditorID]) toggleEditorMode(sEditorID);
	tinyMCE.execInstanceCommand(sEditorID, 'mceSpellCheck');
	return false
}
	
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));
}


/* stars in/out */
function ratingin(eid,star,base) {
  clearTimeout(rateclear)
  ratebase = base
  if (! $(eid + '_' + 1)) {return}
  
  // hilight this star, and all to the left of it
  for (var i = 1; i <= star; i++) {
    $(eid + '_' + i).src = '/s/images/stargold.gif'
  }
  // unhilight others
  for (var i = star+1; i <= 5; i++) {
    $(eid + '_' + i).src = '/s/images/stargrey.gif'
  }
}

function ratingon(eid,star) {  
  clearTimeout(rateclear)
  new Ajax.Updater('rate_' + eid, '/comment/rate/' + eid + '?stars=' + star, {asynchronous:true, evalScripts:true, onLoading:function(request){$('rate_' + eid).innerHTML = "Rating <img border=0 src='/s/images/wait.gif'>"}}); return false
}

function ratingout(eid) {
  clearTimeout(rateclear)
  rateclear = setTimeout("ratingin('" + eid + "', " + ratebase + ")",200)
}

function doratingout(eid) {
  // drat, goes out betewen image elements, so delay by abit first
  if (! $(eid + '_' + 1)) {return}
  
  for (var i = 5; i > 0; i--) {
    $(eid + '_' + i).src = '/s/images/stargrey.gif'
  }
}

/* overwrite encourage function.keep it under 1 line, including Tip: */
function encourage(evt) {
  len = $('comment_body').value.length

  var e = gid('rdesc')
  if (len < 50) {
    msg = "Point out your favorite and least favorite parts."
  } else if (len < 80) {
    msg = "Which areas sound awkward?  Use line numbers."
  } else if (len < 120) {
    msg = "Try relating your emotional response."
  } else if (len < 200) {
    msg = "If you were to change one thing, what would it be?"
  } else if (len < 250) {
    msg = "Analyse the effective use of Beginning / Middle / End."
  } else if (len < 350) {
    msg = "Offer a rephrasing of an awkward area."
  } else if (len < 400) {   
    msg = "Is there another aspect this could address?"
  } else {
    msg = "Any other ideas about how this could be improved?"
  } 
  
  e.innerHTML = "<b>Tip</b>: " + msg
  grow('comment_body', evt)
}



added_to_field = ''
var def_heights = new Hash();

function grow(field, evt, max, defh) {
  if (!max) max=21
  // can we load the default height automatically from the definition of the thing?  only at first, which is difficult... but otherwise should keep its height properly? hmm...
  
  f = $(field)
  linelen = f.cols // attr('cols') // resized by CSS Though..
  //console.log("name is:" + f.id + ", defh is: " + def_heights[field.id] + " rows: " + f.rows)
  
  if (!def_heights[field.id]) def_heights[field.id] = f.rows // will get messed up if no id
  defh = def_heights[field.id]
  
  lines = (f.value+'t').split(/\n/)
  count = lines.length

  for (var i = 0; i < lines.length; i++) {
    lin = lines[i]
    len = Math.floor(lin.length / linelen)
    //d("lin.length " + lin.length + ' linelen = '+ linelen + ' lin:' + lin)
    count += len
  };
  
  if (evt && evt.keyCode == Event.KEY_RETURN) count +=1
  if (isIE) count +=1
    
  //d('count=' + count + ' length=' + lines.length + ' lines:' + lines + ' value:' + f.value)
  
  if (count > max) count = max
  if (count < defh) count = defh
  f.rows = count // need 2 blank to have no scrollbar pop up, arg
}

var swearing = new RegExp("bastard|\\bass\\b|dick\b|dickwad|cunt|f\s+u\s+c\s+k|twat|fucking|fucker|fuck|shit|asshole|bitch|pussy|\\bcum\\b|jiz|whore|douchebag|b!tch|faggot|\\bnigger|\\bnigga|slut|\bprick\\b|\@ss|dammit", "gi")
swearing.compile
var shown_profanity_msg = 0

function profanity(element) {
  var children = element.childNodes
  //if (element == document) t0 = new Date()
      
  for (var i = 0; i < children.length; i++) {
    var child = children[i]
    if (child.nodeName == 'SCRIPT') continue;
    
    if (child.nodeType == 3) {
      //if (child.nodeValue.length > 27) alert('checking node, length:' + child.nodeValue.length)
      // irritating that <br>s cause things to be separate nodes
      if (child.nodeValue.length > 2 &&  child.nodeValue.match(swearing) ) {
        child.nodeValue = child.nodeValue.replace(swearing, '*bunny*')
        if (shown_profanity_msg == 0) {
          Element.show('profanity_msg'); shown_profanity_msg = 1
        }
      }
    }
    
    if (child.childNodes) profanity(child)
  }
}

function imhist(i) {
  $('h_' + i).innerHTML = "<div style='padding-top: 4px'>Loading history... <img src='/s/images/wait.gif' /></div>"
  Element.hide('imr_' + i)
  twist($('imt_' + i),'imr_' + i)
  
  after('r_'+i)
  //setTimeout(function(){Field.focus('r'+i)}, 300);
  //alert($('r_' + i))
  //$('r_' + i).focus(); // not working still @ safari or IE
}

function im(name) {  
  delay = 5
    
  // twist is too slow...
  if (!Element.visible('message_box')) {
    Element.show('message_box')
    $('message_box_h').getElementsByTagName("span")[0].innerHTML = '&darr;'
    //twist($('message_box_h'), 'message_box')//, {sync: true}
    //delay += 70
  }
  
  $('cto').value = name;
  
  //firefox needs a delay before setting focus to the field?  arg.  especially with twist, since it's hidden for 200 ms.
  // still needs delay with Element.show?  wierd
  if ($('fixedcomment')) Element.hide('fixedcomment') // so it wont steal focus
  setTimeout(function(){Field.focus('cmsg')}, delay);
  
  // hmm, it should return false if we're returning im('user')?
  // what problem did we have with it returning false? won't move the browser to top so you can see that you're sending a message?  could use movebrowser to...
  // aah, i really want to move the screen to the IM window... as neither staying in the same place OR goign to the top is a good idea
  // what is this offset stuff?  distance before/below?
  //new Effect.ScrollToFullView('outer_message_box', {duration: 0.3, offset: 536}, {sync: true});
  //new Effect.scrollTo('outer_message_box')
  
    element = $('outer_message_box');
    var pos = Position.cumulativeOffset(element);
    //d(pos[0]);d(pos[1])
    window.scrollTo(pos[0], pos[1]-30);
    

  // how to hide 'quick comment' for a bit?
  //if ($('fixedcomment')) {
  //  Element.hide("fixedcomment")
  //  enabledfixed = setTimeout("Element.show('fixedcomment')", 2000);
  //}
  
  // http://test.to.com hi
	//return true;
	//d( "visible is now" + Element.visible('fixedcomment'))
	return false
}

function default_to() {
    if (gid('to').value == '') gid('to').value = "chat"
}

var timewait
var old_submit = 'Submit';
function pleasewait(field,msg) {
  if (timewait) clearTimeout(timewait)
    
  if (!field) field = 'submit'
	s = $(field)
	if (!s) return
	
	old_submit = s.value
  if (!msg) msg="Please Wait..."
	s.value = msg
	s.disabled = true

	timewait = setTimeout("waited('" + field + "')", 6500);
	return true;
}

function waited(field, msg) {
  clearTimeout(timewait)
  if (!field) field = 'submit'
  if (!old_submit) old_submit = 'Submit'
  if (!msg) msg = old_submit
  
  s = gid(field)
  s.value = msg
  s.disabled = false
}

function validateField(fieldId, alertMessage) {
		if (gid(fieldId).value == "") {
				alert(alertMessage);
				gid(fieldId).focus();
				return false;
		} else {
				return true;
		}
}

	//for fieldId in fields {
 //       alert(alertMessage);
	//      gid(fieldId).focus();
	 //     return false;
		//} else {
		 //   return true;
		//}
	//}


var imgsrv = '/s/images';
function collapse(num, dbl) {
	var it = gid('reply_' + num);
	var img = gid('exp_' + num);
	//alert("Collapsing " + it + ", with image: " + img)

	if (it) {
		if (it.style.display == 'none') {
			it.style.display = 'block';
			img.src = imgsrv + '/minus.gif';
		} else {
			it.style.display = 'none';
			img.src = imgsrv + '/plus.gif';
		}
	} else {
		//alert("Couldn't find it " + num);
	}

	// if double, go down any children and open (close = just close first?)
	// double doesn't work yet :(
	if (dbl) {
		// open submenus, and change images
		var submenu = it.getElementsByTagName("ul");
		for (var i = 0; i < submenu.length; i++) {
			//alert("on " + submenu[i].id);
			submenu[i].style.display = 'block';
		}

		var submenu = it.getElementsByTagName("img");
		for (var i = 0; i < submenu.length; i++) {
			//alert("src is " + img.src);
			if (img.src == imgsrv + '/plus.gif') {
				img.src = imgsrv + '/minus.gif';
			}
		}
		//alert("in doubleclick");
		return false;// stop processing second click?
	}
}

function maxlen(e, evt, max, newlines)
{
  if (!newlines) newlines = 0
  l = e.value.length
  if (evt.keyCode == 8) l -= 1
  $(e.id+'_mnum').innerHTML = l // setting the current number of chars
  
  if (evt.keyCode == 8 || evt.keyCode == 9 || (evt.keyCode >= 63232 && evt.keyCode <= 63235) || (evt.keyCode >= 37 && evt.keyCode <= 40)) return true
  m = $(e.id + "_max")
  bad = e.value.length > max-1
  // also count newlines if requested
  if (newlines != 0 && e.value.split("\n").length > newlines) bad = true
  
  if (bad) {
    Element.show(m)
    //shown_bad = true
    return false
  } else {
    //if (shown_bad) 
    Element.hide(m)
    return true
  }
}

/* from http://www.blakems.com/archives/000087.html, my top javascripts */
function expandCollapse() {
    for (var i=0; i<expandCollapse.arguments.length; i++) {
    var element = gid(expandCollapse.arguments[i]);
    element.style.display = (element.style.display == "none") ? "block" : "none";
    }
		return false;
}


function addLoadEvent(func) {
  // newer faster method, but requires the new prototype, which doesn't work with the old scriptaculus + the new scriptaculus causes errors on IE when they try to add backgrounds.  phew!
  //document.observe("dom:loaded", func);
    
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}

function t(name) {
    //gid('to').value == name;
    //gid('msg').focus();
}

function bb_reply_new(id, unique) {
  if (!unique) unique='r'
  var form = $(unique+'bb_add_li')
  var it = $('bb_' + id)
  if ($('rep_' + id)) return false

  
  new Insertion.After(it, "<li id='rep_" + id + "'>" + form.innerHTML + '</li>')
  return false
}

function bb_reply(id,unique) {
  if (!unique) unique='r'
	var form = $(unique+'bb_add_li');
	  
	// when topic is closed, won't have label...
	// some people are still getting this.  why wouldn't they have the reply form?
	if (!form) {	
	  alert('unable to find reply form');
	  return false; 
	}
	
	Element.show(form) // unhide it for size tests

	var it = $('bb_' + id);
	var submenu = it.getElementsByTagName("ul");

  // update width of text field to fit within the space provided.
  // update to be width of main area, minus indent?  Arg, different width on each browser, irritating
  // try leaving this alone to always be 95% of the available space in css?
  //if ($('main')) { 
  //  posit = Position.page(it) // relative to each browser, but not each other.  damn!
  //  xpos = Number(posit[0])
  //  siz = (625 - (xpos))
  //  if (/Firefox\/1.5/.test(navigator.userAgent)) siz = siz//+50
  //  if (/MSIE/.test(navigator.userAgent)) siz = siz-100
  //  if (siz > 540) siz = 540
  //  $(unique+'comment_body').style.width = siz +'px'; // 769-200-some + space from left side
  //  //alert(gid('review_body').style.width)
  //}
	
	Element.hide(form);
	form.parentNode.removeChild(form);

  // the way it's put before it in IE adds some left spacing to the label unfortunately...
	if (submenu.length > 0) {
	  //alert('putting in submenu')
		it.insertBefore(form, submenu[0]);
	} 
	else {
		insertAfter(form, it);
	}
	
	//Element.show(form)
	new Effect.BlindDown(form, {duration:0.3} );//,
	//alert('showed' + form)
	if ($(unique+'bb_form_label')) $(unique+'bb_form_label').innerHTML = 'Reply to comment';
	
	if (/MSIE/.test(navigator.userAgent)) $(unique+'bb_form_label').style.marginLeft="-12px"
	
	$(unique+'comment_parent').value = id;
	//Field.focus('rcomment_subject')
	//alert('done')
	return false;
}


function bg_move(to,color,ljust) {
      
  b = $('bg_colors')
  bitem = $('bg_id_' + to)
  $('bg_num').value = to
  if (to == 1) $('bg_num').value = current_bg
    
  Element.hide(b)
	b.parentNode.removeChild(b);
	insertAfter(b, bitem);	
	
  if (color) {
    $('cdemo').style.backgroundImage = bitem.style.backgroundImage
    Element.hide('bg_colorinfo');
    
    // any way to only show bg_bginfo if this is NOT a system background? hard2 know what area we're in    
    // also want to show it when it's 'current' and it is our custom or we are a mod.  could do one-time though
    if (active == 'custom') Element.show('bg_bginfo')
    Element.show('choose_text');
    
    $('background_color').style.backgroundColor = color
    $('color0').value = color
    
    if (cs3) { // ap doesn't use edge color anymore
      //puts("showing cs3, it's" + cs3)
      cs3_change_update(color);
      color_selects[3].setrgb(color)
    }
    
    new Effect.BlindDown(b, { duration:0.3, fps:50, queue:'end' })
    //Element.show(b)  
    
  } else { // resetting for plain color
    $('cdemo').style.backgroundImage = 'none'
    $('background_color').style.backgroundColor = $('color0').value;
    Element.hide('bg_bginfo','choose_text');
    Element.show('bg_colorinfo');
    
    Element.show(b)  
  }
  
  if (!ljust || ljust == 0) ljust=6
  $('cdemo').style.paddingLeft=ljust+6+"px"
  
  cs0_change_update($('color0').value);
}

function remove_background() { 
  //document.write('<style type='text/css'>body { bgcolor: green; }</style>') // prob doesn't play happy with history.js
  //return false;
  b = document.body.style;
  b.backgroundColor ='#666666'
  b.color='#000000'
  // linkcolors don't work. headings will be rough as well...
  document.linkColor='#1984c1'
  document.vlinkColor='#19A1B2'
  c=$('container_container');
  c.style.backgroundImage = 'none'
  c.style.backgroundColor = '#EEEEEE'
  return false
}

function insertAfter(newChild,refChild)
{
  var parent=refChild.parentNode;
  if(parent.lastChild==refChild) return parent.appendChild(newChild);
  else return parent.insertBefore(newChild,refChild.nextSibling);
}

function gid(id) {
  return document.getElementById(id)
}

function jinspect(obj) { var s = ""; for(var f in obj) { s += "field "+f+" = "+obj[f]+"\n"; } puts(s); }

function autorhyme() {
  if (gid('autorhyme').checked != true) return;
	lines_back = gid('rhyme_back').value
	var t = gid('item_text').value.replace(/\r/g,"").replace(/\s*$/,'').split("\n");
  //alert( gid('Story_body').selectionStart || document.selection.createRange().length-1 )
  
	var line = t[t.length - lines_back];
	if (!line) {
	  line = t[t.length - lines_back - 1]; // try one farther back, if its a blank line..
	  if (!line) { //alert("no line found back " + lines_back);inspect(t);
  	  return;
  	}
	}
	
	var word = line.match(/([^\s]+)$/)[0]
	word = word.replace(/\d+$/,"").replace(/\W$/,"");
	$('rhyme').value = word
	$('rhyme_form').onsubmit()
}

/* functions for line numbering.  beautiful */
var lines = 2
function removeLines() {
  lines = 0
  c = gid('content')
  c.innerHTML = c.innerHTML.replace(/<p>/gi,'<br>').replace(/<li>|<ol class="linenos">|<\/ol>|<\/p>/gi,'')

  if (gid('togglelines')) gid('togglelines').firstChild.data = 'Line numbers';
}

function showLines() {
  lines = 1
  c = gid('content')

	// go throught the text and create the <div> fields.  this isn't the slow part?  maybe
	// can't we use <ol> instead somehow?  just replace all <br>&<p> with <li>?
	//replace(/(<br>\r?\n<br>/gi,'<p>').
  htm = c.innerHTML
  htm = "<ol class=\"linenos\"><li>" + htm.replace(/<\/p>|(<br>\s*)+\s*\Z|\A\s*(<br>\s*)+/,'').replace(/(<br\s*\/?>\r?\n?\s*){2,}/gi,'<p>').replace(/(<br>|<br \/>|<p>)/gi, "$1<li>") + "</ol>"
  c.innerHTML = htm
  if (gid('togglelines')) gid('togglelines').firstChild.data = 'Hide numbers';

  //width = Element.getDimensions('content').width
  //$('content').style.width = (width-250)+'px'
  //alert($('content').style.width)
  //c.style.width = '' // try to force a re-evaluation of table width?
}



function old_removeLines() {
  lines = 0
  c = gid('content')
  c.innerHTML = c.innerHTML.replace(/<div class="?linenum"?>[^>]+>/gi,'')
  if (gid('togglelines')) gid('togglelines').firstChild.data = 'Line numbers';
}

function old_showLines() {
  lines = 1
  c = gid('content')

	// go throught the text and create the <div> fields.  this isn't the slow part?  maybe
	// can't we use <ol> instead somehow?  just replace all <br>&<p> with <li>?
  htm = c.innerHTML
  htm = "<div class='linenum'></div>" + htm.replace(/(<br>|<br \/>)/gi, "$1<div class='linenum'></div>")
  c.innerHTML = htm.replace(/<\/p>/gi,'').replace(/(<p>)/gi, "$1<div class='linenum'></div>")
	
	// faster to replace innerHTML with the new div characters, all at once?
	
  rows = c.getElementsByTagName('div')
  n = 1
  for (var i = 0; i < rows.length; i++) {
    if (rows[i].className == 'linenum') {
      if (rows[i].nextSibling) if (!rows[i].nextSibling.nodeValue || rows[i].nextSibling.nodeValue.match(/^\s+$/) ) continue;
      rows[i].innerHTML = n++
    }
  }
  
  if (gid('togglelines')) gid('togglelines').firstChild.data = 'Hide numbers';
  gid('Story_table').width = '100%'
  c.style.width = '' // try to force a re-evaluation of table width?
}

function spell_copy() {
  // how to check for ie first?  how to use createTextRange on a textarea field?
  if (navigator.userAgent.indexOf('MSIE')  >= 0) {
    txt = gid('Story_body')
    copied = txt.createTextRange();
    copied.execCommand("Copy");
  }
}

function to_clipboard(textarea) {
  return; // I'm worried this will giv ea security error b/c of recent MS patches.  try again l8r
  if (navigator.userAgent.indexOf('MSIE') >= 0) {
    txt = gid(textarea)
    copied = txt.createTextRange();
    copied.execCommand("Copy");
  }	
}

var word_index
var word_current
var spelling = new Object()
var words = ["spel", "spel2"]

spelling["spel"] = ["test1","test2"]
spelling["spel2"] = ["test1","test2"]

function spell_start() { 
  word_index = -1
  spell_next()
  // make sure the spellcheck is visible
  Toggle.display('spell_window')
  Effect.Highlight('spell_window')
}

function spell_change() {
  // default to first spelling choice
  options = gid('spell_suggestions')
  if ( gid('spell_word').value == word_current ) gid('spell_word').value = options[options.selectedIndex].value
  
  // replace words in the textarea with our replacement
  alert(gid('Story_body').value)
  gid('Story_body').value = gid('Story_body').value.replace(new RegExp('\\b' + word_current + '\\b', 'g'), gid('spell_word').value )  //new RegExp('blah')
  
  //alert( gid('Story_body').value.replace(/\bspel\b/,'testing regex') )
  spell_next();
}

function spell_next() {
  word_index += 1
  if (!words[word_index]) {Toggle.display('spell_window'); return; }
  
  word = words[word_index]
  gid('spell_word').value = word_current = word
  gid('spell_suggestions').options.length = 0
  
  for (i=0; i < spelling[word].length; i++) {
    gid('spell_suggestions').options[i] = new Option( spelling[word][i], spelling[word][i] )
  }
}


function findSelection()
{
	if (document.getSelection) txt = document.getSelection();
	else if (document.selection) txt = document.selection.createRange().text;
	else return "";
	return txt;
};

function keyhandler(e) {
    if (window.event) 
    	Key = window.event.keyCode;
    else 
        Key = e.which;

    if (Key == 100) { 
    	var result = findSelection();
    	result.replace(/\s*$/,'');
    	
        if (result.length && result.length < 125 && result.indexOf(' ') == -1) 
        	var toast = window.open('/word/'+escape(result),'0','toolbar=1,scrollbars=1,location=0,statusbar=1,menubar=1,resizable=1,width=510,height=510,left=280,top=80');
     }
};

var arVersion = navigator.appVersion.split("MSIE")
var version = parseFloat(arVersion[1])
function fixPNG(myImage) 
{
    if ((version >= 5.5) && (version < 7) && (document.body.filters) && isIE) { // if (isIE) { // indow.ie55up
     var imgID = (myImage.id) ? "id='" + myImage.id + "' " : ""
     var imgClass = (myImage.className) ? "class='" + myImage.className + "' " : ""
     var imgTitle = (myImage.title) ? "title='" + myImage.title + "' " : "title='" + myImage.alt + "' "
     var imgStyle = "display:inline-block;" + myImage.style.cssText 
     var strNewHTML = "<span " + imgID + imgClass + imgTitle
     strNewHTML += " style=\"" + "width:" + myImage.width + "px; height:" + myImage.height + "px;" + imgStyle + ";"
     strNewHTML += "filter:progid:DXImagetransform.Microsoft.AlphaImageLoader"
     strNewHTML += "(src=\'" + myImage.src + "\', sizingMethod='scale');\"></span>" 
     myImage.outerHTML = strNewHTML
  }
}


/* comment failure functions */
function complete_comment(request, p) {	
  if (!p) p=''
	waited(p + 'submit');
	
	if (request.status == 200) {
	  //new Effect.BlindDown($('comment_list').lastChild);// to just move the new comment
		Element.hide(p + 'interm')
		
		// what is this from?  !p won't match anymore, since it's =='', doesn't work, no easy way2 access it?
		//if ( p == '' && $(p+'comment_list') ) {
  	//  Element.cleanWhitespace(p + 'comment_list');
		//  new Effect.Highlight($(p + 'comment_list').firstChild);  //hilight the new comment?
		//  //if ($('rating_p')) {// no div around ratings anymore?  odd...
		//    //Element.hide('rating_p','rdesc')
		//    //Effect.BlindUp('rating_p')
		//    //Effect.BlindUp('rdesc')
		//  //}
	  //}
	  
	  // only if we're on an item vs. contest/etc, how to tell...?
	  //if ($('newcomment_msg') && $F('comment_kind') == 'item') {
	  //  len = $F('comment_body').length
	  //  if (len != 0) {
    //    Element.show('newcomment_msg');	  
    //    $('newcomment_size').innerHTML = len
    //	  if ($F('comment_body').length > 100 ) {
    //	    Element.show('newcomment_points');
    //	  } else {
    //	    Element.show('newcomment_short');
    //	  }
    //	}
    //}
    
    if (comment_error==0) {
		  Field.clear(p + 'comment_subject')
		  Field.clear(p + 'comment_body')
		  // resize box... doesn't seem to be working?
		  //console.log('resizing comment body')
      grow('comment_body', false, 5, 3) 
      grow('qc', false, 5, 3)
      //console.log($('comment_body').rows)
	  }
	
		// perhaps change the item's div id or something so that it doesn't move along with us?
	};
}

var comment_error=0;
function interm_comment(p) {
  if (!p) p=''  
  comment_error=0
	pleasewait(p + 'submit');
	//puts('showing ' + p+'interm_wait' + $(p+'interm_wait'))
	
	// use their pic even when we're cached
	if ($('interm_by_src')) {	$('interm_by').innerHTML = $('interm_by_src').innerHTML }
	
	// show it before we set the content for it? odd
	Element.show(p + 'interm_wait')
	// speed of scroll should be porportional to distance, arg...
	if (p == '') new Effect.ScrollToFullView(p + 'comment_list', {duration: 0.9, offset: 236}, {sync: true});
	$(p + 'interm_subject').innerHTML = $(p + 'comment_subject').value
	body = $(p + 'comment_body').value
	
	body = body.replace(/\r|\n|\r\n/g,'<br>') // not working...

	$(p + 'interm_body').innerHTML = body
	// when blinddown is over, then it's alignment is off...

	new Effect.BlindDown(p + 'interm');
	
	// dont show evil msg about featured items, if we were going to
	//window.onbeforeunload = function () { return "You have not saved your ..
  window.onbeforeunload = null	
}

function comment_failure(request, p) {
  if (!p) p=''  
  waited(p + 'submit')  
	Element.hide(p + 'interm_wait')

	$(p + 'interm_subject').innerHTML = 'Error adding comment, please try again.'
	$(p + 'interm_body').innerHTML = "Error number is " + request.status + ". "
	if (request.status == '500') $(p + 'interm_body').innerHTML += "This error was automatically recorded for fixing.";
		
	new Effect.Highlight(p + 'interm', {duration: 5.0})
}

function logsize(num) {
  logConsole.outputElement.style.height = num+'px'
}
Position.Window = {
    //extended prototypes position to return
    //the scrolled window deltas
    getDeltas: function() {
        var deltaX =  window.pageXOffset
            || document.documentElement.scrollLeft
            || document.body.scrollLeft
            || 0;
        var deltaY =  window.pageYOffset
            || document.documentElement.scrollTop
            || document.body.scrollTop
            || 0;
        return [deltaX, deltaY];
    },
    //extended prototypes position to
    //return working window's size, 
    //copied this code from the tooltip.js library
    size: function() {
        var winWidth, winHeight, d=document;
        if (typeof window.innerWidth!='undefined') {
            winWidth = window.innerWidth;
            winHeight = window.innerHeight;
        } else {
            if (d.documentElement && typeof d.documentElement.clientWidth!='undefined' && d.documentElement.clientWidth!=0) {
                winWidth = d.documentElement.clientWidth
                winHeight = d.documentElement.clientHeight
            } else {
                if (d.body && typeof d.body.clientWidth!='undefined') {
                    winWidth = d.body.clientWidth
                    winHeight = d.body.clientHeight
                }
            }
        }
        return [winWidth, winHeight];
    }
}


var lastscroll = -1;
var elementDimensions;

Effect.KeepFixed = function(element, offsetx, offsety, skip, altX) {
    var _scroll = Position.Window.getDeltas();
    // 40 is height we need to be from top to start showing?  moving to 20
    if (_scroll[1] == lastscroll || _scroll[1] < 5) return [false];
    lastscroll = _scroll[1];
    
    //puts("newscroll:" + lastscroll)
    var _window = Position.Window.size();
    var elementDimensions = Element.getDimensions(element);
    //if (!elementDimensions) elementDimensions = Element.getDimensions(element);    
    var eWidth = elementDimensions.width;
    //var eHeight = 88//elementDimensions.height;//this is bigger at first?  odd!
    var eHeight = elementDimensions.height;
    var moveX = _window[0] - eWidth + _scroll[0] + offsetx - 19; // -19 was perfect at edge of sidebar
    // try moving Y up higher, hard to guess based on screen size, but a few hundred... was -19
    var moveY = _window[1] - eHeight + _scroll[1] + offsety; // - 159;
    //d("_window[1]=" + _window[1] + ", eHeight " + eHeight + ", scroll[1]" + _scroll[1] + ", offsety:" + offsety)
    
    //console.log(_window[0]);

    altX = Position.page($('container_container'))[0]
    storysize=Element.getDimensions($('container_container'))
    moveX = altX + storysize.width - 230;
      
    // hmm, on second thought, better aligned flush with the right of the page
    // altered this to use container_container from main before...  much better!
    if (false) {
      altX = Position.page($('container_container'))[0]
      storysize=Element.getDimensions($('container_container'))
      moveX = altX + storysize.width - eWidth - 9;
    }
    
    
    newsize = 215 + altX; // 218 in defaults.  yay, perfect finally!
    if (newsize > 300) newsize = 300;
    $('fixedcomment').style.width = newsize+'px';
  
    if (!skip) new Effect.Move(element, { x: moveX, y: moveY, mode: 'absolute', duration: 0.3, fps: 50 });
    return [moveX, moveY]
}

function movecomment() {
  //d("movecomment")
 //nterval ( 'document.layers.divID.top=window.pageYOffset;', 100 ); }
 // hide it if real comment form is visible
 //|| !Element.visible('fixedcomment')
 // can disable with visibility=='hidden'
  element=$('fixedcomment')
  if (!element || element.style.visibility=='hidden') return
  //if () return
  
  //puts( 0-Position.page($('main'))[0] )
  
  // hmm, moves to 0,0 within it's current div box, want to move releative to the whole window!
  // now that we have ie7, possible to just do this in CSS?  Easier?  Hmm...
  
  // moving to my own css-based solution?
  coords = new Effect.KeepFixed(element, 0, 0, false, true) // isIE, -24
  if (!coords[0]) return
  
  // start how many pixels down from the top of the window?
  // can I do this with relative positioning?
  //element.style.position = 'relative'
  //element.style.top = window.scrollTop+500+ "px"
  //element.style.right = "5px"
  //d("element top: ", element.style.top)
  //return; // aah, detection of where to move it requires coords too.  hmm, maybe 

  
  //d(Position.positionedOffset($('comment_body'))[1] + ' vs ' + coords[1] + ' or' + coords)
  
  // if our destination is past the comment box, hide us
  //puts('pos=' + Position.positionedOffset($('comment_body'))[1] + ' vs coords:' + coords[1] + ' vis: ' + Element.visible(element))
  
  // hide it if we're alongside the regular comment box...  was +230
  // 100 is too small, increasing some again
  if (Position.positionedOffset($('comment_body'))[1] < coords[1]+170) {
    if (Element.visible(element)) {
      //puts('hiding')
      bod = $('comment_body')
      grow(bod, false)
      Field.focus(bod)
      // what is this doing, setting the scroll of the window to the end of the long comment being entered?
      //bod.scrollTop = bod.scrollHeight;      
      bod.scrollTop = bod.scrollHeight - bod.clientHeight;
      
      new Effect.Fade(element, {duration:0.15})      
      if ($F('qc') != '') new Effect.Highlight('comment_body')// probs with firefox ==lines
      
      // automatically copies our value into the main comment box.  but this one might be out of date! how do we keep then synchronized?  a) test for which is bigger?  b) update this one when we change that one, or c) update that one when we change this one (prob best, since this can dissapear then...)
      //$('comment_body').value = $F('qc')  

    }
    
  } else {    
    if (!Element.visible(element)) {
      element.style.top = "0px"
      //element.style.left = coords[0]+'px'; element.style.top=coords[1]+'px';// doesn't really work?
      $('qc').value = $F('comment_body')
      new Effect.Appear(element)
      
      // somehow gets moved to y=6000, fucking A  Position.page($('fixedcomment')) 
      //puts('done showing, vis' +Element.visible(element) + ' loc:' + Position.page(element) )
    }
  }  
}

function app_cst(num) {
	cost = num-freeleft
	
	if (cost < 0) {
		freeleftnow = freeleft - num
		if (freeleftnow < 0) freeleftnow = 0 
	
		Element.hide('acst')
		Element.show('afree')		
		$('afreen').innerHTML = freeleftnow
	} else {
		Element.hide('afree')
		Element.show('acst')
		$('acstn').innerHTML=cost*app_amt	
	}
	
	// what is this part for?
  //console.log(document.comment_form.applause[num])	
	document.comment_form.applause[num].checked = true // array works like that?	
}

function app_inline(sid, num) {
  app = $(sid + '_' + num)
  app.checked = true
	$(sid + "_cost").innerHTML=num*app_amt
}

function tag_in(col, val) {
  v = $(col)
  if (v.value.length + val.length > 85) return false;// max size
  
  if (!v.value.match(val + ',')) {
    if (v.value && !v.value.match(/, $/)) v.value += ', ';
    v.value += val + ', '
  }
  return false
}


var tiny_options = {
	//relative_urls : true, convert_urls : true,
	theme : 'advanced',
	// what is this for? needed to avoid it converting urls oddly...  but breaks spellchecker!
  //relative_urls : false,  remove_script_host : false,	document_base_url : 'http://' + location.hostname + "/s/javascripts/tiny_mce",
  convert_urls : false, // trying this, since it doesn't seem to break spellcheck...
  
	verify_html : true, 
	entities : "160,nbsp", 	// don't fuck with other chars
	extended_valid_elements : 'hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style],b,center,a[href|title|alt|showhide|hideshow|toggle|target|rel],style[type]',
	//,td[style|colspan|rowspan|width|height|bgcolor] // causes problems
	//auto_resize : true,// makes it expand down, infinitely.  wish there was a way to limit it?		
	plugins : 'inlinepopups,emotions,paste,fullscreen,table,spellchecker,searchreplace',
	theme_advanced_toolbar_location : 'top',
	theme_advanced_toolbar_align : 'left',	
	// pretty good list by default, but more are fairly safe, damn, doesn't like this
	theme_advanced_fonts : 'Andale Mono=Andale Mono;Arial=Arial;Arial Black=Arial Black,Sans-serif;Book Antiqua=Book Antiqua,Sans-serif;Bookmark Old Style=Bookmark Old Style,Arial,Sans-serif;Century Gothic=Century Gothic,Sans-serif;Century Schoolbook=Century Schoolbook,Sans-serif;Comic Sans MS=Comic Sans MS,Sans-serif;Courier New=Courier New,Monospace;Garamond=Garamond;Georgia=Georgia;Helvetica=Helvetica;Impact=Impact;Monospace=Monospace;Palatino=Palatino;Sans-serif=Sans-serif;Serif=Serif;Tahoma=Tahoma,Sans-serif;Terminal=Terminal,Monospace;Times New Roman=Times New Roman,Serif;trebuchet MS=trebuchet MS,Serif;Verdana=Verdana,Sans-serif;Webdings=Webdings;Wingdings=Wingdings',
	/* available buttons: 
	 			bold,italic,underline,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,
	 			styleselect,formatselect bullist,numlist,outdent,indent,
	 			undo,redo,link,unlink,anchor,image,cleanup,help,code,
	 			hr,removeformat,visualaid,sub,sup,charmap,separator,code */
	theme_advanced_buttons1 : 'spellchecker, bold,italic,underline,strikethrough,separator,table,row_after,col_after,delete_row, delete_col,separator,justifyleft,justifycenter,justifyright,justifyfull,fontselect,fontsizeselect',
	theme_advanced_buttons2 : 'hr,bullist,numlist,outdent,indent,separator,link,unlink,image,separator,fullscreen,removeformat,code,separator,forecolor, backcolor,emotions,searchreplace,separator,cut,paste,pasteword',	
	theme_advanced_buttons3 : '',
	content_css : '/s/stylesheets/tinymce.css',
	fullscreen_settings : {
		theme_advanced_path_location : "top",
		content_css : '/s/stylesheets/tinymce.css'
	}
	//undo,redo,cleanup
	//,height:'500px'
	, width:'97%' // better to use textarea size, this baad 4 comments, and appears to have problems?
	//... but we don't change editor size anymore?  sure... 600px
}

var free_options = {
	theme : 'advanced',
	//verify_html : true, 
	entities : "160,nbsp",
	plugins : 'inlinepopups, spellchecker',
	theme_advanced_buttons1 : '', //'spellchecker', // just start it automatically...
	theme_advanced_buttons2 : '', theme_advanced_buttons3 : '',	
	content_css : '/s/stylesheets/tinymce.css',
	width:'97%'
}

var silver_options = {
	theme : 'advanced',
	verify_html : true, 
	entities : "160,nbsp",
	plugins : 'inlinepopups, spellchecker, fullscreen',
	theme_advanced_buttons1 : 'spellchecker,bold,italic,underline,strikethrough, fullscreen',
	theme_advanced_buttons2 : '', theme_advanced_buttons3 : '',	
	theme_advanced_toolbar_location : 'top',	
	theme_advanced_toolbar_align : "left",	
	content_css : '/s/stylesheets/tinymce.css',
	fullscreen_settings : {
		theme_advanced_path_location : "top",
		content_css : '/s/stylesheets/tinymce.css'
	},
	width:'97%'
}

function check_all(classname) {
  set = document.getElementsByClassName(classname)
  checked = !set[0].checked
  set.each( function(i) {i.checked = checked} )
  return false
}

function im_show_dates() {
  document.getElementsByClassName('imdate').each( function(i) { Element.show(i) } )
  return false
}

function im_open(classname) {
  document.getElementsByClassName('ir').each( function(i) { Element.show(i) } )
  return false
}

function im_reply(form,id) {
  new Ajax.Request('/chat/say', {asynchronous:true, evalScripts:true, onLoaded:function(request){waited('ims_'+id, 'Your message was sent');$('imm_'+id).innerHTML=' &nbsp; <b>Replied to '+Form.getInputs(form,'hidden','to')[0].value+'</b>'; twist($('imt_'+id),'imr_'+id)}, onLoading:function(request){pleasewait('ims_'+id)}, parameters:Form.serialize(form)}); return false;
}

function im_del(id) {
  //new Ajax.Updater('rate_' + eid, '/comment/rate/' + eid + '?stars=' + star, {asynchronous:true, evalScripts:true, onLoading:function(request){$('rate_' + eid).innerHTML = "Rating <img border=0 src='/s/images/wait.gif'>"}}); return false
    
  new Ajax.Request('/im/delete/'+id, {asynchronous:true, evalScripts:true, onLoaded:function(request){}, onLoading:function(request){new Effect.Fade('im_' + id)} }); return false;
}

var ChatScroll = new Object();
ChatScroll.Pane = function(scrollContainerId) {
    this.bottomThreshold = 50;
    this.scrollContainerId = scrollContainerId;
}

ChatScroll.Pane.prototype.resize =
function()    {
    var scrollDiv           = document.getElementById(this.scrollContainerId);
    scrollDiv.style.height  = (document.documentElement.clientHeight - 225) + 'px';
    scrollDiv               = null;
    
    this.scrollToEnd();
}

ChatScroll.Pane.prototype.scrollToEnd =
function()    {
    var scrollDiv       = document.getElementById(this.scrollContainerId);
    scrollDiv.scrollTop = scrollDiv.scrollHeight;
    scrollDiv           = null;
}

ChatScroll.Pane.prototype.isScrollable = 
function()    {
    var scrollDiv = document.getElementById(this.scrollContainerId);
    var currentHeight = 0;
    
    if (scrollDiv.scrollHeight > 0)
        currentHeight = scrollDiv.scrollHeight;
    else 
        if (objDiv.offsetHeight > 0)
            currentHeight = scrollDiv.offsetHeight;

            // so scrollDiv.scrollHeight - scrollDiv.scrollTop - (scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight) > 50 == minimum scrolled by
    var Result = (currentHeight - scrollDiv.scrollTop - ((scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight) < this.bottomThreshold);
    scrollDiv  = null;
    
    return Result;
}



	function go()	{
		connection = new AFLAX.Socket(aflax, server, 4442, "onConnectEvent", "onDataEvent", "onCloseEvent");
		scroll_now('chatroom')	// doesnt seem to scroll it all the way for some reason in IE?
	}	
	
	function ch_go()	{
		ch_connection = new AFLAX.Socket(ch_aflax, ch_server, 4442, "ch_onConnectEvent", "ch_onDataEvent", "ch_onCloseEvent");
		scroll_now('ch_chatroom')		
	}	
	
	function onConnectEvent(val)	{
	  scroll_now('chatroom')	  
	  
    if(val) {
			connection.send("join%%" + room + "%%" + join_str)
			connected = 1
			if (send_msg) connection.send("say%%" + room + "%%" + send_msg)
		} else {
			onDataEvent("Could not connect to server.");
		}
	}
	
	function scroll_now(chat_id) {
	  // push it by a bit, since it seems to be a bit short still?  No, must just be a timer prob
	  $(chat_id).scrollTop = $(chat_id).scrollHeight + 100
	}
	
	function onDataEvent(str)	{ parse_message(str, 'chatroom') }

	function onCloseEvent()	{
		onDataEvent("Connection lost- <a href='#' onclick='connection.connect(server, 4442); return false'>Reconnect</a>?");
		connected = 0
	}

	function send(str)	{
		scroll_now('chatroom')
		connection.send("say%%" + room + "%%" + str);
		$('livechat_input').focus()
	}
		
	var set_height_under = 0 // 265 if we want it to grow
	var beep_first_enter = 0
	var last_beep = 0 // not used
	
	// WTF is the diff btwn scroll_down and scroll_now?
	function scroll_down(chatroom_id) {
	  s = $(chatroom_id)
	  diff = s.scrollHeight - s.scrollTop - ((s.style.pixelHeight) ? s.style.pixelHeight : s.offsetHeight)
	  //console.log("diff:", diff)
	  //console.log('scrollheight:', s.scrollHeight, 'scrolltop:', s.scrolltop, 'pixelheight:', s.style.pixelHeight)
	  
		if (diff < 82) { // 21 == always, 41 = one line
		  s.scrollTop = s.scrollHeight + 100
		  setTimeout("$('" + chatroom_id + "').scrollTop = $('" + chatroom_id + "').scrollHeight", 300) // always do it twice? hmm
		  return true
		  //console.log('scrolling down')
		} else {
		  
		  //console.log("not scrolling down")
		  return false
		}
	}
	
	// just returns true/false, checks whether it should scroll down
	function to_scroll_down(chatroom_id) {
	  s = $(chatroom_id)
	  diff = s.scrollHeight - s.scrollTop - ((s.style.pixelHeight) ? s.style.pixelHeight : s.offsetHeight)

		if (diff < 82) { // 21 == always, 41 = one line
		  return true
		} else {
		  return false
		}
	}
	
  // hmm, really want to allow js=click to IM w/names?  bold AND linked? no... just link with u('name')
  //if (set_height_under != 0 && chat.scrollHeight > set_height_under) { chat.style.height = set_height_under+'px'}
  //var buffer;// ugg, need a buffer for each chat_id?  not really, since only will be one connected at a time... but better to not require that
  var buffer = new Object()
  //spelling["spel"] = ["test1","test2"]

  var parse_timeout = false;
  
  // only let this be called every 200 ms; else add to buffer.  when user returns to chat, all is processed immediately.  should avoid wierd lag scenario
	function parse_message(str, chat_id)	{
	  c = $(chat_id)
	  //console.log("got data", str, "chat id:", chat_id, c)  //substring(str,0,2)
	  
	  if (str.substring(0,2) == '%%') {
	    //console.log("cmd = '", cmd, "', msg= '", msg, "'")
      // chrome hates this?
			//[skip, cmd, msg, msg2] = str.split('%%')
			my_arr = str.split('%%')
	    skip = my_arr[0]; cmd = my_arr[1]; msg = my_arr[2]; msg2 = my_arr[3];

	    if (cmd == 'clear') {
	      c.innerHTML = 'Cleared<br />'

	    } else if (cmd == 'im') { // %%im%%kevin%%hey how are you?
  		  s = "<a href='/"+msg+"' onMouseOver=\"userdrop(this, event, '"+msg+"')\" onMouseout='delayhidemenu()'>"+msg+"</a>: "+msg2+"<br>"
  		  add_and_shorten(s, 'new_ims', 1000, 200)
        $('new_ims').innerHTML = h
        
      } else if (cmd == 'noti') {
        
      } else if (cmd == 'chat') { // user-to-user real-time chat.  different from ims=how?  no storage really
        // chat server keeps track of which ones you've replied to and re-sends @ connect until X minutes or you've responded?
        // now that we've put the parse_timeout code on the chatroom only, this will have the same problem...
        c = get_chat_div(msg) // make sure its created
        add_and_shorten(msg2, 'chatw_'+user_name, 1000, 200)
        scroll_down('chatw_'+user_name)
	    } else {
	      // show some error message
	      //console.log("unknown cmd:", cmd)
	    }
	  } else {
	    //console.log("msg:", str) //, "parse_timeout:", parse_timeout, "buffer:", buffer[chat_id])
	    // not solving the delay problem.  skip this then for now, and revisit later?
  	  if (parse_timeout) { 
  	    if (!buffer[chat_id]) buffer[chat_id]=''; 
  	    buffer[chat_id] += str + "<br>"; 
  	    console.log("buffer now:", buffer[chat_id])
  	    return
  	  } 
  	    	  
  	  parse_timeout=true
  	  setTimeout("clear_chat_buffer('"+chat_id+"')", 180); // longer than usual for testing.  200
  	  if (buffer[chat_id] && buffer[chat_id] != ''){ str = buffer[chat_id] + str; buffer[chat_id] = '' }
      
      to_scroll = to_scroll_down(chat_id)
      
      // shouldn't it be the other way around?  cbox is small, chatroom is big... ch_chatroom else
      if (chat_id == 'chatroom') {
	      add_and_shorten(str, chat_id, 4500, 500)
	    } else {
	      add_and_shorten(str, chat_id, 9500, 500)
	    }
	    
  		if (to_scroll) {
  		  //scroll_now(chat_id)
  		  scroll_down(chat_id)  		  
  		}
	  }
	}
	
	// new drop down menu will give us the option to send this?	
	function get_chat_div(user_name) {
	  return
	  c = $('chatw_'+user_name)
	  if (c) return c
	  // make sure aux chat window is open.  use bottom after all now?  doesnt really matter where we put it
	  if (!Element.visible('aux_chats')) new Effect.BlindDown('aux_chats')
	  // needs it's own div for the input box, or not?
	  $('aux_chats').innerHTML += "<div id='chatw_"+user_name+"'></div><div id='chatm_"+user_name+"'><input type='text' style='width:100%' onkeypress=\"if (evt.keyCode == Event.KEY_RETURN) {send_user_chat('"+user_name+"',this.value)\"} /></div>"
	  return $('chatw'+user_name)
	}
	
	function send_user_chat(user_name, msg) {
		ch_connection.send("uchat%%" + user_name + "%%" + msg)
		scroll_down('chatw_'+user_name)
	}
	
	// not being used right now
	function clear_chat_buffer(chat_id) { // single threaded, don't worry too much
	  parse_timeout = false; 
	  if (!buffer[chat_id] || buffer[chat_id] == '') {return}
	  
	  console.log('clearing buffer:', buffer[chat_id])	  
	  v = $(chat_id).innerHTML 
	  v += buffer[chat_id]
	  $(chat_id).innerHTML = v // less flicker when we do it like this?
	  buffer[chat_id] = ''
	  scroll_down(chat_id)
	}
	
	// avoid problem of chopping off text and leaving an empty html tag
	function add_and_shorten(message, chat_id, max_size, remove_amount) {
	  h = $(chat_id).innerHTML + message + "<br>"
	  //console.log(h.length, " vs ", max_size)
	  if (h.length > max_size) { 
	    // remove more if it's longer
	    if (h.length > (max_size+remove_amount)) { remove_amount = h.length-max_size-remove_amount;}
	    h = h.substring(remove_amount)
	    lookfor = '<a ';  mat = h.match(/<a /mi); if (mat) lookfor = mat[0]; // could go back to <br> now
	    where = h.indexOf(lookfor) // might it change quotes too??? assume links mostly same+safe
	    //console.log("Where is ", where, ", lookfor is:", lookfor, ", start is:", h.substring(0,75))
	    //where = h.indexOf('<br>')+4
	    //if (where == -1) {where = h.indexOf('<BR>')+4}
	    //if (where == -1) {where = h.indexOf('<BR />')+4}
	    //if (where == -1) {where = h.indexOf('<br />')+4}	//arg!
	    if (where > 800 || where == -1) {where = 0; }
	    
	    h = h.substring(where) // strip up to <br>. case matters!
	  }
	  
	  $(chat_id).innerHTML = h
	}	
	
	function ch_onConnectEvent(val)	{
    if(val) {
			ch_connection.send("join%%" + ch_room + "%%" + join_str)
			ch_connected = 1
			if (ch_send_msg) setTimeout("ch_connection.send('say%%' + ch_room + '%%' + ch_send_msg)", 500)
		} else {
			ch_onDataEvent("Could not connect to server.")
		}
	}
	
	
	function ch_onDataEvent(str)	{ parse_message(str, 'ch_chatroom') }
	
	function ch_onCloseEvent()	{
		ch_onDataEvent("Connection lost- <a href='#' onclick='ch_connection.connect(ch_server, 4442); return false'>Reconnect</a>?");
		ch_connected = 0
	}

	function ch_send(str)	{
		ch_connection.send("say%%" + ch_room + "%%" + str)
		scroll_now('ch_chatroom')
	}

  var num_posts = 0
	var ch_set_height = 0
	
	
/* mp3 player stuff */
	function thisMovie(movieName) {
	  // does this not match anymore?  never alerts...
	  if(navigator.appName.indexOf("Microsoft") != -1) {
			return window[movieName];
		} else {
			return document[movieName];
		}
	};
	
	function sendEvent(typ,prm) {
	  //alert("sending " + typ + prm)
		thisMovie("mediaplayer").sendEvent(typ,prm);
	};	
	
	// looks like we get a state: 0 call when it first initalizes?
  var first_volume = 0;
	function getUpdate(typ,pr1,pr2) {
    //var id = document.getElementById('output');
		//id.innerHTML += "<br>" + typ+ ": "+Math.round(pr1);
		if (typ == "volume" && first_volume == 0 && $("song")) {first_volume=1;sendEvent("playitem",$("song").value)}

		//puts("'" + typ  + ": " + pr1  + ": " +pr2)
		if (first_volume == 1 && typ == "item") $("song").value = pr1
	};
	
	//can we just point our music player at a playlist.com music list?  I doubt it!
	function openMusic(autostart, addVars) {
    if (!autostart) autostart = 'false'
    Element.show('player2');
    
		var FU = {movie:"/s/images/mediaplayer.swf",id:"mediaplayer",name:"mediaplayer",width:"305",height:"300",majorversion:"7",build:"0",bgcolor:"#FFFFFF",	flashvars:"file=/s/music/music.xml&displayheight=0&repeat=true&lightcolor=0xCC9900&backcolor=0x000000&frontcolor=0xCCCCCC&shuffle=false&enablejs=true&"+autostart+"=false"+addVars };
		UFO.create(FU, "player2");
		
		Element.show('music_info')
		Element.hide("show_player")
		$('use_music').checked = true //'checked'
	}
	
	function playitem(num) {
	  // this may be messing up IE, so it stops any execution?  Hmm..
	  // hopefully this is long enough for people over a medium connection? 400 worked 3 me
	  // 550 wasn't enough in AP production, hmm...perhaps we need a better way to handle this?  just skip it and default music_id?
	//  setTimeout("sendEvent(\"playitem\",num)", 950)
	}/* p*pup.js is a bad name for this, as some browsers will block it. but will be combined anyways */
/* <style type="text/css">
#dropmenudiv{
    position:absolute;
    border:1px solid black;
    font:normal 12px Verdana;
    line-height:18px;
    z-index:100;
}
</style> */

function motto(txt) {
  return ''
}

function local_drop_html(name) {
  //return '<a href="/' + name + '">My page</a><br><a href="/journal/add/">Add post</a><br><a href="/user/account/">My Account</a><br><a href="/favorites">My Friends</a><br><a href="/bookmark/show/' + name + '">My Bookmarks</a><br><a href="/login/logout">Logout</a>'  
  return '<a href="/user/account">My account</a><a href="/' + name + '">My profile</a><a href="/user/face/' + name + '">My face</a><a href="/story/by/' + name + '">My stories</a><a href="/favorite/fav">My favorites</a><a href="/favorite/enjoy">I might enjoy</a><a href="/bookmark/show/' + name + '">My bookmarks</a><a href="/story/add">Add story</a><a href="/login/logout">Logout</a>'
}

/***********************************************
* AnyLink Drop Down Menu- © Dynamic Drive (www.dynamicdrive.com)
* This notice MUST stay intact for legal use
* Visit http://www.dynamicdrive.com/ for full source code
http://www.dynamicdrive.com/dynamicindex1/dropmenuindex.htm
heavily edited now of course
***********************************************/
menu = new Hash

menu['store'] = [
  ['Support Allpoetry!']
]



menu['add_sp'] = [
	 ["Poem", "/poem/add" ],
	 ["Journal", "/journal/add" ],
	 ["Story", "/home/send_to/storywrite?ret=~story~add" ],
	 ["From Draft", "/poem/drafts" ]
]


menu['add_sw'] = [
	 ["Story", "/story/add" ],
	 ["Column", "/column/add" ],
	 ["Contest", "/contest/add" ],
	 ["List", "/list/add" ],	 
	 ["Addline", "/addline/add" ],	 
   ["Freewrite", "/topic/list"],	 
	 ["Journal", "/journal/add" ],
	 ["Poem", "/home/send_to/allpoetry?ret=~poem~add" ],
	 ["From Draft", "/story/drafts" ]	 
]

menu['add_ap'] = [
	 ["Poem", "/poem/add" ],
	 ["Column", "/column/add" ],
	 ["Contest", "/contest/add" ],
	 ["List", "/list/add" ],	 
	 ["Addline", "/addline/add" ],	 
   ["Freewrite", "/topic/list"],	 
	 ["Journal", "/journal/add" ],
	 ["Story", "/home/send_to/storywrite?ret=~story~add" ],
	 ["From Draft", "/poem/drafts" ]
]


menu['forums'] = [
  ["Forum list", "/board/list"             ],
  ["Poetry Related", "/board/show/2"       ],
  ["How was your day?", "/board/show/5"       ],
  ["Publishing", "/board/show/1"       ],    
  ["Poetry outside AP", "/board/show/3"       ],
  ['Chatting boards'],
  ["Teen Board", "/board/show/104"         ],
  ["Adult Board - Cafe", "/board/show/109" ],  
  ["Adult Board - Pub", "/board/show/108"  ],
  ["Policy Board", "/board/show/107"       ]
]

menu['forums_sw'] = [
	 ["Board list", "/board/list" ],
	 ["Writing Related", "/board/show/1" ],
	 ["Adult Board", "/board/show/108" ], 
	 ["Social Board", "/board/show/2" ],	
	 ["Policy Board", "/board/show/16" ]
]


menu['forums_sp'] = [
	 ["Board list", "/board/list" ],
	 ["About Poetry Board", "/board/show/2" ], 
	 ["Publishing Board", "/board/show/9" ], 	 
	 ["Social Board", "/board/show/1" ],	
	 ["Policy Board", "/board/show/16" ]
]

menu['forums_aj'] = [
	 ["Board list", "/board/list" ],
	 ["Adult Board", "/board/show/108" ], 
	 ["Social Board", "/board/show/2" ],	
	 ["Policy Board", "/board/show/16" ]
]

menu['forums_ca'] = [
	 ["Board list", "/board/list" ],
	 ["Adult Board", "/board/show/3" ], 
	 ["Social Board", "/board/show/2" ],
	 ["Policy Board", "/board/show/1" ]
]

menu['forums_aph'] = [
	 ["Board list", "/board/list" ],
	 ["General Philosophy", "/board/show/15" ], 
	 ["Controversial", "/board/show/13" ], 	 
	 ["Meet the Philosophers", "/board/show/17" ],	
	 ["Policy Board", "/board/show/71" ]
]


menu['latest'] = [
	 ["Latest list", "/board/list" ],
	 ["Add new topic", "/topic/recent#add" ], 
	 ["Publishing Board", "/board/show/9" ], 	 
	 ["Social Board", "/board/show/1" ],	
	 ["Policy Board", "/board/show/16" ]
]


menu['mods'] = [
['tickets', '/ticket/list'],
['board', '/board/show/Group%20board%20Moderators' ],
['mod help', '/admin/guide'],
['pending spotlights', '/spotlight/pending'],
['group spotlights', '/group/spots'],
['unjudged contests', '/contest/unjudged'],
['verify/remind/tools', '/admin/tools'],
['check ip', '/admin/ip'],
['new users', '/admin/newmembers'],
['files/bans'],
['admin log', '/admin/file?admin=1'],
['name changes', '/help/namechanges'],
['files', '/admin/file'],
['bans', '/ban/list'],
['board bans', '/admin/boardban'],
['chat bans', '/admin/chatban'],
['chat complaints', '/chat/complaintlist'],
['chat log', '/admin/chatlog'],
['mod-deleted', '/item/mod_deleted'],
['deleted groups', '/admin/groupback']
]

menu['modadmins'] = [
['motd', '/admin/motd'],
['combine names', '/admin/combine'],
['in-links', '/in/in_show'],
['sales', '/store/manage_sales'],
['stats', '/admin/stat'	]
]

menu['messages'] = [
  ['My Messages','/im'],
  ['Notes', '/im/notes']]

menu['updates'] = [
  ['Friend Updates','/favorite/updates'],
  ['My Messages','/im'],
  ['Notes', '/im/notes']]
  
menu['mypage'] = [
  ['Items','/item/by/'],
  ['Groups','/group/mine/'],  
  ['Bookmarks', '/bookmark/show/'],  
  ['Favorites', '/favorite/show/'],    
  ['Lists', '/list/by/'],
  ['Columns', '/column/by/'],
  ['Contests', '/contest/by/'],
  ['Drafts', '/item/drafts/'],  
  ['Journals', '/journal/by/'],    
  ['Views', '/item/logall/'],
  ['Comments on', '/comment/to/']
  ]

menu['stories'] = [
["Home", "/" 	                             ],
['Popular','/story/list'],
['Newest','/story/newest'],
["Rewarding", "/reward/show" 	             ],
['Categories','/tag'],
['Random','/story/random'],

["People Related"                          ],
["Find Author", "/user/search"             ],
["New Authors", "/user/new" 		           ],
["By Kids", "/item/kids"                   ],
["By your favorites", "/favorite/recent"   ],

["Type Related"                            ],
["Practice!", "/topic/list" 	             ],
["Latest Comments", "/comment/list" 	     ],
['Lists','/list'],
["Columns", "/column/syndicated"           ],
['Add-lines','/addline/list'],
['Shameless','/shameless']
]

menu['sharepoetry'] = [
['Offering Rewards','/reward/show'],
['Popular this week','/poem/list'],
['Popular all-time','/poem/popular'],
['Newest','/poem/newest'],
['Categories','/tag'],
['Random','/poem/random'],
['Recent comments','/comment/list']]


menu['popular'] = [
['This week','/journal/list'],
['Newest','/journal/newest'],
['New Users','/user/new2'],
['Categories','/tag'],
['Random','/journal/random'],
['Recent comments','/comment/list']]

menu['groups'] = [
	["Featured Groups", "/group"],
	["Browse all Groups", "/group/list"],
  ["Groups I'm In", "/group/mine" ],
  ["Create a Group", "/group/add" ],
	["Site Chatroom", "/chat/show"]
]

menu['help'] = [
 ["Help - FAQ", "/help/list"],
 ["Help - Tutorial", "/help/list/Tutorial"],	
 ["Ask a question", "/ticket/add?help=1"],
 ["Policies", "/home/guide/rules"],
 ["Moderator list", "/help/who"], 
 ["Search the site", "/home/search"]
]



menu['play'] = [
 ["Contest Overview", "/contest"],
 ["Browse Contests", "/contest/list"],
 ["New Contests", "/contest/newest"],
 ["My Contests", "/contest/by"],		
 ["Contests you're in", "/contest/in"],		
 ["Contests by friends", "/favorite/contest"], 
 ["Results", "/contest/closed"],
 ["Start a contest", "/contest/add"],
 ["Contests for Kids", "/tag/contest/Children"]
]


menu['contests'] = [
 ["Contests Overview", "/contest"],
 ["Browse Contests", "/contest/list"],
 ["New Contests", "/contest/newest"],
 ["My Contests", "/contest/by"],		
 ["Contests by friends", "/favorite/contest"], 
 ["Contests you're in", "/contest/in"],		
 ["Results", "/contest/closed"],
 ["Start a contest", "/contest/add"],
 ["Contests for Kids", "/tag/contest/Children"]
]


menu['journals'] = [
["Add a journal", "/journal/add" ],
["Categories", "/tag?order=journals_count"    ],
["Popular", "/journal/list"                   ],
["Newest", "/journal/newest"                  ],
["Search", "/journal/search2" 	              ],
]

menu['read'] = [
["Home", "/" 	                             ],
["By Online", "/poem/current"              ],
["Popular", "/poem/list"                   ],
["Categories", "/tag"                      ],
["Browsing"                                ],
["Shameless", "/shameless" 		             ],
["Random", "/poem/random"                  ],
["Search", "/home/search2" 	               ],

["People Related"                          ],
["Find Author", "/user/search"             ],
["New Authors", "/user/new" 		           ],
["By Kids", "/item/kids"                   ],
["By your favorites", "/favorite/recent"   ],

["Type Related"                            ],
["Practice", "/topic/list"                 ],
["Lists ", "/list" 	                       ],
["Video Poems ", "/poem/slam" 		         ],
["Freewrites", "/topic/list"                 ],
["Latest Comments", "/comment/list" 	     ],
["Add Lines", "/addline/list"              ],
["Columns", "/column/syndicated"           ],
["Stories", "http://storywrite.com"        ]
]
		

menu['learn'] = [
["School of Poetry", "/class"       ],
["Classical Poetry", "/opoem"            ],
["Featured Columns", "/column/list"      ],
["User Columns", "/column/all" 	         ],
["The Sonnet Forms", "/list/show/25619"  ]
]


menu['learn_sw'] = [
["Storywrite Academy", "/class"       ],
["Featured Columns", "/column/list"      ],
["User Columns", "/column/all" 	         ]
]

menu['talk'] = [               
  ['My Messages','/im'],
  ['My Notes', '/im/notes'],            
  ["Forums"],
  ["Board list", "/board/list"             ],
  ["Poetry Related", "/board/show/2"       ],
  ["Teen Board", "/board/show/104"         ],
  ["Adult Board - Pub", "/board/show/108"  ],
  ["Adult Board - Cafe", "/board/show/109" ],
  ["Policy Board", "/board/show/107"       ],
  ['Groups'],
  ["List of Groups", "/group/list" ],
  ["AP Chatroom", "/chat/show"]
]





function userdrop_html(name, tooltip) {
  t = '<a href="#" onclick="return im(\'' + name + '\')" title="Send an instant message to this user">Send Message</a><a href="/' + name + '" title="View this user\'s profile">View Profile</a>'
  if (tooltip && tooltip != '') {t += '<span class="dropcomment">' + decodeURIComponent(tooltip) + "</span>"}
  return t
}

function groupdrop_html(name) {
  return '<a href="#" onclick="return im(\'/group ' + name + '\')" title="Send an instant message to this group">Send Message</a><a href="/group/show/' + name + '" title="Visit this group">Visit Group</a>'
}

var target_name;
function us(obj, e, name) {
    target_name = name;
    dropdownmenu(obj,e,usermenu,'95px')
}

function cmenu(obj, e, commentid) {
    target_name = commentid;
    dropdownmenu(obj,e,commentmenu,'95px')
}

function gotopage(start, nm) {
    document.location = start + nm;
}

var commentmenu=new Array()
commentmenu[0]='<a href="javascript:gotopage(\'/comment/delete/\',target_name)" title="Delete this comment">delete</a><br>'
commentmenu[1]='<a href="javascript:gotopage(\'/comment/complain/\',target_name)" title="Complain about this comment, possibly getting reward points back.">complain</a><br>'
commentmenu[2]='<a href="amt=prompt(\'How many points to reward user?\';javascript:gotopage(\'/comment/reward/\',target_name)" title="Reward this user with extra points">reward User</a><br>'

var menuwidth='165px' //default menu width
var menubgcolor='#EEF'  //menu bgcolor
var disappeardelay=360  //menu disappear speed onMouseout (in miliseconds) was 250 6/22/08
var hidemenu_onclick="yes" //hide menu when user clicks within menu?


var ie4=document.all
var ns6=document.getElementById&&!document.all


if (ie4||ns6)
document.write('<div id="dropmenudiv" style="visibility:hidden;width:'+menuwidth+';background-color:'+menubgcolor+'" onMouseOver="clearhidemenu()" onMouseout="dynamichide(event)"></div>')

function getposOffset(what, offsettype){
var totaloffset=(offsettype=="left")? what.offsetLeft : what.offsetTop;
var parentEl=what.offsetParent;


while (parentEl!=null){
totaloffset=(offsettype=="left")? totaloffset+parentEl.offsetLeft : totaloffset+parentEl.offsetTop;
parentEl=parentEl.offsetParent;
}

return totaloffset;
}

function menushowhide(obj, e, visible, hidden, menuwidth){
if (ie4||ns6)
dropmenuobj.style.top= -500 + 'px'
dropmenuobj.style.left=dropmenuobj.style.top

if (menuwidth!=""){
dropmenuobj.widthobj=dropmenuobj.style
dropmenuobj.widthobj.width=menuwidth
}
if (e.type=="click" && obj.visibility==hidden || e.type=="mouseover")
obj.visibility=visible
else if (e.type=="click")
obj.visibility=hidden
}

function iecompattest(){
return (document.compatMode && document.compatMode!="BackCompat")? document.documentElement : document.body
}

function clearbrowseredge(obj, whichedge){
var edgeoffset=0
if (whichedge=="rightedge"){
var windowedge=ie4 && !window.opera? iecompattest().scrollLeft+iecompattest().clientWidth-15 : window.pageXOffset+window.innerWidth-15
dropmenuobj.contentmeasure=dropmenuobj.offsetWidth
if (windowedge-dropmenuobj.x < dropmenuobj.contentmeasure)
edgeoffset=dropmenuobj.contentmeasure-obj.offsetWidth
}
else{
var windowedge=ie4 && !window.opera? iecompattest().scrollTop+iecompattest().clientHeight-15 : window.pageYOffset+window.innerHeight-18
dropmenuobj.contentmeasure=dropmenuobj.offsetHeight

if (dropmenuobj.contentmeasure > 450) return 0
if (windowedge-dropmenuobj.y < dropmenuobj.contentmeasure)
edgeoffset=dropmenuobj.contentmeasure+obj.offsetHeight
}
return edgeoffset
}

function populatemenu(what){
if (ie4||ns6)
dropmenuobj.innerHTML=what.join("")
}



function drop(obj, e, kind, name, above, width){
  if (!width) width = '130';
  if (window.event) event.cancelBubble=true
  else if (e.stopPropagation) e.stopPropagation()
  clearhidemenu()
  dropmenuobj=document.getElementById? document.getElementById("dropmenudiv") : dropmenudiv

  if (typeof(kind) == 'object') {tmenu = kind}
  else {tmenu = menu[kind]}
  
  if (!tmenu) return; // only work with defined types
  //if (typeof(tmenu) == 'function') {tmenu = tmenu(name) }// call it to get the array.  trouble!
  
  //loop over tmenu forming html via function and joining into string
  // why function vs. string?  gosh, it really re-creates the menu each time you hover over it! of course it would, it's a for loop?
  v = tmenu.collect( function(v) {
    //console.log("inside menu function: " + v)
    if (!v[1]) {h = "<span class='droparea'>"+v[0]+"</span>"
      
    } else {
      if (v[1]) {h="<a href='"+v[1]+"'"} else {h=''}
      if (v[2]) h += " onclick=\""+v[2]+'"'
      if (v[3]) h += " title=\""+v[3]+'"'
      if (v[1]) {h+='>'}
      h += v[0]+"</a>"
    }
    return h
  } ).join('')
  
  dropmenuobj.innerHTML = v
  
  if (ie4||ns6){
    menushowhide(dropmenuobj.style, e, "visible", "hidden", width+'px')
    dropmenuobj.x=getposOffset(obj, "left")
    dropmenuobj.y=getposOffset(obj, "top")
  
    if (above==1) dropmenuobj.y -= 56
    // now it's always wrong, b4 it worked sometimes? working again now 80-0
    //alert("setting left to",dropmenuobj.x-clearbrowseredge(obj, "rightedge"))
  
    dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge")+"px"
    dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge")+obj.offsetHeight+"px"
  }

  return clickreturnvalue()
}

function idrop(obj, e, v, name, above, width){
  if (!width) width = '115';
  if (window.event) event.cancelBubble=true
  else if (e.stopPropagation) e.stopPropagation()
  clearhidemenu()
  dropmenuobj=document.getElementById? document.getElementById("dropmenudiv") : dropmenudiv

  dropmenuobj.innerHTML = v
  
  if (ie4||ns6){
    menushowhide(dropmenuobj.style, e, "visible", "hidden", width+'px')
    dropmenuobj.x=getposOffset(obj, "left")
    dropmenuobj.y=getposOffset(obj, "top")
  
    if (above==1) dropmenuobj.y -= 56
    // now it's always wrong, b4 it worked sometimes? working again now 80-0
    //alert("setting left to",dropmenuobj.x-clearbrowseredge(obj, "rightedge"))
  
    dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge")+"px"
    dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge")+obj.offsetHeight+"px"
  }

  return clickreturnvalue()
}

function userdrop(obj, e, name, above, kind, tooltip){
  if (window.event) event.cancelBubble=true
  else if (e.stopPropagation) e.stopPropagation()
  clearhidemenu()
  dropmenuobj=document.getElementById? document.getElementById("dropmenudiv") : dropmenudiv

  target_name = name
  if (kind == 'group') {
    dropmenuobj.innerHTML = groupdrop_html(name)
  } else if (kind == 'local') {
    dropmenuobj.innerHTML = local_drop_html(name)
  } else {
    dropmenuobj.innerHTML = userdrop_html(name, tooltip)
  }
  
  width = 115
  if (kind == 'tip') width += 45
  
  if (ie4||ns6){
  menushowhide(dropmenuobj.style, e, "visible", "hidden", width+'px')
  dropmenuobj.x=getposOffset(obj, "left")
  dropmenuobj.y=getposOffset(obj, "top")
  
  if (above==1) dropmenuobj.y -= 56
  if (kind == 'tip') dropmenuobj.x += 15
  // now it's always wrong, b4 it worked sometimes? working again now
  // 80-0
  //alert("setting left to",dropmenuobj.x-clearbrowseredge(obj, "rightedge"))
  
  dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge")+"px"
  dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge")+obj.offsetHeight+"px"
  }

  return clickreturnvalue()
}

function dropdownmenu(obj, e, menucontents, menuwidth){
if (window.event) event.cancelBubble=true
else if (e.stopPropagation) e.stopPropagation()
clearhidemenu()
dropmenuobj=document.getElementById? document.getElementById("dropmenudiv") : dropmenudiv
populatemenu(menucontents)

if (ie4||ns6){
menushowhide(dropmenuobj.style, e, "visible", "hidden", menuwidth)
dropmenuobj.x=getposOffset(obj, "left")
dropmenuobj.y=getposOffset(obj, "top")
dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge")+"px"
dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge")+obj.offsetHeight+"px"
}

return clickreturnvalue()
}

function clickreturnvalue(){
if (ie4||ns6) return false
else return true
}

function contains_ns6(a, b) {
  if (!b) return

  while (b.parentNode)
  if ((b = b.parentNode) == a)
  return true;
  return false;
}

function dynamichide(e){
if (ie4&&!dropmenuobj.contains(e.toElement))
delayhidemenu()
else if (ns6&&e.currentTarget!= e.relatedTarget&& !contains_ns6(e.currentTarget, e.relatedTarget))
delayhidemenu()
}

function hidemenu(e){
if (typeof dropmenuobj!="undefined"){
if (ie4||ns6)
dropmenuobj.style.visibility="hidden"
}
}

function delayhidemenu(){
if (ie4||ns6)
delayhide=setTimeout("hidemenu()",disappeardelay)
}

function clearhidemenu(){
if (typeof delayhide!="undefined")
clearTimeout(delayhide)
}

if (hidemenu_onclick=="yes")
document.onclick=hidemenu

function AFLAX(path,trace,enableFlashSettings,localStoreReadyCallback)
{if(path!=null)AFLAX.path=path;if(trace!=null)AFLAX.tracing=trace;if(localStoreReadyCallback==undefined||localStoreReadyCallback==null)localStoreReadyCallback="";this.id="aflax_obj_"+AFLAX.count++;if(enableFlashSettings!=undefined||enableFlashSettings==true)
{if(document.getElementById("flashSettings")==null&&arguments.length>0)
{var flashSettingsStyle="width: 215px; height: 138px; position: absolute; z-index: 100;left: -500px; top: -500px";document.write('<div id="flashSettings" style="'+flashSettingsStyle+'"\>Flash Settings Dialog</div\>\n');AFLAX.settings=new AFLAX();AFLAX.settings.addFlashToElement("flashSettings",215,138,"#FFFFFF",localStoreReadyCallback,true);}}}
AFLAX.version="0.41";AFLAX.tracing=false;AFLAX.count=0;AFLAX.path="aflax.swf";AFLAX.settings=null;AFLAX.prototype.id=null;AFLAX.prototype.flash=null;AFLAX.prototype.root=null;AFLAX.prototype.stage=null;AFLAX.prototype.getHTML=function(width,height,bgcolor,callback,transparent,absolutePosition)
{var requiredVersion=new com.deconcept.PlayerVersion([8,0,0]);var installedVersion=com.deconcept.FlashObjectUtil.getPlayerVersion();if(installedVersion.versionIsValid(requiredVersion)==false)
{return"<div style='border:2px solid #FF0000'>To see this contents you need to install <a target='_blank' href='http://www.macromedia.com/go/getflashplayer'>Flash Player</a> version 8.0 or higher.</div>";}
bgcolor=bgcolor||"#FFFFFF";callback=callback||"_none_";var content='\
<object width="'+width+'" height="'+height+'" id="'+this.id+'" type="application/x-shockwave-flash" data="'+AFLAX.path+'?callback='+callback+'"';if(absolutePosition)
content+='style="position: absolute"';content+='>\
<param name="allowScriptAccess" value="sameDomain" />\
<param name="bgcolor" value="'+bgcolor+'" />\
<param name="movie" value="'+AFLAX.path+'?callback='+callback+'" />\
<param name="scale" value="noscale" />\
<param name="salign" value="lt" />\
';if(transparent)
content+='<param name="wmode" value="transparent" />';content+='</object>';if(AFLAX.tracing)
content+='<div style="border:1px solid #ddd;padding: 4px;background-color: #fafafa;font-size: 8pt;" id="aflaxlogger"></div>';return content;}
AFLAX.prototype.addFlashToElement=function(parentElementOrId,width,height,bgcolor,callback,transparent)
{var parentNode=typeof parentElementOrId=="string"?document.getElementById(parentElementOrId):parentElementOrId;var content=this.getHTML(width,height,bgcolor,callback,transparent);var container=document.createElement("div");container.innerHTML=content;var element=container.removeChild(container.firstChild);while(parentNode.firstChild)
parentNode.removeChild(parentNode.firstChild);parentNode.appendChild(element);return element;}
AFLAX.prototype.insertFlash=function(width,height,bgcolor,callback,transparent,absolutePosition)
{var content=this.getHTML(width,height,bgcolor,callback,transparent,absolutePosition);document.write(content);if(AFLAX.tracing)
AFLAX.trace("AFLAX Logger initialized.");return document.getElementById(this.id);}
AFLAX.prototype.getRoot=function()
{if(this.root==null)
{this.root=new AFLAX.MovieClip(this,null,"_root");}
return this.root;}
AFLAX.prototype.getStage=function()
{if(this.stage==null)
{this.stage=new AFLAX.MovieClip(this,null,"_stage");this.stage.exposeProperty("width",this.stage);this.stage.exposeProperty("height",this.stage);this.stage.exposeProperty("scaleMode",this.stage);this.stage.exposeProperty("showMenu",this.stage);this.stage.exposeProperty("align",this.stage);}
return this.stage;}
AFLAX.prototype.getFlash=function()
{if(this.flash==null)
{this.flash=document[this.id];}
return this.flash;}
AFLAX.returnsHash={"true":true,"false":false,"undefined":undefined,"null":null,"NaN":NaN};AFLAX.prototype.callFunction=function(name)
{var ret=this.getFlash().CallFunction("<invoke name=\""+
name+"\" returntype=\"javascript\">"+
__flash__argumentsToXML(arguments,1)+"</invoke>");if(AFLAX.returnsHash.hasOwnProperty(ret))
{ret=AFLAX.returnsHash[ret];}
else if(ret.charAt(0)=='"')
{if(ret.charAt(ret.length-1)=='"')
ret=ret.substring(1,ret.length-1);}
else
{ret=ret-0;}
return ret;}
AFLAX.prototype.storeValue=function(key,value,statusCallback,serialize)
{if(serialize==true)
value="[JSON]"+JSON.stringify(value);if(statusCallback==undefined||statusCallback==null)
return this.callFunction("aflaxStoreValue",[key,value]);else
return this.callFunction("aflaxStoreValue",[key,value,statusCallback]);}
AFLAX.prototype.getStoredValue=function(key)
{var value=this.callFunction("aflaxGetValue",[key]);value=value.split('\\"').join('"');value=value.split("\\'").join("'");alert(value);if(value.substring(0,6)=="[JSON]")
return JSON.parse(value.substring(6));else
return value;}
AFLAX.hideFlashSettings=function()
{var flashDiv=document.getElementById("flashSettings");flashDiv.style.left=-500+"px";flashDiv.style.top=-500+"px";}
AFLAX.showFlashSettings=function(x,y,mode)
{if(x==undefined)x=100;if(y==undefined)y=100;if(mode==undefined)mode=1;var flashDiv=document.getElementById("flashSettings");flashDiv.style.left=x+"px";flashDiv.style.top=y+"px";AFLAX.settings.callStaticFunction("System","showSettings",mode);}
AFLAX.prototype.callStaticFunction=function(objectName,func)
{var args=new Array();args[0]=objectName;args[1]=func;for(var i=2;i<arguments.length;i++)
{args[i]=arguments[i];}
return this.callFunction("aflaxCallStaticFunction",args);}
AFLAX.prototype.getStaticProperty=function(objectName,property)
{return this.callFunction("aflaxGetStaticProperty",[objectName,property]);}
AFLAX.prototype.attachEventListener=function(obj,event,handler)
{var id=obj;if(obj.id!=undefined)
id=obj.id;this.callFunction("aflaxAttachEventListener",[id,event,handler]);}
AFLAX.prototype.callBulkFunctions=function(funcs)
{var s=new Array(funcs.length);for(var i=0,j=funcs.length;i<j;i++)
{var func=funcs[i];s[i]=func.join("\1");}
var commands=s.join("\2");this.callFunction("aflaxBulkCallFunction",commands);}
AFLAX.prototype.updateAfterEvent=function()
{this.callFunction("aflaxUpdateAfterEvent");}
AFLAX.prototype.createFlashArray=function(elements)
{var _array=new AFLAX.FlashObject(this,"Array")
_array.exposeFunction("push",_array);_array.exposeFunction("reverse",_array);_array.exposeProperty("length",_array);var len=elements.length;for(var i=0;i<len;i++)
_array.push(elements[i]);return _array;}
AFLAX.extend=function(baseClass,newClass)
{var pseudo=function(){};pseudo.prototype=baseClass.prototype;newClass.prototype=new pseudo();newClass.prototype.baseConstructor=baseClass;newClass.prototype.superClass=baseClass.prototype;newClass.prototype._prototype=newClass.prototype;if(baseClass.prototype.superClass==undefined){baseClass.prototype.superClass=Object.prototype;}
return newClass;}
AFLAX.extractArgs=function(args,startIndex)
{var newArgs=new Array();for(var i=startIndex;i<args.length;i++)
{newArgs[i-startIndex]=args[i];}
return newArgs;}
AFLAX.FlashObject=function(aflaxRef,flashObjectName,objectArgs,objectID)
{if(arguments.length==0)return;this.aflax=aflaxRef;this.flash=this.aflax.getFlash();this._prototype=AFLAX.FlashObject.prototype;if(objectArgs==null||objectArgs==undefined)
objectArgs=new Array();if(flashObjectName!=null&&flashObjectName!=undefined)
{var args=new Array();args[0]=flashObjectName;for(i=0;i<objectArgs.length;i++)
{var a=objectArgs[i];if(a.id!=undefined)
{a="ref:"+a.id;}
args[i+1]=a;}
this.id=this.aflax.callFunction("aflaxCreateObject",args);}
else
{if(objectID!=null&&objectID!=undefined)
{this.id=objectID;}}
if(this.bound==false)
{}}
AFLAX.FlashObject.prototype.bound=false;AFLAX.FlashObject.prototype.id=null;AFLAX.FlashObject.prototype._prototype=null;AFLAX.FlashObject.prototype.aflax=null;AFLAX.FlashObject.prototype.flash=null;AFLAX.FlashObject.prototype.callFunction=function(functionName)
{var args=new Array();args[0]=this.id;args[1]=functionName;for(i=1;i<arguments.length;i++)
{var val=arguments[i];if(val==null)
{val="null";}
if(typeof(val)=="string")
{if(val.substring(0,4)=="ref:")
{var varPart=val.substring(4);var restPart=null;if(varPart.indexOf(".")!=-1)
{restPart=varPart.substring(varPart.indexOf("."));varPart=varPart.substring(0,varPart.indexOf("."));}
val="ref:"+eval(varPart).id;if(restPart!=null)
val+=restPart;}}
if(val.id!=undefined)
{val="ref:"+val.id;}
args[i+1]=val;}
var retval=this.aflax.callFunction("aflaxCallFunction",args);return retval;}
AFLAX.FlashObject.prototype.bind=function(properties,functions,mappings)
{if(properties!=null&&properties!=undefined)
{for(var pn=0;pn<properties.length;pn++)
{this.exposeProperty(properties[pn]);}}
if(functions!=null&&functions!=undefined)
{for(var fn=0;fn<functions.length;fn++)
{this.exposeFunction(functions[fn]);}}
if(mappings!=null&&mappings!=undefined)
{for(var mn=0;mn<mappings.length;mn++)
{this.mapFunction(mappings[mn]);}}}
AFLAX.FlashObject.prototype.exposeProperty=function(propertyName,targetPrototype)
{var methodSuffix=propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);var target=this._prototype;if(targetPrototype!=null)
target=targetPrototype;target["get"+methodSuffix]=function()
{var r=this.aflax.callFunction("aflaxGetProperty",[this.id,propertyName]);if(r==null)return null;if(r==undefined)return;if(typeof(r)=="string")
return r.split("\\r").join("\r").split("\\n").join("\n");else
return r;}
target["set"+methodSuffix]=function(val)
{this.aflax.callFunction("aflaxSetProperty",[this.id,propertyName,val]);}}
AFLAX.FlashObject.prototype.exposeFunction=function(functionName,targetPrototype)
{var target=this._prototype;if(targetPrototype!=null)
target=targetPrototype;target[functionName]=function()
{var args=new Array();args[0]=this.id;args[1]=functionName;for(var i=0;i<arguments.length;i++)
args[i+2]=arguments[i];return this.aflax.callFunction("aflaxCallFunction",args);}}
AFLAX.FlashObject.prototype.mapFunction=function(functionName,targetPrototype)
{var target=this._prototype;if(targetPrototype!=null)
target=targetPrototype;target[functionName]=function()
{var args=new Array();args[0]=this.id;for(var i=0;i<arguments.length;i++)
{var a=arguments[i];if(a.id!=undefined)a=a.id;args[i+1]=a;}
var fName="aflax"+functionName.substring(0,1).toUpperCase()+functionName.substring(1);return this.aflax.callFunction(fName,args);}}
AFLAX.MovieClip=function(aflaxRef,parentClipID,clipID)
{if(arguments.length==0)return;arguments.callee.prototype.baseConstructor.call(this,aflaxRef,null,null,clipID);if(clipID==undefined||clipID==null)
{if(parentClipID!=undefined&&parentClipID!=null&&this.flash.aflaxCreateEmptyMovieClip!=undefined&&this.flash.aflaxCreateEmptyMovieClip!=null)
this.id=this.aflax.callFunction("aflaxCreateEmptyMovieClip",[parentClipID]);else
this.id=this.aflax.callFunction("aflaxCreateEmptyMovieClip",["_root"]);}
if(AFLAX.MovieClip.bound==false)
{this.bind(AFLAX.MovieClip.movieClipProperties,AFLAX.MovieClip.movieClipFunctions,AFLAX.MovieClip.movieClipMappings);AFLAX.MovieClip.bound=true;}}
AFLAX.extend(AFLAX.FlashObject,AFLAX.MovieClip);AFLAX.MovieClip.prototype.drawCircle=function(x,y,radius)
{var r=radius;var degToRad=Math.PI/180;var n=8;var theta=45*degToRad;var cr=radius/Math.cos(theta/2);var angle=0;var cangle=angle-theta/2;var commands=new Array(n+1);var commandIndex=0;commands[commandIndex++]=[this.id,"moveTo",x+r,y];for(var i=0;i<n;i++)
{angle+=theta;cangle+=theta;var endX=r*Math.cos(angle);var endY=r*Math.sin(angle);var cX=cr*Math.cos(cangle);var cY=cr*Math.sin(cangle);commands[commandIndex++]=[this.id,"curveTo",x+cX,y+cY,x+endX,y+endY];}
this.aflax.callBulkFunctions(commands);}
AFLAX.MovieClip.bound=false;AFLAX.MovieClip.movieClipProperties=["_x","_y","_height","_width","_rotation","_xmouse","_ymouse","_xscale","_yscale","_alpha","blendMode","_visible","cacheAsBitmap"];AFLAX.MovieClip.movieClipFunctions=["moveTo","lineTo","curveTo","lineStyle","beginFill","endFill","clear","getURL","removeMovieClip"];AFLAX.MovieClip.movieClipMappings=["attachVideo","createTextField","addEventHandler","attachBitmap","applyFilter","loadMovie"];AFLAX.MovieClip.prototype.clone=function()
{var newClip=this.aflax.callFunction("aflaxDuplicateMovieClip",[this.id]);var mc=new AFLAX.MovieClip(this.aflax,null,newClip);return mc;}
AFLAX.CameraClip=function(aflaxRef,parentClipID)
{if(arguments.length==0)return;arguments.callee.prototype.baseConstructor.call(this,aflaxRef,parentClipID,null);if(parentClipID==undefined||parentClipID==null)
{parentClipID="_root";}
this.id=this.aflax.callFunction("aflaxCreateVideoClip",[parentClipID]);var cam=this.aflax.callFunction("aflaxGetCamera");this.attachVideo(cam);}
AFLAX.CameraClip.GetCameras=function(aflaxRef)
{return aflaxRef.getFlash().aflaxGetStaticProperty(["Camera","names"]);}
AFLAX.extend(AFLAX.MovieClip,AFLAX.CameraClip);AFLAX.VideoClip=function(aflaxRef,parentClipID,url,cueCallback,loadCallback)
{if(arguments.length==0)return;arguments.callee.prototype.baseConstructor.call(this,aflaxRef,parentClipID,null);if(parentClipID==undefined||parentClipID==null)
{parentClipID="_root";}
this.id=this.aflax.callFunction("aflaxCreateVideoClip",[parentClipID]);var nc=new AFLAX.FlashObject(this.aflax,"NetConnection");nc.callFunction("connect",null);var ns=new AFLAX.FlashObject(this.aflax,"NetStream",[nc]);ns.exposeProperty("time",ns);this.netStream=ns;this.attachVideo(ns);if(loadCallback!=null&&loadCallback!=undefined)
this.aflax.flash.aflaxAttachVideoStatusEvent([ns.id,loadCallback]);if(cueCallback!=null&&cueCallback!=undefined)
this.aflax.flash.aflaxAttachCuePointEvent([ns.id,cueCallback]);ns.callFunction("setBufferTime",0);ns.callFunction("play",url);}
AFLAX.extend(AFLAX.MovieClip,AFLAX.VideoClip);AFLAX.VideoClip.prototype.netStream=null;AFLAX.VideoClip.GetStatusValue=function(statusString,valueName)
{var s=statusString;var args=s.split(";");var params=new Array();for(var i=0;i<args.length;i++)
{var n=args[i].split("=");if(n[0]!="")
{params[n[0]]=n[1];}}
return params[valueName];}
AFLAX.VideoClip.NetStream_Buffer_Empty="NetStream.Buffer.Empty";AFLAX.VideoClip.NetStream_Buffer_Full="NetStream.Buffer.Full";AFLAX.VideoClip.NetStream_Buffer_Flush="NetStream.Buffer.Flush";AFLAX.VideoClip.NetStream_Play_Start="NetStream.Play.Start";AFLAX.VideoClip.NetStream_Play_Stop="NetStream.Play.Stop";AFLAX.VideoClip.NetStream_Play_StreamNotFound="NetStream.Play.StreamNotFound";AFLAX.VideoClip.NetStream_Seek_InvalidTime="NetStream.Seek.InvalidTime";AFLAX.VideoClip.NetStream_Seek_Notify="NetStream.Seek.Notify";AFLAX.TextField=function(aflaxRef,clipID)
{if(arguments.length==0)return;arguments.callee.prototype.baseConstructor.call(this,aflaxRef,null,clipID);if(AFLAX.TextField.bound==false)
{this.bind(AFLAX.TextField.textFieldProperties,AFLAX.TextField.textFieldFunctions);AFLAX.TextField.bound=true;}}
AFLAX.extend(AFLAX.MovieClip,AFLAX.TextField);AFLAX.TextField.bound=false;AFLAX.TextField.textFieldProperties=["type","multiline","wordWrap","text","htmlText","embedFonts"];AFLAX.TextField.textFieldFunctions=["setTextFormat"];if(AFLAX.tracing==true)
{window.onerror=AFLAX.windowError;}
AFLAX.windowError=function(message,url,line){AFLAX.trace('Error on line '+line+' of document '+url+': '+message);return true;}
AFLAX.trace=function(message)
{if(AFLAX.tracing==true)
{var div=document.getElementById("aflaxlogger");if(div!=null)
{var p=document.createElement('p');p.style.margin=0;p.style.padding=0;p.style.textAlign="left";var text=document.createTextNode(message);p.appendChild(text);div.appendChild(p);}}}
AFLAX.Socket=function(aflax,host,port,onConnectEvent,onDataEvent,onCloseEvent)
{var flash=aflax.getFlash();var connection=new AFLAX.FlashObject(aflax,"XMLSocket");flash.aflaxAttachSocketEvents([connection.id,onConnectEvent,onDataEvent,onCloseEvent]);connection.exposeFunction("connect",connection);connection.exposeFunction("close",connection);connection.exposeFunction("send",connection);connection.connect(host,port);return connection;}
if(typeof com=="undefined")
{var com;com=new Object();}
if(typeof com.deconcept=="undefined")com.deconcept=new Object();if(typeof com.deconcept.util=="undefined")com.deconcept.util=new Object();if(typeof com.deconcept.FlashObjectUtil=="undefined")com.deconcept.FlashObjectUtil=new Object();com.deconcept.FlashObjectUtil.getPlayerVersion=function(){var PlayerVersion=new com.deconcept.PlayerVersion(0,0,0);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){PlayerVersion=new com.deconcept.PlayerVersion(x.description.replace(/([a-z]|[A-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else if(window.ActiveXObject){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");PlayerVersion=new com.deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}catch(e){}}
return PlayerVersion;}
com.deconcept.PlayerVersion=function(arrVersion){this.major=parseInt(arrVersion[0])||0;this.minor=parseInt(arrVersion[1])||0;this.rev=parseInt(arrVersion[2])||0;}
com.deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major)return false;if(this.major>fv.major)return true;if(this.minor<fv.minor)return false;if(this.minor>fv.minor)return true;if(this.rev<fv.rev)return false;return true;}
Array.prototype.______array='______array';var JSON={org:'http://www.JSON.org',copyright:'(c)2005 JSON.org',license:'http://www.crockford.com/JSON/license.html',stringify:function(arg){var c,i,l,s='',v;switch(typeof arg){case'object':if(arg){if(arg.______array=='______array'){for(i=0;i<arg.length;++i){v=this.stringify(arg[i]);if(s){s+=',';}
s+=v;}
return'['+s+']';}else if(typeof arg.toString!='undefined'){for(i in arg){v=arg[i];if(typeof v!='undefined'&&typeof v!='function'){v=this.stringify(v);if(s){s+=',';}
s+=this.stringify(i)+':'+v;}}
return'{'+s+'}';}}
return'null';case'number':return isFinite(arg)?String(arg):'null';case'string':l=arg.length;s='"';for(i=0;i<l;i+=1){c=arg.charAt(i);if(c>=' '){if(c=='\\'||c=='"'){s+='\\';}
s+=c;}else{switch(c){case'\b':s+='\\b';break;case'\f':s+='\\f';break;case'\n':s+='\\n';break;case'\r':s+='\\r';break;case'\t':s+='\\t';break;default:c=c.charCodeAt();s+='\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16);}}}
return s+'"';case'boolean':return String(arg);default:return'null';}},parse:function(text){var at=0;var ch=' ';function error(m){throw{name:'JSONError',message:m,at:at-1,text:text};}
function next(){ch=text.charAt(at);at+=1;return ch;}
function white(){while(ch!=''&&ch<=' '){next();}}
function str(){var i,s='',t,u;if(ch=='"'){outer:while(next()){if(ch=='"'){next();return s;}else if(ch=='\\'){switch(next()){case'b':s+='\b';break;case'f':s+='\f';break;case'n':s+='\n';break;case'r':s+='\r';break;case't':s+='\t';break;case'u':u=0;for(i=0;i<4;i+=1){t=parseInt(next(),16);if(!isFinite(t)){break outer;}
u=u*16+t;}
s+=String.fromCharCode(u);break;default:s+=ch;}}else{s+=ch;}}}
error("Bad string");}
function arr(){var a=[];if(ch=='['){next();white();if(ch==']'){next();return a;}
while(ch){a.push(val());white();if(ch==']'){next();return a;}else if(ch!=','){break;}
next();white();}}
error("Bad array");}
function obj(){var k,o={};if(ch=='{'){next();white();if(ch=='}'){next();return o;}
while(ch){k=str();white();if(ch!=':'){break;}
next();o[k]=val();white();if(ch=='}'){next();return o;}else if(ch!=','){break;}
next();white();}}
error("Bad object");}
function num(){var n='',v;if(ch=='-'){n='-';next();}
while(ch>='0'&&ch<='9'){n+=ch;next();}
if(ch=='.'){n+='.';while(next()&&ch>='0'&&ch<='9'){n+=ch;}}
if(ch=='e'||ch=='E'){n+='e';next();if(ch=='-'||ch=='+'){n+=ch;next();}
while(ch>='0'&&ch<='9'){n+=ch;next();}}
v=+n;if(!isFinite(v)){error("Bad number");}else{return v;}}
function word(){switch(ch){case't':if(next()=='r'&&next()=='u'&&next()=='e'){next();return true;}
break;case'f':if(next()=='a'&&next()=='l'&&next()=='s'&&next()=='e'){next();return false;}
break;case'n':if(next()=='u'&&next()=='l'&&next()=='l'){next();return null;}
break;}
error("Syntax error");}
function val(){white();switch(ch){case'{':return obj();case'[':return arr();case'"':return str();case'-':return num();default:return ch>='0'&&ch<='9'?num():word();}}
return val();}};//-- Google Analytics Urchin Module

var _uacct="";			// set up the Urchin Account
var _userv=1;			// service mode (0=local,1=remote,2=both)

var _ufsc=1;			// set client info flag (1=on|0=off)
var _udn="auto";		// (auto|none|domain) set the domain name for cookies
var _uhash="on";		// (on|off) unique domain hash for cookies
var _utimeout="1800";   	// set the inactive session timeout in seconds
var _ugifpath="/__utm.gif";	// set the web path to the __utm.gif file
var _utsp="|";			// transaction field separator
var _uflash=1;			// set flash version detect option (1=on|0=off)
var _utitle=1;			// set the document title detect option (1=on|0=off)
var _ulink=0;			// enable linker functionality (1=on|0=off)
var _uanchor=0;			// enable use of anchors for campaign (1=on|0=off)
var _utcp="/";			// the cookie path for tracking
var _usample=100;		// The sampling % of visitors to track (1-100).

var _uctm=1;			// set campaign tracking module (1=on|0=off)
var _ucto="15768000";		// set timeout in seconds (6 month default)
var _uccn="utm_campaign";	// name
var _ucmd="utm_medium";		// medium (cpc|cpm|link|email|organic)
var _ucsr="utm_source";		// source
var _uctr="utm_term";		// term/keyword
var _ucct="utm_content";	// content
var _ucid="utm_id";		// id number
var _ucno="utm_nooverride";	// don't override

var _uOsr=new Array();
var _uOkw=new Array();
_uOsr[0]="google";	_uOkw[0]="q";
_uOsr[1]="yahoo";	_uOkw[1]="p";
_uOsr[2]="msn";		_uOkw[2]="q";
_uOsr[3]="aol";		_uOkw[3]="query";
_uOsr[4]="aol";		_uOkw[4]="encquery";
_uOsr[5]="lycos";	_uOkw[5]="query";
_uOsr[6]="ask";		_uOkw[6]="q";
_uOsr[7]="altavista";	_uOkw[7]="q";
_uOsr[8]="netscape";	_uOkw[8]="query";
_uOsr[9]="cnn";	_uOkw[9]="query";
_uOsr[10]="looksmart";	_uOkw[10]="qt";
_uOsr[11]="about";	_uOkw[11]="terms";
_uOsr[12]="mamma";	_uOkw[12]="query";
_uOsr[13]="alltheweb";	_uOkw[13]="q";
_uOsr[14]="gigablast";	_uOkw[14]="q";
_uOsr[15]="voila";	_uOkw[15]="rdata";
_uOsr[16]="virgilio";	_uOkw[16]="qs";
_uOsr[17]="live";	_uOkw[17]="q";
_uOsr[18]="baidu";	_uOkw[18]="wd";
_uOsr[19]="alice";	_uOkw[19]="qs";
_uOsr[20]="yandex";	_uOkw[20]="text";
_uOsr[21]="najdi";	_uOkw[21]="q";
_uOsr[22]="aol";	_uOkw[22]="q";
_uOsr[23]="club-internet"; _uOkw[23]="query";
_uOsr[24]="mama";	_uOkw[24]="query";
_uOsr[25]="seznam";	_uOkw[25]="q";
_uOsr[26]="search";	_uOkw[26]="q";
_uOsr[27]="wp";	_uOkw[27]="szukaj";
_uOsr[28]="onet";	_uOkw[28]="qt";
_uOsr[29]="netsprint";	_uOkw[29]="q";
_uOsr[30]="google.interia";	_uOkw[30]="q";
_uOsr[31]="szukacz";	_uOkw[31]="q";
_uOsr[32]="yam";	_uOkw[32]="k";
_uOsr[33]="pchome";	_uOkw[33]="q";
_uOsr[34]="kvasir";	_uOkw[34]="searchExpr";
_uOsr[35]="sesam";	_uOkw[35]="q";
_uOsr[36]="ozu"; _uOkw[36]="q";
_uOsr[37]="terra"; _uOkw[37]="query";
_uOsr[38]="nostrum"; _uOkw[38]="query";
_uOsr[39]="mynet"; _uOkw[39]="q";
_uOsr[40]="ekolay"; _uOkw[40]="q";
_uOsr[41]="search.ilse"; _uOkw[41]="search_for";
_uOsr[42]="bing"; _uOkw[42]="q";

var _uOno=new Array();

var _uRno=new Array();

var _uff,_udh,_udt,_ubl=0,_udo="",_uu,_ufns=0,_uns=0,_ur="-",_ufno=0,_ust=0,_ubd=document,_udl=_ubd.location,_udlh="",_uwv="1.3";
var _ugifpath2="http://www.google-analytics.com/__utm.gif";
if (_udl.hash) _udlh=_udl.href.substring(_udl.href.indexOf('#'));
if (_udl.protocol=="https:") _ugifpath2="https://ssl.google-analytics.com/__utm.gif";
if (!_utcp || _utcp=="") _utcp="/";
function urchinTracker(page) {
 if (_udl.protocol=="file:") return;
 if (_uff && (!page || page=="")) return;
 var a,b,c,xx,v,z,k,x="",s="",f=0,nv=0;
 var nx=" expires="+_uNx()+";";
 var dc=_ubd.cookie;
 _udh=_uDomain();
 if (!_uVG()) return;
 _uu=Math.round(Math.random()*2147483647);
 _udt=new Date();
 _ust=Math.round(_udt.getTime()/1000);
 a=dc.indexOf("__utma="+_udh+".");
 b=dc.indexOf("__utmb="+_udh);
 c=dc.indexOf("__utmc="+_udh);
 if (_udn && _udn!="") { _udo=" domain="+_udn+";"; }
 if (_utimeout && _utimeout!="") {
  x=new Date(_udt.getTime()+(_utimeout*1000));
  x=" expires="+x.toGMTString()+";";
 }
 if (_ulink) {
  if (_uanchor && _udlh && _udlh!="") s=_udlh+"&";
  s+=_udl.search;
  if(s && s!="" && s.indexOf("__utma=")>=0) {
   if (!(_uIN(a=_uGC(s,"__utma=","&")))) a="-";
   if (!(_uIN(b=_uGC(s,"__utmb=","&")))) b="-";
   if (!(_uIN(c=_uGC(s,"__utmc=","&")))) c="-";
   v=_uGC(s,"__utmv=","&");
   z=_uGC(s,"__utmz=","&");
   k=_uGC(s,"__utmk=","&");
   xx=_uGC(s,"__utmx=","&");
   if ((k*1) != ((_uHash(a+b+c+xx+z+v)*1)+(_udh*1))) {_ubl=1;a="-";b="-";c="-";xx="-";z="-";v="-";}
   if (a!="-" && b!="-" && c!="-") f=1;
   else if(a!="-") f=2;
  }
 }
 if(f==1) {
  _ubd.cookie="__utma="+a+"; path="+_utcp+";"+nx+_udo;
  _ubd.cookie="__utmb="+b+"; path="+_utcp+";"+x+_udo;
  _ubd.cookie="__utmc="+c+"; path="+_utcp+";"+_udo;
 } else if (f==2) {
  a=_uFixA(s,"&",_ust);
  _ubd.cookie="__utma="+a+"; path="+_utcp+";"+nx+_udo;
  _ubd.cookie="__utmb="+_udh+"; path="+_utcp+";"+x+_udo;
  _ubd.cookie="__utmc="+_udh+"; path="+_utcp+";"+_udo;
  _ufns=1;
 } else if (a>=0 && b>=0 && c>=0) {
   b = _uGC(dc,"__utmb="+_udh,";");
   b = ("-" == b) ? _udh : b;  
  _ubd.cookie="__utmb="+b+"; path="+_utcp+";"+x+_udo;
 } else {
  if (a>=0) a=_uFixA(_ubd.cookie,";",_ust);
  else {
   a=_udh+"."+_uu+"."+_ust+"."+_ust+"."+_ust+".1";
   nv=1;
  }
  _ubd.cookie="__utma="+a+"; path="+_utcp+";"+nx+_udo;
  _ubd.cookie="__utmb="+_udh+"; path="+_utcp+";"+x+_udo;
  _ubd.cookie="__utmc="+_udh+"; path="+_utcp+";"+_udo;
  _ufns=1;
 }
 if (_ulink && xx && xx!="" && xx!="-") {
   xx=_uUES(xx);
   if (xx.indexOf(";")==-1) _ubd.cookie="__utmx="+xx+"; path="+_utcp+";"+nx+_udo;
 }
 if (_ulink && v && v!="" && v!="-") {
  v=_uUES(v);
  if (v.indexOf(";")==-1) _ubd.cookie="__utmv="+v+"; path="+_utcp+";"+nx+_udo;
 }
 var wc=window;
 var c=_ubd.cookie;
 if(wc && wc.gaGlobal && wc.gaGlobal.dh==_udh){
  var g=wc.gaGlobal;
  var ua=c.split("__utma="+_udh+".")[1].split(";")[0].split(".");
  if(g.sid)ua[3]=g.sid;
  if(nv>0){
   ua[2]=ua[3];
   if(g.vid){
    var v=g.vid.split(".");
    ua[0]=v[0];
    ua[1]=v[1];
   }
  }
  _ubd.cookie="__utma="+_udh+"."+ua.join(".")+"; path="+_utcp+";"+nx+_udo;
 }
 _uInfo(page);
 _ufns=0;
 _ufno=0;
 if (!page || page=="") _uff=1;
}
function _uGH() {
 var hid;
 var wc=window;
 if (wc && wc.gaGlobal && wc.gaGlobal.hid) {
  hid=wc.gaGlobal.hid;
 } else {
  hid=Math.round(Math.random()*0x7fffffff);
  if (!wc.gaGlobal) wc.gaGlobal={};
  wc.gaGlobal.hid=hid;
 }
 return hid;
}
function _uInfo(page) {
 var p,s="",dm="",pg=_udl.pathname+_udl.search;
 if (page && page!="") pg=_uES(page,1);
 _ur=_ubd.referrer;
 if (!_ur || _ur=="") { _ur="-"; }
 else {
  dm=_ubd.domain;
  if(_utcp && _utcp!="/") dm+=_utcp;
  p=_ur.indexOf(dm);
  if ((p>=0) && (p<=8)) { _ur="0"; }
  if (_ur.indexOf("[")==0 && _ur.lastIndexOf("]")==(_ur.length-1)) { _ur="-"; }
 }
 s+="&utmn="+_uu;
 if (_ufsc) s+=_uBInfo();
 if (_uctm) s+=_uCInfo();
 if (_utitle && _ubd.title && _ubd.title!="") s+="&utmdt="+_uES(_ubd.title);
 if (_udl.hostname && _udl.hostname!="") s+="&utmhn="+_uES(_udl.hostname);
 if (_usample && _usample != 100) s+="&utmsp="+_uES(_usample);
 s+="&utmhid="+_uGH();
 s+="&utmr="+_ur;
 s+="&utmp="+pg;
 if ((_userv==0 || _userv==2) && _uSP()) {
  var i=new Image(1,1);
  i.src=_ugifpath+"?"+"utmwv="+_uwv+s;
  i.onload=function() { _uVoid(); }
 }
 if ((_userv==1 || _userv==2) && _uSP()) {
  var i2=new Image(1,1);
  i2.src=_ugifpath2+"?"+"utmwv="+_uwv+s+"&utmac="+_uacct+"&utmcc="+_uGCS();
  i2.onload=function() { _uVoid(); }
 }
 return;
}
function _uVoid() { return; }
function _uCInfo() {
 if (!_ucto || _ucto=="") { _ucto="15768000"; }
 if (!_uVG()) return;
 var c="",t="-",t2="-",t3="-",o=0,cs=0,cn=0,i=0,z="-",s="";
 if (_uanchor && _udlh && _udlh!="") s=_udlh+"&";
 s+=_udl.search;
 var x=new Date(_udt.getTime()+(_ucto*1000));
 var dc=_ubd.cookie;
 x=" expires="+x.toGMTString()+";";
 if (_ulink && !_ubl) {
  z=_uUES(_uGC(s,"__utmz=","&"));
  if (z!="-" && z.indexOf(";")==-1) { _ubd.cookie="__utmz="+z+"; path="+_utcp+";"+x+_udo; return ""; }
 }
 z=dc.indexOf("__utmz="+_udh+".");
 if (z>-1) { z=_uGC(dc,"__utmz="+_udh+".",";"); }
 else { z="-"; }
 t=_uGC(s,_ucid+"=","&");
 t2=_uGC(s,_ucsr+"=","&");
 t3=_uGC(s,"gclid=","&");
 if ((t!="-" && t!="") || (t2!="-" && t2!="") || (t3!="-" && t3!="")) {
  if (t!="-" && t!="") c+="utmcid="+_uEC(t);
  if (t2!="-" && t2!="") { if (c != "") c+="|"; c+="utmcsr="+_uEC(t2); }
  if (t3!="-" && t3!="") { if (c != "") c+="|"; c+="utmgclid="+_uEC(t3); }
  t=_uGC(s,_uccn+"=","&");
  if (t!="-" && t!="") c+="|utmccn="+_uEC(t);
  else c+="|utmccn=(not+set)";
  t=_uGC(s,_ucmd+"=","&");
  if (t!="-" && t!="") c+="|utmcmd="+_uEC(t);
  else  c+="|utmcmd=(not+set)";
  t=_uGC(s,_uctr+"=","&");
  if (t!="-" && t!="") c+="|utmctr="+_uEC(t);
  else { t=_uOrg(1); if (t!="-" && t!="") c+="|utmctr="+_uEC(t); }
  t=_uGC(s,_ucct+"=","&");
  if (t!="-" && t!="") c+="|utmcct="+_uEC(t);
  t=_uGC(s,_ucno+"=","&");
  if (t=="1") o=1;
  if (z!="-" && o==1) return "";
 }
 if (c=="-" || c=="") { c=_uOrg(); if (z!="-" && _ufno==1)  return ""; }
 if (c=="-" || c=="") { if (_ufns==1)  c=_uRef(); if (z!="-" && _ufno==1)  return ""; }
 if (c=="-" || c=="") {
  if (z=="-" && _ufns==1) { c="utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)"; }
  if (c=="-" || c=="") return "";
 }
 if (z!="-") {
  i=z.indexOf(".");
  if (i>-1) i=z.indexOf(".",i+1);
  if (i>-1) i=z.indexOf(".",i+1);
  if (i>-1) i=z.indexOf(".",i+1);
  t=z.substring(i+1,z.length);
  if (t.toLowerCase()==c.toLowerCase()) cs=1;
  t=z.substring(0,i);
  if ((i=t.lastIndexOf(".")) > -1) {
   t=t.substring(i+1,t.length);
   cn=(t*1);
  }
 }
 if (cs==0 || _ufns==1) {
  t=_uGC(dc,"__utma="+_udh+".",";");
  if ((i=t.lastIndexOf(".")) > 9) {
   _uns=t.substring(i+1,t.length);
   _uns=(_uns*1);
  }
  cn++;
  if (_uns==0) _uns=1;
  _ubd.cookie="__utmz="+_udh+"."+_ust+"."+_uns+"."+cn+"."+c+"; path="+_utcp+"; "+x+_udo;
 }
 if (cs==0 || _ufns==1) return "&utmcn=1";
 else return "&utmcr=1";
}
function _uRef() {
 if (_ur=="0" || _ur=="" || _ur=="-") return "";
 var i=0,h,k,n;
 if ((i=_ur.indexOf("://"))<0 || _uGCse()) return "";
 h=_ur.substring(i+3,_ur.length);
 if (h.indexOf("/") > -1) {
  k=h.substring(h.indexOf("/"),h.length);
  if (k.indexOf("?") > -1) k=k.substring(0,k.indexOf("?"));
  h=h.substring(0,h.indexOf("/"));
 }
 h=h.toLowerCase();
 n=h;
 if ((i=n.indexOf(":")) > -1) n=n.substring(0,i);
 for (var ii=0;ii<_uRno.length;ii++) {
  if ((i=n.indexOf(_uRno[ii].toLowerCase())) > -1 && n.length==(i+_uRno[ii].length)) { _ufno=1; break; }
 }
 if (h.indexOf("www.")==0) h=h.substring(4,h.length);
 return "utmccn=(referral)|utmcsr="+_uEC(h)+"|"+"utmcct="+_uEC(k)+"|utmcmd=referral";
}
function _uOrg(t) {
 if (_ur=="0" || _ur=="" || _ur=="-") return "";
 var i=0,h,k;
 if ((i=_ur.indexOf("://"))<0 || _uGCse()) return "";
 h=_ur.substring(i+3,_ur.length);
 if (h.indexOf("/") > -1) {
  h=h.substring(0,h.indexOf("/"));
 }
 for (var ii=0;ii<_uOsr.length;ii++) {
  if (h.toLowerCase().indexOf(_uOsr[ii].toLowerCase()) > -1) {
   if ((i=_ur.indexOf("?"+_uOkw[ii]+"=")) > -1 || (i=_ur.indexOf("&"+_uOkw[ii]+"=")) > -1) {
    k=_ur.substring(i+_uOkw[ii].length+2,_ur.length);
    if ((i=k.indexOf("&")) > -1) k=k.substring(0,i);
    for (var yy=0;yy<_uOno.length;yy++) {
     if (_uOno[yy].toLowerCase()==k.toLowerCase()) { _ufno=1; break; }
    }
    if (t) return _uEC(k);
    else return "utmccn=(organic)|utmcsr="+_uEC(_uOsr[ii])+"|"+"utmctr="+_uEC(k)+"|utmcmd=organic";
   }
  }
 }
 return "";
}
function _uGCse() {
 var h,p;
 h=p=_ur.split("://")[1];
 if(h.indexOf("/")>-1) {
  h=h.split("/")[0];
  p=p.substring(p.indexOf("/")+1,p.length);
 }
 if(p.indexOf("?")>-1) {
  p=p.split("?")[0];
 }
 if(h.toLowerCase().indexOf("google")>-1) {
  if(_ur.indexOf("?q=")>-1 || _ur.indexOf("&q=")>-1) {
   if (p.toLowerCase().indexOf("cse")>-1) {
    return true;
   }
  }
 }
}
function _uBInfo() {
 var sr="-",sc="-",ul="-",fl="-",cs="-",je=1;
 var n=navigator;
 if (self.screen) {
  sr=screen.width+"x"+screen.height;
  sc=screen.colorDepth+"-bit";
 } else if (self.java) {
  var j=java.awt.Toolkit.getDefaultToolkit();
  var s=j.getScreenSize();
  sr=s.width+"x"+s.height;
 }
 if (n.language) { ul=n.language.toLowerCase(); }
 else if (n.browserLanguage) { ul=n.browserLanguage.toLowerCase(); }
 je=n.javaEnabled()?1:0;
 if (_uflash) fl=_uFlash();
 if (_ubd.characterSet) cs=_uES(_ubd.characterSet);
 else if (_ubd.charset) cs=_uES(_ubd.charset);
 return "&utmcs="+cs+"&utmsr="+sr+"&utmsc="+sc+"&utmul="+ul+"&utmje="+je+"&utmfl="+fl;
}
function __utmSetTrans() {
 var e;
 if (_ubd.getElementById) e=_ubd.getElementById("utmtrans");
 else if (_ubd.utmform && _ubd.utmform.utmtrans) e=_ubd.utmform.utmtrans;
 if (!e) return;
 var l=e.value.split("UTM:");
 var i,i2,c;
 if (_userv==0 || _userv==2) i=new Array();
 if (_userv==1 || _userv==2) { i2=new Array(); c=_uGCS(); }

 for (var ii=0;ii<l.length;ii++) {
  l[ii]=_uTrim(l[ii]);
  if (l[ii].charAt(0)!='T' && l[ii].charAt(0)!='I') continue;
  var r=Math.round(Math.random()*2147483647);
  if (!_utsp || _utsp=="") _utsp="|";
  var f=l[ii].split(_utsp),s="";
  if (f[0].charAt(0)=='T') {
   s="&utmt=tran"+"&utmn="+r;
   f[1]=_uTrim(f[1]); if(f[1]&&f[1]!="") s+="&utmtid="+_uES(f[1]);
   f[2]=_uTrim(f[2]); if(f[2]&&f[2]!="") s+="&utmtst="+_uES(f[2]);
   f[3]=_uTrim(f[3]); if(f[3]&&f[3]!="") s+="&utmtto="+_uES(f[3]);
   f[4]=_uTrim(f[4]); if(f[4]&&f[4]!="") s+="&utmttx="+_uES(f[4]);
   f[5]=_uTrim(f[5]); if(f[5]&&f[5]!="") s+="&utmtsp="+_uES(f[5]);
   f[6]=_uTrim(f[6]); if(f[6]&&f[6]!="") s+="&utmtci="+_uES(f[6]);
   f[7]=_uTrim(f[7]); if(f[7]&&f[7]!="") s+="&utmtrg="+_uES(f[7]);
   f[8]=_uTrim(f[8]); if(f[8]&&f[8]!="") s+="&utmtco="+_uES(f[8]);
  } else {
   s="&utmt=item"+"&utmn="+r;
   f[1]=_uTrim(f[1]); if(f[1]&&f[1]!="") s+="&utmtid="+_uES(f[1]);
   f[2]=_uTrim(f[2]); if(f[2]&&f[2]!="") s+="&utmipc="+_uES(f[2]);
   f[3]=_uTrim(f[3]); if(f[3]&&f[3]!="") s+="&utmipn="+_uES(f[3]);
   f[4]=_uTrim(f[4]); if(f[4]&&f[4]!="") s+="&utmiva="+_uES(f[4]);
   f[5]=_uTrim(f[5]); if(f[5]&&f[5]!="") s+="&utmipr="+_uES(f[5]);
   f[6]=_uTrim(f[6]); if(f[6]&&f[6]!="") s+="&utmiqt="+_uES(f[6]);
  }
  if (_udl.hostname && _udl.hostname!="") s+="&utmhn="+_uES(_udl.hostname);
  if (_usample && _usample != 100) s+="&utmsp="+_uES(_usample);

  if ((_userv==0 || _userv==2) && _uSP()) {
   i[ii]=new Image(1,1);
   i[ii].src=_ugifpath+"?"+"utmwv="+_uwv+s;
   i[ii].onload=function() { _uVoid(); }
  }
  if ((_userv==1 || _userv==2) && _uSP()) {
   i2[ii]=new Image(1,1);
   i2[ii].src=_ugifpath2+"?"+"utmwv="+_uwv+s+"&utmac="+_uacct+"&utmcc="+c;
   i2[ii].onload=function() { _uVoid(); }
  }
 }
 return;
}
function _uFlash() {
 var f="-",n=navigator;
 if (n.plugins && n.plugins.length) {
  for (var ii=0;ii<n.plugins.length;ii++) {
   if (n.plugins[ii].name.indexOf('Shockwave Flash')!=-1) {
    f=n.plugins[ii].description.split('Shockwave Flash ')[1];
    break;
   }
  }
 } else {
  var fl;
  try {
   fl = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
   f = fl.GetVariable("$version");
  } catch(e) {}
  if (f == "-") {
   try {
    fl = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
    f = "WIN 6,0,21,0";
    fl.AllowScriptAccess = "always";
    f = fl.GetVariable("$version");
   } catch(e) {}
  }
  if (f == "-") {
   try {
    fl = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
    f = fl.GetVariable("$version");
   } catch(e) {}
  }
  if (f != "-") {
   f = f.split(" ")[1].split(",");
   f = f[0] + "." + f[1] + " r" + f[2];
  }
 }
 return f;
}
function __utmLinkerUrl(l,h) {
 var p,k,a="-",b="-",c="-",x="-",z="-",v="-";
 var dc=_ubd.cookie;
 var iq = l.indexOf("?");
 var ih = l.indexOf("#");
 var url=l;
 if (dc) {
  a=_uES(_uGC(dc,"__utma="+_udh+".",";"));
  b=_uES(_uGC(dc,"__utmb="+_udh,";"));
  c=_uES(_uGC(dc,"__utmc="+_udh,";"));
  x=_uES(_uGC(dc,"__utmx="+_udh,";"));
  z=_uES(_uGC(dc,"__utmz="+_udh+".",";"));
  v=_uES(_uGC(dc,"__utmv="+_udh+".",";"));
  k=(_uHash(a+b+c+x+z+v)*1)+(_udh*1);
  p="__utma="+a+"&__utmb="+b+"&__utmc="+c+"&__utmx="+x+"&__utmz="+z+"&__utmv="+v+"&__utmk="+k;
 }
 if (p) {
  if (h && ih>-1) return;
  if (h) { url=l+"#"+p; }
  else {
   if (iq==-1 && ih==-1) url=l+"?"+p;
   else if (ih==-1) url=l+"&"+p;
   else if (iq==-1) url=l.substring(0,ih-1)+"?"+p+l.substring(ih);
   else url=l.substring(0,ih-1)+"&"+p+l.substring(ih);
  }
 }
 return url;
}
function __utmLinker(l,h) {
 if (!_ulink || !l || l=="") return;
 _udl.href=__utmLinkerUrl(l,h);
}
function __utmLinkPost(f,h) {
 if (!_ulink || !f || !f.action) return;
 f.action=__utmLinkerUrl(f.action, h);
 return;
}
function __utmSetVar(v) {
 if (!v || v=="") return;
 if (!_udo || _udo == "") {
  _udh=_uDomain();
  if (_udn && _udn!="") { _udo=" domain="+_udn+";"; }
 }
 if (!_uVG()) return;
 var r=Math.round(Math.random() * 2147483647);
 _ubd.cookie="__utmv="+_udh+"."+_uES(v)+"; path="+_utcp+"; expires="+_uNx()+";"+_udo;
 var s="&utmt=var&utmn="+r;
 if (_usample && _usample != 100) s+="&utmsp="+_uES(_usample);
 if ((_userv==0 || _userv==2) && _uSP()) {
  var i=new Image(1,1);
  i.src=_ugifpath+"?"+"utmwv="+_uwv+s;
  i.onload=function() { _uVoid(); }
 }
 if ((_userv==1 || _userv==2) && _uSP()) {
  var i2=new Image(1,1);
  i2.src=_ugifpath2+"?"+"utmwv="+_uwv+s+"&utmac="+_uacct+"&utmcc="+_uGCS();
  i2.onload=function() { _uVoid(); }
 }
}
function _uGCS() {
 var t,c="",dc=_ubd.cookie;
 if ((t=_uGC(dc,"__utma="+_udh+".",";"))!="-") c+=_uES("__utma="+t+";+");
 if ((t=_uGC(dc,"__utmx="+_udh,";"))!="-") c+=_uES("__utmx="+t+";+");
 if ((t=_uGC(dc,"__utmz="+_udh+".",";"))!="-") c+=_uES("__utmz="+t+";+");
 if ((t=_uGC(dc,"__utmv="+_udh+".",";"))!="-") c+=_uES("__utmv="+t+";");
 if (c.charAt(c.length-1)=="+") c=c.substring(0,c.length-1);
 return c;
}
function _uGC(l,n,s) {
 if (!l || l=="" || !n || n=="" || !s || s=="") return "-";
 var i,i2,i3,c="-";
 i=l.indexOf(n);
 i3=n.indexOf("=")+1;
 if (i > -1) {
  i2=l.indexOf(s,i); if (i2 < 0) { i2=l.length; }
  c=l.substring((i+i3),i2);
 }
 return c;
}
function _uDomain() {
 if (!_udn || _udn=="" || _udn=="none") { _udn=""; return 1; }
 if (_udn=="auto") {
  var d=_ubd.domain;
  if (d.substring(0,4)=="www.") {
   d=d.substring(4,d.length);
  }
  _udn=d;
 }
 _udn = _udn.toLowerCase(); 
 if (_uhash=="off") return 1;
 return _uHash(_udn);
}
function _uHash(d) {
 if (!d || d=="") return 1;
 var h=0,g=0;
 for (var i=d.length-1;i>=0;i--) {
  var c=parseInt(d.charCodeAt(i));
  h=((h << 6) & 0xfffffff) + c + (c << 14);
  if ((g=h & 0xfe00000)!=0) h=(h ^ (g >> 21));
 }
 return h;
}
function _uFixA(c,s,t) {
 if (!c || c=="" || !s || s=="" || !t || t=="") return "-";
 var a=_uGC(c,"__utma="+_udh+".",s);
 var lt=0,i=0;
 if ((i=a.lastIndexOf(".")) > 9) {
  _uns=a.substring(i+1,a.length);
  _uns=(_uns*1)+1;
  a=a.substring(0,i);
  if ((i=a.lastIndexOf(".")) > 7) {
   lt=a.substring(i+1,a.length);
   a=a.substring(0,i);
  }
  if ((i=a.lastIndexOf(".")) > 5) {
   a=a.substring(0,i);
  }
  a+="."+lt+"."+t+"."+_uns;
 }
 return a;
}
function _uTrim(s) {
  if (!s || s=="") return "";
  while ((s.charAt(0)==' ') || (s.charAt(0)=='\n') || (s.charAt(0,1)=='\r')) s=s.substring(1,s.length);
  while ((s.charAt(s.length-1)==' ') || (s.charAt(s.length-1)=='\n') || (s.charAt(s.length-1)=='\r')) s=s.substring(0,s.length-1);
  return s;
}
function _uEC(s) {
  var n="";
  if (!s || s=="") return "";
  for (var i=0;i<s.length;i++) {if (s.charAt(i)==" ") n+="+"; else n+=s.charAt(i);}
  return n;
}
function __utmVisitorCode(f) {
 var r=0,t=0,i=0,i2=0,m=31;
 var a=_uGC(_ubd.cookie,"__utma="+_udh+".",";");
 if ((i=a.indexOf(".",0))<0) return;
 if ((i2=a.indexOf(".",i+1))>0) r=a.substring(i+1,i2); else return "";  
 if ((i=a.indexOf(".",i2+1))>0) t=a.substring(i2+1,i); else return "";  
 if (f) {
  return r;
 } else {
  var c=new Array('A','B','C','D','E','F','G','H','J','K','L','M','N','P','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9');
  return c[r>>28&m]+c[r>>23&m]+c[r>>18&m]+c[r>>13&m]+"-"+c[r>>8&m]+c[r>>3&m]+c[((r&7)<<2)+(t>>30&3)]+c[t>>25&m]+c[t>>20&m]+"-"+c[t>>15&m]+c[t>>10&m]+c[t>>5&m]+c[t&m];
 }
}
function _uIN(n) {
 if (!n) return false;
 for (var i=0;i<n.length;i++) {
  var c=n.charAt(i);
  if ((c<"0" || c>"9") && (c!=".")) return false;
 }
 return true;
}
function _uES(s,u) {
 if (typeof(encodeURIComponent) == 'function') {
  if (u) return encodeURI(s);
  else return encodeURIComponent(s);
 } else {
  return escape(s);
 }
}
function _uUES(s) {
 if (typeof(decodeURIComponent) == 'function') {
  return decodeURIComponent(s);
 } else {
  return unescape(s);
 }
}
function _uVG() {
 if((_udn.indexOf("www.google.") == 0 || _udn.indexOf(".google.") == 0 || _udn.indexOf("google.") == 0) && _utcp=='/' && _udn.indexOf("google.org")==-1) {
  return false;
 }
 return true;
}
function _uSP() {
 var s=100;
 if (_usample) s=_usample;
 if(s>=100 || s<=0) return true;
 return ((__utmVisitorCode(1)%10000)<(s*100));
}
function urchinPathCopy(p){
 var d=document,nx,tx,sx,i,c,cs,t,h,o;
 cs=new Array("a","b","c","v","x","z");
 h=_uDomain(); if (_udn && _udn!="") o=" domain="+_udn+";";
 nx=_uNx()+";";
 tx=new Date(); tx.setTime(tx.getTime()+(_utimeout*1000));
 tx=tx.toGMTString()+";";
 sx=new Date(); sx.setTime(sx.getTime()+(_ucto*1000));
 sx=sx.toGMTString()+";";
 for (i=0;i<6;i++){
  t=" expires=";
  if (i==1) t+=tx; else if (i==2) t=""; else if (i==5) t+=sx; else t+=nx;
  c=_uGC(d.cookie,"__utm"+cs[i]+"="+h,";");
  if (c!="-") d.cookie="__utm"+cs[i]+"="+c+"; path="+p+";"+t+o;
 }
}
function _uCO() {
 if (!_utk || _utk=="" || _utk.length<10) return;
 var d='www.google.com';
 if (_utk.charAt(0)=='!') d='analytics.corp.google.com';
 _ubd.cookie="GASO="+_utk+"; path="+_utcp+";"+_udo;
 var sc=document.createElement('script');
 sc.type='text/javascript';
 sc.id="_gasojs";
 sc.src='https://'+d+'/analytics/reporting/overlay_js?gaso='+_utk+'&'+Math.random();
 document.getElementsByTagName('head')[0].appendChild(sc);  
}
function _uGT() {
 var h=location.hash, a;
 if (h && h!="" && h.indexOf("#gaso=")==0) {
  a=_uGC(h,"gaso=","&");
 } else {
  a=_uGC(_ubd.cookie,"GASO=",";");
 }
 return a;
}
var _utk=_uGT();
if (_utk && _utk!="" && _utk.length>10 && _utk.indexOf("=")==-1) {
 if (window.addEventListener) {
  window.addEventListener('load', _uCO, false); 
 } else if (window.attachEvent) { 
  window.attachEvent('onload', _uCO);
 }
}

function _uNx() {
  return (new Date((new Date()).getTime()+63072000000)).toGMTString();
}
/*	Unobtrusive Flash Objects (UFO) v3.20 <http://www.bobbyvandersluis.com/ufo/>
	Copyright 2005, 2006 Bobby van der Sluis
	This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/

var UFO = {
	req: ["movie", "width", "height", "majorversion", "build"],
	opt: ["play", "loop", "menu", "quality", "scale", "salign", "wmode", "bgcolor", "base", "flashvars", "devicefont", "allowscriptaccess", "seamlesstabbing"],
	optAtt: ["id", "name", "align"],
	optExc: ["swliveconnect"],
	ximovie: "ufo.swf",
	xiwidth: "215",
	xiheight: "138",
	ua: navigator.userAgent.toLowerCase(),
	pluginType: "",
	fv: [0,0],
	foList: [],
		
	create: function(FO, id) {
		if (!UFO.uaHas("w3cdom") || UFO.uaHas("ieMac")) return;
		UFO.getFlashVersion();
		UFO.foList[id] = UFO.updateFO(FO);
		UFO.createCSS("#" + id, "visibility:hidden;");
		UFO.domLoad(id);
	},

	updateFO: function(FO) {
		if (typeof FO.xi != "undefined" && FO.xi == "true") {
			if (typeof FO.ximovie == "undefined") FO.ximovie = UFO.ximovie;
			if (typeof FO.xiwidth == "undefined") FO.xiwidth = UFO.xiwidth;
			if (typeof FO.xiheight == "undefined") FO.xiheight = UFO.xiheight;
		}
		FO.mainCalled = false;
		return FO;
	},

	domLoad: function(id) {
		var _t = setInterval(function() {
			if ((document.getElementsByTagName("body")[0] != null || document.body != null) && document.getElementById(id) != null) {
				UFO.main(id);
				clearInterval(_t);
			}
		}, 250);
		if (typeof document.addEventListener != "undefined") {
			document.addEventListener("DOMContentLoaded", function() { UFO.main(id); clearInterval(_t); } , null); // Gecko, Opera 9+
		}
	},

	main: function(id) {
		var _fo = UFO.foList[id];
		if (_fo.mainCalled) return;
		UFO.foList[id].mainCalled = true;
		document.getElementById(id).style.visibility = "hidden";
		if (UFO.hasRequired(id)) {
			if (UFO.hasFlashVersion(parseInt(_fo.majorversion, 10), parseInt(_fo.build, 10))) {
				if (typeof _fo.setcontainercss != "undefined" && _fo.setcontainercss == "true") UFO.setContainerCSS(id);
				UFO.writeSWF(id);
			}
			else if (_fo.xi == "true" && UFO.hasFlashVersion(6, 65)) {
				UFO.createDialog(id);
			}
		}
		document.getElementById(id).style.visibility = "visible";
	},
	
	createCSS: function(selector, declaration) {
		var _h = document.getElementsByTagName("head")[0]; 
		var _s = UFO.createElement("style");
		if (!UFO.uaHas("ieWin")) _s.appendChild(document.createTextNode(selector + " {" + declaration + "}")); // bugs in IE/Win
		_s.setAttribute("type", "text/css");
		_s.setAttribute("media", "screen"); 
		_h.appendChild(_s);
		if (UFO.uaHas("ieWin") && document.styleSheets && document.styleSheets.length > 0) {
			var _ls = document.styleSheets[document.styleSheets.length - 1];
			if (typeof _ls.addRule == "object") _ls.addRule(selector, declaration);
		}
	},
	
	setContainerCSS: function(id) {
		var _fo = UFO.foList[id];
		var _w = /%/.test(_fo.width) ? "" : "px";
		var _h = /%/.test(_fo.height) ? "" : "px";
		UFO.createCSS("#" + id, "width:" + _fo.width + _w +"; height:" + _fo.height + _h +";");
		if (_fo.width == "100%") {
			UFO.createCSS("body", "margin-left:0; margin-right:0; padding-left:0; padding-right:0;");
		}
		if (_fo.height == "100%") {
			UFO.createCSS("html", "height:100%; overflow:hidden;");
			UFO.createCSS("body", "margin-top:0; margin-bottom:0; padding-top:0; padding-bottom:0; height:100%;");
		}
	},

	createElement: function(el) {
		return (UFO.uaHas("xml") && typeof document.createElementNS != "undefined") ?  document.createElementNS("http://www.w3.org/1999/xhtml", el) : document.createElement(el);
	},

	createObjParam: function(el, aName, aValue) {
		var _p = UFO.createElement("param");
		_p.setAttribute("name", aName);	
		_p.setAttribute("value", aValue);
		el.appendChild(_p);
	},

	uaHas: function(ft) {
		var _u = UFO.ua;
		switch(ft) {
			case "w3cdom":
				return (typeof document.getElementById != "undefined" && typeof document.getElementsByTagName != "undefined" && (typeof document.createElement != "undefined" || typeof document.createElementNS != "undefined"));
			case "xml":
				var _m = document.getElementsByTagName("meta");
				var _l = _m.length;
				for (var i = 0; i < _l; i++) {
					if (/content-type/i.test(_m[i].getAttribute("http-equiv")) && /xml/i.test(_m[i].getAttribute("content"))) return true;
				}
				return false;
			case "ieMac":
				return /msie/.test(_u) && !/opera/.test(_u) && /mac/.test(_u);
			case "ieWin":
				return /msie/.test(_u) && !/opera/.test(_u) && /win/.test(_u);
			case "gecko":
				return /gecko/.test(_u) && !/applewebkit/.test(_u);
			case "opera":
				return /opera/.test(_u);
			case "safari":
				return /applewebkit/.test(_u);
			default:
				return false;
		}
	},
	
	getFlashVersion: function() {
		if (UFO.fv[0] != 0) return;  
		if (navigator.plugins && typeof navigator.plugins["Shockwave Flash"] == "object") {
			UFO.pluginType = "npapi";
			var _d = navigator.plugins["Shockwave Flash"].description;
			if (typeof _d != "undefined") {
				_d = _d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				var _m = parseInt(_d.replace(/^(.*)\..*$/, "$1"), 10);
				var _r = /r/.test(_d) ? parseInt(_d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
				UFO.fv = [_m, _r];
			}
		}
		else if (window.ActiveXObject) {
			UFO.pluginType = "ax";
			try { // avoid fp 6 crashes
				var _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
			}
			catch(e) {
				try { 
					var _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
					UFO.fv = [6, 0];
					_a.AllowScriptAccess = "always"; // throws if fp < 6.47 
				}
				catch(e) {
					if (UFO.fv[0] == 6) return;
				}
				try {
					var _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
				}
				catch(e) {}
			}
			if (typeof _a == "object") {
				var _d = _a.GetVariable("$version"); // bugs in fp 6.21/6.23
				if (typeof _d != "undefined") {
					_d = _d.replace(/^\S+\s+(.*)$/, "$1").split(",");
					UFO.fv = [parseInt(_d[0], 10), parseInt(_d[2], 10)];
				}
			}
		}
	},

	hasRequired: function(id) {
		var _l = UFO.req.length;
		for (var i = 0; i < _l; i++) {
			if (typeof UFO.foList[id][UFO.req[i]] == "undefined") return false;
		}
		return true;
	},
	
	hasFlashVersion: function(major, release) {
		return (UFO.fv[0] > major || (UFO.fv[0] == major && UFO.fv[1] >= release)) ? true : false;
	},

	writeSWF: function(id) {
		var _fo = UFO.foList[id];
		var _e = document.getElementById(id);
		if (UFO.pluginType == "npapi") {
			if (UFO.uaHas("gecko") || UFO.uaHas("xml")) {
				while(_e.hasChildNodes()) {
					_e.removeChild(_e.firstChild);
				}
				var _obj = UFO.createElement("object");
				_obj.setAttribute("type", "application/x-shockwave-flash");
				_obj.setAttribute("data", _fo.movie);
				_obj.setAttribute("width", _fo.width);
				_obj.setAttribute("height", _fo.height);
				var _l = UFO.optAtt.length;
				for (var i = 0; i < _l; i++) {
					if (typeof _fo[UFO.optAtt[i]] != "undefined") _obj.setAttribute(UFO.optAtt[i], _fo[UFO.optAtt[i]]);
				}
				var _o = UFO.opt.concat(UFO.optExc);
				var _l = _o.length;
				for (var i = 0; i < _l; i++) {
					if (typeof _fo[_o[i]] != "undefined") UFO.createObjParam(_obj, _o[i], _fo[_o[i]]);
				}
				_e.appendChild(_obj);
			}
			else {
				var _emb = "";
				var _o = UFO.opt.concat(UFO.optAtt).concat(UFO.optExc);
				var _l = _o.length;
				for (var i = 0; i < _l; i++) {
					if (typeof _fo[_o[i]] != "undefined") _emb += ' ' + _o[i] + '="' + _fo[_o[i]] + '"';
				}
				_e.innerHTML = '<embed type="application/x-shockwave-flash" src="' + _fo.movie + '" width="' + _fo.width + '" height="' + _fo.height + '" pluginspage="http://www.macromedia.com/go/getflashplayer"' + _emb + '></embed>';
			}
		}
		else if (UFO.pluginType == "ax") {
			var _objAtt = "";
			var _l = UFO.optAtt.length;
			for (var i = 0; i < _l; i++) {
				if (typeof _fo[UFO.optAtt[i]] != "undefined") _objAtt += ' ' + UFO.optAtt[i] + '="' + _fo[UFO.optAtt[i]] + '"';
			}
			var _objPar = "";
			var _l = UFO.opt.length;
			for (var i = 0; i < _l; i++) {
				if (typeof _fo[UFO.opt[i]] != "undefined") _objPar += '<param name="' + UFO.opt[i] + '" value="' + _fo[UFO.opt[i]] + '" />';
			}
			var _p = window.location.protocol == "https:" ? "https:" : "http:";
			_e.innerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + _objAtt + ' width="' + _fo.width + '" height="' + _fo.height + '" codebase="' + _p + '//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=' + _fo.majorversion + ',0,' + _fo.build + ',0"><param name="movie" value="' + _fo.movie + '" />' + _objPar + '</object>';
		}
	},
		
	createDialog: function(id) {
		var _fo = UFO.foList[id];
		UFO.createCSS("html", "height:100%; overflow:hidden;");
		UFO.createCSS("body", "height:100%; overflow:hidden;");
		UFO.createCSS("#xi-con", "position:absolute; left:0; top:0; z-index:1000; width:100%; height:100%; background-color:#fff; filter:alpha(opacity:75); opacity:0.75;");
		UFO.createCSS("#xi-dia", "position:absolute; left:50%; top:50%; margin-left: -" + Math.round(parseInt(_fo.xiwidth, 10) / 2) + "px; margin-top: -" + Math.round(parseInt(_fo.xiheight, 10) / 2) + "px; width:" + _fo.xiwidth + "px; height:" + _fo.xiheight + "px;");
		var _b = document.getElementsByTagName("body")[0];
		var _c = UFO.createElement("div");
		_c.setAttribute("id", "xi-con");
		var _d = UFO.createElement("div");
		_d.setAttribute("id", "xi-dia");
		_c.appendChild(_d);
		_b.appendChild(_c);
		var _mmu = window.location;
		if (UFO.uaHas("xml") && UFO.uaHas("safari")) {
			var _mmd = document.getElementsByTagName("title")[0].firstChild.nodeValue = document.getElementsByTagName("title")[0].firstChild.nodeValue.slice(0, 47) + " - Flash Player Installation";
		}
		else {
			var _mmd = document.title = document.title.slice(0, 47) + " - Flash Player Installation";
		}
		var _mmp = UFO.pluginType == "ax" ? "ActiveX" : "PlugIn";
		var _uc = typeof _fo.xiurlcancel != "undefined" ? "&xiUrlCancel=" + _fo.xiurlcancel : "";
		var _uf = typeof _fo.xiurlfailed != "undefined" ? "&xiUrlFailed=" + _fo.xiurlfailed : "";
		UFO.foList["xi-dia"] = { movie:_fo.ximovie, width:_fo.xiwidth, height:_fo.xiheight, majorversion:"6", build:"65", flashvars:"MMredirectURL=" + _mmu + "&MMplayerType=" + _mmp + "&MMdoctitle=" + _mmd + _uc + _uf };
		UFO.writeSWF("xi-dia");
	},

	expressInstallCallback: function() {
		var _b = document.getElementsByTagName("body")[0];
		var _c = document.getElementById("xi-con");
		_b.removeChild(_c);
		UFO.createCSS("body", "height:auto; overflow:auto;");
		UFO.createCSS("html", "height:auto; overflow:auto;");
	},

	cleanupIELeaks: function() {
		var _o = document.getElementsByTagName("object");
		var _l = _o.length
		for (var i = 0; i < _l; i++) {
			_o[i].style.display = "none";
			for (var x in _o[i]) {
				if (typeof _o[i][x] == "function") {
					_o[i][x] = null;
				}
			}
		}
	}

};

if (typeof window.attachEvent != "undefined" && UFO.uaHas("ieWin")) {
	window.attachEvent("onunload", UFO.cleanupIELeaks);
}
