/* prototype */
/*  Prototype JavaScript framework, version 1.7
 *  (c) 2005-2010 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {

  Version: '1.7',

  Browser: (function(){
    var ua = navigator.userAgent;
    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
    return {
      IE:             !!window.attachEvent && !isOpera,
      Opera:          isOpera,
      WebKit:         ua.indexOf('AppleWebKit/') > -1,
      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
      MobileSafari:   /Apple.*Mobile/.test(ua)
    }
  })(),

  BrowserFeatures: {
    XPath: !!document.evaluate,

    SelectorsAPI: !!document.querySelector,

    ElementExtensions: (function() {
      var constructor = window.Element || window.HTMLElement;
      return !!(constructor && constructor.prototype);
    })(),
    SpecificElementExtensions: (function() {
      if (typeof window.HTMLDivElement !== 'undefined')
        return true;

      var div = document.createElement('div'),
          form = document.createElement('form'),
          isSupported = false;

      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
        isSupported = true;
      }

      div = form = null;

      return isSupported;
    })()
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },

  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


var Abstract = { };


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

/* Based on Alex Arnell's inheritance implementation. */

var Class = (function() {

  var IS_DONTENUM_BUGGY = (function(){
    for (var p in { toString: 1 }) {
      if (p === 'toString') return false;
    }
    return true;
  })();

  function subclass() {};
  function create() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0, length = properties.length; i < length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;
    return klass;
  }

  function addMethods(source) {
    var ancestor   = this.superclass && this.superclass.prototype,
        properties = Object.keys(source);

    if (IS_DONTENUM_BUGGY) {
      if (source.toString != Object.prototype.toString)
        properties.push("toString");
      if (source.valueOf != Object.prototype.valueOf)
        properties.push("valueOf");
    }

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames()[0] == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }

  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();
(function() {

  var _toString = Object.prototype.toString,
      NULL_TYPE = 'Null',
      UNDEFINED_TYPE = 'Undefined',
      BOOLEAN_TYPE = 'Boolean',
      NUMBER_TYPE = 'Number',
      STRING_TYPE = 'String',
      OBJECT_TYPE = 'Object',
      FUNCTION_CLASS = '[object Function]',
      BOOLEAN_CLASS = '[object Boolean]',
      NUMBER_CLASS = '[object Number]',
      STRING_CLASS = '[object String]',
      ARRAY_CLASS = '[object Array]',
      DATE_CLASS = '[object Date]',
      NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
        typeof JSON.stringify === 'function' &&
        JSON.stringify(0) === '0' &&
        typeof JSON.stringify(Prototype.K) === 'undefined';

  function Type(o) {
    switch(o) {
      case null: return NULL_TYPE;
      case (void 0): return UNDEFINED_TYPE;
    }
    var type = typeof o;
    switch(type) {
      case 'boolean': return BOOLEAN_TYPE;
      case 'number':  return NUMBER_TYPE;
      case 'string':  return STRING_TYPE;
    }
    return OBJECT_TYPE;
  }

  function extend(destination, source) {
    for (var property in source)
      destination[property] = source[property];
    return destination;
  }

  function inspect(object) {
    try {
      if (isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  }

  function toJSON(value) {
    return Str('', { '': value }, []);
  }

  function Str(key, holder, stack) {
    var value = holder[key],
        type = typeof value;

    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
      value = value.toJSON(key);
    }

    var _class = _toString.call(value);

    switch (_class) {
      case NUMBER_CLASS:
      case BOOLEAN_CLASS:
      case STRING_CLASS:
        value = value.valueOf();
    }

    switch (value) {
      case null: return 'null';
      case true: return 'true';
      case false: return 'false';
    }

    type = typeof value;
    switch (type) {
      case 'string':
        return value.inspect(true);
      case 'number':
        return isFinite(value) ? String(value) : 'null';
      case 'object':

        for (var i = 0, length = stack.length; i < length; i++) {
          if (stack[i] === value) { throw new TypeError(); }
        }
        stack.push(value);

        var partial = [];
        if (_class === ARRAY_CLASS) {
          for (var i = 0, length = value.length; i < length; i++) {
            var str = Str(i, value, stack);
            partial.push(typeof str === 'undefined' ? 'null' : str);
          }
          partial = '[' + partial.join(',') + ']';
        } else {
          var keys = Object.keys(value);
          for (var i = 0, length = keys.length; i < length; i++) {
            var key = keys[i], str = Str(key, value, stack);
            if (typeof str !== "undefined") {
               partial.push(key.inspect(true)+ ':' + str);
             }
          }
          partial = '{' + partial.join(',') + '}';
        }
        stack.pop();
        return partial;
    }
  }

  function stringify(object) {
    return JSON.stringify(object);
  }

  function toQueryString(object) {
    return $H(object).toQueryString();
  }

  function toHTML(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  }

  function keys(object) {
    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
    var results = [];
    for (var property in object) {
      if (object.hasOwnProperty(property)) {
        results.push(property);
      }
    }
    return results;
  }

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

  function clone(object) {
    return extend({ }, object);
  }

  function isElement(object) {
    return !!(object && object.nodeType == 1);
  }

  function isArray(object) {
    return _toString.call(object) === ARRAY_CLASS;
  }

  var hasNativeIsArray = (typeof Array.isArray == 'function')
    && Array.isArray([]) && !Array.isArray({});

  if (hasNativeIsArray) {
    isArray = Array.isArray;
  }

  function isHash(object) {
    return object instanceof Hash;
  }

  function isFunction(object) {
    return _toString.call(object) === FUNCTION_CLASS;
  }

  function isString(object) {
    return _toString.call(object) === STRING_CLASS;
  }

  function isNumber(object) {
    return _toString.call(object) === NUMBER_CLASS;
  }

  function isDate(object) {
    return _toString.call(object) === DATE_CLASS;
  }

  function isUndefined(object) {
    return typeof object === "undefined";
  }

  extend(Object, {
    extend:        extend,
    inspect:       inspect,
    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
    toQueryString: toQueryString,
    toHTML:        toHTML,
    keys:          Object.keys || keys,
    values:        values,
    clone:         clone,
    isElement:     isElement,
    isArray:       isArray,
    isHash:        isHash,
    isFunction:    isFunction,
    isString:      isString,
    isNumber:      isNumber,
    isDate:        isDate,
    isUndefined:   isUndefined
  });
})();
Object.extend(Function.prototype, (function() {
  var slice = Array.prototype.slice;

  function update(array, args) {
    var arrayLength = array.length, length = args.length;
    while (length--) array[arrayLength + length] = args[length];
    return array;
  }

  function merge(array, args) {
    array = slice.call(array, 0);
    return update(array, args);
  }

  function argumentNames() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  }

  function bind(context) {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = slice.call(arguments, 1);
    return function() {
      var a = merge(args, arguments);
      return __method.apply(context, a);
    }
  }

  function bindAsEventListener(context) {
    var __method = this, args = slice.call(arguments, 1);
    return function(event) {
      var a = update([event || window.event], args);
      return __method.apply(context, a);
    }
  }

  function curry() {
    if (!arguments.length) return this;
    var __method = this, args = slice.call(arguments, 0);
    return function() {
      var a = merge(args, arguments);
      return __method.apply(this, a);
    }
  }

  function delay(timeout) {
    var __method = this, args = slice.call(arguments, 1);
    timeout = timeout * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  }

  function defer() {
    var args = update([0.01], arguments);
    return this.delay.apply(this, args);
  }

  function wrap(wrapper) {
    var __method = this;
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

  function methodize() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      var a = update([this], arguments);
      return __method.apply(null, a);
    };
  }

  return {
    argumentNames:       argumentNames,
    bind:                bind,
    bindAsEventListener: bindAsEventListener,
    curry:               curry,
    delay:               delay,
    defer:               defer,
    wrap:                wrap,
    methodize:           methodize
  }
})());



(function(proto) {


  function toISOString() {
    return this.getUTCFullYear() + '-' +
      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
      this.getUTCDate().toPaddedString(2) + 'T' +
      this.getUTCHours().toPaddedString(2) + ':' +
      this.getUTCMinutes().toPaddedString(2) + ':' +
      this.getUTCSeconds().toPaddedString(2) + 'Z';
  }


  function toJSON() {
    return this.toISOString();
  }

  if (!proto.toISOString) proto.toISOString = toISOString;
  if (!proto.toJSON) proto.toJSON = toJSON;

})(Date.prototype);


RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
var PeriodicalExecuter = Class.create({
  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);
  },

  execute: function() {
    this.callback(this);
  },

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

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
        this.currentlyExecuting = false;
      } catch(e) {
        this.currentlyExecuting = false;
        throw e;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

Object.extend(String.prototype, (function() {
  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
    typeof JSON.parse === 'function' &&
    JSON.parse('{"test": true}').test;

  function prepareReplacement(replacement) {
    if (Object.isFunction(replacement)) return replacement;
    var template = new Template(replacement);
    return function(match) { return template.evaluate(match) };
  }

  function gsub(pattern, replacement) {
    var result = '', source = this, match;
    replacement = prepareReplacement(replacement);

    if (Object.isString(pattern))
      pattern = RegExp.escape(pattern);

    if (!(pattern.length || pattern.source)) {
      replacement = replacement('');
      return replacement + source.split('').join(replacement) + 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;
  }

  function sub(pattern, replacement, count) {
    replacement = prepareReplacement(replacement);
    count = Object.isUndefined(count) ? 1 : count;

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

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

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

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

  function stripTags() {
    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
  }

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

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

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

  function escapeHTML() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  }

  function unescapeHTML() {
    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
  }


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

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

        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  }

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

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

  function times(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  }

  function camelize() {
    return this.replace(/-+(.)?/g, function(match, chr) {
      return chr ? chr.toUpperCase() : '';
    });
  }

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

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

  function dasherize() {
    return this.replace(/_/g, '-');
  }

  function inspect(useDoubleQuotes) {
    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
      if (character in String.specialChar) {
        return String.specialChar[character];
      }
      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }

  function unfilterJSON(filter) {
    return this.replace(filter || Prototype.JSONFilter, '$1');
  }

  function isJSON() {
    var str = this;
    if (str.blank()) return false;
    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    return (/^[\],:{}\s]*$/).test(str);
  }

  function evalJSON(sanitize) {
    var json = this.unfilterJSON(),
        cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    if (cx.test(json)) {
      json = json.replace(cx, function (a) {
        return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
      });
    }
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  }

  function parseJSON() {
    var json = this.unfilterJSON();
    return JSON.parse(json);
  }

  function include(pattern) {
    return this.indexOf(pattern) > -1;
  }

  function startsWith(pattern) {
    return this.lastIndexOf(pattern, 0) === 0;
  }

  function endsWith(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.indexOf(pattern, d) === d;
  }

  function empty() {
    return this == '';
  }

  function blank() {
    return /^\s*$/.test(this);
  }

  function interpolate(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }

  return {
    gsub:           gsub,
    sub:            sub,
    scan:           scan,
    truncate:       truncate,
    strip:          String.prototype.trim || strip,
    stripTags:      stripTags,
    stripScripts:   stripScripts,
    extractScripts: extractScripts,
    evalScripts:    evalScripts,
    escapeHTML:     escapeHTML,
    unescapeHTML:   unescapeHTML,
    toQueryParams:  toQueryParams,
    parseQuery:     toQueryParams,
    toArray:        toArray,
    succ:           succ,
    times:          times,
    camelize:       camelize,
    capitalize:     capitalize,
    underscore:     underscore,
    dasherize:      dasherize,
    inspect:        inspect,
    unfilterJSON:   unfilterJSON,
    isJSON:         isJSON,
    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
    include:        include,
    startsWith:     startsWith,
    endsWith:       endsWith,
    empty:          empty,
    blank:          blank,
    interpolate:    interpolate
  };
})());

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (object && Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return (match[1] + '');

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3],
          pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;

      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

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

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

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

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

  function collect(iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator.call(context, value, index));
    });
    return results;
  }

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

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

  function grep(filter, iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(RegExp.escape(filter));

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator.call(context, value, index));
    });
    return results;
  }

  function include(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

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

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

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

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

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

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

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

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

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

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

  function toArray() {
    return this.map();
  }

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

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

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

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









  return {
    each:       each,
    eachSlice:  eachSlice,
    all:        all,
    every:      all,
    any:        any,
    some:       any,
    collect:    collect,
    map:        collect,
    detect:     detect,
    findAll:    findAll,
    select:     findAll,
    filter:     findAll,
    grep:       grep,
    include:    include,
    member:     include,
    inGroupsOf: inGroupsOf,
    inject:     inject,
    invoke:     invoke,
    max:        max,
    min:        min,
    partition:  partition,
    pluck:      pluck,
    reject:     reject,
    sortBy:     sortBy,
    toArray:    toArray,
    entries:    toArray,
    zip:        zip,
    size:       size,
    inspect:    inspect,
    find:       detect
  };
})();

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}


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

Array.from = $A;


(function() {
  var arrayProto = Array.prototype,
      slice = arrayProto.slice,
      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available

  function each(iterator, context) {
    for (var i = 0, length = this.length >>> 0; i < length; i++) {
      if (i in this) iterator.call(context, this[i], i, this);
    }
  }
  if (!_each) _each = each;

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

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

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

  function compact() {
    return this.select(function(value) {
      return value != null;
    });
  }

  function flatten() {
    return this.inject([], function(array, value) {
      if (Object.isArray(value))
        return array.concat(value.flatten());
      array.push(value);
      return array;
    });
  }

  function without() {
    var values = slice.call(arguments, 0);
    return this.select(function(value) {
      return !values.include(value);
    });
  }

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

  function uniq(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  }

  function intersect(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  }


  function clone() {
    return slice.call(this, 0);
  }

  function size() {
    return this.length;
  }

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

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

  function lastIndexOf(item, i) {
    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
    var n = this.slice(0, i).reverse().indexOf(item);
    return (n < 0) ? n : i - n - 1;
  }

  function concat() {
    var array = slice.call(this, 0), item;
    for (var i = 0, length = arguments.length; i < length; i++) {
      item = arguments[i];
      if (Object.isArray(item) && !('callee' in item)) {
        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
          array.push(item[j]);
      } else {
        array.push(item);
      }
    }
    return array;
  }

  Object.extend(arrayProto, Enumerable);

  if (!arrayProto._reverse)
    arrayProto._reverse = arrayProto.reverse;

  Object.extend(arrayProto, {
    _each:     _each,
    clear:     clear,
    first:     first,
    last:      last,
    compact:   compact,
    flatten:   flatten,
    without:   without,
    reverse:   reverse,
    uniq:      uniq,
    intersect: intersect,
    clone:     clone,
    toArray:   clone,
    size:      size,
    inspect:   inspect
  });

  var CONCAT_ARGUMENTS_BUGGY = (function() {
    return [].concat(arguments)[0][0] !== 1;
  })(1,2)

  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
})();
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {
  function initialize(object) {
    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
  }


  function _each(iterator) {
    for (var key in this._object) {
      var value = this._object[key], pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  }

  function set(key, value) {
    return this._object[key] = value;
  }

  function get(key) {
    if (this._object[key] !== Object.prototype[key])
      return this._object[key];
  }

  function unset(key) {
    var value = this._object[key];
    delete this._object[key];
    return value;
  }

  function toObject() {
    return Object.clone(this._object);
  }



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

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

  function index(value) {
    var match = this.detect(function(pair) {
      return pair.value === value;
    });
    return match && match.key;
  }

  function merge(object) {
    return this.clone().update(object);
  }

  function update(object) {
    return new Hash(object).inject(this, function(result, pair) {
      result.set(pair.key, pair.value);
      return result;
    });
  }

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  function toQueryString() {
    return this.inject([], function(results, pair) {
      var key = encodeURIComponent(pair.key), values = pair.value;

      if (values && typeof values == 'object') {
        if (Object.isArray(values)) {
          var queryValues = [];
          for (var i = 0, len = values.length, value; i < len; i++) {
            value = values[i];
            queryValues.push(toQueryPair(key, value));
          }
          return results.concat(queryValues);
        }
      } else results.push(toQueryPair(key, values));
      return results;
    }).join('&');
  }

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

  function clone() {
    return new Hash(this);
  }

  return {
    initialize:             initialize,
    _each:                  _each,
    set:                    set,
    get:                    get,
    unset:                  unset,
    toObject:               toObject,
    toTemplateReplacements: toObject,
    keys:                   keys,
    values:                 values,
    index:                  index,
    merge:                  merge,
    update:                 update,
    toQueryString:          toQueryString,
    inspect:                inspect,
    toJSON:                 toObject,
    clone:                  clone
  };
})());

Hash.from = $H;
Object.extend(Number.prototype, (function() {
  function toColorPart() {
    return this.toPaddedString(2, 16);
  }

  function succ() {
    return this + 1;
  }

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

  function toPaddedString(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  }

  function abs() {
    return Math.abs(this);
  }

  function round() {
    return Math.round(this);
  }

  function ceil() {
    return Math.ceil(this);
  }

  function floor() {
    return Math.floor(this);
  }

  return {
    toColorPart:    toColorPart,
    succ:           succ,
    times:          times,
    toPaddedString: toPaddedString,
    abs:            abs,
    round:          round,
    ceil:           ceil,
    floor:          floor
  };
})());

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

var ObjectRange = Class.create(Enumerable, (function() {
  function initialize(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  }

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

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

  return {
    initialize: initialize,
    _each:      _each,
    include:    include
  };
})());



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 (Object.isFunction(responder[callback])) {
        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 = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});
Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

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

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

    if (!['get', 'post'].include(this.method)) {
      params += (params ? '&' : '') + "_method=" + this.method;
      this.method = 'post';
    }

    if (params && this.method === 'get') {
      this.url += (this.url.include('?') ? '&' : '?') + params;
    }

    this.parameters = params.toQueryParams();

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

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

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

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

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.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';
    }

    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        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() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300) || status == 304;
  },

  getStatus: function() {
    try {
      if (this.transport.status === 1223) return 204;
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

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

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

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

    if (state == 'Complete') {
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

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

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

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

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];








Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if (readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,

  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

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

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(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(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  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 (Object.isString(element))
    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(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

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

if (!Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}



(function(global) {
  function shouldUseCache(tagName, attributes) {
    if (tagName === 'select') return false;
    if ('type' in attributes) return false;
    return true;
  }

  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
    try {
      var el = document.createElement('<input name="x">');
      return el.tagName.toLowerCase() === 'input' && el.name === 'x';
    }
    catch(err) {
      return false;
    }
  })();

  var element = global.Element;

  global.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;

    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }

    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));

    var node = shouldUseCache(tagName, attributes) ?
     cache[tagName].cloneNode(false) : document.createElement(tagName);

    return Element.writeAttribute(node, attributes);
  };

  Object.extend(global.Element, element || { });
  if (element) global.Element.prototype = element.prototype;

})(this);

Element.idCounter = 1;
Element.cache = { };

Element._purgeElement = function(element) {
  var uid = element._prototypeUID;
  if (uid) {
    Element.stopObserving(element);
    element._prototypeUID = void 0;
    delete Element.Storage[uid];
  }
}

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 = $(element);
    element.style.display = 'none';
    return element;
  },

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

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

  update: (function(){

    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
      var el = document.createElement("select"),
          isBuggy = true;
      el.innerHTML = "<option value=\"test\">test</option>";
      if (el.options && el.options[0]) {
        isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
      }
      el = null;
      return isBuggy;
    })();

    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
      try {
        var el = document.createElement("table");
        if (el && el.tBodies) {
          el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
          var isBuggy = typeof el.tBodies[0] == "undefined";
          el = null;
          return isBuggy;
        }
      } catch (e) {
        return true;
      }
    })();

    var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
      try {
        var el = document.createElement('div');
        el.innerHTML = "<link>";
        var isBuggy = (el.childNodes.length === 0);
        el = null;
        return isBuggy;
      } catch(e) {
        return true;
      }
    })();

    var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
     TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;

    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
      var s = document.createElement("script"),
          isBuggy = false;
      try {
        s.appendChild(document.createTextNode(""));
        isBuggy = !s.firstChild ||
          s.firstChild && s.firstChild.nodeType !== 3;
      } catch (e) {
        isBuggy = true;
      }
      s = null;
      return isBuggy;
    })();


    function update(element, content) {
      element = $(element);
      var purgeElement = Element._purgeElement;

      var descendants = element.getElementsByTagName('*'),
       i = descendants.length;
      while (i--) purgeElement(descendants[i]);

      if (content && content.toElement)
        content = content.toElement();

      if (Object.isElement(content))
        return element.update().insert(content);

      content = Object.toHTML(content);

      var tagName = element.tagName.toUpperCase();

      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
        element.text = content;
        return element;
      }

      if (ANY_INNERHTML_BUGGY) {
        if (tagName in Element._insertionTranslations.tags) {
          while (element.firstChild) {
            element.removeChild(element.firstChild);
          }
          Element._getContentFromAnonymousElement(tagName, content.stripScripts())
            .each(function(node) {
              element.appendChild(node)
            });
        } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
          while (element.firstChild) {
            element.removeChild(element.firstChild);
          }
          var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
          nodes.each(function(node) { element.appendChild(node) });
        }
        else {
          element.innerHTML = content.stripScripts();
        }
      }
      else {
        element.innerHTML = content.stripScripts();
      }

      content.evalScripts.bind(content).defer();
      return element;
    }

    return update;
  })(),

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  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(),
          value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property, maximumLength) {
    element = $(element);
    maximumLength = maximumLength || -1;
    var elements = [];

    while (element = element[property]) {
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
      if (elements.length == maximumLength)
        break;
    }

    return elements;
  },

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

  descendants: function(element) {
    return Element.select(element, "*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    var results = [], child = $(element).firstChild;
    while (child) {
      if (child.nodeType === 1) {
        results.push(Element.extend(child));
      }
      child = child.nextSibling;
    }
    return results;
  },

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

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

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

  match: function(element, selector) {
    element = $(element);
    if (Object.isString(selector))
      return Prototype.Selector.match(element, selector);
    return selector.match(element);
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = Element.ancestors(element);
    return Object.isNumber(expression) ? ancestors[expression] :
      Prototype.Selector.find(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return Element.firstDescendant(element);
    return Object.isNumber(expression) ? Element.descendants(element)[expression] :
      Element.select(element, expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (Object.isNumber(expression)) index = expression, expression = false;
    if (!Object.isNumber(index)) index = 0;

    if (expression) {
      return Prototype.Selector.find(element.previousSiblings(), expression, index);
    } else {
      return element.recursivelyCollect("previousSibling", index + 1)[index];
    }
  },

  next: function(element, expression, index) {
    element = $(element);
    if (Object.isNumber(expression)) index = expression, expression = false;
    if (!Object.isNumber(index)) index = 0;

    if (expression) {
      return Prototype.Selector.find(element.nextSiblings(), expression, index);
    } else {
      var maximumLength = Object.isNumber(index) ? index + 1 : 1;
      return element.recursivelyCollect("nextSibling", index + 1)[index];
    }
  },


  select: function(element) {
    element = $(element);
    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
    return Prototype.Selector.select(expressions, element);
  },

  adjacent: function(element) {
    element = $(element);
    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
    return Prototype.Selector.select(expressions, element.parentNode).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = Element.readAttribute(element, 'id');
    if (id) return id;
    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
    Element.writeAttribute(element, 'id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

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

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

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

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

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!Element.hasClassName(element, className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

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

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

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (ancestor.contains)
      return ancestor.contains(element) && ancestor !== element;

    while (element = element.parentNode)
      if (element == ancestor) return true;

    return false;
  },

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

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value || value == 'auto') {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      if (Prototype.Browser.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.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== '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;
  },

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

    source = $(source);
    var p = Element.viewportOffset(source), delta = [0, 0], parent = null;

    element = $(element);

    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = Element.getOffsetParent(element);
      delta = Element.viewportOffset(parent);
    }

    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

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

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,

  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'height': case 'width':
          if (!Element.visible(element)) return null;

          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {
  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = (function(){

    var classProp = 'className',
        forProp = 'for',
        el = document.createElement('div');

    el.setAttribute(classProp, 'x');

    if (el.className !== 'x') {
      el.setAttribute('class', 'x');
      if (el.className === 'x') {
        classProp = 'class';
      }
    }
    el = null;

    el = document.createElement('label');
    el.setAttribute(forProp, 'x');
    if (el.htmlFor !== 'x') {
      el.setAttribute('htmlFor', 'x');
      if (el.htmlFor === 'x') {
        forProp = 'htmlFor';
      }
    }
    el = null;

    return {
      read: {
        names: {
          'class':      classProp,
          'className':  classProp,
          'for':        forProp,
          'htmlFor':    forProp
        },
        values: {
          _getAttr: function(element, attribute) {
            return element.getAttribute(attribute);
          },
          _getAttr2: function(element, attribute) {
            return element.getAttribute(attribute, 2);
          },
          _getAttrNode: function(element, attribute) {
            var node = element.getAttributeNode(attribute);
            return node ? node.value : "";
          },
          _getEv: (function(){

            var el = document.createElement('div'), f;
            el.onclick = Prototype.emptyFunction;
            var value = el.getAttribute('onclick');

            if (String(value).indexOf('{') > -1) {
              f = function(element, attribute) {
                attribute = element.getAttribute(attribute);
                if (!attribute) return null;
                attribute = attribute.toString();
                attribute = attribute.split('{')[1];
                attribute = attribute.split('}')[0];
                return attribute.strip();
              };
            }
            else if (value === '') {
              f = function(element, attribute) {
                attribute = element.getAttribute(attribute);
                if (!attribute) return null;
                return attribute.strip();
              };
            }
            el = null;
            return f;
          })(),
          _flag: function(element, attribute) {
            return $(element).hasAttribute(attribute) ? attribute : null;
          },
          style: function(element) {
            return element.style.cssText.toLowerCase();
          },
          title: function(element) {
            return element.title;
          }
        }
      }
    }
  })();

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr2,
      src:         v._getAttr2,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);

  if (Prototype.BrowserFeatures.ElementExtensions) {
    (function() {
      function _descendants(element) {
        var nodes = element.getElementsByTagName('*'), results = [];
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName !== "!") // Filter out comment nodes.
            results.push(node);
        return results;
      }

      Element.Methods.down = function(element, expression, index) {
        element = $(element);
        if (arguments.length == 1) return element.firstDescendant();
        return Object.isNumber(expression) ? _descendants(element)[expression] :
          Element.select(element, expression)[index || 0];
      }
    })();
  }

}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if (element.tagName.toUpperCase() == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };
}

if ('outerHTML' in document.documentElement) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next(),
          fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html, force) {
  var div = new Element('div'),
      t = Element._insertionTranslations.tags[tagName];

  var workaround = false;
  if (t) workaround = true;
  else if (force) {
    workaround = true;
    t = ['', '', 0];
  }

  if (workaround) {
    div.innerHTML = '&nbsp;' + t[0] + html + t[1];
    div.removeChild(div.firstChild);
    for (var i = t[2]; i--; ) {
      div = div.firstChild;
    }
  }
  else {
    div.innerHTML = html;
  }
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  var tags = Element._insertionTranslations.tags;
  Object.extend(tags, {
    THEAD: tags.TBODY,
    TFOOT: tags.TBODY,
    TH:    tags.TD
  });
})();

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

(function(div) {

  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
    window.HTMLElement = { };
    window.HTMLElement.prototype = div['__proto__'];
    Prototype.BrowserFeatures.ElementExtensions = true;
  }

  div = null;

})(document.createElement('div'));

Element.extend = (function() {

  function checkDeficiency(tagName) {
    if (typeof window.Element != 'undefined') {
      var proto = window.Element.prototype;
      if (proto) {
        var id = '_' + (Math.random()+'').slice(2),
            el = document.createElement(tagName);
        proto[id] = 'x';
        var isBuggy = (el[id] !== 'x');
        delete proto[id];
        el = null;
        return isBuggy;
      }
    }
    return false;
  }

  function extendElementWith(element, methods) {
    for (var property in methods) {
      var value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }
  }

  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

  if (Prototype.BrowserFeatures.SpecificElementExtensions) {
    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
      return function(element) {
        if (element && typeof element._extendedByPrototype == 'undefined') {
          var t = element.tagName;
          if (t && (/^(?:object|applet|embed)$/i.test(t))) {
            extendElementWith(element, Element.Methods);
            extendElementWith(element, Element.Methods.Simulated);
            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
          }
        }
        return element;
      }
    }
    return Prototype.K;
  }

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || typeof element._extendedByPrototype != 'undefined' ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
        tagName = element.tagName.toUpperCase();

    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    extendElementWith(element, methods);

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

if (document.documentElement.hasAttribute) {
  Element.hasAttribute = function(element, attribute) {
    return element.hasAttribute(attribute);
  };
}
else {
  Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
}

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods),
      "BUTTON":   Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    var element = document.createElement(tagName),
        proto = element['__proto__'] || element.constructor.prototype;

    element = null;
    return proto;
  }

  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
   Element.prototype;

  if (F.ElementExtensions) {
    copy(Element.Methods, elementPrototype);
    copy(Element.Methods.Simulated, elementPrototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};


document.viewport = {

  getDimensions: function() {
    return { width: this.getWidth(), height: this.getHeight() };
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
  }
};

(function(viewport) {
  var B = Prototype.Browser, doc = document, element, property = {};

  function getRootElement() {
    if (B.WebKit && !doc.evaluate)
      return document;

    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
      return document.body;

    return document.documentElement;
  }

  function define(D) {
    if (!element) element = getRootElement();

    property[D] = 'client' + D;

    viewport['get' + D] = function() { return element[property[D]] };
    return viewport['get' + D]();
  }

  viewport.getWidth  = define.curry('Width');

  viewport.getHeight = define.curry('Height');
})(document.viewport);


Element.Storage = {
  UID: 1
};

Element.addMethods({
  getStorage: function(element) {
    if (!(element = $(element))) return;

    var uid;
    if (element === window) {
      uid = 0;
    } else {
      if (typeof element._prototypeUID === "undefined")
        element._prototypeUID = Element.Storage.UID++;
      uid = element._prototypeUID;
    }

    if (!Element.Storage[uid])
      Element.Storage[uid] = $H();

    return Element.Storage[uid];
  },

  store: function(element, key, value) {
    if (!(element = $(element))) return;

    if (arguments.length === 2) {
      Element.getStorage(element).update(key);
    } else {
      Element.getStorage(element).set(key, value);
    }

    return element;
  },

  retrieve: function(element, key, defaultValue) {
    if (!(element = $(element))) return;
    var hash = Element.getStorage(element), value = hash.get(key);

    if (Object.isUndefined(value)) {
      hash.set(key, defaultValue);
      value = defaultValue;
    }

    return value;
  },

  clone: function(element, deep) {
    if (!(element = $(element))) return;
    var clone = element.cloneNode(deep);
    clone._prototypeUID = void 0;
    if (deep) {
      var descendants = Element.select(clone, '*'),
          i = descendants.length;
      while (i--) {
        descendants[i]._prototypeUID = void 0;
      }
    }
    return Element.extend(clone);
  },

  purge: function(element) {
    if (!(element = $(element))) return;
    var purgeElement = Element._purgeElement;

    purgeElement(element);

    var descendants = element.getElementsByTagName('*'),
     i = descendants.length;

    while (i--) purgeElement(descendants[i]);

    return null;
  }
});

(function() {

  function toDecimal(pctString) {
    var match = pctString.match(/^(\d+)%?$/i);
    if (!match) return null;
    return (Number(match[1]) / 100);
  }

  function getPixelValue(value, property, context) {
    var element = null;
    if (Object.isElement(value)) {
      element = value;
      value = element.getStyle(property);
    }

    if (value === null) {
      return null;
    }

    if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
      return window.parseFloat(value);
    }

    var isPercentage = value.include('%'), isViewport = (context === document.viewport);

    if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
      var style = element.style.left, rStyle = element.runtimeStyle.left;
      element.runtimeStyle.left = element.currentStyle.left;
      element.style.left = value || 0;
      value = element.style.pixelLeft;
      element.style.left = style;
      element.runtimeStyle.left = rStyle;

      return value;
    }

    if (element && isPercentage) {
      context = context || element.parentNode;
      var decimal = toDecimal(value);
      var whole = null;
      var position = element.getStyle('position');

      var isHorizontal = property.include('left') || property.include('right') ||
       property.include('width');

      var isVertical =  property.include('top') || property.include('bottom') ||
        property.include('height');

      if (context === document.viewport) {
        if (isHorizontal) {
          whole = document.viewport.getWidth();
        } else if (isVertical) {
          whole = document.viewport.getHeight();
        }
      } else {
        if (isHorizontal) {
          whole = $(context).measure('width');
        } else if (isVertical) {
          whole = $(context).measure('height');
        }
      }

      return (whole === null) ? 0 : whole * decimal;
    }

    return 0;
  }

  function toCSSPixels(number) {
    if (Object.isString(number) && number.endsWith('px')) {
      return number;
    }
    return number + 'px';
  }

  function isDisplayed(element) {
    var originalElement = element;
    while (element && element.parentNode) {
      var display = element.getStyle('display');
      if (display === 'none') {
        return false;
      }
      element = $(element.parentNode);
    }
    return true;
  }

  var hasLayout = Prototype.K;
  if ('currentStyle' in document.documentElement) {
    hasLayout = function(element) {
      if (!element.currentStyle.hasLayout) {
        element.style.zoom = 1;
      }
      return element;
    };
  }

  function cssNameFor(key) {
    if (key.include('border')) key = key + '-width';
    return key.camelize();
  }

  Element.Layout = Class.create(Hash, {
    initialize: function($super, element, preCompute) {
      $super();
      this.element = $(element);

      Element.Layout.PROPERTIES.each( function(property) {
        this._set(property, null);
      }, this);

      if (preCompute) {
        this._preComputing = true;
        this._begin();
        Element.Layout.PROPERTIES.each( this._compute, this );
        this._end();
        this._preComputing = false;
      }
    },

    _set: function(property, value) {
      return Hash.prototype.set.call(this, property, value);
    },

    set: function(property, value) {
      throw "Properties of Element.Layout are read-only.";
    },

    get: function($super, property) {
      var value = $super(property);
      return value === null ? this._compute(property) : value;
    },

    _begin: function() {
      if (this._prepared) return;

      var element = this.element;
      if (isDisplayed(element)) {
        this._prepared = true;
        return;
      }

      var originalStyles = {
        position:   element.style.position   || '',
        width:      element.style.width      || '',
        visibility: element.style.visibility || '',
        display:    element.style.display    || ''
      };

      element.store('prototype_original_styles', originalStyles);

      var position = element.getStyle('position'),
       width = element.getStyle('width');

      if (width === "0px" || width === null) {
        element.style.display = 'block';
        width = element.getStyle('width');
      }

      var context = (position === 'fixed') ? document.viewport :
       element.parentNode;

      element.setStyle({
        position:   'absolute',
        visibility: 'hidden',
        display:    'block'
      });

      var positionedWidth = element.getStyle('width');

      var newWidth;
      if (width && (positionedWidth === width)) {
        newWidth = getPixelValue(element, 'width', context);
      } else if (position === 'absolute' || position === 'fixed') {
        newWidth = getPixelValue(element, 'width', context);
      } else {
        var parent = element.parentNode, pLayout = $(parent).getLayout();

        newWidth = pLayout.get('width') -
         this.get('margin-left') -
         this.get('border-left') -
         this.get('padding-left') -
         this.get('padding-right') -
         this.get('border-right') -
         this.get('margin-right');
      }

      element.setStyle({ width: newWidth + 'px' });

      this._prepared = true;
    },

    _end: function() {
      var element = this.element;
      var originalStyles = element.retrieve('prototype_original_styles');
      element.store('prototype_original_styles', null);
      element.setStyle(originalStyles);
      this._prepared = false;
    },

    _compute: function(property) {
      var COMPUTATIONS = Element.Layout.COMPUTATIONS;
      if (!(property in COMPUTATIONS)) {
        throw "Property not found.";
      }

      return this._set(property, COMPUTATIONS[property].call(this, this.element));
    },

    toObject: function() {
      var args = $A(arguments);
      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
       args.join(' ').split(' ');
      var obj = {};
      keys.each( function(key) {
        if (!Element.Layout.PROPERTIES.include(key)) return;
        var value = this.get(key);
        if (value != null) obj[key] = value;
      }, this);
      return obj;
    },

    toHash: function() {
      var obj = this.toObject.apply(this, arguments);
      return new Hash(obj);
    },

    toCSS: function() {
      var args = $A(arguments);
      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
       args.join(' ').split(' ');
      var css = {};

      keys.each( function(key) {
        if (!Element.Layout.PROPERTIES.include(key)) return;
        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;

        var value = this.get(key);
        if (value != null) css[cssNameFor(key)] = value + 'px';
      }, this);
      return css;
    },

    inspect: function() {
      return "#<Element.Layout>";
    }
  });

  Object.extend(Element.Layout, {
    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),

    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),

    COMPUTATIONS: {
      'height': function(element) {
        if (!this._preComputing) this._begin();

        var bHeight = this.get('border-box-height');
        if (bHeight <= 0) {
          if (!this._preComputing) this._end();
          return 0;
        }

        var bTop = this.get('border-top'),
         bBottom = this.get('border-bottom');

        var pTop = this.get('padding-top'),
         pBottom = this.get('padding-bottom');

        if (!this._preComputing) this._end();

        return bHeight - bTop - bBottom - pTop - pBottom;
      },

      'width': function(element) {
        if (!this._preComputing) this._begin();

        var bWidth = this.get('border-box-width');
        if (bWidth <= 0) {
          if (!this._preComputing) this._end();
          return 0;
        }

        var bLeft = this.get('border-left'),
         bRight = this.get('border-right');

        var pLeft = this.get('padding-left'),
         pRight = this.get('padding-right');

        if (!this._preComputing) this._end();

        return bWidth - bLeft - bRight - pLeft - pRight;
      },

      'padding-box-height': function(element) {
        var height = this.get('height'),
         pTop = this.get('padding-top'),
         pBottom = this.get('padding-bottom');

        return height + pTop + pBottom;
      },

      'padding-box-width': function(element) {
        var width = this.get('width'),
         pLeft = this.get('padding-left'),
         pRight = this.get('padding-right');

        return width + pLeft + pRight;
      },

      'border-box-height': function(element) {
        if (!this._preComputing) this._begin();
        var height = element.offsetHeight;
        if (!this._preComputing) this._end();
        return height;
      },

      'border-box-width': function(element) {
        if (!this._preComputing) this._begin();
        var width = element.offsetWidth;
        if (!this._preComputing) this._end();
        return width;
      },

      'margin-box-height': function(element) {
        var bHeight = this.get('border-box-height'),
         mTop = this.get('margin-top'),
         mBottom = this.get('margin-bottom');

        if (bHeight <= 0) return 0;

        return bHeight + mTop + mBottom;
      },

      'margin-box-width': function(element) {
        var bWidth = this.get('border-box-width'),
         mLeft = this.get('margin-left'),
         mRight = this.get('margin-right');

        if (bWidth <= 0) return 0;

        return bWidth + mLeft + mRight;
      },

      'top': function(element) {
        var offset = element.positionedOffset();
        return offset.top;
      },

      'bottom': function(element) {
        var offset = element.positionedOffset(),
         parent = element.getOffsetParent(),
         pHeight = parent.measure('height');

        var mHeight = this.get('border-box-height');

        return pHeight - mHeight - offset.top;
      },

      'left': function(element) {
        var offset = element.positionedOffset();
        return offset.left;
      },

      'right': function(element) {
        var offset = element.positionedOffset(),
         parent = element.getOffsetParent(),
         pWidth = parent.measure('width');

        var mWidth = this.get('border-box-width');

        return pWidth - mWidth - offset.left;
      },

      'padding-top': function(element) {
        return getPixelValue(element, 'paddingTop');
      },

      'padding-bottom': function(element) {
        return getPixelValue(element, 'paddingBottom');
      },

      'padding-left': function(element) {
        return getPixelValue(element, 'paddingLeft');
      },

      'padding-right': function(element) {
        return getPixelValue(element, 'paddingRight');
      },

      'border-top': function(element) {
        return getPixelValue(element, 'borderTopWidth');
      },

      'border-bottom': function(element) {
        return getPixelValue(element, 'borderBottomWidth');
      },

      'border-left': function(element) {
        return getPixelValue(element, 'borderLeftWidth');
      },

      'border-right': function(element) {
        return getPixelValue(element, 'borderRightWidth');
      },

      'margin-top': function(element) {
        return getPixelValue(element, 'marginTop');
      },

      'margin-bottom': function(element) {
        return getPixelValue(element, 'marginBottom');
      },

      'margin-left': function(element) {
        return getPixelValue(element, 'marginLeft');
      },

      'margin-right': function(element) {
        return getPixelValue(element, 'marginRight');
      }
    }
  });

  if ('getBoundingClientRect' in document.documentElement) {
    Object.extend(Element.Layout.COMPUTATIONS, {
      'right': function(element) {
        var parent = hasLayout(element.getOffsetParent());
        var rect = element.getBoundingClientRect(),
         pRect = parent.getBoundingClientRect();

        return (pRect.right - rect.right).round();
      },

      'bottom': function(element) {
        var parent = hasLayout(element.getOffsetParent());
        var rect = element.getBoundingClientRect(),
         pRect = parent.getBoundingClientRect();

        return (pRect.bottom - rect.bottom).round();
      }
    });
  }

  Element.Offset = Class.create({
    initialize: function(left, top) {
      this.left = left.round();
      this.top  = top.round();

      this[0] = this.left;
      this[1] = this.top;
    },

    relativeTo: function(offset) {
      return new Element.Offset(
        this.left - offset.left,
        this.top  - offset.top
      );
    },

    inspect: function() {
      return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
    },

    toString: function() {
      return "[#{left}, #{top}]".interpolate(this);
    },

    toArray: function() {
      return [this.left, this.top];
    }
  });

  function getLayout(element, preCompute) {
    return new Element.Layout(element, preCompute);
  }

  function measure(element, property) {
    return $(element).getLayout().get(property);
  }

  function getDimensions(element) {
    element = $(element);
    var display = Element.getStyle(element, 'display');

    if (display && display !== 'none') {
      return { width: element.offsetWidth, height: element.offsetHeight };
    }

    var style = element.style;
    var originalStyles = {
      visibility: style.visibility,
      position:   style.position,
      display:    style.display
    };

    var newStyles = {
      visibility: 'hidden',
      display:    'block'
    };

    if (originalStyles.position !== 'fixed')
      newStyles.position = 'absolute';

    Element.setStyle(element, newStyles);

    var dimensions = {
      width:  element.offsetWidth,
      height: element.offsetHeight
    };

    Element.setStyle(element, originalStyles);

    return dimensions;
  }

  function getOffsetParent(element) {
    element = $(element);

    if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
      return $(document.body);

    var isInline = (Element.getStyle(element, 'display') === 'inline');
    if (!isInline && element.offsetParent) return $(element.offsetParent);

    while ((element = element.parentNode) && element !== document.body) {
      if (Element.getStyle(element, 'position') !== 'static') {
        return isHtml(element) ? $(document.body) : $(element);
      }
    }

    return $(document.body);
  }


  function cumulativeOffset(element) {
    element = $(element);
    var valueT = 0, valueL = 0;
    if (element.parentNode) {
      do {
        valueT += element.offsetTop  || 0;
        valueL += element.offsetLeft || 0;
        element = element.offsetParent;
      } while (element);
    }
    return new Element.Offset(valueL, valueT);
  }

  function positionedOffset(element) {
    element = $(element);

    var layout = element.getLayout();

    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (isBody(element)) break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);

    valueL -= layout.get('margin-top');
    valueT -= layout.get('margin-left');

    return new Element.Offset(valueL, valueT);
  }

  function cumulativeScrollOffset(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return new Element.Offset(valueL, valueT);
  }

  function viewportOffset(forElement) {
    element = $(element);
    var valueT = 0, valueL = 0, docBody = document.body;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == docBody &&
        Element.getStyle(element, 'position') == 'absolute') break;
    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (element != docBody) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);
    return new Element.Offset(valueL, valueT);
  }

  function absolutize(element) {
    element = $(element);

    if (Element.getStyle(element, 'position') === 'absolute') {
      return element;
    }

    var offsetParent = getOffsetParent(element);
    var eOffset = element.viewportOffset(),
     pOffset = offsetParent.viewportOffset();

    var offset = eOffset.relativeTo(pOffset);
    var layout = element.getLayout();

    element.store('prototype_absolutize_original_styles', {
      left:   element.getStyle('left'),
      top:    element.getStyle('top'),
      width:  element.getStyle('width'),
      height: element.getStyle('height')
    });

    element.setStyle({
      position: 'absolute',
      top:    offset.top + 'px',
      left:   offset.left + 'px',
      width:  layout.get('width') + 'px',
      height: layout.get('height') + 'px'
    });

    return element;
  }

  function relativize(element) {
    element = $(element);
    if (Element.getStyle(element, 'position') === 'relative') {
      return element;
    }

    var originalStyles =
     element.retrieve('prototype_absolutize_original_styles');

    if (originalStyles) element.setStyle(originalStyles);
    return element;
  }

  if (Prototype.Browser.IE) {
    getOffsetParent = getOffsetParent.wrap(
      function(proceed, element) {
        element = $(element);

        if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
          return $(document.body);

        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);

        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );

    positionedOffset = positionedOffset.wrap(function(proceed, element) {
      element = $(element);
      if (!element.parentNode) return new Element.Offset(0, 0);
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);

      var offsetParent = element.getOffsetParent();
      if (offsetParent && offsetParent.getStyle('position') === 'fixed')
        hasLayout(offsetParent);

      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    });
  } else if (Prototype.Browser.Webkit) {
    cumulativeOffset = function(element) {
      element = $(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 new Element.Offset(valueL, valueT);
    };
  }


  Element.addMethods({
    getLayout:              getLayout,
    measure:                measure,
    getDimensions:          getDimensions,
    getOffsetParent:        getOffsetParent,
    cumulativeOffset:       cumulativeOffset,
    positionedOffset:       positionedOffset,
    cumulativeScrollOffset: cumulativeScrollOffset,
    viewportOffset:         viewportOffset,
    absolutize:             absolutize,
    relativize:             relativize
  });

  function isBody(element) {
    return element.nodeName.toUpperCase() === 'BODY';
  }

  function isHtml(element) {
    return element.nodeName.toUpperCase() === 'HTML';
  }

  function isDocument(element) {
    return element.nodeType === Node.DOCUMENT_NODE;
  }

  function isDetached(element) {
    return element !== document.body &&
     !Element.descendantOf(element, document.body);
  }

  if ('getBoundingClientRect' in document.documentElement) {
    Element.addMethods({
      viewportOffset: function(element) {
        element = $(element);
        if (isDetached(element)) return new Element.Offset(0, 0);

        var rect = element.getBoundingClientRect(),
         docEl = document.documentElement;
        return new Element.Offset(rect.left - docEl.clientLeft,
         rect.top - docEl.clientTop);
      }
    });
  }
})();
window.$$ = function() {
  var expression = $A(arguments).join(', ');
  return Prototype.Selector.select(expression, document);
};

Prototype.Selector = (function() {

  function select() {
    throw new Error('Method "Prototype.Selector.select" must be defined.');
  }

  function match() {
    throw new Error('Method "Prototype.Selector.match" must be defined.');
  }

  function find(elements, expression, index) {
    index = index || 0;
    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;

    for (i = 0; i < length; i++) {
      if (match(elements[i], expression) && index == matchIndex++) {
        return Element.extend(elements[i]);
      }
    }
  }

  function extendElements(elements) {
    for (var i = 0, length = elements.length; i < length; i++) {
      Element.extend(elements[i]);
    }
    return elements;
  }


  var K = Prototype.K;

  return {
    select: select,
    match: match,
    find: find,
    extendElements: (Element.extend === K) ? K : extendElements,
    extendElement: Element.extend
  };
})();
Prototype._original_property = window.Sizzle;
/*!
 * Sizzle CSS Selector Engine - v1.0
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
	done = 0,
	toString = Object.prototype.toString,
	hasDuplicate = false,
	baseHasDuplicate = true;

[0, 0].sort(function(){
	baseHasDuplicate = false;
	return 0;
});

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	var origContext = context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
		soFar = selector;

	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
		soFar = m[3];

		parts.push( m[1] );

		if ( m[2] ) {
			extra = m[3];
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] )
					selector += parts.shift();

				set = posProcess( selector, set );
			}
		}
	} else {
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
			var ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
		}

		if ( context ) {
			var ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray(set);
			} else {
				prune = false;
			}

			while ( parts.length ) {
				var cur = parts.pop(), pop = cur;

				if ( !Expr.relative[ cur ] ) {
					cur = "";
				} else {
					pop = parts.pop();
				}

				if ( pop == null ) {
					pop = context;
				}

				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}
		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		throw "Syntax error, unrecognized expression: " + (cur || selector);
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context && context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, origContext, results, seed );
		Sizzle.uniqueSort( results );
	}

	return results;
};

Sizzle.uniqueSort = function(results){
	if ( sortOrder ) {
		hasDuplicate = baseHasDuplicate;
		results.sort(sortOrder);

		if ( hasDuplicate ) {
			for ( var i = 1; i < results.length; i++ ) {
				if ( results[i] === results[i-1] ) {
					results.splice(i--, 1);
				}
			}
		}
	}

	return results;
};

Sizzle.matches = function(expr, set){
	return Sizzle(expr, null, null, set);
};

Sizzle.find = function(expr, context, isXML){
	var set, match;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var type = Expr.order[i], match;

		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
			var left = match[1];
			match.splice(1,1);

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace(/\\/g, "");
				set = Expr.find[ type ]( match, context, isXML );
				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = context.getElementsByTagName("*");
	}

	return {set: set, expr: expr};
};

Sizzle.filter = function(expr, set, inplace, not){
	var old = expr, result = [], curLoop = set, match, anyFound,
		isXMLFilter = set && set[0] && isXML(set[0]);

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
				var filter = Expr.filter[ type ], found, item;
				anyFound = false;

				if ( curLoop == result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;
					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;
								} else {
									curLoop[i] = false;
								}
							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		if ( expr == old ) {
			if ( anyFound == null ) {
				throw "Syntax error, unrecognized expression: " + expr;
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	leftMatch: {},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !/\W/.test(part) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !/\W/.test(part) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
			if ( match[1] == "nth" ) {
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");

			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}

			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {
		enabled: function(elem){
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
			return !!elem.firstChild;
		},
		empty: function(elem){
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i < match[3] - 0;
		},
		gt: function(elem, i, match){
			return i > match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i < l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while ( (node = node.previousSibling) )  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while ( (node = node.nextSibling) )  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}

					var doneName = match[0],
						parent = elem.parentNode;

					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						}
						parent.sizcache = doneName;
					}

					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;
					} else {
						return ( diff % first == 0 && diff / first >= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array, 0 );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}

	return array;
};

try {
	Array.prototype.slice.call( document.documentElement.childNodes, 0 );

} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( "sourceIndex" in document.documentElement ) {
	sortOrder = function( a, b ) {
		if ( !a.sourceIndex || !b.sourceIndex ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		if ( !a.ownerDocument || !b.ownerDocument ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return 0;
		}

		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.setStart(a, 0);
		aRange.setEnd(a, 0);
		bRange.setStart(b, 0);
		bRange.setEnd(b, 0);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

(function(){
	var form = document.createElement("div"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<a name='" + id + "'/>";

	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	if ( !!document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
	root = form = null; // release memory in IE
})();

(function(){

	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	div.innerHTML = "<a href='#'></a>";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}

	div = null; // release memory in IE
})();

if ( document.querySelectorAll ) (function(){
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "<p class='TEST'></p>";

	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}

	Sizzle = function(query, context, extra, seed){
		context = context || document;

		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}

		return oldSizzle(query, context, extra, seed);
	};

	for ( var prop in oldSizzle ) {
		Sizzle[ prop ] = oldSizzle[ prop ];
	}

	div = null; // release memory in IE
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
	var div = document.createElement("div");
	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	if ( div.getElementsByClassName("e").length === 0 )
		return;

	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;

	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};

	div = null; // release memory in IE
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

var contains = document.compareDocumentPosition ?  function(a, b){
	return a.compareDocumentPosition(b) & 16;
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

var isXML = function(elem){
	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
		!!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
};

var posProcess = function(selector, context){
	var tmpSet = [], later = "", match,
		root = context.nodeType ? [context] : context;

	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};


window.Sizzle = Sizzle;

})();

;(function(engine) {
  var extendElements = Prototype.Selector.extendElements;

  function select(selector, scope) {
    return extendElements(engine(selector, scope || document));
  }

  function match(element, selector) {
    return engine.matches(selector, [element]).length == 1;
  }

  Prototype.Selector.engine = engine;
  Prototype.Selector.select = select;
  Prototype.Selector.match = match;
})(Sizzle);

window.Sizzle = Prototype._original_property;
delete Prototype._original_property;

var Form = {
  reset: function(form) {
    form = $(form);
    form.reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit, accumulator, initial;

    if (options.hash) {
      initial = {};
      accumulator = function(result, key, value) {
        if (key in result) {
          if (!Object.isArray(result[key])) result[key] = [result[key]];
          result[key].push(value);
        } else result[key] = value;
        return result;
      };
    } else {
      initial = '';
      accumulator = function(result, key, value) {
        return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
      }
    }

    return elements.inject(initial, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          result = accumulator(result, key, value);
        }
      }
      return result;
    });
  }
};

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

  getElements: function(form) {
    var elements = $(form).getElementsByTagName('*'),
        element,
        arr = [ ],
        serializers = Form.Element.Serializers;
    for (var i = 0; element = elements[i]; i++) {
      arr.push(element);
    }
    return arr.inject([], function(elements, child) {
      if (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(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return /^(?:input|select|textarea)$/i.test(element.tagName);
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    var element = form.findFirstElement();
    if (element) element.activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

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


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 Object.toQueryString(pair);
      }
    }
    return '';
  },

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

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

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

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

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !(/^(?:button|reset|submit)$/i.test(element.type))))
        element.select();
    } catch (e) { }
    return element;
  },

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

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

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

var Field = Form.Element;

var $F = Form.Element.Methods.getValue;

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

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

  function inputSelector(element, value) {
    if (Object.isUndefined(value))
      return element.checked ? element.value : null;
    else element.checked = !!value;
  }

  function valueSelector(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  }

  function select(element, value) {
    if (Object.isUndefined(value))
      return (element.type === 'select-one' ? selectOne : selectMany)(element);

    var opt, currentValue, single = !Object.isArray(value);
    for (var i = 0, length = element.length; i < length; i++) {
      opt = element.options[i];
      currentValue = this.optionValue(opt);
      if (single) {
        if (currentValue == value) {
          opt.selected = true;
          return;
        }
      }
      else opt.selected = value.include(currentValue);
    }
  }

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

  function selectMany(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(optionValue(opt));
    }
    return values;
  }

  function optionValue(opt) {
    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
  }

  return {
    input:         input,
    inputSelector: inputSelector,
    textarea:      valueSelector,
    select:        select,
    selectOne:     selectOne,
    selectMany:    selectMany,
    optionValue:   optionValue,
    button:        valueSelector
  };
})();

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


Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

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

Abstract.EventObserver = Class.create({
  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, 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(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
(function() {

  var 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,
    KEY_INSERT:   45,

    cache: {}
  };

  var docEl = document.documentElement;
  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
    && 'onmouseleave' in docEl;



  var isIELegacyEvent = function(event) { return false; };

  if (window.attachEvent) {
    if (window.addEventListener) {
      isIELegacyEvent = function(event) {
        return !(event instanceof window.Event);
      };
    } else {
      isIELegacyEvent = function(event) { return true; };
    }
  }

  var _isButton;

  function _isButtonForDOMEvents(event, code) {
    return event.which ? (event.which === code + 1) : (event.button === code);
  }

  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
  function _isButtonForLegacyEvents(event, code) {
    return event.button === legacyButtonMap[code];
  }

  function _isButtonForWebKit(event, code) {
    switch (code) {
      case 0: return event.which == 1 && !event.metaKey;
      case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
      case 2: return event.which == 3;
      default: return false;
    }
  }

  if (window.attachEvent) {
    if (!window.addEventListener) {
      _isButton = _isButtonForLegacyEvents;
    } else {
      _isButton = function(event, code) {
        return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
         _isButtonForDOMEvents(event, code);
      }
    }
  } else if (Prototype.Browser.WebKit) {
    _isButton = _isButtonForWebKit;
  } else {
    _isButton = _isButtonForDOMEvents;
  }

  function isLeftClick(event)   { return _isButton(event, 0) }

  function isMiddleClick(event) { return _isButton(event, 1) }

  function isRightClick(event)  { return _isButton(event, 2) }

  function element(event) {
    event = Event.extend(event);

    var node = event.target, type = event.type,
     currentTarget = event.currentTarget;

    if (currentTarget && currentTarget.tagName) {
      if (type === 'load' || type === 'error' ||
        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
          && currentTarget.type === 'radio'))
            node = currentTarget;
    }

    if (node.nodeType == Node.TEXT_NODE)
      node = node.parentNode;

    return Element.extend(node);
  }

  function findElement(event, expression) {
    var element = Event.element(event);

    if (!expression) return element;
    while (element) {
      if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
        return Element.extend(element);
      }
      element = element.parentNode;
    }
  }

  function pointer(event) {
    return { x: pointerX(event), y: pointerY(event) };
  }

  function pointerX(event) {
    var docElement = document.documentElement,
     body = document.body || { scrollLeft: 0 };

    return event.pageX || (event.clientX +
      (docElement.scrollLeft || body.scrollLeft) -
      (docElement.clientLeft || 0));
  }

  function pointerY(event) {
    var docElement = document.documentElement,
     body = document.body || { scrollTop: 0 };

    return  event.pageY || (event.clientY +
       (docElement.scrollTop || body.scrollTop) -
       (docElement.clientTop || 0));
  }


  function stop(event) {
    Event.extend(event);
    event.preventDefault();
    event.stopPropagation();

    event.stopped = true;
  }


  Event.Methods = {
    isLeftClick:   isLeftClick,
    isMiddleClick: isMiddleClick,
    isRightClick:  isRightClick,

    element:     element,
    findElement: findElement,

    pointer:  pointer,
    pointerX: pointerX,
    pointerY: pointerY,

    stop: stop
  };

  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (window.attachEvent) {
    function _relatedTarget(event) {
      var element;
      switch (event.type) {
        case 'mouseover':
        case 'mouseenter':
          element = event.fromElement;
          break;
        case 'mouseout':
        case 'mouseleave':
          element = event.toElement;
          break;
        default:
          return null;
      }
      return Element.extend(element);
    }

    var additionalMethods = {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return '[object Event]' }
    };

    Event.extend = function(event, element) {
      if (!event) return false;

      if (!isIELegacyEvent(event)) return event;

      if (event._extendedByPrototype) return event;
      event._extendedByPrototype = Prototype.emptyFunction;

      var pointer = Event.pointer(event);

      Object.extend(event, {
        target: event.srcElement || element,
        relatedTarget: _relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });

      Object.extend(event, methods);
      Object.extend(event, additionalMethods);

      return event;
    };
  } else {
    Event.extend = Prototype.K;
  }

  if (window.addEventListener) {
    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
    Object.extend(Event.prototype, methods);
  }

  function _createResponder(element, eventName, handler) {
    var registry = Element.retrieve(element, 'prototype_event_registry');

    if (Object.isUndefined(registry)) {
      CACHE.push(element);
      registry = Element.retrieve(element, 'prototype_event_registry', $H());
    }

    var respondersForEvent = registry.get(eventName);
    if (Object.isUndefined(respondersForEvent)) {
      respondersForEvent = [];
      registry.set(eventName, respondersForEvent);
    }

    if (respondersForEvent.pluck('handler').include(handler)) return false;

    var responder;
    if (eventName.include(":")) {
      responder = function(event) {
        if (Object.isUndefined(event.eventName))
          return false;

        if (event.eventName !== eventName)
          return false;

        Event.extend(event, element);
        handler.call(element, event);
      };
    } else {
      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
       (eventName === "mouseenter" || eventName === "mouseleave")) {
        if (eventName === "mouseenter" || eventName === "mouseleave") {
          responder = function(event) {
            Event.extend(event, element);

            var parent = event.relatedTarget;
            while (parent && parent !== element) {
              try { parent = parent.parentNode; }
              catch(e) { parent = element; }
            }

            if (parent === element) return;

            handler.call(element, event);
          };
        }
      } else {
        responder = function(event) {
          Event.extend(event, element);
          handler.call(element, event);
        };
      }
    }

    responder.handler = handler;
    respondersForEvent.push(responder);
    return responder;
  }

  function _destroyCache() {
    for (var i = 0, length = CACHE.length; i < length; i++) {
      Event.stopObserving(CACHE[i]);
      CACHE[i] = null;
    }
  }

  var CACHE = [];

  if (Prototype.Browser.IE)
    window.attachEvent('onunload', _destroyCache);

  if (Prototype.Browser.WebKit)
    window.addEventListener('unload', Prototype.emptyFunction, false);


  var _getDOMEventName = Prototype.K,
      translations = { mouseenter: "mouseover", mouseleave: "mouseout" };

  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
    _getDOMEventName = function(eventName) {
      return (translations[eventName] || eventName);
    };
  }

  function observe(element, eventName, handler) {
    element = $(element);

    var responder = _createResponder(element, eventName, handler);

    if (!responder) return element;

    if (eventName.include(':')) {
      if (element.addEventListener)
        element.addEventListener("dataavailable", responder, false);
      else {
        element.attachEvent("ondataavailable", responder);
        element.attachEvent("onlosecapture", responder);
      }
    } else {
      var actualEventName = _getDOMEventName(eventName);

      if (element.addEventListener)
        element.addEventListener(actualEventName, responder, false);
      else
        element.attachEvent("on" + actualEventName, responder);
    }

    return element;
  }

  function stopObserving(element, eventName, handler) {
    element = $(element);

    var registry = Element.retrieve(element, 'prototype_event_registry');
    if (!registry) return element;

    if (!eventName) {
      registry.each( function(pair) {
        var eventName = pair.key;
        stopObserving(element, eventName);
      });
      return element;
    }

    var responders = registry.get(eventName);
    if (!responders) return element;

    if (!handler) {
      responders.each(function(r) {
        stopObserving(element, eventName, r.handler);
      });
      return element;
    }

    var i = responders.length, responder;
    while (i--) {
      if (responders[i].handler === handler) {
        responder = responders[i];
        break;
      }
    }
    if (!responder) return element;

    if (eventName.include(':')) {
      if (element.removeEventListener)
        element.removeEventListener("dataavailable", responder, false);
      else {
        element.detachEvent("ondataavailable", responder);
        element.detachEvent("onlosecapture", responder);
      }
    } else {
      var actualEventName = _getDOMEventName(eventName);
      if (element.removeEventListener)
        element.removeEventListener(actualEventName, responder, false);
      else
        element.detachEvent('on' + actualEventName, responder);
    }

    registry.set(eventName, responders.without(responder));

    return element;
  }

  function fire(element, eventName, memo, bubble) {
    element = $(element);

    if (Object.isUndefined(bubble))
      bubble = true;

    if (element == document && document.createEvent && !element.dispatchEvent)
      element = document.documentElement;

    var event;
    if (document.createEvent) {
      event = document.createEvent('HTMLEvents');
      event.initEvent('dataavailable', bubble, true);
    } else {
      event = document.createEventObject();
      event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
    }

    event.eventName = eventName;
    event.memo = memo || { };

    if (document.createEvent)
      element.dispatchEvent(event);
    else
      element.fireEvent(event.eventType, event);

    return Event.extend(event);
  }

  Event.Handler = Class.create({
    initialize: function(element, eventName, selector, callback) {
      this.element   = $(element);
      this.eventName = eventName;
      this.selector  = selector;
      this.callback  = callback;
      this.handler   = this.handleEvent.bind(this);
    },

    start: function() {
      Event.observe(this.element, this.eventName, this.handler);
      return this;
    },

    stop: function() {
      Event.stopObserving(this.element, this.eventName, this.handler);
      return this;
    },

    handleEvent: function(event) {
      var element = Event.findElement(event, this.selector);
      if (element) this.callback.call(this.element, event, element);
    }
  });

  function on(element, eventName, selector, callback) {
    element = $(element);
    if (Object.isFunction(selector) && Object.isUndefined(callback)) {
      callback = selector, selector = null;
    }

    return new Event.Handler(element, eventName, selector, callback).start();
  }

  Object.extend(Event, Event.Methods);

  Object.extend(Event, {
    fire:          fire,
    observe:       observe,
    stopObserving: stopObserving,
    on:            on
  });

  Element.addMethods({
    fire:          fire,

    observe:       observe,

    stopObserving: stopObserving,

    on:            on
  });

  Object.extend(document, {
    fire:          fire.methodize(),

    observe:       observe.methodize(),

    stopObserving: stopObserving.methodize(),

    on:            on.methodize(),

    loaded:        false
  });

  if (window.Event) Object.extend(window.Event, Event);
  else window.Event = Event;
})();

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearTimeout(timer);
    document.loaded = true;
    document.fire('dom:loaded');
  }

  function checkReadyState() {
    if (document.readyState === 'complete') {
      document.stopObserving('readystatechange', checkReadyState);
      fireContentLoadedEvent();
    }
  }

  function pollDoScroll() {
    try { document.documentElement.doScroll('left'); }
    catch(e) {
      timer = pollDoScroll.defer();
      return;
    }
    fireContentLoadedEvent();
  }

  if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
  } else {
    document.observe('readystatechange', checkReadyState);
    if (window == top)
      timer = pollDoScroll.defer();
  }

  Event.observe(window, 'load', fireContentLoadedEvent);
})();

Element.addMethods();

/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

var Position = {
  includeScrollOffsets: false,

  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.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 = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.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);
  },

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


  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

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

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

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

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

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

(function() {
  window.Selector = Class.create({
    initialize: function(expression) {
      this.expression = expression.strip();
    },

    findElements: function(rootElement) {
      return Prototype.Selector.select(this.expression, rootElement);
    },

    match: function(element) {
      return Prototype.Selector.match(element, this.expression);
    },

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

    inspect: function() {
      return "#<Selector: " + this.expression + ">";
    }
  });

  Object.extend(Selector, {
    matchElements: function(elements, expression) {
      var match = Prototype.Selector.match,
          results = [];

      for (var i = 0, length = elements.length; i < length; i++) {
        var element = elements[i];
        if (match(element, expression)) {
          results.push(Element.extend(element));
        }
      }
      return results;
    },

    findElement: function(elements, expression, index) {
      index = index || 0;
      var matchIndex = 0, element;
      for (var i = 0, length = elements.length; i < length; i++) {
        element = elements[i];
        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
          return Element.extend(element);
        }
      }
    },

    findChildElements: function(element, expressions) {
      var selector = expressions.toArray().join(', ');
      return Prototype.Selector.select(selector, element || document);
    }
  });
})();


/* common */
if ( !window.pc3 ) window.pc3 = {};
var pc3 = window.pc3;

pc3.Browser = (function(){
	var ua = navigator.userAgent;
	var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
	
	var browser = {
		'features':{},
		'IE':             !!window.attachEvent && !isOpera,
		'Opera':          isOpera,
		'WebKit':         ua.indexOf('AppleWebKit/') > -1,
		'Gecko':          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
		'MobileSafari':   /Apple.*Mobile/.test(ua)
	};
	
	var ffVersion = browser.Gecko?parseInt(ua.substring(ua.indexOf("Firefox")+8)):0;
	var ieVersion = browser.IE?parseInt(ua.substring(ua.indexOf("MSIE")+5)):0;
	
	browser.FF4 = ffVersion == 4;
	browser.FF5 = ffVersion == 5;
	browser.IE6 = ieVersion == 6;
	browser.IE7 = ieVersion == 7;
	browser.IE8 = ieVersion == 8;
	browser.IE9 = ieVersion == 9;
	browser.Safari = ( browser.WebKit && ua.indexOf("Safari") > 1 && ua.indexOf("Chrome") < 1 );
	
	return browser;
})();

function cp(event){
	event = Event.extend(event?event:window.event);
	event.stop();
}

function addScrollPosition(link) {
	var windowScrollPositions = getWindowScrollPositions();
	
	link.href +=
		(link.href.search(/\?/) > 0?'&':'?')
		+ 'pc3Scroll='
		+ windowScrollPositions['left']
		+ 'x'
		+ windowScrollPositions['top']
	;
}

function applyScrollPosition(){
	var key;
	var match = document.location.search.match('pc3Scroll=([0-9]+)x([0-9]+)');
	if ( !match ) return;
	
	setWindowScrollPositions(match[1], match[2]);
}

/**
 * set the scrollposition of the window
 * 
 * @author acn
 * @param {int} left position from left of the top-left corner of the window
 * @param {int} top position from top of the top-left corner of the window
 */
function setWindowScrollPositions(left, top) {
	if ( typeof window.pageXOffset != 'undefined' && typeof window.pageYOffset != 'undefined' ) {
		window.scrollTo(left, top);
	}
	else if ( typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat' ) {
		document.documentElement.scrollLeft = left;
		document.documentElement.scrollTop = top;
	}
	else if ( typeof document.body != 'undefined' ) {
		document.body.scrollLeft = left;
		document.body.scrollTop = top;
	}
}

/**
 * get the scrollposition of the window
 * 
 * @author acn
 * @return {array} windowScrollPositions['left'] and windowScrollPositions['top']
 */
function getWindowScrollPositions() {
	var windowScrollPositions = {};
		windowScrollPositions['left'] = 0;
		windowScrollPositions['top']  = 0;
	
	if (  typeof window.pageXOffset != 'undefined' && typeof window.pageYOffset != 'undefined' ) {
		windowScrollPositions['left'] = window.pageXOffset;
		windowScrollPositions['top'] = window.pageYOffset;
	}
	else if ( typeof document.compatMode != 'undefined' && document.compatMode != 'BackCompat' ) {
		windowScrollPositions['left'] = document.documentElement.scrollLeft;
		windowScrollPositions['top'] = document.documentElement.scrollTop;
	}
	else if ( typeof document.body != 'undefined' ) {
		windowScrollPositions['left'] = document.body.scrollLeft;
		windowScrollPositions['top'] = document.body.scrollTop;
	}
	
	return windowScrollPositions;
}

/**
 * Get the GET parameters from the URL.<br>
 * <ul><li>If no filter is defined, all URL parameters are returned.</li><li>If a filter is defined (array with parameter keys), only these parameters are returned.</li></ul>
 * Note: parameter names and parameter values are case sensitive.<br>
 * 
 * @author acn
 * @param {Array} filter : If the paramter filter isn't defined, all URL paramters are collected and returned. If you are looking for specific parameters, you can define the filter with an array.
 * @return {Object} urlParams : The parameter name is used as the name of the object member variable, the according value is the parameter value. Note that if no parameter is found, an empty object is returned. 
 */
function getUrlParam(filter) {
	var urlParamNames = null;
	var href = window.location.href;
	
	if (!href) return;
	var urlParamNames = new Array();
	var regExp = /[\?&]([^=]+)=/g;
	
	while ((results = regExp.exec(href)) != null) urlParamNames.push(results[1]);
	
	if (!urlParamNames || urlParamNames.length < 1) return;
	
	var urlParams = new Object();
	var uPs = new Object();
	var name, regExpS, regExp, result;
	
	for (var i = 0; i < urlParamNames.length; i++) {
		name = urlParamNames[i].replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]'); // escape square brackets for regExpS (PHP-style)
		
		regExpS = '[\\?&]'+ name +'=([^&#]*)';
		regExp = new RegExp(regExpS);
		result = regExp.exec(href);
		
		name = name.replace(/[\\]/g, ''); // re-escape square brackets
		uPs[name] = result[1];
	}
	
	var behaviour = arguments.length; // 0 : no filter; all url params asked, 1 : filter; array with asked param names
	switch (behaviour) {
		case 1 :
			for (var idx in filter) {
				key = filter[idx];
				if (uPs[key]) urlParams[key] = uPs[key];
			}
			break;

		case 0 :
		default:
			urlParams = uPs;
	}
	
	return urlParams;
}

/**
 * @author cri
 */
function getCookieParam(paramName) {
	var params = document.cookie;
	if (params == '') return;
	
	var cookieParams = new Object;
	var cPs = new Object;
	
	//-- get all url params (for internal use)
	cookies = params.split('; '); // array with 'key=value'
	for (var i = 0; i < cookies.length; ++i) {
		var keyAndValue = cookies[i];
		keyAndValue = keyAndValue.split('='); // array with 0 = 'key' and 1 = 'value'
		
		var key = null;
		var value = null;
		
		key = keyAndValue[0];
		if (keyAndValue[1]) value = keyAndValue[1];
		cPs[key] = value;
	}
	//--
	
	//-- filter params when wanted
	var behaviour = arguments.length; // 0 : no filter; all url params asked, 1 : filter; array with asked param names
	switch (behaviour) {
		case 1 : // array given, loop over all and collect only wanted for return
			for (var idx in paramName) {
				key = paramName[idx];
				if (cPs[key]) cookieParams[key] = cPs[key];
			}
			break;
		
		case 0 :
		default:
			cookieParams = cPs;
	}
	//--
	
	return cookieParams;
}

/**
 * Set GET parameters on a link object (link.href).<br>
 * Keeps (or overwrites) existing parameters and adds nonexisting ones.<br>
 * 
 * @author acn
 * @param {Object} HTML link object (DOM)
 * @param {Object} params containing member variables are used as key, values as value
 * @return void
 */
function setUrlParam(link, params) {
	if (!link.href) return;
	if (!params || typeof params == 'undefined' || typeof params != 'object' || isEmpty(params)) return;
	
	var urlParams = new Object();
	var doEscape = false;
	
	var before = '';
	var href = new String(link.href); // get url from link object, may contain "garbage code" like "javascript void; window.open(..." and my be 'pc3-like' encoded
	var after = '';
	
	var regExpHref = /(.*window\.open\((?:%22))([^,]*)((?:%22)(?:,|).*)/i; // get url from window.open() command
	if ((results = href.match(regExpHref))) {
		doEscape = true;
		
		before = results[1];
		href = results[2];
		after = results[3];
	}
	
	decHref = unescape(href).replace(/\\/g, ''); // decode 'pc3-like' encoding
	
	var results = '';
	var regExpGets = /[\?&]([^=]+)=([^&#]*)/gi; // get all existing get params
	while ((results = regExpGets.exec(decHref)) != null) urlParams[results[1]] = results[2];
	
	for (var key in params) urlParams[key] = params[key]; // overwrite existing, add new ones
	
	var regExpUrl = /([^?]+)/i; // get url without get params
	var results = decHref.match(regExpUrl);
	var url = results[1];
	
	var gets = '?'; // collect all get params
	for (var key in urlParams) {
		if (doEscape) gets += key +'='+ urlParams[key] +'&';
		else gets += escape(key) +'='+ escape(urlParams[key]) +'&';
	}
	gets = gets.substring(0, gets.length - 1); // trim last &
	
	if (doEscape) { // encode 'pc3-like'
		url = escape(url);
		url = url.replace(/\//g, '%5C%2F');
		
		gets = escape(gets); // nothing escaped until now
	}
	
	url = before + url + gets + after; // build new url
	
	link.href = url;
}


/**
 * Check if object is empty (no member variables).
 * 
 * @author acn
 * @param {Object} object
 * @return boolean
 */
function isEmpty(object) {
	for (var prop in object) if (object.hasOwnProperty(prop)) return false;
	
	return true;
}

function pc3CreateElementsByHTML(html,parent,parentTag){
	if ( !html ) return new Array(); //empty html
	
	var nestingLevel = 0;
	var content = document.createElement('div');
	
	parentTag = (parent?parent.nodeName:parentTag);
	
	switch( parentTag ) {
		case 'TR':
			html = '<table><tbody><tr>'+ html +'</tr></tbody></table>';
			nestingLevel = 3;
			break;
			
		case 'TBODY':
			html = '<table><tbody>'+ html +'</tbody></table>';
			nestingLevel = 2;
			break;
			
		case 'TABLE':
			html = '<table>'+ html +'</table>';
			nestingLevel = 1;
			break;
	}
	
	content.innerHTML = html;
	
	if ( !content.firstChild ) {
		// 20081223.acn : BUG #437
		// alert('failed to create: '+ html + ' within '+ parentTag);
		return null;
	}
	
	while ( nestingLevel-- > 0 ) {
		content = content.removeChild(content.firstChild);
	};
	
	var childs = new Array();
	
	while( content.firstChild ) {
		var child = content.removeChild(content.firstChild);
		
		if ( parent ) parent.appendChild(child);
		childs.push(child);
	}
	
	return childs;
}

function pc3GetOuterDimension(element){
	var dimension = {};
	var locations = new Array('Left','Top','Right','Bottom');
	for (var i = 0; i<=3; i++){
		var location = locations[i];
		var styleLocation = location.toLowerCase();
		dimension['offset'+location] = (pc3GetStyle(element, 'border-'+styleLocation+'-style')=='none'?0:parseInt((pc3GetStyle(element, 'border-'+styleLocation+'-width')?pc3GetStyle(element, 'border-'+styleLocation+'-width'):0))) + parseInt((pc3GetStyle(element, 'padding-'+styleLocation)?pc3GetStyle(element, 'padding-'+styleLocation):0));
		dimension['margin'+location] = parseInt((pc3GetStyle(element, 'margin-'+styleLocation)?pc3GetStyle(element, 'margin-'+styleLocation):0));
	}
	dimension.offsetsX = dimension.offsetLeft + dimension.offsetRight;
	dimension.offsetsY = dimension.offsetTop + dimension.offsetBottom;
	dimension.width = (element.offsetWidth<0?0:element.offsetWidth);
	dimension.innerWidth = dimension.width - dimension.offsetsX;
	dimension.width = dimension.width + dimension.marginLeft + dimension.marginRight;
	dimension.height = (element.offsetHeight<0?0:element.offsetHeight);
	dimension.innerHeight = dimension.height - dimension.offsetsY;
	dimension.height = dimension.height + dimension.marginTop + dimension.marginBottom;
	return dimension;
}


function pc3GetStyle(element, property){
	var value = '';
	if ( element.currentStyle ){
		var propertyParts = property.split('-');
		property = propertyParts[0];
		for ( var i=1; i<propertyParts.length;i++){
			property = property + propertyParts[i].charAt(0).toUpperCase() + propertyParts[i].substr(1);
		}
		value = element.currentStyle[property];
	} else if ( window.getComputedStyle ){
		value = window.getComputedStyle(element, null).getPropertyValue(property);
	}
	return value;
}


function dc(code){
	code = Base64.decode(code);
	
	var plain = '';
	
	for(var i=0;i<code.length;i++) {
		plain += String.fromCharCode(code.charCodeAt(i)^xEncryptionKey.charCodeAt(i%xEncryptionKey.length));
	}
	
	return plain;
}

function dcw(code){ document.write(dc(code)); }
function dcm(code){ return 'mailto:'+ dc(code); }

var Base64 = {

    // private property
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode : function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
            this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

        }

        return output;
    },

    // public method for decoding
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }

        output = Base64._utf8_decode(output);

        return output;

    },

    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }

        return string;
    }

};

/**
 * catch console.log calls on browsers without console
 * 
 * @author acn
 */ 
if (!window.console) {
	var a = false;
	var uP = getUrlParam(new Array('pc3JSDebug'));
	if (uP && uP.pc3JSDebug == '1') a = true;
	
	window.console = {
		log: function(m){ if(a) alert('console.log\n'+ m); },
		debug: function(m){ if(a) alert('console.debug\n'+ m); },
		info: function(m){ if(a) alert('console.info\n'+ m); },
		warn: function(m){ if(a) alert('console.warn\n'+ m); },
		error: function(m){ if(a) alert('console.error\n'+ m); }
	};
}

/**
 * @author acn
 */
if (!pc3) var pc3 = function(){};

pc3.trackLink = function(tracking) {
	if (!pageTracker) return;
	if (!pageTracker._trackEvent) return;
	
	/*
	 * google
	 * 
	 * @see http://code.google.com/intl/de-DE/apis/analytics/docs/tracking/gaTrackingOverview.html
	 * @see http://code.google.com/intl/de-DE/apis/analytics/docs/tracking/eventTrackerGuide.html
	 */
	pageTracker._trackEvent(tracking.category, tracking.action, tracking.label, tracking.value);
};

/**
 * @author acn
 */
function replaceRssWithXml() {
	if (!/Safari/.test(navigator.userAgent) && !/MSIE (\d+\.\d+);/.test(navigator.userAgent)) return; // ms ie and safari only
	
	var anchors = document.links;
	var regexp = /\.rss(?!\.)/i; // '.rss' allowed, '.rss.' disallowed
	
	for (var i = 0; i < anchors.length; i++) { // loop over each entry
		var href = anchors[i].href;
		if (regexp.test(href)) { // modify only if attribute 'href' do match regexp
			href = href.replace(regexp, '.xml');
			anchors[i].href = href;
		}
	}
}

/* form */
if ( window.pc3 && window.pc3.Browser ) {
	window.pc3.Browser.features.placeholder = (function() {
	  var i = document.createElement('input');
	  return 'placeholder' in i;
	})();
}

function pc3SubmitForm( form,addScrollPosition ){
	if ( addScrollPosition ) pc3AddScrollPositionOnForm(form);
	
	if ( form.getAttribute('onsubmit') ){
		if ( form.onsubmit() ) {
			form.submit();
		}
	} else {
		form.submit();
	}
}

function addScrollPositionOnForm(formId) {
	if ( !formId ) formId = 'layout';
	var form = document.getElementById(formId);
	
	pc3AddScrollPositionOnForm(form); // FIX #1095 : was a recursive call: addScrollPositionOnForm(formId)
}

function pc3AddScrollPositionOnForm(form) {
	if ( !form ) return;
	
	var formAction = form.action;
	if ( formAction == '' ) { return; }
	
	var regSearch  = "(?:[^?]*)(\\?)([^?]*)";
	var oRegExp    = new RegExp(regSearch);
	var regResult  = oRegExp.exec(formAction);
	var windowScrollPositions = getWindowScrollPositions();
	
	formAction +=
		( regResult && regResult[1] != '' ? '&' : '?')
		+ 'pc3Scroll='
		+ windowScrollPositions['left']
		+ 'x'
		+ windowScrollPositions['top']
	;
	
	form.action = formAction;
}


//pc3FileUpload
var pc3FileUpload = function(){};

pc3FileUpload.instances = {};

pc3FileUpload.get = function(id){
	return this.instances[id];
};

pc3FileUpload.prototype.__construct = function(id,config){
	this.id = id;
	this.element = $(id);
	if ( !this.element ) throw 'pc3FileUpload: id not found ('+ id +')';

	pc3FileUpload.instances[id] = this;
	
	this.handlers = {};
	this.contents = {};
	this.types = config.types;
	
	var self = this;
	var initHandler = function(suffix){
		var handlerName = 'onUpload'+ suffix;
		var contentName = 'upload'+ suffix +'Content';
		
		if ( config[handlerName] && window[config[handlerName]] ) {
			self.handlers[handlerName] = window[config[handlerName]];
		}
		
		if ( config[contentName] ) {
			var content = new Element('div');
			content.insert(config[contentName]);
			self.contents['upload'+ suffix] = content.firstChild;
		}
	};
	
	initHandler('');
	initHandler('Complete');
	initHandler('Error');
	
	if ( config.uploadOnChange ) this.enableOnChange();

	return this;
};

pc3FileUpload.prototype.enableOnChange = function(){
	this.uploadOnChange = true;
	this.element.observe('change',this.uploadHandler.bind(this));
};

pc3FileUpload.prototype.upload = function(){
	var form  = $('pc3FileUploadForm');
	if ( !form ) throw 'pc3FileUploadForm not found';
	
	var idElement = form.getInputs('text','id')[0];
	if ( !idElement ) throw 'pc3FileUploadForm.id not found';
	
	var typesElement = form.getInputs('text','types')[0];
	if ( !typesElement ) throw 'pc3FileUploadForm.types not found';

	this.iframe = new Element('iframe',{'name':this.id+'_Frame'});
	this.iframe.hide();

	document.body.appendChild(this.iframe);

	this.elementSibling = this.element.nextSibling;
	this.elementParent = this.element.parentNode;
	this.element.remove();

	if ( this.contents.upload ) this.setContent(this.contents.upload);
	
	form.appendChild(this.element);

	idElement.value = this.id;
	typesElement.value = (this.types?Object.toJSON(this.types):null);
	
	form.target = this.iframe.name;
	form.submit();
};

pc3FileUpload.prototype.setContent = function(content){
	if ( this.elementSibling ) this.elementParent.insertBefore(content,this.elementSibling);
	else this.elementParent.appendChild(content);
};

pc3FileUpload.prototype.errorHandler = function(errorCode){
	this.cleanUp();
	
	if ( this.contents.uploadError ) this.contents.uploadError.addClassName(errorCode);
	
	this.finish('Error',errorCode);
};

pc3FileUpload.prototype.finish = function(type,params){
	if ( this.contents['upload'+type] ){
		this.setContent(this.contents['upload'+type]);
	} else {
		this.reset();
	}
	
	var handler = this.handlers['onUpload'+type];
	if ( handler ) handler(this,params);
	
	this.iframe.remove();
	
	this.iframe = null;
};

pc3FileUpload.prototype.cleanUp = function(){
	this.element.remove();
	if ( this.contents.upload ) this.contents.upload.remove();
};

pc3FileUpload.prototype.completeUploadHandler = function(fileId,fileInfo){
	this.cleanUp();
	
	if ( !this.fileIdElement ) {
		this.fileIdElement = new Element('input',{'type':'hidden','name':this.element.name+'_UploadId'});
		this.setContent(this.fileIdElement);
	}
	
	this.fileIdElement.value = fileId;
	
	this.finish('Complete',fileInfo);
};

pc3FileUpload.prototype.clear = function(){
	if ( this.fileIdElement ) this.fileIdElement.value = '';
	this.reset();
};

pc3FileUpload.prototype.reset = function(){
	this.setContent(this.element);
};

pc3FileUpload.prototype.uploadHandler = function(event){
	if ( this.handlers.onUpload ) this.handlers.onUpload(this);
	
	this.upload();
};


//pc3SubmitLink
function pc3SubmitLink(formId,fieldName,value,addScrollPosition){
	var form = document.getElementById(formId);
	if ( !form ) return true;
	
	if ( fieldName ) {
		var fieldId = formId+'Submit';
		
		var field = document.getElementById(fieldId);
		if ( !field ) {
			field = document.createElement('input');
			field.type = 'hidden';
			field.id = fieldId;
			
			form.appendChild(field);
		}
		
		field.name = fieldName;
		field.value = value;
	}
	
	pc3SubmitForm(form,addScrollPosition);
	
	if ( field ) {
		field.name = null;
		field.value = null;
	}
	
	return false; //form was submitted by other means. returning false means to cancel the default form submit.
}

function pc3SetPlaceholders(){
	if ( pc3.Browser && pc3.Browser.features.placeholder ) return;
	
	var forms = document.getElementsByTagName('form');
	var length = forms.length;
	for ( var i = 0; i < length; i++ ) {
		pc3SetFormPlaceholders(forms[i]);
	}
}

function pc3SetFormPlaceholders(form){
	if ( !form ) return;
	
	if ( pc3.Browser && pc3.Browser.features.placeholder ) return;
	
	form.pc3Placeholders = new Array();
	var inputElements = form.elements;
	var length = inputElements.length;
	for ( var i = 0; i < length; i++ ) {
		var inputElement = inputElements[i];
		
		if ( inputElement.tagName == 'TEXTAREA' || ( inputElement.tagName == 'INPUT' && inputElement.type != 'password' ) ){
			var placeholder = inputElement.getAttribute('placeholder');
			
			if ( placeholder && placeholder != '' ) {
				pc3InitFormPlaceholder(inputElement,placeholder);
				form.pc3Placeholders.push(inputElement);
			}
		}
	}
	return true;
}

function pc3InitFormPlaceholder(inputElement,placeholder){
	inputElement.setAttribute('placeholder','');
	if ( inputElement.value == '' ) inputElement.setAttribute('value',placeholder);
	
	if ( inputElement.captureEvents ) {
		inputElement.captureEvents(Event.FOCUS);
		inputElement.captureEvents(Event.BLUR);
	}
	inputElement.onfocus = function () { if ( this.value == placeholder ) this.value = ''; };
	inputElement.onblur = function () { if ( this.value == '' ) this.value = placeholder; };
	
	inputElement.onblur();
}

function pc3ClearPlaceholders(form){
	if ( !form || !form.pc3Placeholders) return true;
	var length = form.pc3Placeholders.length;
	for( var i = 0; i < length; i++ ) {
		form.pc3Placeholders[i].onfocus();
	}	
	return true;
}

function pc3ClearForm(form){
	if ( !form ) return;
	var inputElements = form.elements;
	var length = inputElements.length;
	for ( var i = 0; i < length; i++ ) {
		inputElement = inputElements[i];
		if ( inputElement.pc3ClearInput ){
			inputElement.pc3ClearInput();
		} else if ( inputElement.tagName == 'SELECT' || inputElement.tagName == 'TEXTAREA' || ( inputElement.tagName == 'INPUT' && ( inputElement.type == 'text' ))){
			inputElement.value = '';
		}
	}
	pc3SetFormPlaceholders(form);
	return true;
}



/* widget */
// registry, used to store the design configs
var pc3PopupDesignData = {};
var pc3PopupData = new Array();
var pc3Accordions = {};
var pc3Scrollers = {};

var pc3History = {
	setParam : function( widget, id, params, blind ){
		if ( blind ){
			if ( !pc3History.blindParams[widget] ) pc3History.blindParams[widget] = {};
			if ( pc3History.blindParams[widget][id] && pc3History.blindParams[widget][id] == params ) return;
			pc3History.blindParams[widget][id] = params;
		} else {
			var currentParams = pc3History.getCurrentParams();
			if ( !currentParams[widget] ) currentParams[widget] = {};
			if ( currentParams[widget][id] && currentParams[widget][id] == params ) return;
			currentParams[widget][id] = params;

			location.hash = '#pc3Widget'+Object.toJSON(currentParams);
			pc3History.lastParams = location.hash;
		}
	},
	
	unsetParam : function( widget, id ){
		if ( pc3History.blindParams[widget] && pc3History.blindParams[widget][id] ){
			delete pc3History.blindParams[widget][id];
			if ( pc3History.blindParams[widget].length == 0 ) delete pc3History.blindParams[widget];
		} else {
			var currentParams = $H(pc3History.getCurrentParams());
			var newParams = {};
			currentParams.each(function(currentWidget){
				$H(currentWidget.value).each(function(currentId){
					if ( widget == currentWidget.key && id == currentId.key ) return;
					if ( !newParams[currentWidget.key] ) newParams[currentWidget.key] = {};
					if ( !newParams[currentWidget.key][currentId.key] ) newParams[currentWidget.key][currentId.key] = currentId.value;
				});
			});

			if ( $H(newParams).size() ) location.hash = '#pc3Widget'+Object.toJSON(newParams);
			else location.hash = '#pc3Widget';
			pc3History.lastParams = location.hash;
		}
	},
	
	getParam : function( widget, id ){
		if ( pc3History.blindParams[widget] && pc3History.blindParams[widget][id] ){
			return pc3History.blindParams[widget][id];
		} else {
			var currentParams = pc3History.getCurrentParams();
			if ( !currentParams[widget] ) return false;
			if ( id && !currentParams[widget][id] )  return false;
			if ( !id ) return currentParams[widget];
			return currentParams[widget][id];
		}
	},

	getCurrentParams : function(){
		var currentParams = {};
		if ( location.hash && location.hash.substring(0,10) == '#pc3Widget' && location.hash.substring(10) ){
			currentParams = decodeURI(location.hash.substring(10)).evalJSON();
		}
		return currentParams;
	},

	checkHistoryChange : function(){
		if ( location.hash != pc3History.lastParams ){
			pc3History.lastParams = location.hash;
			var currentParams = pc3History.getCurrentParams();
			if ( currentParams['accordion'] ) pc3Widget.updateAccordions(currentParams['accordion']);
			else pc3Widget.updateAccordions({});
		}
	},
	
	lastParams : location.hash,
	blindParams : {}
	
}
	
function pc3WidgetInit(){
	if ( typeof( pc3Widget ) == "undefined" ){
		pc3Widget = new pc3WidgetManagement();
		pc3Widget.init();
		new PeriodicalExecuter(function(pe) { pc3History.checkHistoryChange(); }, 0.5);
	} else {
		pc3Widget.updateAll();
	}
}

function pc3WidgetManagement(){
	var self = this;
	this.mousePosition = {x:0,y:0};
	this.popupManagement = '';
	this.accordions = $H({});
	this.scrollers = $H({});
	this.body = $(document.body);
	this.backgrounds = $H({});
	this.blurs = $H({});
	this.outerDimensionElements = $H({});
		
	this.init = function(){
		Event.observe(document, "mousemove", this.handleMouseMove.bindAsEventListener(this));
		Event.observe(document, "click", this.handleClick.bindAsEventListener(this));
		Event.observe(document, "mouseup", this.handleMouseUp.bindAsEventListener(this));
		Event.observe(window, "resize", this.handleResize.bindAsEventListener(this));
		Event.observe(window, "scroll", this.handleScroll.bindAsEventListener(this));
		this.changeContents = '';
		
		this.initAccordions();
		this.initScrollers();
		this.initPopupManagement();
	}

	this.updateAll = function(){
		if ( typeof( pc3DatepickerManagement ) != "undefined" ) pc3DatepickerManagement.update();
		this.initAccordions();
		this.updateScrollers();
		this.initScrollers();
		if ( !this.popupManagement ) this.initPopupManagement();
		else this.popupManagement.init();
	}

	this.update = function(){ return true; }

	//Widget managements
		//accordions
			this.initAccordions = function(){
				var accordionSates = $H(pc3History.getParam('accordion',''));
				for( var accordionId in pc3Accordions ){ 
					this.accordions.set(accordionId, new pc3Accordion(accordionId, pc3Accordions[accordionId], (accordionSates && accordionSates.get(accordionId)?accordionSates.get(accordionId)['boxId']:-1)));
				}
				pc3Accordions = {};
			}


			this.updateAccordions = function(accordions){
				this.accordions.each(function(accordion){
					if ( accordions[accordion.key] ) accordion.value.toggleBox(accordions[accordion.key]['boxId']);
					else accordion.value.toggleBox(0);
				
				});
			}
		//-----
		//scrollers
			this.initScrollers = function(){
				for( var scrollerId in pc3Scrollers ){
					var bars = new Array();
					if ( pc3Scrollers[scrollerId]['bars'] ) bars = pc3Scrollers[scrollerId]['bars'];				
					var arrows = new Array();
					if ( pc3Scrollers[scrollerId]['arrows'] ) arrows = pc3Scrollers[scrollerId]['arrows'];
					this.scrollers.set(scrollerId, new pc3Scroller(scrollerId, pc3Scrollers[scrollerId]['scroller'], bars, arrows));
				}
				
				pc3Scrollers = {};
			}
	
			this.updateScrollers = function(scrollToTop){ this.scrollers.each(function(scroller){ scroller.value.update(scrollToTop); }); }
			
			this.updateScroller = function(scrollerId, scrollToTop){ if ( this.scrollers.get(scrollerId) ) this.scrollers.get(scrollerId).update(scrollToTop); }

			this.getScrollpositions = function(scrollerId){
				if ( this.scrollers.get(scrollerId) ) return this.scrollers.get(scrollerId).getScrollpositions(); 
			}
		
			this.scrollTo = function(scrollerId, x, y, elementId, alignment){
				//console.log(scrollerId, x, y, elementId, alignment,( this.scrollers.get(scrollerId)?'true':'false') );
				if ( this.scrollers.get(scrollerId) ) this.scrollers.get(scrollerId).scrollTo({x:x,y:y}, elementId, alignment);
			}
		//-----
		//popups
			this.initPopupManagement = function(){ if ( $H(pc3PopupDesignData).size() && $H(pc3PopupData).size() ) this.popupManagement = new pc3PopupManagement(); }

			this.openPopup = function(trigger){
				if ( this.popupManagement ) this.popupManagement.openPopup(trigger);
			}
			
			this.closePopup = function(trigger){
				if ( this.popupManagement ) this.popupManagement.closePopup(trigger); 
			}

			this.closeParentPopup = function(objectId){
				var popup = this.getParentWidget(objectId);
				if ( popup.triggerId ) this.popupManagement.closePopup(popup.triggerId);
			}
			
			this.updatePopup = function(trigger){ if ( this.popupManagement ) this.popupManagement.updatePopup(trigger); }

			this.setClosePopupFunction = function(trigger, callBack){ if ( this.popupManagement ) this.popupManagement.setClosePopupFunction(trigger, callBack); }

			this.setAjaxSucessPopupFunction = function(trigger, callBack){ if ( this.popupManagement ) this.popupManagement.setAjaxSucessPopupFunction(trigger, callBack); }

			this.setPopupContentSize = function(trigger, width, height){
				if ( this.popupManagement ) this.popupManagement.setPopupContentSize(trigger, width, height); 
			}

		//-----
		
		
		this.getParentWidget = function(element){
			parentWidget = $(element).up('[pc3Widget]');
			if ( parentWidget ) return parentWidget.popup;		
			else return this;
		}
		
	//---------------


	// Utilities
		this.setDocumentDimension = function( fast ){
		
			if ( window.innerHeight && window.scrollMaxY ) var scrollY = window.innerHeight + window.scrollMaxY;
			else if ( document.body.scrollHeight > document.body.offsetHeight ) var scrollY = document.body.scrollHeight;
			else if ( document.documentElement && document.documentElement.scrollHeight > document.documentElement.offsetHeight ) var scrollY = document.documentElement.scrollHeight;
			else var scrollY = document.body.offsetHeight;

			if ( self.innerHeight ) var windowHeight = self.innerHeight;
			else if ( document.documentElement && document.documentElement.clientHeight ) var windowHeight = document.documentElement.clientHeight;
			else if ( document.body ) var windowHeight = document.body.clientHeight;

			if ( scrollY < windowHeight ) var height = windowHeight;
			else var height = scrollY;
				
			if ( fast && this.documentDimension ){
				var width = this.documentDimension.width;
			} else {
				var dummyContent = new Element('div').setStyle({position:'absolute', left:'0px', top:'0px', width:'auto', height:'auto'});
				var html = this.body.innerHTML;
				dummyContent.innerHTML = html.stripScripts();
				dummyContent.hide();
				this.body.appendChild(dummyContent);
				var width = document.viewport.getScrollOffsets().left + document.viewport.getWidth();
				if (dummyContent.getWidth() > width ) width = dummyContent.getWidth();
				dummyContent.remove();
			}
			this.documentDimension = {left:0,top:0,width:width,height:height,right:width,bottom:height};
		}
	
		this.getDocumentDimension = function(){
			if ( !this.documentDimension ) this.setDocumentDimension(false);
			return this.documentDimension; 
		}
		
		this.getElementDimension = function(element){
			var dummyContent = element.cloneNode(true);
			dummyContent.hide();
			dummyContent.innerHTML = dummyContent.innerHTML.stripScripts();
			this.body.appendChild(dummyContent);
			dummyContent.setStyle({position:'absolute',left:'100000px', top:'100000px', width:'auto', height:'auto'});
			var width = dummyContent.getWidth();
			dummyContent.setStyle({left:'0px', top:'0px'});
			var height = dummyContent.getHeight();
			dummyContent.remove();
			return {width:width,height:height};
		}

		this.getOuterDimension = function(element, inline){
			var dummyElement = element;
			if ( !inline ){
				//var dimension = this.outerDimensionElements.get(element);
				//if ( dimension ) return dimension;
				dummyElement = element.cloneNode(true);
				dummyElement.setStyle({position:'absolute',left:'0px', top:'0px',width:'1000px', height:'1000px'});
				dummyElement.hide();
				dummyElement.innerHTML = dummyElement.innerHTML.stripScripts();
				this.body.appendChild(dummyElement);
			}
			
			var dimension = {};
			['Left','Top','Right','Bottom'].each(function(location){
				dimension['offset'+location] = (dummyElement.getStyle('border'+location+'Style')=='none'?0:parseInt((dummyElement.getStyle('border'+location+'Width')?dummyElement.getStyle('border'+location+'Width'):0))) + parseInt((dummyElement.getStyle('padding'+location)?dummyElement.getStyle('padding'+location):0));
			});
			
			dimension.offsetsX = dimension.offsetLeft + dimension.offsetRight;
			dimension.offsetsY = dimension.offsetTop + dimension.offsetBottom;
			dimension.width = (element.getWidth()<0?0:element.getWidth());
			dimension.innerWidth = dimension.width - dimension.offsetsX;
			dimension.height = (element.getHeight()<0?0:element.getHeight());
			dimension.innerHeight = dimension.height - dimension.offsetsY;
			var positions = element.positionedOffset();
			dimension.left = positions.left;
			dimension.top = positions.top;
			if ( !inline ){
				dummyElement.remove();
				//this.outerDimensionElements.set(element, dimension);
			}
			return dimension;
		}

		this.mouseIsInside = function(event, element){
			var pointerX = event.pointerX();
			var pointerY = event.pointerY();
			if ( pointerX < 1 || pointerY < 1 ) return true;
			if ( pc3.Browser.IE ){
				pointerX = pointerX - 2;
				pointerY = pointerY - 2;
			}
			var position = element.cumulativeOffset();
			var triggerArea = {left:position.left,top:position.top,right:(position.left + element.getWidth()),bottom:(position.top + element.getHeight())};
			if ( (pointerX >= triggerArea.left && pointerX < triggerArea.right) && (pointerY >= triggerArea.top && pointerY < triggerArea.bottom) ) return true;
			return false;
		}

		this.findElementById = function(parent, id){
			var needle = '';
			parent.childElements().each(function(element){
				if ( element.id == id ){
					needle = element;
					throw $break;
				} else {
					needle = self.findElementById(element, id);
					if ( needle ) throw $break;
				}
			});
			return needle;
		}
		
		this.getOpacity = function(element){
			var opacity = element.getStyle('filter');
			if ( opacity && opacity != 'none' ) return opacity.slice((opacity.indexOf('=')+1),(opacity.length-1))/100;
			return element.getStyle('opacity');
		}
		
		this.addBackground = function(parentElement, color, opacity){
			if ( this.backgrounds.get(parentElement) ) return false;
			this.backgrounds.set(parentElement, new pc3Background(this, parentElement, color, opacity));
			return this.backgrounds.get(parentElement);
		}
		
		this.removeBackground = function(parentElement){
			if ( this.backgrounds.get(parentElement) ){
				var element = this.backgrounds.get(parentElement);
				delete element;
				this.backgrounds.unset(parentElement);
			}
		}
		
		this.blur = function(radius){
			var blur = this.blurs.get(radius);
			if ( blur ) return blur;
			var breakOff = (1+radius);
			var dimension = 2 * breakOff;
			var bluredMatrix = {};
			$A($R(2, dimension)).each(function(row){
				$A($R(2, dimension)).each(function(column){
					var counter = 0;
					var sum = 0;
					$A($R(Math.max(parseInt(row-radius), 1), Math.min(parseInt(row)+radius, dimension))).each(function(sectionRow){
						$A($R(Math.max(parseInt(column-radius), 1), Math.min(parseInt(column)+radius, dimension))).each(function(sectionColumn){
							if ( sectionColumn > breakOff && sectionRow > breakOff ) sum = sum + 1;
							counter++;
						});
					});
					var average = sum/counter;
					if ( column == 2 ) var columns = {1:average};
					else var columns = bluredMatrix[row-1];
					if ( column == 2 ) bluredMatrix[row-1] = columns;
					else columns[column-1] = average;
					
				});
			});
			this.blurs.set(radius, bluredMatrix);
			return bluredMatrix;
		}

		this.getFlashObject = function(id){
			if ( pc3.Browser.IE && window[id] ) return window[id];
			if ( !pc3.Browser.IE && document[id] ) return document[id];
			return false;
		}
	//---------------
	


	// Event handlers
		this.handleMouseMove = function(event){
			this.mousePosition.x = event.pointerX();
			this.mousePosition.y = event.pointerY();
			if ( this.popupManagement ) this.popupManagement.handleMouseMove();
			this.scrollers.each(function(scroller){ scroller.value.handleMouseMove(event); });
		}
	
		this.handleClick = function(event){
			if ( this.popupManagement ) this.popupManagement.handleClick(event);
		}

		this.handleMouseUp = function(event){
			this.scrollers.each(function(scroller){ scroller.value.handleMouseUp(event); });
		}
	
		this.handleResize = function(event){
			if ( this.backgrounds.size() > 0 ) this.setDocumentDimension(false);
			this.backgrounds.each(function(background){ background.value.resize() });
			if ( this.popupManagement ) this.popupManagement.handleResize();
		}
	
		this.handleScroll = function(event){
			if ( this.popupManagement ){
				this.setDocumentDimension(true);
				this.popupManagement.handleScroll();
			}
		}


//---------------
	
}

function pc3Background(management, parent, color, opacity){
	var self = this;
	this.management = management;
	this.parent = parent;
	
	this.background = '';
	this.effects = $H({});
	this.state = '';

	this.init = function(color, opacity){
		this.background = new Element('div').setStyle({position:'absolute', zIndex:9999, backgroundColor:(color?color:'none')});
		this.opacity = (opacity?opacity:75);
		this.background.hide();
		this.parent.appendChild(this.background);
	}
	
	this.open = function(delay, effect, duration){
		if ( delay > 0 ) this.waitingToOpen = this.openBackground.bind(this, effect, duration).delay(delay);
		else this.openBackground(effect, duration);
	}

	this.openBackground = function(effect, duration){
		this.waitingToOpen = '';
		this.state = 'opening';
		if ( this.parent == pc3Widget.body ){
			var boundingBox = pc3Widget.getDocumentDimension();
		} else {
			var boundingBox = {
				left:0,
				top:0,
				right:this.parent.getWidth(),
				bottom:this.parent.getHeight()
			};
		}

		this.background.setStyle({
			left:boundingBox.left +'px',
			top:boundingBox.top +'px',
			width:(boundingBox.right - boundingBox.left) +'px',
			height:(boundingBox.bottom - boundingBox.top) +'px'
		});
		
		// fadeIn
		if ( effect == 'fadeIn' ){
			this.setOpacity({value:0});
			var duration = (duration?parseFloat(duration)*1000:0);
			this.effects.set('opacity', new pc3Tween('opacity', 0, this.opacity, duration, 'EaseNone', this.setOpacity.bind(self), this.removeEffect.bind(self)));
		} else {
			this.setOpacity({value:this.opacity});
		}
		this.background.show();
		this.animate();
	}
	
	this.close = function(delay, effect, duration){
		if ( delay > 0 ) this.waitingToClose = this.closeBackground.bind(this, effect, duration).delay(delay);
		else this.closeBackground(effect, duration);
	}
	
	this.closeBackground = function(effect, duration){
		this.waitingToClose = '';
		this.state = 'closing';
		// fadeOut
		if ( effect == 'fadeOut' ){
			var duration = (duration?parseFloat(duration)*1000:0);
			this.effects.set('opacity', new pc3Tween('opacity', this.opacity, 0, duration, 'EaseNone', this.setOpacity.bind(self), this.removeEffect.bind(self)));
		}
		this.animate();
	}
	
	this.removeEffect = function(effect){ this.effects.unset(effect.name); }
	
	this.setOpacity = function(effect){ this.background.setOpacity(effect.value/100); }

	this.animate = function(){
		var finished = true;
		for ( var name in this.effects ) finished = false;
		
		if ( this.effects.size() ){
			this.animate.bind(this).delay(0.02)
		} else {
			if ( this.state == 'closing' ){
				this.state = 'closed';
				this.background.remove();
				this.management.removeBackground(this.parent);
			} else {
				this.state = 'opened';
			}
		}
	}

	this.resize = function(){
		if ( this.parent != pc3Widget.body ) return;
		var boundingBox = this.management.getDocumentDimension();
		this.background.setStyle({
			left:boundingBox.left +'px',
			top:boundingBox.top +'px',
			width:(boundingBox.right - boundingBox.left) +'px',
			height:(boundingBox.bottom - boundingBox.top) +'px'
		
		});
	}
	
	this.init(color, opacity);
}



function pc3Tween(name, start, end, duration, transition, onUpdate, onComplete){
	var self = this;
	this.name = name;
	this.value = start;
	this.end = end;
	this.onCompleteFunction = onComplete;
	this.onUpdateFunction = onUpdate;

	this.onUpdate = function(value){
		this.value = value;
		this.onUpdateFunction(this);
	}
	
	this.onComplete = function(){
		this.value = this.end;
		this.onUpdateFunction(this);
		if ( this.onCompleteFunction ) this.onCompleteFunction(this);
	};
	
	this.init = function(duration, transition){
		Tweener.AddTween(
			{ value:this.value },
			{ value:this.end },
			{
				Duration: duration,
				Transition: Tweener.Transitions[transition],
				OnComplete: this.onComplete.bind(self),
				OnUpdate: function(tweener){
					if ( tweener.ScopeClean.value ) self.onUpdate.bind(self)(tweener.ScopeClean.value); 
				}
			}
		);
	}

	this.init(duration, transition);
}


/* ajax */
function pc3Ajax(url,params,method){
	this.setURL(url);
	this.setParams(params);
	this.setMethod(method);
	
	this.onSuccess = null;
	this.onError = null;
}

pc3Ajax.createRequest = function (){
	if ( window.XMLHttpRequest ) return new XMLHttpRequest();
	if ( window.ActiveXObject ){
		try {
			return new ActiveXObject("MSXML2.XMLHTTP.3.0");
		} catch(e){
			return null;
		}
	}
	return null;
};

pc3Ajax.addParams = function(url,params){
	if ( !params ) return url;
	
	var paramString = '';
	switch ( typeof params ) {
		case 'object':
			for( var name in params ) {
				paramString += (paramString?'&':'') + encodeURIComponent(name) +'='+ encodeURIComponent(params[name]);
			}
			break;
			
		default:
			paramString = params;
			break;
	}
	
	if ( url.indexOf('?') >= 0 ) url += '&';
	else url += '?';
	
	return url + paramString;
};

pc3Ajax.getFormData = function(form, event){
	var params = '';

	for ( var i=0; i<form.elements.length; i++ ) {
		var element = form.elements[i];
		if ( !element.name ) continue;
		
		if ( element.type == 'checkbox' && !element.checked ) continue;
		if ( element.type == 'radio' && !element.checked ) continue;
		if ( element.type == 'submit' ) continue;
		
		params += (params?'&':'') + encodeURIComponent(element.name) +'='+ encodeURIComponent(element.value);
	}
	pc3SetFormPlaceholders(form);
	return params;
};

pc3Ajax.prototype.setURL = function (url){
	this.url = url;
};

pc3Ajax.prototype.setParams = function (params){
	this.params = params;
};

pc3Ajax.prototype.setMethod = function (method){
	this.method = method;
	
	if ( !this.method ) this.method = 'GET';
	else this.method = this.method.toUpperCase();
	
	if ( this.method != 'GET' ) this.method = 'POST';
};

pc3Ajax.prototype.onStateChange = function(){
	if ( this.request.readyState == 4 ){
		if ( (this.request.status == 200 || this.request.status == 304) ){
			if ( this.onSuccess ) this.onSuccess(this.request.responseText, this.request.responseXML, this.request );
		} else {
			if ( this.onError ) this.onError('Request failed: '+'['+this.request.status+', '+this.request.statusText+']', this.request );
		}
	}
	
	return false;
};

pc3Ajax.prototype.abort = function(){
	if ( !this.request ) return false;
	
	this.request.onreadystatechange = function(){};
	if ( this.request.abort ) this.request.abort();
	
	return true;
};

pc3Ajax.prototype.doRequest = function(){
	if ( !this.url ) return false;
	
	var postParams = '';
	
	var url = this.url;
	
	if ( this.method == 'GET' ) {
		url = pc3Ajax.addParams(url,this.params);
	} else {
		postParams = this.params;
	}
	
	var self = this;
	this.request = pc3Ajax.createRequest();
	if ( !this.request ) return false;
	
	this.request.onreadystatechange = function(){ self.onStateChange(); };
	
	this.request.open( this.method, url, true );
	if ( this.method == 'POST' ) this.request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	
	if ( this.onRequest ) this.onRequest();
	
	this.request.send(postParams);
	
	return true;
};

function pc3AjaxContent(id,onRequestHTML,onErrorHTML, parentWidget, element, onRequestOverlayId){
	this.sucessCallbackFunction = null;
	if ( element ) this.defaultContent = element;
	else this.defaultContent = document.getElementById(id);
	if ( !this.defaultContent ) return;

	this.parentWidget = parentWidget;
	if ( !this.parentWidget && typeof( pc3Widget ) != "undefined" ) this.parentWidget = pc3Widget.getParentWidget(this.defaultContent);

	this.id = this.defaultContent.id;
	
	this.onRequestOverlayId = onRequestOverlayId;
	if ( onRequestHTML ) {
		this.onRequestHTML = onRequestHTML;
	} else {
		var content = $(id+'_onRequestContent');
		if ( content ) {
			this.onRequestHTML = content.innerHTML;
			content.remove();
		}
	}

	if ( onErrorHTML ) {
		this.onErrorHTML = onErrorHTML;
	} else {
		var content = $(id+'_onErrorContent');
		if ( content ) {
			this.onErrorHTML = content.innerHTML;
			content.remove();
		}
	}
	
	this.replaceDefaultContent = function(newHTML){
		try {
			if ( this.overlay ) {
				this.overlay.remove();
				this.overlay = null;
			}
			var cleanHTML = newHTML.stripScripts();
			this.defaultContent.innerHTML = cleanHTML;
			newHTML.evalScripts();
		} catch( exception ) {
			if ( window.console ) window.console.error(exception);
		}
	};
	
	this.onSuccessHandler = function(responseText,responseXML){
		this.replaceDefaultContent(responseText);
		if ( this.sucessCallbackFunction ) this.sucessCallbackFunction();

		if ( this.parentWidget ) this.parentWidget.update();

		if ( typeof( pc3Widget ) != "undefined" ) pc3Widget.updateAll();
		
		if ( pc3AjaxContent.onload ) {
			pc3AjaxContent.onload();
			pc3AjaxContent.onload = null;
		}
	};
	
	this.onRequestHandler = function(){
		if ( this.onRequestHTML ) {
			if ( this.onRequestOverlayId ) {
				var overlayTarget = $(this.onRequestOverlayId);
				if ( !overlayTarget ) return;
				
				var pos = overlayTarget.positionedOffset();
				var dim = pc3GetOuterDimension(overlayTarget);
				
				this.overlay = new Element('div');
				this.overlay.setStyle({
					'position':'absolute',
					'overflow':'hidden',
					'left':pos.left+'px',
					'top':pos.top+'px',
					'width':dim.width+'px',
					'height':dim.height+'px'
				});
				
				this.overlay.innerHTML = this.onRequestHTML;
				$(overlayTarget.parentNode).insert(this.overlay);
			} else {
				this.replaceDefaultContent(this.onRequestHTML);
			}
		}
		if ( this.parentWidget ) this.parentWidget.update();
	};
	
	this.onErrorHandler = function(errorMsg){
		if ( this.onErrorHTML ){
			this.replaceDefaultContent(this.onErrorHTML);
		}
		if ( this.parentWidget ) this.parentWidget.update();
	};
	
	this.abort = function(abortHTML){
		if ( !this.ajax ) return false;
		
		this.ajax.onRequest = null;
		this.ajax.onSuccess = null;
		this.ajax.onError = null;
		
		this.ajax.abort();
		
		this.ajax = null;
		
		if ( abortHTML ) this.replaceDefaultContent(abortHTML);
		
		return true;
	}
	this.requestContent = function(url,postData){
		if ( this.ajax ) return true;
		
		url = pc3Ajax.addParams(url,'pc3AjaxContent='+ this.id);
		this.ajax = new pc3Ajax(url,postData,(postData?'POST':'GET'));
		
		var self = this;
		this.ajax.onRequest = function(){ self.onRequestHandler(); };
		this.ajax.onSuccess = function(responseText,responseXML){
			self.onSuccessHandler(responseText,responseXML);
			self.ajax = null;
		};
		
		this.ajax.onError = function(errorMsg){
			self.onErrorHandler(errorMsg);
			self.ajax = null;
		};
		
		if ( !this.ajax.doRequest() ) return false;
		
		return true;
	};
	
	this.setSuccessCallbackFunction = function(callback){
		this.sucessCallbackFunction = callback;
		return true;
	};
}

pc3AjaxContent.requestPost = function(event,form,id,url){
	if ( !window.pc3AjaxContentObjects[id] ) return true;
	var ajaxContent = window.pc3AjaxContentObjects[id];
	var data = pc3Ajax.getFormData(form, event);
	
	if ( !ajaxContent.requestContent((url?url:form.action),data) ) return true;
	
	return false;
};

pc3AjaxContent.requestLinkOnClick = function(event,link,id,url){
	if ( !window.pc3AjaxContentObjects[id] ) return false;
	
	var ajaxContent = window.pc3AjaxContentObjects[id];
	
	if ( !ajaxContent.requestContent(url) ) return false;
	
	cp(event);
	
	return true;
};

pc3AjaxContent.requestURL = function(id,url){
	var ajaxContent = window.pc3AjaxContentObjects[id];
	
	ajaxContent.requestContent(url);
};

pc3AjaxContent.requestLink = function(event,link,id,url){
	if ( !window.pc3AjaxContentObjects[id] ) return;
	
	if ( !url ) {
		if ( !link.pc3AjaxRequest ) link.pc3AjaxRequest = link.href;
		url = link.pc3AjaxRequest;
	}
	
	var ajaxContent = window.pc3AjaxContentObjects[id];
	
	if ( !ajaxContent.requestContent(url) ) return;
	
	if ( link ) link.href = 'javascript: void(0);';
	
	cp(event);
};

pc3AjaxContent.setSuccessCallbackFunction = function(id, callback){
	if ( !window.pc3AjaxContentObjects[id] ) return false;
	var ajaxContent = window.pc3AjaxContentObjects[id];
	if ( !ajaxContent.setSuccessCallbackFunction(callback) ) return false;
	return true;
};

/* popup */

function pc3PopupManagement(){
	var self = this;
	
	this.designs = $H({});
	this.popups = $H({});
	this.hiddenSelects = new Array();
	this.triggers = $H({});
	this.openPopups = $H({});
	this.popup = $(pc3Widget.body);
	this.content = $(pc3Widget.body);
	this.id = 'management';
	this.parentNode	= '';
	
	this.init = function(){
		this.selects = this.content.select('select');
		this.initPopups();
	}

	this.initPopups = function(){
		var popupData = pc3PopupData;
		if ( !popupData.size() ) return;
		var popupDesignData = $H(pc3PopupDesignData);

		popupData.each(function(popup){
			var designId = popup.designId;
			if ( !self.designs.get(designId) && ( designId && $(designId) && popupDesignData.get(designId) )){
				self.designs.set(designId, new pc3PopupDesign(designId, popupDesignData.get(designId), self));
			}
			var trigger = $(popup.triggerId);
			if ( self.designs.get(designId) && trigger ){
				var popupId = popup.triggerId+"_"+designId;
				var cachedPopup = self.popups.get(popupId);
				if ( !cachedPopup || cachedPopup.trigger != trigger ){
					self.popups.set(popupId, new pc3Popup(popupId, popup.triggerId, popup, self.designs.get(designId), self));
					self.triggers.set(popup.triggerId, self.popups.get(popupId));
				} else {
					self.popups.get(popupId).init();
				}
				self.popups.get(popupId).openInitially();
			}
		});
		pc3PopupData = new Array();
	}

	this.getParent = function(popup){
		if ( popup.parent ) return;
		var dataNode = $(popup.id+'Data');
		if ( !dataNode ) return;
		var parent = dataNode.up('.pc3popupdata');
		if ( parent ){
			parent = self.popups.get(parent.id.slice(0,(parent.id.length-4)));
		} else {
			parent = dataNode.up('[pc3popup]');
			if ( parent ) parent = parent.popup;
		}
		if ( !parent ) parent = this;
		return parent;
	}
		
	this.addOpenPopup = function(popup){
		if ( !this.openPopups.size() && pc3.Browser.IE6 && this.selects ) {
			this.selects.each(function(select){
				if ( select.visible() ){
					self.hiddenSelects.push(select);
					select.style.visibility = "hidden";
				}
			});
		}
		this.openPopups.set(popup.id, popup);
	}

	this.removeOpenPopup = function(popup){
		var zIndex = 10000;
		this.openPopups.unset(popup.id);
		this.openPopups.each(function(currentPopup){
			currentPopup.value.setZIndex(zIndex);
			zIndex++;
		});
		if ( !this.openPopups.size() && pc3.Browser.IE6 ) {
			this.hiddenSelects.each(function(select){ select.style.visibility = "visible"; });
			this.hiddenSelects = new Array();
		}
	}

	this.removePopup = function(popupId){
		this.popups.unset(popupId);
	}

	this.removeAjaxPopups = function(ajaxContent){
		this.triggers.each(function(currentTrigger){
			var ancestors = currentTrigger.value.trigger.ancestors();
			if ( !ancestors.size() || ancestors.last().tagName != 'HTML' ){
				if ( currentTrigger.value.parent ) currentTrigger.value.parent.removePopup(currentTrigger.value.id);
				else self.popups.unset(currentTrigger.value.id);
			}
		});
	}

	this.closeOtherOpenPopups = function(popup){
		if ( popup.groupname == 'pc3DefaultPopupGroup' ) return;
		this.openPopups.each(function(currentPopup){ if ( popup.id != currentPopup.key && popup.groupname == currentPopup.value.groupname ) currentPopup.value.close(''); });		
	}

	this.addPopup = function(popup){ if ( !this.popups.get(popup.id) ) this.popups.set(popup.id, popup); }
	
	this.doForAllOpenPopups = function(func){ this.openPopups.each(function(currentPopup){ func.bind(currentPopup.value)(); }); }

	this.doForAllPopups = function(func){ this.popups.each(function(currentPopup){ func.bind(currentPopup.value)(); }); }
	
	this.getZIndex = function(){ return (10000 + this.openPopups.size()); }
		
	this.isOpen = function(){ return true; }
	
	this.close = function(){ return false; }

	this.getPopup = function(trigger){
		var element = $(trigger);
		if ( typeof(trigger) == 'string' ){ 
			if ( element && element.popup ) return element.popup;
		} else {
			var parent = element.up('[pc3Popup]');
			if ( parent ) parent = parent.popup;
			if ( parent ) return parent;
		}
		return false;
	}
	
	this.openPopup = function(trigger){
		var popup = this.getPopup(trigger);
		if ( popup ) popup.open('', '');
	}

	this.closePopup = function(trigger){
		var popup = this.getPopup(trigger);
		if ( popup ) popup.close('');
	}

	this.updatePopup = function(trigger){
		var popup = this.getPopup(trigger);
		if ( popup ) popup.update();
	}

	this.setClosePopupFunction = function(trigger, callBack){
		var popup = this.getPopup(trigger);
		if ( popup ) popup.setCloseFunction(callBack);
	}

	this.setAjaxSucessPopupFunction = function(trigger, callBack){
		var popup = this.getPopup(trigger);
		if ( popup ) popup.setAjaxSucessFunction(callBack);
	}

	this.setPopupContentSize = function(trigger, width, height){
		var popup = this.getPopup(trigger);
		if ( popup ){
			popup.setContentSize(width, height);
			popup.update();
		}
	}
	
	// Event handlers
		this.handleMouseMove = function(event){ this.doForAllOpenPopups(function(){ this.handleMouseMove(); }); }
	
		this.handleClick = function(event){ this.doForAllOpenPopups(function(){ this.handleClick(event); }); }
	
		this.handleResize = function(event){ this.doForAllOpenPopups(function(){ this.handleResize(); }); }
	
		this.handleScroll = function(event){ this.doForAllOpenPopups(function(){ this.handleScroll(); }); }
	//---------------
		
	this.init();
	
}


function pc3PopupDesign(designId, designData, management){
	var self = this;
	this.id = designId;
	this.management = management;
	
	//"behaviour","openTrigger","openTriggerDelay","openSizeAnimation","openAnimation","openPathAnimation",
	//"openEffect","openDuration","originPosition","closedPosition","closeTriggers","closeDelay","closeSizeAnimation","closeAnimation",
	//"closePathAnimation","closeEffect","closeDuration","endPosition","backgroundColor","backgroundOpacity","backgroundOpenAnimation",
	//"backgroundOpenEffect","backgroundCloseAnimation","backgroundCloseEffect","displayShadow","placeholders",
	//"cornerSize","shadowOffsetTop","shadowOffsetRight","shadowOffsetBottom","shadowOffsetLeft","shadowTop","shadowRight","shadowBottom","shadowLeft"

	this.init = function(designData){
		var designData = $H(designData);
		//console.log(this.id, designData);
		designData.each(function(attribute){ self[attribute.key] = attribute.value; });
		if ( this.shadow && this.shadow.enabled ){ 
			this.shadow.blurRadius = parseInt(this.shadow.blurRadius);
			this.shadow.marginLeft = parseInt(this.shadow.marginLeft);
			this.shadow.marginRight = parseInt(this.shadow.marginRight);
			this.shadow.marginTop = parseInt(this.shadow.marginTop);
			this.shadow.marginBottom = parseInt(this.shadow.marginBottom);
			this.shadow.cornerSize = parseInt(this.shadow.cornerSize);
			this.shadowImages = {};
			['Left','Top','Right','Bottom'].each(function(location){
				if ( !self.shadow['image'+location] ) return;
				self.shadowImages[location.toLowerCase()] = self.shadow['image'+location];
				var loader = new Image();
				loader.src = self.shadow['image'+location];
			});
		}
		this.template = $(this.id);
		this.template.select('script').each(function(scriptElement){ scriptElement.remove(); });
		this.template.id = '';
		this.width = this.template.getWidth();
		this.height = this.template.getHeight();
		this.opacity = pc3Widget.getOpacity(this.template) * 100;
		this.template.remove();
	}	

	this.init(designData);
}

function pc3Popup(popupId, triggerId, popupData, design, management){
	var self = this;
	this.id = popupId;
	this.design = design;
		//if ( this.id == 'AssignedInfo_81704_Preview_Arrow_infobox1' ) console.log(this.design);
	
	this.management = management;
	this.popupData = $H(popupData);
	this.parent = '';
	this.waitingToOpen = '';
	this.waitingToClose = '';
	this.closeAfterOpen = '';
	this.openAfterClose = '';
	this.popup = '';
	this.hiddenSelects = new Array();
	this.closeCallbackFunction = '';	
	this.ajaxSucessFunction = '';	
	
	this.openedPosition = {x:0,y:0,width:0,height:0,marginLeft:0,marginRight:0,marginTop:0,marginBottom:0,factorWidth:0,factorHeight:0};
	this.closedPosition = {x:0,y:0,width:0,height:0,marginLeft:0,marginRight:0,marginTop:0,marginBottom:0,factorWidth:0,factorHeight:0};
	this.currentPosition = {x:0,y:0,width:0,height:0};
	this.lastSize = {width:0,height:0};
	this.effects = $H({});
	this.shadow = '';
	this.shadowOffsets = {offsetLeft:0, offsetTop:0, offsetRight:0, offsetBottom:0, offsetsX:0, offsetsY:0};
	this.eventLayer = '';
	this.popups = $H({});
	this.openPopups = $H({});
	this.background = '';
	this.ajaxRequest = false;
	this.triggerId = triggerId;
	this.postUpdate = false;

	this.flashvars = '';
		
	this.init = function(){
		this.trigger = $(this.triggerId);
		this.trigger[this.id] = 'closed';
		this.trigger.popup = this;
		this.groupname = this.popupData.get('groupName');
		if ( !this.groupname ) this.groupname = 'pc3DefaultPopupGroup';
		this.initTriggerEvents();		
		this.openTriggerDelay = parseFloat( this.design.open.delay ) || 0;
		this.closeTriggerDelay = parseFloat(this.design.close.delay) || 0;
		
		this.initialState = this.popupData.get('initialState');
		this.isFullscreen = this.popupData.get('fullscreen');
		this.fixedDimension = {'width':this.popupData.get('fixedContentWidth'), 'height':this.popupData.get('fixedContentHeight')}
	}

	this.initTriggerEvents = function(){
		if ( this.popupData.get('application') != 'default' ) return;
		var openHandler = this.open.bindAsEventListener(this);
		if ( this.design.open.trigger == 'click' ) openHandler = this.handleClickOnTrigger.bindAsEventListener(this);
		Event.observe(this.trigger, this.design.open.trigger, openHandler);
		if ( this.design.close.trigger && this.design.close.trigger.indexOf('mouseout') >= 0 ) {
			Event.observe(this.trigger, 'mouseout', this.close.bindAsEventListener(this));
		}
	}

	this.initPopupEvents = function(){
		if ( this.popupData.get('application') != 'default' ) return;
		if ( this.design.close.trigger && this.design.close.trigger.indexOf('mouseout') >= 0 ) {
			Event.observe(this.popup, 'mouseout', this.close.bindAsEventListener(this));
			Event.observe(this.content, 'mouseout', this.close.bindAsEventListener(this));
		}
		
		if ( this.design.close.trigger && this.design.close.trigger.indexOf('clickInsidePopup') >= 0 ) {
			this.eventLayer = new Element('div').setStyle({position:'absolute',zIndex:3});
			Event.observe(this.eventLayer, 'click', this.close.bindAsEventListener(this));
			this.innerBox.appendChild(this.eventLayer);
		}
	}

	this.bindToParent = function(){
		this.parent = this.management.getParent(this);
		this.parent.addPopup(this);
	}
	
	this.initPopup = function(){
		this.outerBox = new Element('div').setStyle({position:'absolute', overflow:'hidden'}).hide();
		this.outerBox.popup = this;
		this.outerBox.widget = this;
		this.outerBox.writeAttribute({pc3Popup:1});
		this.outerBox.writeAttribute({pc3Widget:1});
		this.innerBox = new Element('div').setStyle({position:'relative'});
		this.outerBox.appendChild(this.innerBox);
		this.popup = this.design.template.cloneNode(true).removeClassName('pc3popupdesign').setStyle({position:'absolute',zIndex:1});
		this.innerBox.appendChild(this.popup);
		this.popup.outerDimension = pc3Widget.getOuterDimension(this.popup, false);
		this.content = new Element('div').setStyle({position:'relative'});

		this.contentBox = this.content.wrap('div').setStyle({position:'absolute', overflow:'hidden',zIndex:2});
		this.innerBox.appendChild(this.contentBox);

		//transfer childs of pupup design to content
		this.popup.childElements().each( function(item) {
			Element.insert(self.content, item); 
		});

		this.initPopupEvents();

		//populate popup
		var dataNode = $(this.id+'Data');
		var topDataNode = dataNode;
		//if ( this.id == 'AssignedInfo_81704_Preview_Arrow_infobox1' ) console.log(dataNode);
		
		if ( this.design.placeholders != null ){
			this.design.placeholders.each(function(name){ //loop through placeholders
				var dataElement = '';
				dataNode.childElements().each(function(child){ if ( child.className == name ) dataElement = child; });		
				var targetElement = self.content.down('.'+name);
				if ( targetElement ) targetElement.removeClassName(name);
				if ( targetElement && dataElement ) dataElement.childElements().each(function(child){ targetElement.appendChild(child); });
				else if ( targetElement ) targetElement.remove();
			});
		}
		
		if ( this.design.positionChilds == 'absolute' ){
			if ( this.parent.parentNode ){
				this.parentNode = this.parent.parentNode;
			} else {
				dataNode.ancestors().each(function(element){
					if ( element.className == 'pc3popupwrapper' ) topDataNode = element;
					else if ( !self.parentNode && element.tagName != 'TD' && element.tagName != 'TR' && element.tagName != 'TBODY' && element.tagName != 'TABLE' ) self.parentNode = element;
				});
			}
		} else {
			this.parentNode = this.parent.content;
		}

		topDataNode.remove();
		this.parentNode.appendChild(this.outerBox);
		this.setSettings();
	}

	this.setSettings = function(){
		this.settings = {};
		['origin','opened','closed'].each(function(state){
			var values = {};
			var definition = self.popupData.get(state);
			values['setting'] = definition.element;
			values['element'] = '';
			switch( self.popupData.get('application') ){
				case 'e-paper':
				case 'mediaplayer':
					if ( (values.setting == 'triggerElement' || values.setting == 'customElement') && $(self.triggerId+"_wrapper") ) values['element'] = $(self.triggerId+"_wrapper");
					else if ( values.setting == 'parentElement' ) values['element'] = self.parent.popup;
					break;
				
				default:
					if ( values.setting == 'triggerElement' ) values['element'] = self.trigger;
					else if ( values.setting == 'parentElement' ) values['element'] = self.parent.popup;
					else if ( values.setting == 'customElement' && definition.custom && $(definition.custom) ) values['element'] = $(definition.custom);
					break;
			}

			values['position'] = definition.position;
			values['margins'] = {x:(definition.marginLeft?parseInt(definition.marginLeft):0),y:(definition.marginTop?parseInt(definition.marginTop):0)};

			if ( values['setting'] == 'atMouse' || !values['element'] ){
				if ( !values['margins'].x ) values['margins'].x = 1;
				if ( !values['margins'].y ) values['margins'].y = 1;
			}

			self.settings[state] = values;
			
		});

	}
		
	this.openInitially = function(){
		if ( this.initialState == 'opened' ) this.open('', '');
	}
	
	this.open = function(event, delay){
		if ( event ) event.stop();
		if ( !this.parent ) this.bindToParent();
		
		if ( !this.parent.isOpen() ) return;
		if ( this.waitingToOpen ) return;
		if ( this.waitingForClose ) {
			window.clearTimeout(this.waitingForClose);		
			this.waitingForClose = '';
			return;
		}
		if ( this.state == 'opened' || this.state == 'opening' ) return;
		if ( this.state == 'closing' ){
			this.openAfterClose = this.open.bind(this);
			return;
		}

		if ( !this.popup ) {
			this.initPopup();
			if ( this.design.shadow && this.design.shadow.enabled ){
				this.shadow = new pc3Shadow(
					this.popup,
					{left:this.design.shadow.marginLeft,top:this.design.shadow.marginTop,right:this.design.shadow.marginRight,bottom:this.design.shadow.marginBottom},
					this.design.shadowImages,
					this.design.shadow.cornerSize,
					this.design.shadow.color,
					this.design.shadow.opacity,
					this.design.shadow.blurRadius,
					(this.settings.origin.setting != 'atEndPosition' || this.settings.closed.setting != 'atEndPosition' || this.design.open.animationSize != '' || this.design.close.animationSize != '' ),
					(this.design.open.effect == 'fadeIn' || this.design.close.effect == 'fadeOut' )
				);
			}
		} else {
			this.setSettings();
		}
		
		if ( this.design.ajaxContentId ){
			var ajaxContent = pc3Widget.findElementById(this.content, this.design.ajaxContentId);

			if ( ajaxContent && this.popupData.get('ajaxContentId') && window.pc3AjaxContentObjects && window.pc3AjaxContentObjects[this.design.ajaxContentId] && this.popupData.get('ajaxLink') ){
				if ( this.isFullscreen ) this.setOpenedDimension();
				var dimension = {width:parseInt(this.isFullscreen?this.openedPosition.width:this.popupData.get('ajaxContentCustomWidth')),height:parseInt(this.isFullscreen?this.openedPosition.height:this.popupData.get('ajaxContentCustomHeight'))};
				this.ajaxRequest = new pc3PopupAjaxRequest(this.design.ajaxContentId, this.popupData.get('ajaxContentId'), this.popupData.get('ajaxLink'), ajaxContent, dimension, this.isFullscreen, this.ajaxSucessFunction);
				var duration = (this.design.open.duration?parseFloat(this.design.open.duration):0) + (this.openTriggerDelay?parseFloat(this.openTriggerDelay):0);
				this.ajaxRequest.open(duration);
			}
		}
		
		this.setOpenedDimension();
		if ( this.design.background && this.design.background.enabled ) this.background = pc3Widget.addBackground(this.parent.popup, this.design.background.color, this.design.background.opacity);
		if ( this.background ) this.background.open(this.design.background.openDelay, this.design.background.openEffect, this.design.background.openDuration);
		
		this.parent.closeOtherOpenPopups(this);
		this.trigger[this.id] = 'opened';
		
		if ( this.openTriggerDelay > 0 && (event || delay) ) this.waitingToOpen = this.openPopup.bind(this).delay(this.openTriggerDelay);
		else this.openPopup();
	}

	
	this.openPopup = function(){

		window.clearTimeout(this.waitingToOpen);
		this.waitingToOpen = '';
		this.state = 'opening';
		this.parent.addOpenPopup(this);

		this.setZIndex(this.management.getZIndex());
		this.setClosedDimension(this.design.open.animationSize, this.settings.origin.element, this.settings.origin.setting);

		if ( this.closedPosition.width != this.openedPosition.width || this.closedPosition.height != this.openedPosition.height ) this.setDimension({value:this.closedPosition});
		else this.setDimension({value:this.openedPosition});

		this.setPositions('opened', this.openedPosition);
		this.setPositions('origin', this.closedPosition);
		if ( this.closedPosition.x != this.openedPosition.x || this.closedPosition.y != this.openedPosition.y ) this.setPosition({value:this.closedPosition});
		else this.setPosition({value:this.openedPosition})

		this.boundingBox = this.getBoundingBox(this.design.open.animationType);	

		var duration = (this.design.open.duration?parseFloat(this.design.open.duration)*1000:0);
		
		// fadeIn
		if ( this.design.open.effect == 'fadeIn' ){
			this.setOpacity({value:0});
			this.effects.set('opacity', new pc3Tween('opacity', 0, 100, (duration*0.8).round(), 'EaseInQuad', this.setOpacity.bind(self), this.removeEffect.bind(self)));
		} else {
			this.setOpacity({value:100});
		}
		
		// pathAnimation
		var pathAnimationTransition = '';
		if ( this.closedPosition.x != this.openedPosition.x || this.closedPosition.y != this.openedPosition.y ){
			pathAnimationTransition = this.getPathAnimationTransition(this.design.open.animation, 'open');
			this.effects.set('path', new pc3PopupPathAnimation('path', this.getPath(this.design.open.animationPath, 'open'), 'open', (duration*0.75).round(), pathAnimationTransition, this.setPosition.bind(self), this.removeEffect.bind(self)));
		}
		
		// sizeAnimation
		if ( this.closedPosition.width != this.openedPosition.width || this.closedPosition.height != this.openedPosition.height ){
			this.effects.set('size', new pc3PopupSizeAnimation('size', 0, 10000, 'open', this.design.open.animationSize, pathAnimationTransition, duration, this.setDimension.bind(self), this.removeEffect.bind(self)));
		}
		
		this.outerBox.show();
		pc3Widget.updateScrollers();
		this.animate();
	}
	
	this.close = function(event){
		if ( !this.popup ) return;
		if ( this.waitingForClose ) return;
		if ( event && event.type == 'mouseout' ){
			event.stop();
			if ( this.mouseIsInside(event) ) return;
			this.parent.close(event);
		}
		if ( this.waitingToOpen ) {
			window.clearTimeout(this.waitingToOpen);		
			this.waitingToOpen = '';
			return;
		}
		if ( this.state == 'opening' ){
			this.closeAfterOpen = this.close.bind(this);
			return;
		}
		if ( this.state == 'closed' || this.state == 'closing' ) return;

		this.trigger[this.id] = 'closed';
		if ( this.background ) this.background.close(this.design.background.closeDelay, this.design.background.closeEffect, this.design.background.closeDuration);
		this.background = '';
		this.state = 'closing';

		if ( this.closeTriggerDelay > 0 && event ) this.waitingForClose = this.closeChildPopups.bind(this).delay(this.closeTriggerDelay);
		else this.closeChildPopups();
	
	}

	this.closeChildPopups = function(){
		window.clearTimeout(this.waitingForClose);
		this.waitingForClose = '';
		this.doForAllOpenPopups(function(){ this.close(''); });
		this.waitForChildsToClose();
	}

	this.waitForChildsToClose = function(){
		if ( this.openPopups.size() ){
			this.doForAllOpenPopups(function(){ this.close(''); });
			this.waitForChildsToClose.bind(this).delay(0.02);
		} else {
			this.closePopup();
		}
	}
	
	this.closePopup = function(){
		this.setClosedDimension(this.design.close.animationSize, this.settings.closed.element, this.settings.closed.setting);
		
		this.openedPosition.x = this.currentPosition.x;
		this.openedPosition.y = this.currentPosition.y;
		
		this.setPositions('closed', this.closedPosition);
		this.boundingBox = this.getBoundingBox(this.design.close.animationType);	
				
		var duration = (this.design.close.duration?parseFloat(this.design.close.duration)*1000:0);
		
		// fadeOut
		if ( this.design.close.effect == 'fadeOut' ){
			this.effects.set('opacity', new pc3Tween('opacity', 100, 0, (duration*1.1).round(), 'EaseOutQuad', this.setOpacity.bind(self), this.removeEffect.bind(self)));
		}
		
		// pathAnimation
		var pathAnimationTransition = '';
		if ( this.closedPosition.x != this.openedPosition.x || this.closedPosition.y != this.openedPosition.y ){
			pathAnimationTransition = this.getPathAnimationTransition(this.design.close.animation, 'close');
			this.effects.set('path', new pc3PopupPathAnimation('path', this.getPath(this.design.open.animationPath, 'close'), 'close', duration, pathAnimationTransition, this.setPosition.bind(self), this.removeEffect.bind(self)));
		}
		
		// sizeAnimation
		if ( this.closedPosition.width != this.openedPosition.width || this.closedPosition.height != this.openedPosition.height ){
			this.effects.set('size', new pc3PopupSizeAnimation('size', 10000, 0, 'close', this.design.close.animationSize, pathAnimationTransition, duration, this.setDimension.bind(self), this.removeEffect.bind(self)));
		}
		
		this.animate();
	}

	this.handleClick = function(event){
		if (!this.design.close.trigger) return;
		if ( this.design.close.trigger.indexOf('clickOutsidePopup') >= 0 && !this.mouseIsInside(event)){
			this.trigger[this.id] = 'closed';
			this.close('');
		}
		this.doForAllOpenPopups(function(){ this.handleClick(event)});
	}


	this.handleClickOnTrigger = function(event){
		if ( this.popupData.get('application') != 'default' ) return;
		if ( this.design.close.trigger && this.design.close.trigger.indexOf('clickOnTrigger') >= 0 ){
			if ( this.trigger[this.id] == 'opened' ){
				this.close(event);
			} else {
				this.open(event);
			}
		} else {
			this.open(event);
		}
	}

	this.handleMouseMove = function(){
		if ( this.design.behaviour == 'attachedToMouse' ){
			position = this.getPositionAt(this.currentPosition, this.settings.opened.margins, '');
			this.positionPopup(position);
		}
		this.doForAllOpenPopups(function(){ this.handleMouseMove()});
	}

	this.handleResize = function(){
		if ( this.design.behaviour == 'attachedToScreen' || this.popupData.get('application') != 'default' || this.isFullscreen ){
			if ( this.popupData.get('application') != 'default' || this.isFullscreen ) this.setOpenedDimension();
			this.setPositions('opened', this.openedPosition);
			this.setDimension({value:this.openedPosition});
			this.setCurrentPosition(this.openedPosition);
			this.boundingBox = this.getBoundingBox(this.design.open.animationType);
			this.positionPopup(this.openedPosition);
			if ( this.isFullscreen && this.ajaxRequest ) this.ajaxRequest.setDimension(this.openedPosition);
		}
		this.doForAllOpenPopups(function(){ this.handleResize()});
	}

	this.handleScroll = function(){
		if ( this.design.behaviour == 'attachedToScreen' || this.popupData.get('application') != 'default' || this.isFullscreen ){
			this.setPositions('opened', this.openedPosition);
			this.setCurrentPosition(this.openedPosition);
			this.boundingBox = this.getBoundingBox(this.design.open.animationType);
			this.positionPopup(this.openedPosition);
		}
		this.doForAllOpenPopups(function(){ this.handleScroll()});
	}

	this.update = function(){
		if ( this.state != 'opened' ){
			this.postUpdate = true;
			return;
		}
		this.setOpenedDimension();
		this.setPositions('opened', this.openedPosition);
		this.setDimension({value:this.openedPosition});
		this.setCurrentPosition(this.openedPosition);
		this.boundingBox = this.getBoundingBox(this.design.open.animationType);
		this.positionPopup(this.openedPosition);
	}

	this.setCloseFunction = function(callBack){
		this.closeCallbackFunction = callBack;
	}

	this.setAjaxSucessFunction = function(callBack){
		this.ajaxSucessFunction = callBack;
	}

	this.setContentSize = function(width, height){
		this.fixedDimension.width = parseInt(width);
		this.fixedDimension.height = parseInt(height);
	}

	this.setCurrentPosition = function(values){
		this.currentPosition.x = values.x;
		this.currentPosition.y = values.y;
	}
	
	
	this.mouseIsInside = function(event){
		var pointerX = event.pointerX();
		var pointerY = event.pointerY();
		if ( pointerX < 1 || pointerY < 1 ) return true;
		if ( pc3.Browser.IE ){
			pointerX = pointerX - 2;
			pointerY = pointerY - 2;
		}
		var position = this.trigger.cumulativeOffset();
		var triggerArea = {left:position.left,top:position.top,right:(position.left + this.trigger.getWidth()),bottom:(position.top + this.trigger.getHeight())};
		if ( (pointerX >= triggerArea.left && pointerX < triggerArea.right) && (pointerY >= triggerArea.top && pointerY < triggerArea.bottom) ) return true;
		if ( this.design.behaviour == 'attachedToMouse' ) return false;
		position = this.popup.cumulativeOffset();
		var popupArea = {left:position.left,top:position.top,right:(position.left + this.popup.getWidth()),bottom:(position.top + this.popup.getHeight())};
		if ( (pointerX >= popupArea.left && pointerX < popupArea.right) && (pointerY >= popupArea.top && pointerY < popupArea.bottom) ) return true;

		if ( this.design.close.trigger && this.design.close.trigger.indexOf('mouseout') >= 0 ){
			var connectingArea = {};
			if ( triggerArea.left > popupArea.left ){
				if ( popupArea.right > triggerArea.left ){
					connectingArea['left'] = triggerArea.left;
					connectingArea['right'] = popupArea.right;
				} else {
					connectingArea['left'] = popupArea.right;
					connectingArea['right'] = triggerArea.left;
				}
			} else {
				if ( triggerArea.right > popupArea.left ){
					connectingArea['left'] = popupArea.left;
					connectingArea['right'] = triggerArea.right;
				} else {
					connectingArea['left'] = triggerArea.right;
					connectingArea['right'] = popupArea.left;
				}
			}
			if ( triggerArea.top > popupArea.top ){
				if ( popupArea.bottom > triggerArea.top ){
					connectingArea['top'] = triggerArea.top;
					connectingArea['bottom'] = popupArea.bottom;
				} else {
					connectingArea['top'] = popupArea.bottom;
					connectingArea['bottom'] = triggerArea.top;
				}
			} else {
				if ( triggerArea.bottom > popupArea.top ){
					connectingArea['top'] = popupArea.top;
					connectingArea['bottom'] = triggerArea.bottom;
				} else {
					connectingArea['top'] = triggerArea.bottom;
					connectingArea['bottom'] = popupArea.top;
				}
			}
	
			if ( (pointerX >= connectingArea.left && pointerX < connectingArea.right) && (pointerY >= connectingArea.top && pointerY < connectingArea.bottom) ) return true;
		}

		if ( this.settings.opened.setting != 'triggerElement' ) return false;
		
		var mouseIsInside = false;
		this.openPopups.each(function(currentPopup){ if ( currentPopup.value.mouseIsInside(event) ) mouseIsInside = true; });		
		return mouseIsInside;
	}

	this.setOpenedDimension = function(dimension){
		switch( self.popupData.get('application') ){
			case 'e-paper':
				var dimension = {
					'width':document.viewport.getWidth()-(2*this.settings.opened.margins.x)-this.popup.outerDimension.offsetLeft-this.popup.outerDimension.offsetRight,
					'height':document.viewport.getHeight()-(2*this.settings.opened.margins.y)-this.popup.outerDimension.offsetTop-this.popup.outerDimension.offsetBottom
				};
				break;
			case 'mediaplayer':
				var originalDimension = $(self.triggerId+"_wrapper").getDimensions();
				
				var windowDimension = {
					'width':document.viewport.getWidth()-(2*this.settings.opened.margins.x)-this.popup.outerDimension.offsetLeft-this.popup.outerDimension.offsetRight,
					'height':document.viewport.getHeight()-(2*this.settings.opened.margins.y)-this.popup.outerDimension.offsetTop-this.popup.outerDimension.offsetBottom
				};
				if ( windowDimension.width/originalDimension.width > windowDimension.height/originalDimension.height ){
					var dimension = {'width':(windowDimension.height * originalDimension.width) / originalDimension.height,'height':windowDimension.height};
				} else {
					var dimension = {'width':windowDimension.width,'height':(windowDimension.width * originalDimension.height) / originalDimension.width};
				}
				break;				
			default:
				if ( !this.isFullscreen ) {
					if ( this.fixedDimension.width && this.fixedDimension.height ){
						var dimension = this.fixedDimension;
					} else {
						var dimension = pc3Widget.getElementDimension(this.content);
						if ( this.fixedDimension.width ) dimension.width = this.fixedDimension.width;
						if ( this.fixedDimension.height ) dimension.height = this.fixedDimension.height;
					}
				} else {
					var dimension = {
						'width':document.viewport.getWidth()-(2*this.settings.opened.margins.x)-this.popup.outerDimension.offsetLeft-this.popup.outerDimension.offsetRight,
						'height':document.viewport.getHeight()-(2*this.settings.opened.margins.y)-this.popup.outerDimension.offsetTop-this.popup.outerDimension.offsetBottom
					};
				}
				break;
		}
		this.openedPosition.width = (dimension.width>0?dimension.width:1);
		this.openedPosition.height = (dimension.height>0?dimension.height:1);
		this.openedPosition.factorWidth = 10000;
		this.openedPosition.factorHeight = 10000;
	}

	this.setClosedDimension = function(sizeAnimation, offsetElement, elementSetting){
		switch( sizeAnimation ){
			case 'zoomIn':
			case 'zoomOut':
			case 'bubble':
			case 'growBoth':
			case 'shrinkBoth':
				if ( (elementSetting == 'triggerElement' || elementSetting == 'customElement') && offsetElement ){
					this.closedPosition.width = offsetElement.getWidth() - this.popup.outerDimension.offsetsX;
					this.closedPosition.height = offsetElement.getHeight() - this.popup.outerDimension.offsetsY;
				} else {
					this.closedPosition.width = (this.popup.outerDimension.offsetsX?0:3);
					this.closedPosition.height = (this.popup.outerDimension.offsetsY?0:3);
				}
				break;
				
			case 'growHorizontal':
			case 'shrinkHorizontal':
				this.closedPosition.width = (this.popup.outerDimension.offsetsX?0:1);
				this.closedPosition.height = this.openedPosition.height;
				break;
				
			case 'growVertical':
			case 'shrinkVertical':
				this.closedPosition.width = this.openedPosition.width;
				this.closedPosition.height = (this.popup.outerDimension.offsetsY?0:1);
				break;
				
			default:
				this.closedPosition.width = this.openedPosition.width;
				this.closedPosition.height = this.openedPosition.height;
				break;
		}
		this.closedPosition.factorWidth = 0;
		this.closedPosition.factorHeight = 0;
	}

	this.setPositions = function(state, dimensions){
		var newPosition = $H({x:0,y:0,marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});
		
		var offsetElement = this.settings[state].element;
		var elementSetting = this.settings[state].setting;
		var positionSetting = this.settings[state].position;
		var margins = this.settings[state].margins;
		if ( elementSetting == 'atEndPosition' ){
			newPosition.each(function(pair){ newPosition.set(pair.key, self.openedPosition[pair.key]) });
		} else if ( elementSetting == 'atMouse' || !offsetElement ){
			var position = self.getPositionAt(dimensions, margins, '');
			newPosition.each(function(pair){ newPosition.set(pair.key, position[pair.key]) });			
		} else if ( positionSetting == 'atElement' ){
			var boxDimension = { left:offsetElement.cumulativeOffset().left, top:offsetElement.cumulativeOffset().top, width:offsetElement.getWidth(), height:offsetElement.getHeight() };
			var position = self.getPositionAt(dimensions, margins, boxDimension);
			newPosition.each(function(pair){ newPosition.set(pair.key, position[pair.key]) });
		} else {
			var offsetLeft = document.viewport.getScrollOffsets().left;
			var offsetTop = document.viewport.getScrollOffsets().top;
			var screenWidth = document.viewport.getWidth();
			var screenHeight = document.viewport.getHeight();
			
			if ( offsetElement != pc3Widget.body ){
				if ( !this.positionIsRelative() ){
					offsetLeft = offsetElement.cumulativeOffset().left;
					offsetTop = offsetElement.cumulativeOffset().top;
					screenWidth = offsetElement.getWidth();
					screenHeight = offsetElement.getHeight();
				} else {
					if ( offsetElement != this.parent.popup ){
						offsetLeft = offsetElement.cumulativeOffset().left - this.parent.popup.cumulativeOffset().left;
						offsetTop = offsetElement.cumulativeOffset().top - this.parent.popup.cumulativeOffset().top;
						screenWidth = offsetElement.getWidth();
						screenHeight = offsetElement.getHeight();
					} else {
						offsetLeft = 0;
						offsetTop = 0;
						screenWidth = offsetElement.getWidth();
						screenHeight = offsetElement.getHeight();
					}
				}
			}
			
			
			
			var halfWidth = (dimensions.width / 2).round();
			var halfHeight = (dimensions.height / 2).round();
			switch( positionSetting ){
				case 'leftTopOutside':
				case 'leftCenterOutside':
				case 'leftBottomOutside':
					newPosition.set('marginLeft', 0);
					newPosition.set('marginRight', margins.x);
					newPosition.set('x', offsetLeft - halfWidth - this.popup.outerDimension.offsetRight - newPosition.get('marginRight'));
					break;

				case 'topLeftOutside':
				case 'topLeftInside':
				case 'leftTopInside':
				case 'leftCenterInside':
				case 'leftBottomInside':
				case 'bottomLeftInside':
				case 'bottomLeftOutside':
					newPosition.set('marginLeft', margins.x);
					newPosition.set('marginRight', 0);
					newPosition.set('x', offsetLeft + halfWidth + this.popup.outerDimension.offsetLeft + newPosition.get('marginLeft'));
					break;

				case 'centerCenter':
				case 'topCenterInside':
				case 'bottomCenterInside':
				case 'topCenterOutside':
				case 'bottomCenterOutside':
					newPosition.set('marginLeft', 0);
					newPosition.set('marginRight', 0);
					newPosition.set('x', (offsetLeft + (screenWidth / 2)).round());
					break;

				case 'topRightOutside':
				case 'topRightInside':
				case 'rightTopInside':
				case 'rightCenterInside':
				case 'rightBottomInside':
				case 'bottomRightInside':
				case 'bottomRightOutside':
					newPosition.set('marginLeft', 0);
					newPosition.set('marginRight', margins.x);
					newPosition.set('x', offsetLeft + screenWidth - halfWidth - this.popup.outerDimension.offsetRight - newPosition.get('marginRight'));
					break;
					
				case 'rightTopOutside':
				case 'rightCenterOutside':
				case 'rightBottomOutside':
					newPosition.set('marginLeft', margins.x);
					newPosition.set('marginRight', 0);
					newPosition.set('x', offsetLeft + screenWidth + halfWidth + this.popup.outerDimension.offsetLeft + newPosition.get('marginLeft'));
					break;
			}


			switch( positionSetting ){
				case 'topLeftOutside':
				case 'topCenterOutside':
				case 'topRightOutside':
					newPosition.set('marginTop', 0);
					newPosition.set('marginBottom', margins.y);
					newPosition.set('y', offsetTop - halfHeight - this.popup.outerDimension.offsetTop - newPosition.get('marginBottom'));
					break;

				case 'leftTopOutside':
				case 'leftTopInside':
				case 'topLeftInside':
				case 'topCenterInside':
				case 'topRightInside':
				case 'rightTopInside':
				case 'rightTopOutside':
					newPosition.set('marginTop', margins.y);
					newPosition.set('marginBottom', 0);
					newPosition.set('y', offsetTop + halfHeight + this.popup.outerDimension.offsetTop + newPosition.get('marginTop'));
					break;

				case 'leftCenterOutside':
				case 'leftCenterInside':
				case 'centerCenter':
				case 'rightCenterInside':
				case 'rightCenterOutside':
					newPosition.set('marginTop', 0);
					newPosition.set('marginBottom', 0);
					newPosition.set('y', (offsetTop + (screenHeight / 2)).round());
					break;

				case 'leftBottomOutside':
				case 'leftBottomInside':
				case 'bottomLeftInside':
				case 'bottomCenterInside':
				case 'bottomRightInside':
				case 'rightBottomInside':
				case 'rightBottomOutside':
					newPosition.set('marginTop', 0);
					newPosition.set('marginBottom', margins.y);
					newPosition.set('y', offsetTop + screenHeight - halfHeight - this.popup.outerDimension.offsetBottom - newPosition.get('marginBottom'));
					break;

				case 'bottomLeftOutside':
				case 'bottomCenterOutside':
				case 'bottomRightOutside':
					newPosition.set('marginTop', margins.y);
					newPosition.set('marginBottom', 0);
					newPosition.set('y', offsetTop + screenHeight + halfHeight + this.popup.outerDimension.offsetTop + newPosition.get('marginTop'));
					break;
			}
		}
		if ( state == 'opened' ) newPosition.each(function(pair){ self.openedPosition[pair.key] = pair.value });			
		else newPosition.each(function(pair){ self.closedPosition[pair.key] = pair.value });
		//console.log('setPositions '+(offsetElement?offsetElement.identify():'noOffsetElement')+" "+elementSetting+" "+positionSetting+" {"+dimensions.x+","+dimensions.y+"} {"+dimensions.width+","+dimensions.height+"} {"+margins.x+","+margins.y+"} "+state);
	}

	this.getPath = function(type, direction){
		var x = 0;
		var y = 0;
		var startX = (direction=='open'?this.closedPosition.x:this.openedPosition.x);
		var endX = (direction=='open'?this.openedPosition.x:this.closedPosition.x);
		var startY = (direction=='open'?this.closedPosition.y:this.openedPosition.y);
		var endY = (direction=='open'?this.openedPosition.y:this.closedPosition.y);
		
		switch( type ){
			case 'straight':
				x = (startX + ((endX - startX) / 2)).round();
				y = (startY + ((endY - startY) / 2)).round();
				break;
			
			case 'swish':
				if ( startX < endX ) x = (startX + ((((endX - startX) / 3)) * 2)).round();
				else x = (startX - ((((startX - endX) / 3)) * 2)).round();
				if ( startY < endY ) y = (endY + (((endY - startY) / 4) * 3)).round();
				else y = (endY - (((startY - endY) / 4) * 3)).round();
				break;
			
			case 'swishStrong':
				if ( startX < endX ) x = (startX + ((((endX - startX) / 2)) * 1)).round();
				else x = (startX - ((((startX - endX) / 2)) * 1)).round();
				if ( startY < endY ) y = (endY + ((endY - startY) * 2)).round();
				else y = (endY - ((startY - endY) * 2)).round();
				break;
		}

		return { start:{ x:startX, y:startY }, middle:{ x:x, y:y }, end:{ x:endX, y:endY } };
	}

	this.getPathAnimationTransition = function(type, direction){
		switch( type ){
			case 'easeOut':
				return 'EaseOutQuad';
				
			case 'easeIn':
				return 'EaseInQuad';

			case 'drop':
				if ( direction == 'open' ) return 'EaseOutBounce';
				return 'EaseOutBounce';
				
			case 'elastic':
				if ( direction == 'open' ) return 'EaseOutElastic';
				return 'EaseInElastic';
		}
		
		return 'EaseNone';
	}				

	this.setZIndex = function(zIndex){
		this.outerBox.setStyle({zIndex:zIndex});
	}
	
	this.setOpacity = function(effect){
		this.currentPosition.opacity = effect.value;
		this.innerBox.setOpacity(this.currentPosition.opacity/100);
	}

	this.setPosition = function(effect){
		this.currentPosition.x = (effect.value.x).round();
		this.currentPosition.y = (effect.value.y).round();
	}

	this.setDimension = function(effect){
		this.currentPosition.width = (((this.openedPosition.width - this.closedPosition.width) * effect.value.factorWidth) / this.openedPosition.factorWidth).round() + this.closedPosition.width;
		this.currentPosition.height = (((this.openedPosition.height - this.closedPosition.height) * effect.value.factorHeight) / this.openedPosition.factorHeight).round() + this.closedPosition.height;
	}

	this.positionIsRelative = function(){
		if ( this.design.positionChilds == 'absolute' || (this.design.positionChilds != 'absolute' && this.parent == this.management) ) return false;
		return true;
	}
	
	this.removeEffect = function(effect){ this.effects.unset(effect.name); }

	this.getBoundingBox = function(type){
		var outerBoundingBox = pc3Widget.getDocumentDimension();
		
		var offsetElement = this.settings.opened.element;
		var positionSetting = this.settings.opened.position;

		if (type == 'visible' || !offsetElement || positionSetting == 'atElement' ) return outerBoundingBox;
		
		var left = outerBoundingBox.left;
		var top = outerBoundingBox.top;
		var right = outerBoundingBox.right;
		var bottom = outerBoundingBox.bottom;
		
		if ( positionSetting.endsWith('Inside') || positionSetting == 'centerCenter' || positionSetting == 'atMouse' ){
		
			if ( this.design.positionChilds == 'absolute' || (this.design.positionChilds != 'absolute' && this.parent == this.management) ){
				if ( offsetElement != pc3Widget.body ){
					left = offsetElement.cumulativeOffset().left;
					top = offsetElement.cumulativeOffset().top;
					right = left + offsetElement.getWidth();
					bottom = top + offsetElement.getHeight();
				}
			} else {
				left = offsetElement.cumulativeOffset().left - this.parent.popup.cumulativeOffset().left;
				top = offsetElement.cumulativeOffset().top - this.parent.popup.cumulativeOffset().top;
				right = left + offsetElement.getWidth();
				bottom = top + offsetElement.getHeight();
			}

		} else {
			if ( positionSetting.startsWith('left') ){
				if ( this.positionIsRelative() ) right = offsetElement.cumulativeOffset().left - this.parent.popup.cumulativeOffset().left;
				else right = offsetElement.cumulativeOffset().left;
			} else if ( positionSetting.startsWith('right') ){
				if ( this.positionIsRelative() ) left = offsetElement.cumulativeOffset().left - this.parent.popup.cumulativeOffset().left + offsetElement.getWidth();
				else left = offsetElement.cumulativeOffset().left + offsetElement.getWidth();
			}
			if ( positionSetting.startsWith('top') ){
				if ( this.positionIsRelative() ) bottom = offsetElement.cumulativeOffset().top - this.parent.popup.cumulativeOffset().top;
				bottom = offsetElement.cumulativeOffset().top;
			} else if ( positionSetting.startsWith('bottom') ){
				if ( this.positionIsRelative() ) top = offsetElement.cumulativeOffset().top - this.parent.popup.cumulativeOffset().top + offsetElement.getHeight();
				top = offsetElement.cumulativeOffset().top + offsetElement.getHeight();
			}
		}
		return {left:left,top:top,right:right,bottom:bottom};
	}

	this.fitToBoundingBox = function(contentDimension, boundingBox){
		var offsetX = 0;
		var left = contentDimension.left;
		var width = contentDimension.width;
		if ( left < boundingBox.left ){
			offsetX = left - boundingBox.left;
			if ( (left + width) < boundingBox.left ){
				width = 0;
			} else {
				width = width + offsetX;
			}
			left = boundingBox.left;
		}
		
		if ( left + width > boundingBox.right ){
			if ( left > boundingBox.right ){
				left = boundingBox.right;
				width = 0;
			} else {
				width = boundingBox.right - left;
			}
		}

		var offsetY = 0;
		var top = contentDimension.top;
		var height = contentDimension.height;

		if ( top < boundingBox.top ){
			offsetY = top - boundingBox.top;
			if ( (top + height) < boundingBox.top ){
				height = 0;
			} else {
				height = height + offsetY;
			}
			top = boundingBox.top;
		}
		if ( top + height > boundingBox.bottom ){
			if ( top > boundingBox.bottom ){
				top = boundingBox.bottom;
				height = 0;
			} else {
				height = boundingBox.bottom - top;
			}
		}
		return {left:(left).round(), top:(top).round(), width:(width).round(), height:(height).round(), offsetX:(offsetX).round(), offsetY:(offsetY).round()};
	}

	this.getPositionAt = function(dimension, margins, box){
		if ( !box ) {
			box = {
				left:(pc3Widget.mousePosition.x?pc3Widget.mousePosition.x:0),
				top:(pc3Widget.mousePosition.y?pc3Widget.mousePosition.y:0),
				width:0,
				height:0
			}
		}
		
		if ( this.positionIsRelative() ){
			var offsetLeft = this.parent.popup.cumulativeOffset().left;
			var offsetTop = this.parent.popup.cumulativeOffset().top;
			var screenWidth = this.parent.popup.getWidth();
			var screenHeight = this.parent.popup.getHeight();
		} else {
			var offsetLeft = document.viewport.getScrollOffsets().left;
			var offsetTop = document.viewport.getScrollOffsets().top;
			var screenWidth = document.viewport.getWidth();
			var screenHeight = document.viewport.getHeight();
		}

		var shadowOffsets = this.getShadowOffsets(dimension);

		var width = dimension.width;
		var height = dimension.height;
		
		var position = {x:0, y:0, marginLeft:0, marginTop:0, marginRight:0, marginBottom:0};

		position.marginLeft = margins.x;

		position.x = ((box.left + box.width) + position.marginLeft + this.popup.outerDimension.offsetLeft + shadowOffsets.offsetLeft + (width/2)).ceil();
		var correction = (position.x + (width/2).ceil() + this.popup.outerDimension.offsetRight + shadowOffsets.offsetRight + 10) - (screenWidth + offsetLeft);
		if ( correction > 0 ) position.x = position.x - correction;
		
		if ( position.x - (position.marginLeft + this.popup.outerDimension.offsetLeft + shadowOffsets.offsetLeft + (width/2)).ceil() < offsetLeft ){
			position.x = (offsetLeft + position.marginLeft + this.popup.outerDimension.offsetLeft + shadowOffsets.offsetLeft + (width/2)).ceil();
		}

		position.marginTop = margins.y;
		position.y = ((box.top + box.height) + position.marginTop + this.popup.outerDimension.offsetTop + shadowOffsets.offsetTop + (height/2)).ceil();
		correction = (position.y + (height/2).ceil() + this.popup.outerDimension.offsetBottom + 2) - (screenHeight + offsetTop);
		if ( correction > 0 && box.top - offsetTop >= height + this.popup.outerDimension.offsetsY + shadowOffsets.offsetTop + shadowOffsets.offsetBottom){
			position.marginTop = 0;
			position.marginBottom = margins.y;
			position.y = (box.top - position.marginBottom - this.popup.outerDimension.offsetBottom - shadowOffsets.offsetBottom - (height/2)).ceil();
		}
		
		if ( this.positionIsRelative() ){
			position.x = position.x - this.parent.cumulativeOffset().left;
			position.y = position.y - this.parent.cumulativeOffset().top;
		}
		return position;
		
	}	





	this.animate = function(){
		var position = this.currentPosition;
		if ( this.design.behaviour == 'attachedToMouse' ) position = this.getPositionAt(this.currentPosition, this.settings.opened.margins, '');
		this.positionPopup(position);

		if ( this.effects.size() ){
			this.animate.bind(this).delay(0.02)
		} else {
			if ( this.state == 'closing' ){
				this.state = 'closed';
				this.postUpdate = false;
				if ( this.ajaxRequest ) this.removeAjaxPopups();
				if ( this.popupData.get('application') != 'default' ){
					if ( this.trigger.flashvars ){
						this.trigger.childElements().each(function(element){
							if ( element.name != 'flashvars' ) return;
							element.value = self.trigger.flashvars;
							self.trigger.flashvars = '';
						});
					}
					$(this.triggerId+"_wrapper").appendChild(this.trigger);
				}
				this.parent.removeOpenPopup(this);
				this.outerBox.hide();
				if ( this.closeCallbackFunction ) this.closeCallbackFunction();
			} else {
				this.state = 'opened';
				if ( this.popupData.get('application') != 'default' ){
					if ( !pc3.Browser.IE ) {
						this.trigger.childElements().each(function(element){
							if ( element.name != 'flashvars' ) return;
							self.trigger.flashvars = element.value;
							element.value = element.value+'&displayType=maximized';
						});
					}
					this.content.appendChild(this.trigger);
				}
				
				if ( this.postUpdate ) this.update();
				this.postUpdate = false;
				this.doForAllPopups( function(){ this.openInitially(); });
			}
			if ( this.closeAfterOpen ){
				var closeAfterOpen = this.closeAfterOpen;
				this.closeAfterOpen = '';
				closeAfterOpen();
			}
			if ( this.openAfterClose ){
				var openAfterClose = this.openAfterClose;
				this.openAfterClose = '';
				openAfterClose();
			}
		}
	}

	this.positionPopup = function(position){
		var endLeft = (position.x - (this.openedPosition.width/2)).round();
		var left = endLeft + ((this.openedPosition.width - this.currentPosition.width)/2).round();

		var endTop = (position.y - (this.openedPosition.height/2)).round();
		var top = endTop + ((this.openedPosition.height - this.currentPosition.height)/2).round();

		var shadowOffsets = this.getShadowOffsets(this.currentPosition);
		
		var popupOffsetLeft = shadowOffsets.offsetLeft;
		var popupOffsetTop = shadowOffsets.offsetTop;

		var innerBoxDimension = {};

		innerBoxDimension['left'] = left - this.popup.outerDimension.offsetLeft - popupOffsetLeft;
		innerBoxDimension['top'] = top - this.popup.outerDimension.offsetTop - popupOffsetTop;
		innerBoxDimension['width'] = this.currentPosition.width + (left - innerBoxDimension.left) + this.popup.outerDimension.offsetRight + shadowOffsets.offsetRight;
		innerBoxDimension['height'] = this.currentPosition.height + (top - innerBoxDimension.top) + this.popup.outerDimension.offsetBottom + shadowOffsets.offsetBottom;

		var boxDimension = this.fitToBoundingBox(innerBoxDimension, this.boundingBox);
		
		

		this.outerBox.setStyle({
			left:boxDimension.left +'px',
			top:boxDimension.top +'px',
			width:boxDimension.width +'px',
			height:boxDimension.height +'px'
		});

		if ( boxDimension.offsetX != 0) popupOffsetLeft = popupOffsetLeft + boxDimension.offsetX;
		if ( boxDimension.offsetY != 0) popupOffsetTop = popupOffsetTop + boxDimension.offsetY;

		this.innerBox.setStyle({
			left:popupOffsetLeft +'px',
			top:popupOffsetTop +'px',
			width:innerBoxDimension.width +'px',
			height:innerBoxDimension.height +'px'
		});


		if ( (this.lastSize && this.lastSize.width != this.currentPosition.width) || (this.lastSize && this.lastSize.height != this.currentPosition.height) ){
			this.popup.setStyle({
				left:'0px',
				top:'0px',
				width:this.currentPosition.width +'px',
				height:this.currentPosition.height +'px'
			});
	
			this.contentBox.setStyle({
				left:this.popup.outerDimension.offsetLeft +'px',
				top:this.popup.outerDimension.offsetTop +'px',
				width:this.currentPosition.width +'px',
				height:this.currentPosition.height +'px'
			});
	
			this.content.setStyle({
				left:-(left - endLeft) +'px',
				top:-(top - endTop) +'px',
				width:this.openedPosition.width +'px',
				height:this.openedPosition.height +'px'
			});
				
			if ( this.eventLayer ){
				this.eventLayer.setStyle({
					left:shadowOffsets.offsetLeft +'px',
					top:shadowOffsets.offsetTop +'px',
					width:(this.currentPosition.width + shadowOffsets.offsetsX) +'px',
					height:(this.currentPosition.height + shadowOffsets.offsetsY) +'px'
				});
			}
			
			if ( this.shadow ){
				this.shadow.setZoomFactor(this.getShadowFactor(this.currentPosition));
				this.shadow.show();
			}
		}


		this.lastSize.width = this.currentPosition.width;
		this.lastSize.height = this.currentPosition.height;

	}

	this.getShadowFactor = function(position){
		if ( this.openedPosition.width < 1 || this.openedPosition.height < 1 ) return 0;
		var shadowFactor = position.width / this.openedPosition.width;
		if ( shadowFactor > position.height / this.openedPosition.height ) shadowFactor = position.height / this.openedPosition.height;
		return (shadowFactor * 100);
	}	
	
	this.getShadowOffsets = function(position){
		if ( this.shadowOffsets && (this.lastSize && this.lastSize.width == position.width) || (this.lastSize && this.lastSize.height == position.height) ) return this.shadowOffsets;
		if ( this.shadow ){
			this.shadow.setZoomFactor(this.getShadowFactor(position));
			this.shadowOffsets = this.shadow.getOuterDimension();
			['Left','Top','Right','Bottom'].each(function(location){ if ( self.shadowOffsets['offset'+location] < 0 ) self.shadowOffsets['offset'+location] = 0;});
		}
		return this.shadowOffsets;
	}
	
	this.addOpenPopup = function(popup){
		if ( !this.openPopups.size() && pc3.Browser.IE6 && this.selects ) {
			this.selects.each(function(select){
				if ( select.visible() ){
					self.hiddenSelects.push(select);
					select.style.visibility = "hidden";
				}
			});
		}
		this.openPopups.set(popup.id, popup);
	}

	this.removeOpenPopup = function(popup){
		this.openPopups.unset(popup.id);
		if ( !this.openPopups.size() && pc3.Browser.IE6 ) {
			this.hiddenSelects.each(function(select){ select.style.visibility = "visible"; });
			this.hiddenSelects = new Array();
		}
	}


	this.removePopup = function(popupId){
		this.popups.unset(popupId);
		if ( this.parent ) this.parent.removePopup(popupId);
	}

	this.closeOtherOpenPopups = function(popup){
		if ( popup.groupname == 'pc3DefaultPopupGroup' ) return;
		this.openPopups.each(function(currentPopup){ if ( popup.id != currentPopup.key && popup.groupname == currentPopup.value.groupname ) currentPopup.value.close(''); });		
	}

	this.removeChildPopups = function(){
		this.popups.each(function(currentPopup){
			if ( currentPopup.value.outerBox ) currentPopup.value.outerBox.remove();
			currentPopup.value.parent.removePopup(currentPopup.value.id);
			delete currentPopup;
		});
	}
	
	this.removeAjaxPopups = function(){
		this.ajaxRequest.resetAjaxContent();
		this.management.removeAjaxPopups(this.ajaxRequest.getAjaxContent());
	}

	this.doForAllOpenPopups = function(func){ this.openPopups.each(function(currentPopup){ func.bind(currentPopup.value)(); }); }

	this.doForAllPopups = function(func){ this.popups.each(function(currentPopup){ func.bind(currentPopup.value)(); }); }

	this.addPopup = function(popup){ this.popups.set(popup.id, popup); }

	this.isOpen = function(){ return (this.state == 'opened'); }
	
	this.init();
}



function pc3PopupSizeAnimation(name, startDimension, endDimension, direction, type, pathAnimationTransition, duration, onUpdate, onComplete){
	var self = this;
	this.name = name;
	this.direction = direction;
	
	this.onUpdateFunction = onUpdate;
	this.onCompleteFunction = onComplete;
	
	this.start = {
		factorWidth:startDimension,
		factorHeight:startDimension
	};
	
	this.end = {
		factorWidth:endDimension,
		factorHeight:endDimension
	};

	this.value = $H({
		factorWidth:startDimension,
		factorHeight:startDimension
	});
		
	this.onUpdate = function(effect){
		var factor = effect.value/10000;
		if ( effect.name == 'both' || effect.name == 'width' ) this.value.factorWidth = this.start.factorWidth + ((this.end.factorWidth - this.start.factorWidth) * factor);
 		if ( effect.name == 'both' || effect.name == 'height' ) this.value.factorHeight = this.start.factorHeight + ((this.end.factorHeight - this.start.factorHeight) * factor);
		this.onUpdateFunction(this);
	}

	this.extendEffect = function(name, start, end, duration, transition, onUpdate, onComplete, effect ){
		var nextEffect = new pc3Tween(name, start, end, duration, transition, onUpdate, onComplete);
		delete effect;
	};
	
	this.onComplete = function(effect){
		delete effect;
		this.value = this.end;
		this.onUpdateFunction(this);
		this.onCompleteFunction(this);
	};
	
	
	this.init = function(duration, type){
		var transition = '';
		switch( type ){
			case 'bubble':
				var effect = new pc3Tween('both', 0, 10000, (duration * 0.5).round(), (direction=='open'?'EaseOutBounce':'EaseOutBounce'), this.onUpdate.bind(self), this.onComplete.bind(self));
				break;

			case 'growBoth':
			case 'shrinkBoth':
				if ( pathAnimationTransition ){
					if ( pathAnimationTransition.startsWith('EaseIn') ) transition = 'EaseInQuad';
					else transition = 'EaseOutQuad';
				} else {
					transition = (direction=='open'?'EaseInQuad':'EaseOutQuad');
				}
				var nextEffect = this.extendEffect.bind(self, 'height', 0, 10000, (duration * 0.5).round(), transition, this.onUpdate.bind(self), this.onComplete.bind(self));
				var effect = new pc3Tween('width', 0, 10000, (duration * 0.5).round(), transition, this.onUpdate.bind(self), nextEffect);
				break;

			default:
				if ( pathAnimationTransition ){
					if ( pathAnimationTransition.startsWith('EaseIn') ) transition = 'EaseInQuad';
					else transition = 'EaseOutQuad';
				} else {
					transition = (direction=='open'?'EaseInQuad':'EaseOutQuad');
				}
				var effect = new pc3Tween('both', 0, 10000, (duration * 1).round(), transition, this.onUpdate.bind(self), this.onComplete.bind(self));
				break;		
		}

	}

	this.init(duration, type);
}

function pc3PopupPathAnimation(name, path, direction, duration, transition, onUpdate, onComplete){
	var self = this;
	this.name = name;
	this.path = path;
	this.onUpdateFunction = onUpdate;
	this.onCompleteFunction = onComplete;
	
	this.value = this.path.start;
	this.end = this.path.end;
	
	this.onUpdate = function(effect){
		this.value = this.getBezierPosition(effect.value, this.path);
		this.onUpdateFunction(this);
	}
	
	this.onComplete = function(effect){
		delete effect;
		this.value = this.end;
		this.onUpdateFunction(this);
		this.onCompleteFunction(this);
	};
	
	this.init = function(duration, transition, direction){
		var effect = new pc3Tween('position', 0.0, 1.0, duration, transition, this.onUpdate.bind(self), this.onComplete.bind(self));
	}

	this.getBezierPosition = function(time, path){
		return this.getIntermedianPosition(time, this.getIntermedianPosition(time, path.start, path.middle), this.getIntermedianPosition(time, path.middle, path.end));
	}
	
	this.getIntermedianPosition = function(time, start, end){
		return { x:start.x + (end.x-start.x)*time, y:start.y + (end.y-start.y)*time };
	}

	this.init(duration, transition, direction);
}

function pc3PopupAjaxRequest(originialId, newId, URL, ajaxContent, dimension, isFullscreen, sucessCallbackFunction){
	var self = this;
	this.url = URL;
	this.ajaxRequest = '';
	this.dimension = dimension;
	this.originialId = originialId;
	this.isFullscreen = isFullscreen;
	this.sucessCallbackFunction = sucessCallbackFunction;
	
	this.init = function(){
		ajaxContent.id = newId;
		var onErrorHTML = new String(window.pc3AjaxContentObjects[originialId].onErrorHTML);
		this.loadContent = new String(window.pc3AjaxContentObjects[originialId].onRequestHTML);
		
		this.ajaxRequest = new pc3AjaxContent('','', onErrorHTML, '', ajaxContent);
	}
	
	this.getAjaxContent = function(){
		return this.ajaxContent;
	}

	this.resetAjaxContent = function(){
		this.ajaxRequest.replaceDefaultContent('');
		this.ajaxRequest.defaultContent.id = this.originialId;
		if ( this.sucessCallbackFunction ) this.ajaxRequest.setSuccessCallbackFunction(null);
	}

	this.open = function(delay){
		if ( !this.ajaxRequest ) return;
		this.ajaxRequest.replaceDefaultContent(this.loadContent);

		if ( this.sucessCallbackFunction ) this.ajaxRequest.setSuccessCallbackFunction(this.sucessCallbackFunction);
		
		if ( this.isFullscreen ) var ajaxElement = this.ajaxRequest.defaultContent;
		else var ajaxElement = this.ajaxRequest.defaultContent.down();
		
		if ( this.dimension.width ) ajaxElement.setStyle({width:this.dimension.width+'px'});
		if ( this.dimension.height ) ajaxElement.setStyle({height:this.dimension.height+'px'});
		
		this.requestContent.bind(this).delay(delay);
	}

	this.requestContent = function(){
		this.ajaxRequest.requestContent(this.url);
	}

	this.setDimension = function(dimension){
		if ( dimension.width ) this.ajaxRequest.defaultContent.setStyle({width:dimension.width+'px'});
		if ( dimension.height ) this.ajaxRequest.defaultContent.setStyle({height:dimension.height+'px'});
	}
	
	this.init();
}

function pc3Shadow(element, offsets, images, cornerSize, color, opacity, blurRadius, animated, fade){
	var self = this;
	this.element = element;
	this.shadows = {};
	this.zoomFactor = 100;
	this.ready = false;
	this.showAfterInit = false;

	this.maxExpansion = 2000;
		
	this.init = function(){
		var parent = this.element.ancestors()[0];
		var useImages = false;
		if ( images.left && images.top && images.right && images.bottom ) useImages = true;
		if ( pc3.Browser.IE6 ) useImages = false;
		if ( pc3.Browser.IE7 && fade ) useImages = false;
		this.status = 'hidden';

		if ( useImages ){
			this.cornerSize = cornerSize;
			var dimension = this.cornerSize;
			this.loadShadowImages(images, parent, dimension, offsets, true);
		} else {
			this.ready = true;
			this.cornerSize = Math.max(offsets.left,offsets.top,offsets.right,offsets.bottom);
			if ( animated && pc3.Browser.IE6 ) blurRadius = Math.min(2, blurRadius);
			var corner = pc3Widget.blur(blurRadius);
			var dimension = (2*blurRadius)+1;
			opacity = opacity/100;
			this.generateShadows(useImages, parent, dimension, offsets, corner, opacity);
		}
	}
	
	
	this.loadShadowImages = function(images, parent, dimension, offsets, first){
		if ( first ){
			this.shadowImages = $H({});
			['left','top','right','bottom'].each(function(position){
				['element1','element2'].each(function(element){
					self.shadowImages.set(position+"_"+element, $(new Image()));
					self.shadowImages.get(position+"_"+element).src = images[position];
				});
			});
		}
		var isLoaded = true;
		this.shadowImages.each(function(image){
			if ( !image.value.width ) isLoaded = false;
		});
		
		if ( isLoaded ) this.generateShadows(true, parent, dimension, offsets, '', '');
		else this.loadShadowImages.bind(this, '', parent, dimension, offsets).delay(0.02);
	}


	this.generateShadows = function(useImages, parent, dimension, offsets, corner, opacity){
		['left','top','right','bottom'].each(function(position){
			var outerBox = '';
			var shadows = {};
			if ( offsets[position] > 0 ){
				outerBox = new Element('div').setStyle({position:'absolute',overflow:'hidden'}).hide();
				parent.appendChild(outerBox);
				var filler = '';
				if ( position == 'left' && offsets.top < 0 ) filler = new Element('div').setStyle({overflow:'hidden',left:'0px', width:offsets[position]+'px'});
				if ( position == 'top' && offsets.left < 0 ) filler = new Element('div').setStyle({overflow:'hidden',float:'left', top:'0px', height:offsets[position]+'px'});
				if ( position == 'right' && offsets.top < 0 ) filler = new Element('div').setStyle({overflow:'hidden',right:'0px', width:offsets[position]+'px'});
				if ( position == 'bottom' && offsets.left < 0 ) filler = new Element('div').setStyle({overflow:'hidden',float:'left', bottom:'0px', height:offsets[position]+'px'});
				if ( filler ) outerBox.appendChild(filler);
				
				['element1', 'element2'].each( function(element) {
					var columnRange = new Array();
					var rowRange = new Array();
					var expanedWidth = 0;
					var expanedHeight = 0;
					var width = 1;
					var height = 1;
					var addPixels = false;
					var box = new Element('div').setStyle({overflow:'hidden',position:'relative'});
					var shadow = new Element('div').setStyle({overflow:'hidden',position:'absolute'});
					if ( useImages ) var image = self.shadowImages.get(position+"_"+element);
					if ( offsets[position] < 1 ) return;
					switch( position ){
						case 'left':
							switch( element ){
								case 'element1':
									if ( useImages ){
										var top = -Math.max(0,offsets.top);
										shadow.setStyle({width:image.width+'px',height:image.height+'px',top:top+'px'});
										width = offsets[position];
										height = self.cornerSize;
									} else {
										width = offsets[position];
										height = Math.max(0, dimension - Math.max(0,offsets.top));
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										rowRange = $A($R(1+Math.max(0,offsets.top), dimension));
										expanedWidth = width - dimension;
										expanedHeight = 1;
										shadow.setStyle({width:width+'px',height:height+'px'});
									}
								break;
								case 'element2':
									if ( useImages ){
										var bottom = -Math.max(0,offsets.bottom);
										shadow.setStyle({width:image.width+'px',height:image.height+'px',bottom:bottom+'px'});
										width = offsets[position];
										height = image.height - self.cornerSize;
									} else {
										width = offsets[position];
										height = self.maxExpansion;
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										var startRange = Math.min((1 + Math.max(0,offsets.bottom)),dimension + 1);
										rowRange = $A($R(startRange, dimension + 1));
										rowRange.reverse();
										expanedWidth = width - dimension;
										expanedHeight = self.maxExpansion - (dimension - startRange);
										shadow.setStyle({width:width+'px',height:height+'px',bottom:'0px'});
									}
								break;
							}
						break;
						case 'top':
							switch( element ){
								case 'element1':
									if ( useImages ){
										shadow.setStyle({width:image.width+'px',height:image.height+'px'});
										width = self.cornerSize;
										height = offsets[position];
									} else {
										width = dimension;
										height = offsets[position];
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										rowRange = $A($R(1, Math.min((dimension + 1), height)));
										expanedWidth = 1;
										expanedHeight = self.cornerSize - dimension;
										shadow.setStyle({width:width+'px',height:height+'px'});
									}
								break;
								case 'element2':
									if ( useImages ){
										shadow.setStyle({width:image.width+'px',height:image.height+'px', right:'0px'});
										width = self.cornerSize;
										height = offsets[position];
									} else {
										width = self.maxExpansion;
										height = offsets[position];
										columnRange = $A($R(1, dimension+1));
										columnRange.reverse();
										rowRange = $A($R(1, Math.min((dimension + 1), height)));
										expanedWidth = self.maxExpansion - dimension;
										expanedHeight = height - dimension;
										shadow.setStyle({width:width+'px',height:height+'px', right:'0px'});
									}
								break;
							}
						break;
						case 'right':
							switch( element ){
								case 'element1':
									if ( useImages ){
										var top = -Math.max(0,offsets.top);
										shadow.setStyle({width:image.width+'px',height:image.height+'px',top:top+'px',right:'0px'});
										width = offsets[position];
										height = self.cornerSize;
									} else {
										width = offsets[position];
										height = Math.max(0, dimension - Math.max(0,offsets.top));
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										columnRange.reverse();
										rowRange = $A($R(1+Math.max(0,offsets.top), dimension));
										expanedWidth = width - dimension;
										expanedHeight = 1;
										shadow.setStyle({width:width+'px',height:height+'px'});
									}
								break;
								case 'element2':
									if ( useImages ){
										var bottom = -Math.max(0,offsets.bottom);
										shadow.setStyle({width:image.width+'px',height:image.height+'px',bottom:bottom+'px',right:'0px'});
										width = offsets[position];
										height = image.height - self.cornerSize;
									} else {
										width = offsets[position];
										height = self.maxExpansion;
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										columnRange.reverse();
										var startRange = Math.min((1 + Math.max(0,offsets.bottom)),dimension + 1);
										rowRange = $A($R(startRange, dimension + 1));
										rowRange.reverse();
										expanedWidth = width - dimension;
										expanedHeight = self.maxExpansion - (dimension - startRange);
										shadow.setStyle({width:width+'px',height:height+'px',bottom:'0px'});
									}
								break;
							}
						break;
						case 'bottom':
							switch( element ){
								case 'element1':
									if ( useImages ){
										shadow.setStyle({width:image.width+'px',height:image.height+'px',bottom:'0px'});
										width = self.cornerSize;
										height = offsets[position];
									} else {
										width = dimension;
										height = offsets[position];
										columnRange = $A($R(1, Math.min((dimension + 1), width)));
										rowRange = $A($R(1, Math.min((dimension + 1), height)));
										rowRange.reverse();
										expanedWidth = width - dimension;
										expanedHeight = height - dimension;
										shadow.setStyle({width:width+'px',height:height+'px', bottom:'0px'});
									}
								break;
								case 'element2':
									if ( useImages ){
										shadow.setStyle({width:image.width+'px',height:image.height+'px', right:'0px', bottom:'0px'});
										width = self.cornerSize;
										height = offsets[position];
									} else {
										width = self.maxExpansion;
										height = offsets[position];
										columnRange = $A($R(1, dimension+1));
										columnRange.reverse();
										rowRange = $A($R(1, Math.min((dimension + 1), height)));
										rowRange.reverse();
										expanedWidth = self.maxExpansion - dimension;
										expanedHeight = height - dimension;
										shadow.setStyle({width:width+'px',height:height+'px', bottom:'0px', right:'0px'});
									}
								break;
							}
						break;
			
					}
					box.setStyle({float:'left',width:width+'px',height:height+'px'});
					outerBox.appendChild(box);
					box.appendChild(shadow);
					shadows[element] = {element:box,width:width,height:height};
					
					if ( useImages ){
						shadow.appendChild(image);
					} else {
						rowRange.each(function(row){
							columnRange.each(function(column){
								var pixelWidth = 1;
								var pixelHeight = 1;
								var pixelOpacity = 0;
								
								if ( column <= dimension ){
									if ( row <= dimension ){
										pixelOpacity = corner[row][column];
									} else {
										pixelOpacity = corner[dimension][column];
										pixelHeight = expanedHeight;
									}
								} else {
									if ( row <= dimension ){
										pixelOpacity = corner[row][dimension];
										pixelWidth = expanedWidth;
									} else {
										pixelOpacity = corner[dimension][dimension];
										pixelWidth = expanedWidth;
										pixelHeight = expanedHeight;
									}
								}
								
								if ( column <= dimension + 1 && row <= dimension + 1 ){
									var pixel = new Element('div').setStyle({float:'left',overflow:'hidden',width:pixelWidth+'px',height:pixelHeight+'px',backgroundColor:color});
									pixel.setOpacity(pixelOpacity * opacity);
									shadow.appendChild(pixel);
								}
							});
						});
					}
				});
			}
			self.shadows[position] = {element:outerBox,style:{},offsets:{},offset:offsets[position],filler:filler,shadows:shadows};
		});
		this.ready = true;
		if ( this.showAfterInit ) this.show();
	}
		
		
	this.hide = function(){
		if ( this.status == 'hidden' ) return;
		['top', 'bottom', 'right', 'left'].each( function(position) { if ( self.shadows[position].element ) self.shadows[position].element.hide(); });
		this.status = 'hidden'
	}

	this.unhide = function(){
		if ( this.status == 'shown' ) return;
		['top', 'bottom', 'right', 'left'].each( function(position) { if ( self.shadows[position].element ) self.shadows[position].element.show(); });
		this.status = 'shown'
	}

	this.setZoomFactor = function(zoomFactor){
		if ( zoomFactor > 100 ) this.zoomFactor = 100;
		else this.zoomFactor = zoomFactor;
	}

	this.getOuterDimension = function(){
		var dimension = {};
		['Left','Top','Right','Bottom'].each(function(location){
			dimension['offset'+location] = (self.shadows[location.toLowerCase()]?((self.shadows[location.toLowerCase()].offset * self.zoomFactor)/100).round():0);
		});
		dimension.offsetsX = dimension.offsetLeft + dimension.offsetRight;
		dimension.offsetsY = dimension.offsetTop + dimension.offsetBottom;
		var outerDimension = pc3Widget.getOuterDimension(this.element, false);
		dimension.width = outerDimension.width + dimension.offsetsX;
		dimension.height = outerDimension.height + dimension.offsetsY;
		dimension.left = outerDimension.left - dimension.offsetLeft;
		dimension.top = outerDimension.top - dimension.offsetTop;
		return dimension;
	}
	
	
	this.show = function(){
		if ( !this.ready ){
			this.showAfterInit = true;
			return;
		}
		if ( this.cornerSize < 1 ) return;
		var outerDimension = this.getOuterDimension();
		var minSizeReached = true;
		if ( outerDimension.width  < (2 * this.cornerSize) ) minSizeReached = false;
		if ( outerDimension.height < (2 * this.cornerSize) ) minSizeReached = false;

		var popupDimension = pc3Widget.getOuterDimension(this.element, false);
		
		if ( !minSizeReached ){
			this.hide();
		} else {
			this.status = 'shown';
			['top', 'bottom', 'right', 'left'].each( function(position) {
				if ( self.shadows[position] ){
					self.shadows[position].style = {display:'none'};
					self.shadows[position].offsets = {x:0,y:0};
				}
			});
			
			

			var getStyle = function(left, top, width, height){ return {display:'block',left:left +'px',top:top +'px',width:width +'px',height:height +'px'}; };

			if ( outerDimension.offsetTop > 0 || outerDimension.offsetBottom > 0 ){
				var shadowWidth = popupDimension.width + Math.max(outerDimension.offsetLeft, 0) + Math.max(outerDimension.offsetRight, 0);

				if ( outerDimension.offsetTop > 0 ){
					var left = Math.min(popupDimension.left, (popupDimension.left - outerDimension.offsetLeft));
					var width = shadowWidth;
					var top = popupDimension.top - outerDimension.offsetTop;
					var height = outerDimension.offsetTop;
					this.shadows.top.style = getStyle(left, top, width, height);
					if ( this.shadows.top.filler ) this.shadows.top.filler.setStyle({width:Math.abs(this.shadows.left.offset)+'px'});
					width = width - (this.shadows.top.filler?Math.abs(this.shadows.left.offset):0) - this.shadows.top.shadows.element1.width + (this.shadows.right.offset<0?this.shadows.right.offset:0);
					this.shadows.top.shadows.element2.element.setStyle({width:width+'px'});
				}

				if ( outerDimension.offsetBottom > 0 ){
					var left = Math.min(popupDimension.left, (popupDimension.left - outerDimension.offsetLeft));
					var width = shadowWidth;
					var top = popupDimension.top + popupDimension.height;
					var height = outerDimension.offsetBottom;
					this.shadows.bottom.style = getStyle(left, top, width, height);
					if ( this.shadows.bottom.filler ) this.shadows.bottom.filler.setStyle({width:Math.abs(outerDimension.offsetLeft)+'px'});
					width = width - (this.shadows.bottom.filler?Math.abs(outerDimension.offsetLeft):0) - this.shadows.bottom.shadows.element1.width + (this.shadows.right.offset<0?this.shadows.right.offset:0);
					this.shadows.bottom.shadows.element2.element.setStyle({width:width+'px'});
				}
			}
			
			if ( outerDimension.offsetRight > 0 || outerDimension.offsetLeft > 0 ){

				if ( outerDimension.offsetLeft > 0 ){
					var left = Math.min(popupDimension.left, (popupDimension.left - outerDimension.offsetLeft));
					var width = outerDimension.offsetLeft;
					var top = popupDimension.top;
					var height = popupDimension.height;
					this.shadows.left.style = getStyle(left, top, width, height);
					if ( this.shadows.left.filler ) this.shadows.left.filler.setStyle({height:Math.abs(outerDimension.offsetTop)+'px'});
					height = height - (this.shadows.left.filler?Math.abs(outerDimension.offsetTop):0) - this.shadows.left.shadows.element1.height + (this.shadows.bottom.offset<0?this.shadows.bottom.offset:0);
					this.shadows.left.shadows.element2.element.setStyle({height:height+'px'});
				}

				if ( outerDimension.offsetRight > 0 ){
					var left = popupDimension.left + popupDimension.width;
					var width = outerDimension.offsetRight;
					var top = popupDimension.top;
					var height = popupDimension.height;
					this.shadows.right.style = getStyle(left, top, width, height);
					if ( this.shadows.right.filler ) this.shadows.right.filler.setStyle({height:Math.abs(outerDimension.offsetTop)+'px'});
					height = height - (this.shadows.right.filler?Math.abs(outerDimension.offsetTop):0) - this.shadows.right.shadows.element1.height + (this.shadows.bottom.offset<0?this.shadows.bottom.offset:0);
					this.shadows.right.shadows.element2.element.setStyle({height:height+'px'});
				}
			}
	
			['top', 'bottom', 'right', 'left'].each( function(position) {
				if ( self.shadows[position].element ){ self.shadows[position].element.setStyle(self.shadows[position].style); }
			});

		}
	}

	this.init();
}

/* tweener */
/*
 | Tweener
 | ------
 |   - this is a javascript port of the flash project Tweener, http://code.google.com/p/tweener/.  
 | 
 | author
 | ------
 |   - mike macmillan(mikejmacmillan@gmail.com)
*/
Tweener = function() {
	var tweenList = [];

	function getTime() {
		return new Date()*1;
	}

	function getTweenByIndex(idx) {
		if(tweenList.length==0) return;

		//** find the tween object at the given index
		for(var i=0;i<tweenList.length;i++)
			if(i==idx)
				return tweenList[i];
	}

	var tweener = {
		Running:false,
		FrameRate:1000/50, //** (50 frames per second by defaul by default...)

		Initialize:function() {
			if(!tweenList)
				tweenList = [];
		},

		AddTween:function(obj, properties, options) {
			options = options||{};

			//** if the object to tween is an id reference, get the object by its id
			if(typeof(obj) === 'string')
				obj = document.getElementById(obj);

			if(!obj || !properties || properties.length==0)
				return;

			//** create the tween object
			var tweenObj = {
				Id:new Date()*1,
				Scope:obj,
				ScopeClean: {},
				Properties:[],

				//** time scale properties
				TimeStart:0,
				TimeComplete:0,
				TimePaused:0,

				//** animation settings
				Transition:tweener.Transitions.EaseOutExpo,
				Delay:0,
				Duration:0,

				//** listeners
				OnStart:null,
				OnUpdate:null,
				OnComplete:null
			};

			//** verify the properties to tween are valid
			var propObj, start, complete;
			for(var prop in properties) {
				if(typeof(obj[prop]) === 'undefined')
					continue;

				//** determine our starting and ending values
				start = parseInt(obj[prop]);
				complete = parseInt(properties[prop]); 

				//** create the property object
				propObj = {
					Name:prop,
					Unit:"px",

					Start:start,
					Complete:complete,
					Change:complete-start,

					//** the current value being used to update the object, and the last value used...helps for eliminating unecessary updates do to the transition returning duplicate values
					Current:0,
					Last:0
				};

				//** add the valid property object to the list
				tweenObj.Properties.push(propObj);
			}

			//** iterate our options object, overwriting any tweenObj properties if necessary
			for(var prop in options) {
				if(typeof(tweenObj[prop]) !== 'undefined' && options[prop])
					tweenObj[prop] = options[prop];
			}


			//** add the tween to the animation loop 
			setTimeout(function() {
				//** timestamp it...
				tweenObj.TimeStart = getTime();
				tweenObj.TimeComplete = tweenObj.TimeStart+tweenObj.Duration;

				//** if there is a start listener, fire it
				if(tweenObj.OnStart)
					tweenObj.OnStart(tweenObj);
				
				//** add the tween to the list
				tweenList.push(tweenObj);
				
				//** start the animation loop
				tweener.Start();

			}, tweenObj.Delay);

			return tweenObj;
		},

		PauseTween:function(idx) {
			//** get the tween at the given index
			var tweenObj = getTweenByIndex(idx);

			if(!tweenObj || tweenObj.TimePaused>0) return;

			//** set the state of the tween to paused, and record the time which it was paused
			tweenObj.TimePaused = getTime();
		},

		PauseAllTweens:function() {
			if(!this.Running) return;

			//** pause each tween by index
			for(var i=0;i<tweenList.length;i++)
				this.PauseTween(i);
		},

		ResumeTween:function(idx) {
			//** get the tween at the given index
			var tweenObj = getTweenByIndex(idx);

			if(!tweenObj || tweenObj.TimePaused==0) return;

			//** set the state of the tween to resumed, and adjust the start/complete times accordingly
			var time = getTime();
			tweenObj.TimeStart += time - tweenObj.TimePaused;
			tweenObj.TimeComplete += time - tweenObj.TimePaused;
			tweenObj.TimePaused = 0;
		},

		ResumeAllTweens:function() {
			if(!this.Running) return;

			//** resume each tween by index
			for(var i=0;i<tweenList.length;i++)
				this.ResumeTween(i);
		},

		RemoveTween:function(idx) {
			//** get the tween at the given index
			var tweenObj = getTweenByIndex(idx);

			//** remove the tween if it exists
			if(tweenObj) tweenList.splice(idx, 1);
		},

		RemoveAllTweens:function() {
			//** pause each tween by index
			for(var i=0;i<tweenList.length;i++)
				this.RemoveTween(i);
		},

		Stop:function() {
			tweenList = null;
			this.Running = false;
		},

		Start:function() {
			//** if the tweening engine isn't running, start it
			if(!this.Running) {
				this.Initialize();
				this.Run();
			}
		},

		Run:function() {
			var tweenObj, runTime, duration, current, property;
	
			function updateProperties(obj, time, dur, transition) {
				var property, value;
	
				//** update the value of each transition-property for the object given with the value given
				for(var i=0;i<obj.Properties.length;i++) {
					property = obj.Properties[i];
	
					//** get the current value for this point the timescale either using the transition algorithm given, or the expected end value
					property.Current = transition?transition(time, property.Start, property.Change, dur):property.Complete;
	
					//** fire the update event before we set the propertys value
					if (obj.OnUpdate) {
						obj.OnUpdate(obj);
					}
	
					//** persist the last used value against the property.  certain transition algorithms might update more than once, 
					//** but not actually change the value of the property they're updating (ex: exponential algo when close to zero).  by
					//** persisting the last used value, objects who handle update can know if the value has changed by comparing it to the
					//** last used value...
					property.Last = property.Current;
	
					//** lastly, set the property's value
					obj.Scope[property.Name] = parseInt(property.Current) + property.Unit;
					obj.ScopeClean[property.Name] = property.Current;
				}
			}
			
			if(!tweenList || tweenList.length == 0) return;
	
			//** in case of any outside updates, every iteration, update the engine status
			this.Running = true;

			//** get the current time in ticks
			var now = getTime();
	
			//** iterate our tween objects and update them based on our current time
			for(var i=0;i<tweenList.length;i++) {
				tweenObj = tweenList[i];
	
				//** if the current animation is paused, skip it
				if(tweenObj.TimePaused>0) continue;
	
				//** get the animations state
				runTime = parseInt(now) - parseInt(tweenObj.TimeStart);
				duration = parseInt(tweenObj.TimeComplete) - parseInt(tweenObj.TimeStart);

	
				//** if the tween is complete, remove our tween object from the collection
				if(runTime >= duration) {
					//** update the tween with its end value
					updateProperties(tweenObj, runTime, duration);
	
					//** remove the tween from the list
					tweenList.splice(i, 1);
	
					//** finally, fire the complete event if implemented
					if(tweenObj.OnComplete)
						tweenObj.OnComplete(tweenObj);
				} else {
					//** update the animation with the current value, given the timeline, and the transition algorith
					updateProperties(tweenObj, runTime, duration, tweenObj.Transition);
				}
			}
	
			if(tweenList.length>0)
				//** if there are more tween objects, keep the animation loop running at the engines framerate
				setTimeout(function() { tweener.Run(); }, tweener.FrameRate);
			else
				//** otherwise, there is nothing left to animate, so flag the engine as not running
				tweener.Running = false;
		},



		Transitions: {
			EaseNone:function(t, b, c, d) {
				return c * t/d + b;
			},
		
		
			EaseInQuad:function(t, b, c, d) {
				return c * (t/=d) * t + b;
			},
		
			EaseOutQuad:function(t, b, c, d) {
				return -c * (t/=d) * (t-2) + b;
			},
		
			EaseInOutQuad:function(t, b, c, d) {
				if((t/=d/2) < 1) 
					return c/2 * t * t + b;
		
				return -c/2 * ((--t) * (t-2) - 1) + b;
			}, 
		
			EaseOutInQuad:function(t, b, c, d) {
				if(t < d/2)
					return Tweener.Transitions.EaseOutQuad(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInQuad((t*2)-2, b+c/2, c/2, d);
			}, 
		
		
			EaseInCubic:function(t, b, c, d) {
				return c * (t/=d) * t * t + b;
			},
		
			EaseOutCubic:function(t, b, c, d) {
				return c * ((t=t/d-1) * t * t + 1) + b;
			},
		
			EaseInOutCubic:function(t, b, c, d) {
				if((t/=d/2) < 1) 
					return c/2 * t * t * t + b;
		
				return c/2 * ((t-=2) * t * t + 2) + b;
			}, 
		
			EaseOutInCubic:function(t, b, c, d) {
				if(t < d/2)
					return Tweener.Transitions.EaseOutCubic(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInCubic((t*2)-d, b+c/2, c/2, d);
			}, 
		
		
			EaseInQuart:function(t, b, c, d) {
				return c * (t/=d) * t * t * t + b;
			},
		
			EaseOutQuart:function(t, b, c, d) {
				return -c * ((t=t/d-1) * t * t * t - 1) + b;
			},
		
			EaseInOutQuart:function(t, b, c, d) {
				if ((t/=d/2) < 1) 
					return c/2 * t * t * t * t + b;
		
				return -c/2 * ((t-=2) * t * t * t - 2) + b;
			}, 
		
			EaseOutInQuart:function(t, b, c, d) {
				if(t < d/2)
					return Tweener.Transitions.EaseOutQuart(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInQuart((t*2)-d, b+c/2, c/2, d);
			}, 
		
		
			EaseInQuint:function(t, b, c, d) {
				return c * (t/=d) * t * t * t * t + b;
			},
		
			EaseOutQuint:function(t, b, c, d) {
				return c * ((t=t/d-1) * t * t * t * t + 1) + b;
			},
		
			EaseInOutQuint:function(t, b, c, d) {
				if ((t/=d/2) < 1) 
					return c/2 * t * t * t * t * t + b;
		
				return c/2 * ((t-=2) * t * t * t * t + 2) + b;
			}, 
		
			EaseOutInQuint:function(t, b, c, d) {
				if(t < d/2)
					return Tweener.Transitions.EaseOutQuint(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInQuint((t*2)-d, b+c/2, c/2, d);
			}, 
		
		
			EaseInSine:function(t, b, c, d) {
				return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
			},
		
			EaseOutSine:function(t, b, c, d) {
				return -c * Math.sin(t/d * (Math.PI/2)) + b;
			},
		
			EaseInOutSine:function(t, b, c, d) {
				return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
			}, 
		
			EaseOutInSine:function(t, b, c, d) {
				if(t < d/2)
					return Tweener.Transitions.EaseOutSine(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInSine((t*2)-d, b+c/2, c/2, d);
			}, 
		
		
			EaseInExpo:function(t, b, c, d) {
				return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b - c * 0.001;
			},
		
			EaseOutExpo:function(t, b, c, d) {
				return(t==d) ? b+c : c * 1.001 *(-Math.pow(2, -10 * t/d) + 1) + b;
			},
		
			EaseInOutExpo:function(t, b, c, d) {
				if (t==0) return b;
				if (t==d) return b+c;
				if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b - c * 0.0005;
				return c/2 * 1.0005 * (-Math.pow(2, -10 * --t) + 2) + b;
			},
		
			EaseOutInExpo:function(t, b, c, d) {
				if (t < d/2) 
					return Tweener.Transitions.EaseOutExpo(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInExpo((t*2)-d, b+c/2, c/2, d);
			},
		
		
			EaseInCirc:function(t, b, c, d) {
				return -c * (Math.sqrt(1 - (t/=d) * t) - 1) + b;
			},
		
			EaseOutCirc:function(t, b, c, d) {
				return c * Math.sqrt(1 - (t=t/d-1) * t) + b;
			},
		
			EaseInOutCirc:function(t, b, c, d) {
				if ((t/=d/2) < 1) 
					return -c/2 * (Math.sqrt(1 - t * t) - 1) + b;
		
				return c/2 * (Math.sqrt(1 - (t-=2) * t) + 1) + b;
			},
		
			EaseOutInCirc:function(t, b, c, d) {
				if (t < d/2) 
					return Tweener.Transitions.EaseOutCirc(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInCirc((t*2)-d, b+c/2, c/2, d);
			},
		
		
			EaseInElastic:function(t, b, c, d, a, p) {
				if (t==0) return b;
				if ((t/=d)==1) return b + c;
				if(!p) p = d * .3;
		
				var s;
				if(!a || a < Math.abs(c)) {
					a = c;
					s = p/4;
				} else
					s = p / (2 * Math.PI) * Math.asin(c/a);
		
				return -(a * Math.pow(2,10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
			},
		
			EaseOutElastic:function(t, b, c, d, a, p) {
				if (t==0) return b;
				if ((t/=d)==1) return b + c;
				if(!p) p = d * .3;
		
				var s;
				if(!a || a < Math.abs(c)) {
					a = c;
					s = p/4;
				} else
					s = p / (2 * Math.PI) * Math.asin(c/a);
		
				return (a * Math.pow(2,-10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
			},
		
			EaseInOutElastic:function(t, b, c, d, a, p) {
				if (t==0) return b;
				if ((t/=d/2)==2) return b + c;
				if(!p) p = d * (.3 * 1.5);
		
				var s;
				if(!a || a < Math.abs(c)) {
					a = c;
					s = p/4;
				} else
					s = p / (2 * Math.PI) * Math.asin(c/a);
		
				if (t < 1) 
					return -.5 * (a * Math.pow(2,10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
		
				return a * Math.pow(2,-10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
			},
		
			EaseOutInElastic:function(t, b, c, d, a, p) {
				if (t < d/2) 
					return Tweener.Transitions.EaseOutElastic(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInElastic((t*2)-d, b+c/2, c/2, d);
			},
		
		
		
			EaseInBack:function(t, b, c, d, s) {
				if(!s) s = 1.70158;
		
				return c * (t/=d) * t * ((s+1) * t - s) + b;
			},
		
			EaseOutBack:function(t, b, c, d, s) {
				if(!s) s = 1.70158;
		
				return c * ((t=t/d-1) * t * ((s+1) * t + s) + 1) + b;
			},
		
			EaseInOutBack:function(t, b, c, d, s) {
				if(!s) s = 1.70158;
		
				if ((t/=d/2) < 1) 
					return c / 2 * (t * t * (((s*=(1.525)) + 1) * t - s)) + b;
		
				return c / 2 * ((t-=2) * t * (((s*=(1.525)) + 1) * t + s) + 2) + b;
			},
		
			EaseOutInBack:function(t, b, c, d, s) {
				if (t < d/2) 
					return Tweener.Transitions.EaseOutBack(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInBack((t*2)-d, b+c/2, c/2, d);
			},
		
		
			EaseInBounce:function(t, b, c, d) {
				return c - Tweener.Transitions.EaseOutBounce(d-t, 0, c, d) + b;
			},
		
			EaseOutBounce:function(t, b, c, d) {
				if ((t/=d) < (1/2.75))
					return c * (7.5625 * t * t) + b;
				else if (t < (2/2.75))
					return c * (7.5625 * (t-=(1.5/2.75)) * t + .75) + b;
				else if (t < (2.5/2.75))
					return c * (7.5625 * (t-=(2.25/2.75)) * t + .9375) + b;
				else
					return c * (7.5625 * (t-=(2.625/2.75)) * t + .984375) + b;
			},
		
			EaseInOutBounce:function(t, b, c, d) {
				if (t < d/2) 
					return Tweener.Transitions.EaseInBounce(t*2, 0, c, d) * .5 + b;
		
				return Tweener.Transitions.EaseOutBounce(t*2-d, 0, c, d) * .5 + c * .5 + b;
			},
		
			EaseOutInBounce:function(t, b, c, d) {
				if (t < d/2) 
					return Tweener.Transitions.EaseOutBounce(t*2, b, c/2, d);
		
				return Tweener.Transitions.EaseInBounce((t*2)-d, b+c/2, c/2, d);
			}
			
		}
	}

	return tweener;
}();

/* scriptaculous */
// script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Scriptaculous = {
  Version: '1.9.0',
  require: function(libraryName) {
    try{
      // inserting via DOM fails in Safari 2.0, so brute force approach
      document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
    } catch(e) {
      // for xhtml+xml served content, fall back to DOM methods
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = libraryName;
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) <
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js') });
    });
  }
};

Scriptaculous.load();

/* effects */
// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

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

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

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

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

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

Element.setContentZoom = function(element, percent) {
  element = $(element);
  element.setStyle({fontSize: (percent/100) + 'em'});
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  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) { }
};

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

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    spring: function(pos) {
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';

    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character),
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') ||
        Object.isFunction(element)) &&
       (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, options) {
    element = $(element);
    effect  = (effect || 'appear').toLowerCase();
    
    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, options || {}));
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

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

Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ?
      effect.options.queue : effect.options.queue.position;

    switch(position) {
      case 'front':
        // move unstarted effects after this effect
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }

    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);

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

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;

    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    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.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, 'afterUpdate');
        }
      };
    })();

    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        '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.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  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.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(),
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

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

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!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(Effect.Base, {
  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() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

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

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

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
  scrollOffsets = document.viewport.getScrollOffsets(),
  elementOffsets = $(element).cumulativeOffset();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

/* ------------- 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 options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, 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');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false,
    scaleX: false,
    scaleMode: 'box',
    scaleFrom: 100,
    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().show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

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

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

  var dims = element.getDimensions();
  var initialMoveX, initialMoveY;
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0;
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }

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

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

  var dims = element.getDimensions();
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }

  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  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(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });

    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        };
      }
    }
    this.start(options);
  },

  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 );
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

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

      var originalValue = this.element.getStyle(property);
      return {
        style: property.camelize(),
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      );
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] =
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');

Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }

  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]);
  });

  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) {
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);
