/*  Prototype JavaScript framework, version 1.6.0
 *  (c) 2005-2007 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.6.0',

  Browser: {
    IE:     !!(window.attachEvent && !window.opera),
    Opera:  !!window.opera,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div').__proto__ &&
      document.createElement('div').__proto__ !==
        document.createElement('form').__proto__
  },

  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;

if (Prototype.Browser.WebKit)
  Prototype.BrowserFeatures.XPath = false;

/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    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) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  }
};

Class.Methods = {
  addMethods: function(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);

    if (!Object.keys({ toString: true }).length)
      properties.push("toString", "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().first() == "$super") {
        var method = value, value = Object.extend((function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method), {
          valueOf:  function() { return method },
          toString: function() { return method.toString() }
        });
      }
      this.prototype[property] = value;
    }

    return this;
  }
};

var Abstract = { };

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (object === undefined) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : object.toString();
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (value !== undefined)
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({ }, object);
  },

  isElement: function(object) {
    return object && object.nodeType == 1;
  },

  isArray: function(object) {
    return object && object.constructor === Array;
  },

  isHash: function(object) {
    return object instanceof Hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && arguments[0] === undefined) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

Function.prototype.defer = Function.prototype.delay.curry(0.01);

Date.prototype.toJSON = function() {
  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"';
};

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;
  }
};

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();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
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, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = count === undefined ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var 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;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
});

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (Object.isFunction(replacement)) return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

with (String.prototype.escapeHTML) div.appendChild(text);

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return '';

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3];
      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : 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);
    }.bind(this));
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    iterator = iterator.bind(context);
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator(value, index));
    });
    return results;
  },

  include: function(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;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = fillWith === undefined ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == undefined || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (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));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  function $A(iterable) {
    if (!iterable) return [];
    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
        iterable.toArray) return iterable.toArray();
    var length = iterable.length, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  }
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (value !== undefined) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
});

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(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;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(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;
};

Array.prototype.toArray = Array.prototype.clone;

function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {
  if (function() {
    var i = 0, Test = function(value) { this.key = value };
    Test.prototype.key = 'foo';
    for (var property in new Test('bar')) i++;
    return i > 1;
  }()) {
    function each(iterator) {
      var cache = [];
      for (var key in this._object) {
        var value = this._object[key];
        if (cache.include(key)) continue;
        cache.push(key);
        var pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    }
  } else {
    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 toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

    _each: each,

    set: function(key, value) {
      return this._object[key] = value;
    },

    get: function(key) {
      return this._object[key];
    },

    unset: function(key) {
      var value = this._object[key];
      delete this._object[key];
      return value;
    },

    toObject: function() {
      return Object.clone(this._object);
    },

    keys: function() {
      return this.pluck('key');
    },

    values: function() {
      return this.pluck('value');
    },

    index: function(value) {
      var match = this.detect(function(pair) {
        return pair.value === value;
      });
      return match && match.key;
    },

    merge: function(object) {
      return this.clone().update(object);
    },

    update: function(object) {
      return new Hash(object).inject(this, function(result, pair) {
        result.set(pair.key, pair.value);
        return result;
      });
    },

    toQueryString: function() {
      return this.map(function(pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values))
            return values.map(toQueryPair.curry(key)).join('&');
        }
        return toQueryPair(key, values);
      }).join('&');
    },

    inspect: function() {
      return '#<Hash:{' + this.map(function(pair) {
        return pair.map(Object.inspect).join(': ');
      }).join(', ') + '}>';
    },

    toJSON: function() {
      return Object.toJSON(this.toObject());
    },

    clone: function() {
      return new Hash(this);
    }
  }
})());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
};

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (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.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
  }
});

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.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    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';
    }

    // user-defined headers
    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);
  },

  getStatus: function() {
    try {
      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 && 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') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } catch (e) { return null }
  },

  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  = xml === undefined ? 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);
    } 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')))
        return null;
    try {
      return this.transport.responseText.evalJSON(options.sanitizeJSON);
    } 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 = options || { };
    var onComplete = options.onComplete;
    options.onComplete = (function(response, param) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, param);
    }).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);
    }

    if (this.success()) {
      if (this.onComplete) this.onComplete.bind(this).defer();
    }
  }
});

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 (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  // DOM level 2 ECMAScript Language Binding
  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() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && 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));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
}).call(window);

Element.cache = { };

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  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, t, range;

    for (position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      t = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        t.insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      range = element.ownerDocument.createRange();
      t.initializeRange(element, range);
      t.insert(element, range.createContextualFragment(content.stripScripts()));

      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();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $A($(element).getElementsByTagName('*')).each(Element.extend);
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (Object.isString(selector))
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = element.ancestors();
    return expression ? Selector.findElement(ancestors, expression, index) :
      ancestors[index || 0];
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    var descendants = element.descendants();
    return expression ? Selector.findElement(descendants, expression, index) :
      descendants[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return expression ? Selector.findElement(previousSiblings, expression, index) :
      previousSiblings[index || 0];
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return expression ? Selector.findElement(nextSiblings, expression, index) :
      nextSiblings[index || 0];
  },

  select: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute('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] = value === undefined ? true : value;

    for (var attr in attributes) {
      var 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().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!element.hasClassName(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(className) ?
      'removeClassName' : 'addClassName'](className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (element.sourceIndex && !Prototype.Browser.Opera) {
      var e = element.sourceIndex, a = ancestor.sourceIndex,
       nextAncestor = ancestor.nextSibling;
      if (!nextAncestor) {
        do { ancestor = ancestor.parentNode; }
        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
      }
      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
    }

    while (element = element.parentNode)
      if (element == ancestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = element.cumulativeOffset();
    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) {
      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') ?
          (elementStyle.styleFloat === undefined ? '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;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = Element.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;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
    return element;
  },

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return $(element);

    return $(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return Element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = $(source);
    var p = source.viewportOffset();

    // find coordinate system to use
    element = $(element);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if (options.setLeft)   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;
  }
};

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};


if (!document.createRange || Prototype.Browser.Opera) {
  Element.Methods.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 t = Element._insertionTranslations, content, position, pos, tagName;

    for (position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      pos      = t[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        pos.insert(element, content);
        continue;
      }

      content = Object.toHTML(content);
      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      if (t.tags[tagName]) {
        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
        if (position == 'top' || position == 'after') fragments.reverse();
        fragments.each(pos.insert.curry(element));
      }
      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());

      content.evalScripts.bind(content).defer();
    }

    return element;
  };
}

if (Prototype.Browser.Opera) {
  Element.Methods._getStyle = Element.Methods.getStyle;
  Element.Methods.getStyle = function(element, style) {
    switch(style) {
      case 'left':
      case 'top':
      case 'right':
      case 'bottom':
        if (Element._getStyle(element, 'position') == 'static') return null;
      default: return Element._getStyle(element, style);
    }
  };
  Element.Methods._readAttribute = Element.Methods.readAttribute;
  Element.Methods.readAttribute = function(element, attribute) {
    if (attribute == 'title') return element.title;
    return Element._readAttribute(element, attribute);
  };
}

else if (Prototype.Browser.IE) {
  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        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;
      }
    );
  });

  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 = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          var attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _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.clone(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').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      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);
}

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 == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
  // KHTML/WebKit only.
  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  Element.Methods.update = function(element, content) {
    element = $(element);

    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 in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

if (document.createElement('div').outerHTML) {
  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();
      var 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) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  div.innerHTML = t[0] + html + t[1];
  t[2].times(function() { div = div.firstChild });
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: {
    adjacency: 'beforeBegin',
    insert: function(element, node) {
      element.parentNode.insertBefore(node, element);
    },
    initializeRange: function(element, range) {
      range.setStartBefore(element);
    }
  },
  top: {
    adjacency: 'afterBegin',
    insert: function(element, node) {
      element.insertBefore(node, element.firstChild);
    },
    initializeRange: function(element, range) {
      range.selectNodeContents(element);
      range.collapse(true);
    }
  },
  bottom: {
    adjacency: 'beforeEnd',
    insert: function(element, node) {
      element.appendChild(node);
    }
  },
  after: {
    adjacency: 'afterEnd',
    insert: function(element, node) {
      element.parentNode.insertBefore(node, element.nextSibling);
    },
    initializeRange: function(element, range) {
      range.setStartAfter(element);
    }
  },
  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() {
  this.bottom.initializeRange = this.top.initializeRange;
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

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);

if (!Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement('div').__proto__) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div').__proto__;
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || element._extendedByPrototype ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
      tagName = element.tagName, property, value;

    // extend methods for specific tags
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

Element.hasAttribute = function(element, attribute) {
  if (element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element, attribute);
};

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)
    });
  }

  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];

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName).__proto__;
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, 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() {
    var dimensions = { };
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = self['inner' + D] ||
       (document.documentElement['client' + D] || document.body['client' + D]);
    });
    return dimensions;
  },

  getWidth: function() {
    return this.getDimensions().width;
  },

  getHeight: function() {
    return this.getDimensions().height;
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();
    this.compileMatcher();
  },

  compileMatcher: function() {
    // Selectors with namespaced attributes can't use the XPath version
    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
      return this.compileXPathMatcher();

    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      this.matcher = Selector._cache[e];
      return;
    }

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
    	      new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
    return this.matcher(root);
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the Selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString: function() {
    return this.expression;
  },

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: "[@#{1}]",
    attr: function(m) {
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
      'checked':     "[@checked]",
      'disabled':    "[@disabled]",
      'enabled':     "[not(@disabled)]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, m, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
    className:    'n = h.className(n, r, "#{1}", c); c = false;',
    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

    // selectors follow
    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
    attrPresence: /^\[([\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },

  // for Selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return Selector.operators[matches[2]](nodeValue, matches[3]);
    }
  },

  handlers: {
    // UTILITY FUNCTIONS
    // joins two collections
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    // marks an array of nodes for counting
    mark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._counted = true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._counted = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._counted = true;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._counted) {
          n._counted = true;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
	      if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      tagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() == tagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._counted) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._counted) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled) results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv.startsWith(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
  },

  matchElements: function(elements, expression) {
    var matches = new Selector(expression).findElements(), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._counted) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    var exprs = expressions.join(','), expressions = [];
    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (options.hash === undefined) options.hash = true;
    var key, value, submitted = false, submit = options.submit;

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            // a key is already present; construct an array of values
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(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'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().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'].include(element.type)))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.blur();
    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 = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element, value);
      default:
        return Form.Element.Serializers.textarea(element, value);
    }
  },

  inputSelector: function(element, value) {
    if (value === undefined) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

  textarea: function(element, value) {
    if (value === undefined) return element.value;
    else element.value = value;
  },

  select: function(element, index) {
    if (index === undefined)
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, value, single = !Object.isArray(index);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        value = this.optionValue(opt);
        if (single) {
          if (value == index) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = index.include(value);
      }
    }
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = 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);
  }
});
if (!window.Event) var Event = { };

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      var node = Event.extend(event).target;
      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      return element.match(expression) ? element : element.up(expression);
    },

    pointer: function(event) {
      return {
        x: event.pageX || (event.clientX +
          (document.documentElement.scrollLeft || (document.body ? document.body.scrollLeft : 0))),
        y: event.pageY || (event.clientY +
          (document.documentElement.scrollTop || (document.body ? document.body.scrollTop : 0)))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._eventID) return element._eventID;
    arguments.callee.id = arguments.callee.id || 1;
    return element._eventID = ++arguments.callee.id;
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event)
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }

  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      if (document.createEvent) {
        var event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        var event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return event;
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize()
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer, fired = false;

  function fireContentLoadedEvent() {
    if (fired) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    fired = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- 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');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = 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);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  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);

/*--------------------------------------------------------------------------*/

Element.addMethods();
/**
*	@fileoverview Extensions of Prototype Framework classes.
*	Requires Prototype Javascript Framework 1.4
*
*	Copyright (c) 2006 Yoox Group (http://www.yoox.com)
*	Created: 15/02/2006
*	@author Giuseppe Scaligina
*	@version 1.0.0
*/


/**
*	Extending Prototype Form.Element adding new methods "getText" and "getRadioValue"
*	@extends Form.Element
*/
Form.XYElement = {
	// getText take element id as parameter
	getText: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter)
      return (parameter.length==3)?parameter[2]:parameter[1];
	},

	// getRadioValue take element name as parameter
	getRadioValue: function(elementName) {
		var element;
		var elements = new Array();
		var method = 'radio';

		var elems = $A(document.getElementsByTagName('input'));
		elems.each(function(elem){
			if (elem.type==method && elem.name==elementName) {
				element = elem;
				elements.push(element);
			}
		});
		if (!element) return;

		var parameter = Form.Element.Serializers[method](elements);
		if (parameter)
      		return parameter[1];
	}
};
Object.extend(Form.Element, Form.XYElement);


/**
*	Extending Prototype Form.Element.Serializers overriding "selectOne" method and adding new method "radio"
*	@extends Form.Element.Serializers
*/
Form.Element.XYSerializers = {
	/*
		selectOne: function(element) {
	    var value = '', opt, index = element.selectedIndex, text = '';
	    if (index >= 0) {
	      opt = element.options[index];
	      value = opt.value;
	      if (!value && !('value' in opt))
	        value = opt.text;
	      text = opt.text;
	    }
	    return [element.name, value, text];
	  },
	*/
	radio: function(elements) {
		var name = '';
		var value = '';
		elements.each(function(elem){
			if (elem.checked) {
				value=elem.value;
				name=elem.name;
				throw $break;
			}
		});
		return [name, value];
  }
};
Object.extend(Form.Element.Serializers, Form.Element.XYSerializers);

/**
*	Utility function used to retrieve text value of form select element
*	Usage:
*		<select name="myselect" id="myselect" onchange="alert($O('myselect'))">
*			<option value="1">primo</option>
*			<option value="2">secondo</option>
*		</select>
*/
var $O = Form.Element.getText;


/**
*	Utility function used to retrieve value of form radio group
*	Usage:
*	<form id="myform">
*		<input name="myradio" id="myradio1" value="a">Value A<br>
*		<input name="myradio" id="myradio2" value="b">Value B<br>
*		<input name="myradio" id="myradio3" value="c">Value C<br>
*	</form>
*	<a href="javascript:showRadioValue()">
*	<script language="javascript">function showRadioValue(){alert($FR('myradio'));}</script>
*/
var $FR = Form.Element.getRadioValue;


String.prototype.parseHtmlJSON = function () {
    try {
        return eval('(' + this.replace(/[\n\r\t\f]/gi,'').replace(/&quot;/gi,'"').replace(/\s/gi,' ') + ')');
    } catch (e) {
        return false;
    }
};


Function.prototype.log = function() {}

/**
 * Redefine base Prototype Class function to handle automatic function tracing
 */


/**
 * Check that element passed as first param to the original function exists
 */
Function.prototype.preCheck = function() { 
  var __method = this;
  return function() {
  	var args = $A(arguments);
	if (!$(args[0])) return;

    return __method.apply(this, args);
  }
};

/**
 * Adding element pre check to Event.observe function
 */
Event.observe = Event.observe.preCheck();

/**
 * Adding element pre check to Element.setStyle function
 */
Element.setStyle = Element.setStyle.preCheck();

Object.extend(Element, {
	/**
	 * Set the class of an element removing all others
	 * @param element the element
	 * @param className the class name to set for the element
	 */
	resetToClassName : function(element, className) {
		if (!(element = $(element))) return;
		Element.classNames(element).each(function(cl){
			if (cl!=className) Element.removeClassName(element, cl)
		});
		if (!Element.hasClassName(element, className))
			Element.addClassName(element, className);
	},

	/**
	 * Remove all closures for an element and its descendant
	 * @param element The element to clean
	 * @param noself true if cleaning is only for the descendant
	 */
	dispose: function(element, noself){
		if (!element) return;
		try {
			var elemsLen = element.childNodes.length;
			for (var i = 0; i < elemsLen; i++) {
				Element.dispose(element.childNodes[i]);
			};
			if (!noself){
		  		if (element.onclick) element.onclick = null;
		  		if (element.onmouseover) element.onmouseover = null;
		  		if (element.onmouseout) element.onmouseout = null;
		  		if (element.onmousemove) element.onmousemove = null;
		  		if (element.onload) element.onload = null;
		  		Element.remove(element);
			}
		} catch(e){
			Logger.error('Element -> dispose() - '+$H(e).inspect());
		}
	},

	/**
	 * Overrides Prototype method. Retrieves the dimensions of an element.
	 * @param element The element
	 * @return an hash object with "width" and "height" properties
	 */
	getDimensions: function(element) {
		Logger.info('Element -> getDimensions()');
	    element = $(element);
	    if (Element.getStyle(element, 'display') != 'none' &&
	    	Element.getStyle(element, 'display') != null) // Added for Safari
	      return {width: element.offsetWidth, height: element.offsetHeight};

	    // All *Width and *Height properties give 0 on elements with display none,
	    // so enable the element temporarily
	    var els = element.style;
	    var originalVisibility = els.visibility;
	    var originalPosition = els.position;
	    var originalTop = els.top;
	    var originalLeft = els.left;
	    els.visibility = 'hidden';
	    els.position = 'absolute';
	    els.top = '1';
	    els.left = '1';
	    // bug: YOOX correction from '' to 'block'
	    els.display = 'block';
	    Logger.warn('Element -> getDimensions - '+element.offsetWidth);
	    var originalWidth = element.clientWidth;
	    var originalHeight = element.clientHeight;
	    els.display = 'none';
	    els.top = originalTop;
	    els.left = originalLeft;
	    els.position = originalPosition;
	    els.visibility = originalVisibility;
	    return {width: originalWidth, height: originalHeight};
	}
});

/**
 * Overrides Prototype method. Returns a collection of elements by a classname (added elementType)
 * @param className the class for the filter
 * @param parentElement search only in the descendants of this element
 * @param elementType search only among the elements of this type
 */
document.getElementsByClassName = function(className, parentElement, elementType) {
  var children = ($(parentElement) || document.body).getElementsByTagName((elementType || '*'));
  return $A(children).inject([], function(elements, child) {
    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      elements.push(child);
    return elements;
  });
};

/**
 * Returns a collection of elements of type "DIV" filtering by classname
 */
document.getDivByClassName = function(className, parentElement) {
  return document.getElementsByClassName(className, parentElement, 'div');
};

document.getImagesByClassName = function(className, parentElement) {
  return document.getElementsByClassName(className, parentElement, 'img');
};



/**
 * Calculates window dimensions
 */
Object.extend(document, {
	getDims: function(){
		var x=0,y=0;
		if (window.innerHeight) {// all except Explorer
			x = window.innerWidth;
			y = window.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) {
			// Explorer 6 Strict Mode
			x = document.documentElement.clientWidth;
			y = document.documentElement.clientHeight;
		} else if (document.body) {// other Explorers
			x = document.body.clientWidth;
			y = document.body.clientHeight;
		}
		// Adding y scroll dimensions
		if (document.body.scrollTop > document.documentElement.scrollTop) {
			y += document.body.scrollTop;
		} else {
			y += document.documentElement.scrollTop;
		}
		return {width: x, height: y};
	}
});



/**
*	@fileoverview Yoox LITBox Library.
*	Requires Prototype Javascript Framework 1.4
*
*	Copyright (c) 2006 Yoox Group (http://www.yoox.com)
*	Created: 11/07/2006
*	@author Giuseppe Scaligina
*	@version 2.0.0
*/ 
LITBox = Class.create('LITBox');
LITBox.prototype = {
    initialize: function(mes) {
        this.mes = mes;
        this.options = Object.extend({
            type: 'window',
            draggable: true,
            overlay: true,
            opacity: 0,
            left: false,
            top: false,
            parentElem: false,
            flashvars: [],
            enableHeader: true,
            moveToMouse: false,
            enableClose: false,
            ghost: true
        }, arguments[1] || {});
        this.setup();
    },

    setup: function(force) {
        if (!this.options.manualstart || force) {
            this.getWindow();
        }

        switch (this.options.type) {
            case 'window': this.getPageByAjax(this.mes, this.options.ajaxOptions);
                break;
            case 'alert': this.d4.innerHTML = this.mes;
                break;
            case 'node': if (this.mes && this.d4) this.d4.appendChild(this.mes);
                break;
            case 'flash': this.createFlash();
        }
        if (!this.options.manualstart) {
            this.display();
        }
    },

    getWindow: function() {
        if (this.options.overlay == true) {
            this.d = document.createElement('div');
            Element.addClassName(this.d, 'LB_overlay');
            Element.setStyle(this.d, { display: 'block' });
            Element.setStyle(this.d, { height: document.body.scrollHeight + 'px' });
            document.body.insertBefore(this.d, document.body.firstChild);
            Event.observe(this.d, 'click', this.remove.bindAsEventListener(this));
        };

        // Setting main div
        this.d2 = document.createElement('div');
        if (this.options.id)
            this.d2.id = this.options.id;
        Element.addClassName(this.d2, 'LB_window');
        if (this.options.className) {
            $A(this.options.className.split(' ')).each(function(className) {
                Element.addClassName(this.d2, className);
            } .bind(this));
        }
        if (this.options.width)
            this.d2.style.width = parseInt(this.options.width) + 'px';
        if (this.options.height)
            this.d2.style.height = parseInt(this.options.height) + 'px';
        if ($(this.options.parentElem))
            $(this.options.parentElem).appendChild(this.d2);
        else {
            //document.body.appendChild(this.d2);
            document.body.insertBefore(this.d2, document.body.firstChild);
        }

        var topLeft = document.createElement('div');
        Element.addClassName(topLeft, 'top-left');
        this.d2.appendChild(topLeft);

        var topRight = document.createElement('div');
        Element.addClassName(topRight, 'top-right');
        this.d2.appendChild(topRight);

        var inside = document.createElement('div');
        Element.addClassName(inside, 'inside');
        this.d2.inside = inside;
        this.d2.appendChild(inside);

        var notopgap = document.createElement('p');
        Element.addClassName(notopgap, 'notopgap');
        inside.appendChild(notopgap);

        // Adding Header
        if (this.options.enableHeader) {
            this.d3 = document.createElement('div');
            notopgap.appendChild(this.d3);
            if (typeof (Draggable) != 'undefined') {
                Event.observe(this.d3, 'mouseover', this.getDraggable.bindAsEventListener(this));
                Event.observe(this.d3, 'mouseout', this.dropDraggable.bindAsEventListener(this));
            }
            if (this.options.enableClose)
                Element.addClassName(this.d3, 'LB_closeAjaxWindow');
            this.close = document.createElement('a');
            this.close.href = 'javascript:Prototype.emptyFunction()';
            this.d3.appendChild(this.close);
            Event.observe(this.close, 'click', this.remove.bindAsEventListener(this));
        }

        this.d4 = document.createElement('div');
        Element.addClassName(this.d4, 'LB_content');
        inside.appendChild(this.d4);

        var nobottomgap = document.createElement('p');
        Element.addClassName(nobottomgap, 'nobottomgap');
        inside.appendChild(nobottomgap);

        if (this.options.enableHeader) {
            var bottomLeft = document.createElement('div');
            Element.addClassName(bottomLeft, 'bottom-left');
            this.d2.appendChild(bottomLeft);

            var bottomRight = document.createElement('div');
            Element.addClassName(bottomRight, 'bottom-right');
            this.d2.appendChild(bottomRight);
        }

        Event.observe(window, 'unload', function() {
            if (this.timeoutId) clearTimeout(this.timeoutId);
            this.remove();
        } .bind(this));
    },
    /*
    createFlash: function() {
    var so = new SWFObject(this.options.flashPath+this.mes, this.options.flashId, this.options.flashWidth , this.options.flashHeight, '7');
    this.options.flashvars.each(function(flashvar) {
    Logger.debug('LITBox -> createFlash() - '+flashvar.name+'='+flashvar.value);
    so.addVariable(flashvar.name, flashvar.value);
    });
    so.write(this.d4);
    delete so;
    },
    */
    createFlash: function() {
        var flashAttribs = {
            src: this.options.flashPath + this.mes,
            width: this.options.width-40,
            height: this.options.height-40,
            id: this.d4
        };

        var flashVariables = this.options;
        if (Object.isArray(flashVariables)) {
            flashVariables.flashvars.each(function(flashVar) {
                flashVariables[flashVar.name] = flashVar.value;
            });
        } else {
            for (prop in flashVariables.flashvars) {
                if (prop != 'toJSONString') {
                    flashVariables[prop] = flashVariables.flashvars[prop];
                }
            }
        }

        XYooxJF.createFlashObj15(flashAttribs, flashVariables);
    },

    replace: function(options) {
        if (options)
            Object.extend(this.options, options);
        if (this.d) Element.setStyle(this.d, { display: 'block' });
        this.display();
    },

    replaceFlash: function(options) {
        this.replace(options);
        this.createFlash();
    },

    replaceNode: function(node, options) {
        this.replace(options);
        if (node) this.d4.appendChild(node);
    },

    display: function() {
        this.hideSelects('hide');
        this.locate();
        if (this.d2) Element.setStyle(this.d2, { display: 'block' });
    },

    remove: function() {
        this.hideSelects('show');

        if (this.options.onClose)
            this.options.onClose.apply();

        if (this.options.conservative) {
            Element.dispose(this.d4, true);
            if (this.d2) Element.setStyle(this.d2, { display: 'none' });
            if (this.d) Element.setStyle(this.d, { display: 'none' });
        } else {
            if (this.d) Element.dispose(this.d);
            Element.dispose(this.d2);
        }

        return false;
    },

    // In IE, select elements hover on top of the lightbox
    hideSelects: function(visibility) {
        var selects = $A(document.getElementsByTagName('select')) || [];
        selects.each(function(selectElem) {
            if (visibility == 'hide') {
                $(selectElem).setStyle({
                    visibility: 'hidden'
                });
                //Element.hide(selectElem);
            } else {
                $(selectElem).setStyle({
                    visibility: 'visible'
                });
                //Element.show(selectElem);
            }
        });
    },

    activateTip: function(activator, options) {
        this.activator = activator;
        this.tooltipOptions = {
            showMethod: 'mouseover',
            hideMethod: 'mouseout',
            delay: 300
        };
        if (options) Object.extend(this.tooltipOptions, options);
        Event.observe(this.activator, this.tooltipOptions.showMethod, this.showTip.bindAsEventListener(this));
        Event.observe(this.activator, this.tooltipOptions.hideMethod, this.hideTip.bindAsEventListener(this));
    },

    showTip: function(e) {
        if (!this.d2) this.setup(true);

        /*Event.observe(this.activator, 'mousemove', function(e){
        this.options.pointer = {pointerX: Event.pointerX(e), pointerY: Event.pointerY(e)};
        }.bindAsEventListener(this));
        */
        this.options.pointer = { pointerX: Event.pointerX(e), pointerY: Event.pointerY(e) };

        if (this.options.followMouse)
            Event.observe(this.activator, 'mousemove', this.locate.bindAsEventListener(this));



        this.timeoutId = setTimeout(function() {
            if (this.tooltipOptions.onActivate)
                this.tooltipOptions.onActivate.apply();
            else {
                this.locate();
                if (this.d2) Element.setStyle(this.d2, { display: 'block' });
            }
        } .bindAsEventListener(this), this.tooltipOptions.delay);

    },

    hideTip: function() {
        clearTimeout(this.timeoutId);
        if (this.d2) Element.setStyle(this.d2, { display: 'none' });
        if (this.options.followMouse)
            Event.stopObserving(this.activator, 'mousemove', this.locate.bindAsEventListener(this));
        Event.stopObserving(this.activator, 'mousemove', function(e) {
            this.options.pointer = { pointerX: Event.pointerX(e), pointerY: Event.pointerY(e) };
        } .bindAsEventListener(this));
    },

    fillContentByAjax: function(serviceElementId, serviceObjectId, options) {

        var ajaxOptions = {
            method: 'GET',
            onComplete: function(response) {
                this.locate();
                if (this.d2) Element.setStyle(this.d2, { display: 'block' });
                this.ajaxUpdate(response.responseText)
            } .bind(this)
        };
        try {
            Object.extend(ajaxOptions, options);
            new Ajax.Request(this.serviceUrl, ajaxOptions);
        } catch (e) {
            Logger.error('yooxlitbox -> fillContentByAjax() - ' + $H(e).inspect(), e);
        }
    },

    ajaxUpdate: function(response) {

        this.json = response.evalJSON();
        if (this.tooltipOptions.onAjaxUpdate) {
            var divElm = this.tooltipOptions.onAjaxUpdate.apply();
            if (divElm) {
                try {
                    Element.dispose(this.d4, true);
                } catch (e) {
                    Logger.error('LITBox -> ajaxUpdate([jsonElement]) - ' + $H(e).inspect());
                    this.d4.innerHTML = '';
                }
                this.d4.appendChild(divElm);
            }
        }
    },

    getPageByAjax: function(url, options) {
        var ajaxOptions = {
            method: 'GET',
            parameters: '',
            timeout: 5000,
            onTimeoutFailure: Ajax.ajaxFailure,
            onFailure: Ajax.ajaxFailure,
            onSuccess: this.display.bind(this)
        };
        Object.extend(ajaxOptions, options);
        var ajax = new Ajax.Updater({ success: this.d4 }, url, ajaxOptions);
    },


    locate: function(event) {
        var pageDim = document.getDims();
        var winHeight = pageDim.height;
        var winWidth = pageDim.width;

        var tipDim = Element.getDimensions(this.d2);
        var tipWidth = tipDim.width;
        var tipHeight = tipDim.height;

        var left = 0, top = 0;
        if (this.options.moveToMouse || this.options.followMouse) {
            if (event) {
                left = Event.pointerX(event);
                top = Event.pointerY(event);
            } else if (this.options.pointer) {
                left = this.options.pointer.pointerX;
                top = this.options.pointer.pointerY;
            }

            // Make sure the Tooltip doesn't go off the page.
            if ((left + parseInt(tipWidth)) > winWidth) {
                left = left - parseInt(tipWidth) * 1.1;
            }
            if ((top + parseInt(tipHeight)) > winHeight) {
                top = top - parseInt(tipHeight) + 10;
                if (top < 0) top = 1;
            }
        } else if (this.options.left && this.options.top) {
            left = this.options.left;
            top = this.options.top;
        } else {
            left = (winWidth - tipWidth) / 2;
            top = (winHeight - tipHeight) / 2;
        };

        /*Logger.debug('LITBox -> locate() - winWidth:'+winWidth);
        Logger.debug('LITBox -> locate() - winHeight:'+winHeight);
        Logger.debug('LITBox -> locate() - tooltipWidth:'+tipWidth);
        Logger.debug('LITBox -> locate() - tooltipHeight:'+tipHeight);
        Logger.debug('LITBox -> locate() - top:'+top);
        Logger.debug('LITBox -> locate() - left:'+left);*/

        if (this.d2) Element.setStyle(this.d2, { top: (top + 7) + 'px' });
        if (this.d2) Element.setStyle(this.d2, { left: (left + 10) + 'px' });
    },

    getDraggable: function() {
        if (this.options.draggable) {
            if (!this.drag || (this.drag && !this.drag.dragging))
                this.drag = new Draggable(this.d2, { starteffect: false, endeffect: false });
        }
    },

    dropDraggable: function() {
        if (this.options.draggable) {
            if (this.drag && !this.drag.dragging) {
                this.drag.destroy();
            }
        }
    },

    update: function(html) {
        this.d4.innerHTML = html;
    }

};




// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 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.8.1',
  require: function(libraryName) {
    // inserting via DOM fails in Safari 2.0, so brute force approach
    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
  },
  REQUIRED_PROTOTYPE: '1.6.0',
  load: function() {
    function convertVersionString(versionString){
      var r = versionString.split('.');
      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
    }
 
    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);
    /*
    $A(document.getElementsByTagName("script")).findAll( function(s) {
      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
    }).each( function(s) {
      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
      var 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();

// script.aculo.us builder.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

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

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();

    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;

    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];

    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);

    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1])
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        }

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return $(element);
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e);
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) {
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope

    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);

    tags.each( function(tag){
      scope[tag] = function() {
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));
      };
    });
  }
};
// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 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) + 0.5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
    },
    pulse: function(pos, pulses) { 
      pulses = pulses || 5; 
      return (
        ((pos % (1/pulses)) * pulses).round() == 0 ? 
              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
        );
    },
    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) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || { });
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

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) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    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;
    
    eval('this.render = function(pos){ '+
      'if (this.state=="idle"){this.state="running";'+
      codeForEvent(this.options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(this.options,'afterSetup')+
      '};if (this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(this.options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(this.options,'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(),
    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1] > max ? max : 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] || { };
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(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);

// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs 
//
// 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/

if (!Control) var Control = { };

// options:
//  axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
//  onChange(value)
//  onSlide(value)
Control.Slider = Class.create({
initialize: function(handle, track, options) {
    var slider = this;
    
    if (Object.isArray(handle)) {
      this.handles = handle.collect( function(e) { return $(e) });
    } else {
      this.handles = [$(handle)];
    }
    
    this.track   = $(track);
    this.options = options || { };

    this.axis      = this.options.axis || 'horizontal';
    this.increment = this.options.increment || 1;
    this.step      = parseInt(this.options.step || '1');
    this.range     = this.options.range || $R(0,1);
    
    this.value     = 0; // assure backwards compat
    this.values    = this.handles.map( function() { return 0 });
    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
    this.options.startSpan = $(this.options.startSpan || null);
    this.options.endSpan   = $(this.options.endSpan || null);

    this.restricted = this.options.restricted || false;

    this.maximum   = this.options.maximum || this.range.end;
    this.minimum   = this.options.minimum || this.range.start;

    // Will be used to align the handle onto the track, if necessary
    this.alignX = parseInt(this.options.alignX || '0');
    this.alignY = parseInt(this.options.alignY || '0');
    
    this.trackLength = this.maximumOffset() - this.minimumOffset();

    this.handleLength = this.isVertical() ? 
      (this.handles[0].offsetHeight != 0 ? 
        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : 
      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : 
        this.handles[0].style.width.replace(/px$/,""));

    this.active   = false;
    this.dragging = false;
    this.disabled = false;

    if (this.options.disabled) this.setDisabled();

    // Allowed values array
    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
    if (this.allowedValues) {
      this.minimum = this.allowedValues.min();
      this.maximum = this.allowedValues.max();
    }

    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
    this.eventMouseMove = this.update.bindAsEventListener(this);

    // Initialize handles in reverse (make sure first handle is active)
    this.handles.each( function(h,i) {
      i = slider.handles.length-1-i;
      slider.setValue(parseFloat(
        (Object.isArray(slider.options.sliderValue) ? 
          slider.options.sliderValue[i] : slider.options.sliderValue) || 
         slider.range.start), i);
      h.makePositioned().observe("mousedown", slider.eventMouseDown);
    });
    
    this.track.observe("mousedown", this.eventMouseDown);
    document.observe("mouseup", this.eventMouseUp);
    document.observe("mousemove", this.eventMouseMove);
    
    this.initialized = true;
  },
  dispose: function() {
    var slider = this;    
    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
    Event.stopObserving(document, "mouseup", this.eventMouseUp);
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
    this.handles.each( function(h) {
      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
    });
  },
  setDisabled: function(){
    this.disabled = true;
  },
  setEnabled: function(){
    this.disabled = false;
  },  
  getNearestValue: function(value){
    if (this.allowedValues){
      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
      if (value <= this.allowedValues.min()) return(this.allowedValues.min());
      
      var offset = Math.abs(this.allowedValues[0] - value);
      var newValue = this.allowedValues[0];
      this.allowedValues.each( function(v) {
        var currentOffset = Math.abs(v - value);
        if (currentOffset <= offset){
          newValue = v;
          offset = currentOffset;
        } 
      });
      return newValue;
    }
    if (value > this.range.end) return this.range.end;
    if (value < this.range.start) return this.range.start;
    return value;
  },
  setValue: function(sliderValue, handleIdx){
    if (!this.active) {
      this.activeHandleIdx = handleIdx || 0;
      this.activeHandle    = this.handles[this.activeHandleIdx];
      this.updateStyles();
    }
    handleIdx = handleIdx || this.activeHandleIdx || 0;
    if (this.initialized && this.restricted) {
      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
        sliderValue = this.values[handleIdx-1];
      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
        sliderValue = this.values[handleIdx+1];
    }
    sliderValue = this.getNearestValue(sliderValue);
    this.values[handleIdx] = sliderValue;
    this.value = this.values[0]; // assure backwards compat
    
    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
      this.translateToPx(sliderValue);
    
    this.drawSpans();
    if (!this.dragging || !this.event) this.updateFinished();
  },
  setValueBy: function(delta, handleIdx) {
    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
      handleIdx || this.activeHandleIdx || 0);
  },
  translateToPx: function(value) {
    return Math.round(
      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
      (value - this.range.start)) + "px";
  },
  translateToValue: function(offset) {
    return ((offset/(this.trackLength-this.handleLength) * 
      (this.range.end-this.range.start)) + this.range.start);
  },
  getRange: function(range) {
    var v = this.values.sortBy(Prototype.K); 
    range = range || 0;
    return $R(v[range],v[range+1]);
  },
  minimumOffset: function(){
    return(this.isVertical() ? this.alignY : this.alignX);
  },
  maximumOffset: function(){
    return(this.isVertical() ? 
      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
        this.track.style.height.replace(/px$/,"")) - this.alignY : 
      (this.track.offsetWidth != 0 ? this.track.offsetWidth : 
        this.track.style.width.replace(/px$/,"")) - this.alignX);
  },  
  isVertical:  function(){
    return (this.axis == 'vertical');
  },
  drawSpans: function() {
    var slider = this;
    if (this.spans)
      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
    if (this.options.startSpan)
      this.setSpan(this.options.startSpan,
        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
    if (this.options.endSpan)
      this.setSpan(this.options.endSpan, 
        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
  },
  setSpan: function(span, range) {
    if (this.isVertical()) {
      span.style.top = this.translateToPx(range.start);
      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
    } else {
      span.style.left = this.translateToPx(range.start);
      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
    }
  },
  updateStyles: function() {
    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
    Element.addClassName(this.activeHandle, 'selected');
  },
  startDrag: function(event) {
    if (Event.isLeftClick(event)) {
      if (!this.disabled){
        this.active = true;
        
        var handle = Event.element(event);
        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
        var track = handle;
        if (track==this.track) {
          var offsets  = Position.cumulativeOffset(this.track); 
          this.event = event;
          this.setValue(this.translateToValue( 
           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
          ));
          var offsets  = Position.cumulativeOffset(this.activeHandle);
          this.offsetX = (pointer[0] - offsets[0]);
          this.offsetY = (pointer[1] - offsets[1]);
        } else {
          // find the handle (prevents issues with Safari)
          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
            handle = handle.parentNode;
            
          if (this.handles.indexOf(handle)!=-1) {
            this.activeHandle    = handle;
            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
            this.updateStyles();
            
            var offsets  = Position.cumulativeOffset(this.activeHandle);
            this.offsetX = (pointer[0] - offsets[0]);
            this.offsetY = (pointer[1] - offsets[1]);
          }
        }
      }
      Event.stop(event);
    }
  },
  update: function(event) {
   if (this.active) {
      if (!this.dragging) this.dragging = true;
      this.draw(event);
      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
      Event.stop(event);
   }
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.track);
    pointer[0] -= this.offsetX + offsets[0];
    pointer[1] -= this.offsetY + offsets[1];
    this.event = event;
    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
    if (this.initialized && this.options.onSlide)
      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  },
  endDrag: function(event) {
    if (this.active && this.dragging) {
      this.finishDrag(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.dragging = false;
  },  
  finishDrag: function(event, success) {
    this.active = false;
    this.dragging = false;
    this.updateFinished();
  },
  updateFinished: function() {
    if (this.initialized && this.options.onChange) 
      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
    this.event = null;
  }
});

// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
// 
// 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/

// Autocompleter.Base handles all the autocompletion functionality 
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least, 
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method 
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most 
// useful when one of the tokens is \n (a newline), as it 
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { }
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element)
    this.element     = element; 
    this.update      = $(update);  
    this.hasFocus    = false; 
    this.changed     = false; 
    this.active      = false; 
    this.index       = 0;     
    this.entryCount  = 0;
    this.mouseInside = false;
    this.oldElementValue = this.element.innerHTML;
	this.elementsNo = 0;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || { };

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow || 
      function(element, update){ 
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false,
			setWidth: false,
			setLeft: false,
			setTop: false
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide || 
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string') 
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;
    
    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);
	
	Event.observe(this.update, "mouseover", this.onMouseOverIEFIX.bindAsEventListener(this));
	Event.observe(this.update, "mouseout", this.onMouseOutIEFIX.bindAsEventListener(this));
    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix && 
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update, 
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },
  
  onMouseOverIEFIX: function(){
    this.mouseInside = true;
  },

  onMouseOutIEFIX: function(){
    this.mouseInside = false;
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
     else 
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer = 
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
	if (this.hasFocus){
		this.onBlur();
		return;
	}
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex) 
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },
  
  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },
  
  onBlur: function(event) {
    // needed to make click events working
    if(!this.mouseInside){
        setTimeout(this.hide.bind(this), 250);
        this.hasFocus = false;
        this.active = false;     
    }
  }, 
  
  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ? 
          Element.addClassName(this.getEntry(i),"selected") : 
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) { 
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },
  
  markPrevious: function() {
    if(this.index > 0) this.index--
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },
  
  markNext: function() {
    if(this.index < this.entryCount-1) this.index++
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },
  
  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },
  
  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },
  
  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    
    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.innerHTML.substr(0, bounds[0]);
      var whitespace = this.element.innerHTML.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.innerHTML = newValue + value + this.element.innerHTML.substr(bounds[1]);
    } else {
      this.element.innerHTML = value;
    }
    this.oldElementValue = this.element.innerHTML;
    this.element.focus();
    
    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount = 
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else { 
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;
      
      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;   
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.innerHTML;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.innerHTML.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.innerHTML;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();
    
    var entry = encodeURIComponent(this.options.paramName) + '=' + 
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams) 
      this.options.parameters += '&' + this.options.defaultParams;
    
    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the 
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector' 
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
}

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML;
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw 'Server returned an invalid collection representation.';
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer, 
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element); 
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});

/*!	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/

var swfobject = function() {
	
	var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",
		ON_READY_STATE_CHANGE = "onreadystatechange",
		
		win = window,
		doc = document,
		nav = navigator,
		
		plugin = false,
		domLoadFnArr = [main],
		regObjArr = [],
		objIdArr = [],
		listenersArr = [],
		storedAltContent,
		storedAltContentId,
		storedCallbackFn,
		storedCallbackObj,
		isDomLoaded = false,
		isExpressInstallActive = false,
		dynamicStylesheet,
		dynamicStylesheetMedia,
		autoHideShow = true,
	
	/* Centralized function for browser feature detection
		- User agent string detection is only used when no good alternative is possible
		- Is executed directly for optimal performance
	*/	
	ua = function() {
		var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
			u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			windows = p ? /win/.test(p) : /win/.test(u),
			mac = p ? /mac/.test(p) : /mac/.test(u),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
			playerVersion = [0,0,0],
			d = null;
		if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
			d = nav.plugins[SHOCKWAVE_FLASH].description;
			if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
				plugin = true;
				ie = false; // cascaded feature detection for Internet Explorer
				d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
				playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
				playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			try {
				var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
				if (a) { // a will return null when ActiveX is disabled
					d = a.GetVariable("$version");
					if (d) {
						ie = true; // cascaded feature detection for Internet Explorer
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
			}
			catch(e) {}
		}
		return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
	}(),
	
	/* Cross-browser onDomLoad
		- Will fire an event as soon as the DOM of a web page is loaded
		- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
		- Regular onload serves as fallback
	*/ 
	onDomLoad = function() {
		if (!ua.w3) { return; }
		if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
			callDomLoadFunctions();
		}
		if (!isDomLoaded) {
			if (typeof doc.addEventListener != UNDEF) {
				doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
			}		
			if (ua.ie && ua.win) {
				doc.attachEvent(ON_READY_STATE_CHANGE, function() {
					if (doc.readyState == "complete") {
						doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
						callDomLoadFunctions();
					}
				});
				if (win == top) { // if not inside an iframe
					(function(){
						if (isDomLoaded) { return; }
						try {
							doc.documentElement.doScroll("left");
						}
						catch(e) {
							setTimeout(arguments.callee, 0);
							return;
						}
						callDomLoadFunctions();
					})();
				}
			}
			if (ua.wk) {
				(function(){
					if (isDomLoaded) { return; }
					if (!/loaded|complete/.test(doc.readyState)) {
						setTimeout(arguments.callee, 0);
						return;
					}
					callDomLoadFunctions();
				})();
			}
			addLoadEvent(callDomLoadFunctions);
		}
	}();
	
	function callDomLoadFunctions() {
		if (isDomLoaded) { return; }
		try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
			var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
			t.parentNode.removeChild(t);
		}
		catch (e) { return; }
		isDomLoaded = true;
		var dl = domLoadFnArr.length;
		for (var i = 0; i < dl; i++) {
			domLoadFnArr[i]();
		}
	}
	
	function addDomLoadEvent(fn) {
		if (isDomLoaded) {
			fn();
		}
		else { 
			domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
		}
	}
	
	/* Cross-browser onload
		- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
		- Will fire an event as soon as a web page including all of its assets are loaded 
	 */
	function addLoadEvent(fn) {
		if (typeof win.addEventListener != UNDEF) {
			win.addEventListener("load", fn, false);
		}
		else if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("load", fn, false);
		}
		else if (typeof win.attachEvent != UNDEF) {
			addListener(win, "onload", fn);
		}
		else if (typeof win.onload == "function") {
			var fnOld = win.onload;
			win.onload = function() {
				fnOld();
				fn();
			};
		}
		else {
			win.onload = fn;
		}
	}
	
	/* Main function
		- Will preferably execute onDomLoad, otherwise onload (as a fallback)
	*/
	function main() { 
		if (plugin) {
			testPlayerVersion();
		}
		else {
			matchVersions();
		}
	}
	
	/* Detect the Flash Player version for non-Internet Explorer browsers
		- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
		  a. Both release and build numbers can be detected
		  b. Avoid wrong descriptions by corrupt installers provided by Adobe
		  c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
		- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
	*/
	function testPlayerVersion() {
		var b = doc.getElementsByTagName("body")[0];
		var o = createElement(OBJECT);
		o.setAttribute("type", FLASH_MIME_TYPE);
		var t = b.appendChild(o);
		if (t) {
			var counter = 0;
			(function(){
				if (typeof t.GetVariable != UNDEF) {
					var d = t.GetVariable("$version");
					if (d) {
						d = d.split(" ")[1].split(",");
						ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				else if (counter < 10) {
					counter++;
					setTimeout(arguments.callee, 10);
					return;
				}
				b.removeChild(o);
				t = null;
				matchVersions();
			})();
		}
		else {
			matchVersions();
		}
	}
	
	/* Perform Flash Player and SWF version matching; static publishing only
	*/
	function matchVersions() {
		var rl = regObjArr.length;
		if (rl > 0) {
			for (var i = 0; i < rl; i++) { // for each registered object element
				var id = regObjArr[i].id;
				var cb = regObjArr[i].callbackFn;
				var cbObj = {success:false, id:id};
				if (ua.pv[0] > 0) {
					var obj = getElementById(id);
					if (obj) {
						if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
							setVisibility(id, true);
							if (cb) {
								cbObj.success = true;
								cbObj.ref = getObjectById(id);
								cb(cbObj);
							}
						}
						else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
							var att = {};
							att.data = regObjArr[i].expressInstall;
							att.width = obj.getAttribute("width") || "0";
							att.height = obj.getAttribute("height") || "0";
							if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
							if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
							// parse HTML object param element's name-value pairs
							var par = {};
							var p = obj.getElementsByTagName("param");
							var pl = p.length;
							for (var j = 0; j < pl; j++) {
								if (p[j].getAttribute("name").toLowerCase() != "movie") {
									par[p[j].getAttribute("name")] = p[j].getAttribute("value");
								}
							}
							showExpressInstall(att, par, id, cb);
						}
						else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
							displayAltContent(obj);
							if (cb) { cb(cbObj); }
						}
					}
				}
				else {	// if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
					setVisibility(id, true);
					if (cb) {
						var o = getObjectById(id); // test whether there is an HTML object element or not
						if (o && typeof o.SetVariable != UNDEF) { 
							cbObj.success = true;
							cbObj.ref = o;
						}
						cb(cbObj);
					}
				}
			}
		}
	}
	
	function getObjectById(objectIdStr) {
		var r = null;
		var o = getElementById(objectIdStr);
		if (o && o.nodeName == "OBJECT") {
			if (typeof o.SetVariable != UNDEF) {
				r = o;
			}
			else {
				var n = o.getElementsByTagName(OBJECT)[0];
				if (n) {
					r = n;
				}
			}
		}
		return r;
	}
	
	/* Requirements for Adobe Express Install
		- only one instance can be active at a time
		- fp 6.0.65 or higher
		- Win/Mac OS only
		- no Webkit engines older than version 312
	*/
	function canExpressInstall() {
		return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
	}
	
	/* Show the Adobe Express Install dialog
		- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
	*/
	function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
		isExpressInstallActive = true;
		storedCallbackFn = callbackFn || null;
		storedCallbackObj = {success:false, id:replaceElemIdStr};
		var obj = getElementById(replaceElemIdStr);
		if (obj) {
			if (obj.nodeName == "OBJECT") { // static publishing
				storedAltContent = abstractAltContent(obj);
				storedAltContentId = null;
			}
			else { // dynamic publishing
				storedAltContent = obj;
				storedAltContentId = replaceElemIdStr;
			}
			att.id = EXPRESS_INSTALL_ID;
			if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
			if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
			doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
			var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
			if (typeof par.flashvars != UNDEF) {
				par.flashvars += "&" + fv;
			}
			else {
				par.flashvars = fv;
			}
			// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
			// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			if (ua.ie && ua.win && obj.readyState != 4) {
				var newObj = createElement("div");
				replaceElemIdStr += "SWFObjectNew";
				newObj.setAttribute("id", replaceElemIdStr);
				obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						obj.parentNode.removeChild(obj);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			createSWF(att, par, replaceElemIdStr);
		}
	}
	
	/* Functions to abstract and display alternative content
	*/
	function displayAltContent(obj) {
		if (ua.ie && ua.win && obj.readyState != 4) {
			// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
			// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
			var el = createElement("div");
			obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
			el.parentNode.replaceChild(abstractAltContent(obj), el);
			obj.style.display = "none";
			(function(){
				if (obj.readyState == 4) {
					obj.parentNode.removeChild(obj);
				}
				else {
					setTimeout(arguments.callee, 10);
				}
			})();
		}
		else {
			obj.parentNode.replaceChild(abstractAltContent(obj), obj);
		}
	} 

	function abstractAltContent(obj) {
		var ac = createElement("div");
		if (ua.win && ua.ie) {
			ac.innerHTML = obj.innerHTML;
		}
		else {
			var nestedObj = obj.getElementsByTagName(OBJECT)[0];
			if (nestedObj) {
				var c = nestedObj.childNodes;
				if (c) {
					var cl = c.length;
					for (var i = 0; i < cl; i++) {
						if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
							ac.appendChild(c[i].cloneNode(true));
						}
					}
				}
			}
		}
		return ac;
	}
	
	/* Cross-browser dynamic SWF creation
	*/
	function createSWF(attObj, parObj, id) {
		var r, el = getElementById(id);
		if (ua.wk && ua.wk < 312) { return r; }
		if (el) {
			if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
				attObj.id = id;
			}
			if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
				var att = "";
				for (var i in attObj) {
					if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
						if (i.toLowerCase() == "data") {
							parObj.movie = attObj[i];
						}
						else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							att += ' class="' + attObj[i] + '"';
						}
						else if (i.toLowerCase() != "classid") {
							att += ' ' + i + '="' + attObj[i] + '"';
						}
					}
				}
				var par = "";
				for (var j in parObj) {
					if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
						par += '<param name="' + j + '" value="' + parObj[j] + '" />';
					}
				}
				el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
				objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
				r = getElementById(attObj.id);	
			}
			else { // well-behaving browsers
				var o = createElement(OBJECT);
				o.setAttribute("type", FLASH_MIME_TYPE);
				for (var m in attObj) {
					if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
						if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							o.setAttribute("class", attObj[m]);
						}
						else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
							o.setAttribute(m, attObj[m]);
						}
					}
				}
				for (var n in parObj) {
					if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
						createObjParam(o, n, parObj[n]);
					}
				}
				el.parentNode.replaceChild(o, el);
				r = o;
			}
		}
		return r;
	}
	
	function createObjParam(el, pName, pValue) {
		var p = createElement("param");
		p.setAttribute("name", pName);	
		p.setAttribute("value", pValue);
		el.appendChild(p);
	}
	
	/* Cross-browser SWF removal
		- Especially needed to safely and completely remove a SWF in Internet Explorer
	*/
	function removeSWF(id) {
		var obj = getElementById(id);
		if (obj && obj.nodeName == "OBJECT") {
			if (ua.ie && ua.win) {
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						removeObjectInIE(id);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			else {
				obj.parentNode.removeChild(obj);
			}
		}
	}
	
	function removeObjectInIE(id) {
		var obj = getElementById(id);
		if (obj) {
			for (var i in obj) {
				if (typeof obj[i] == "function") {
					obj[i] = null;
				}
			}
			obj.parentNode.removeChild(obj);
		}
	}
	
	/* Functions to optimize JavaScript compression
	*/
	function getElementById(id) {
		var el = null;
		try {
			el = doc.getElementById(id);
		}
		catch (e) {}
		return el;
	}
	
	function createElement(el) {
		return doc.createElement(el);
	}
	
	/* Updated attachEvent function for Internet Explorer
		- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
	*/	
	function addListener(target, eventType, fn) {
		target.attachEvent(eventType, fn);
		listenersArr[listenersArr.length] = [target, eventType, fn];
	}
	
	/* Flash Player and SWF content version matching
	*/
	function hasPlayerVersion(rv) {
		var pv = ua.pv, v = rv.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
		v[2] = parseInt(v[2], 10) || 0;
		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
	}
	
	/* Cross-browser dynamic CSS creation
		- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
	*/	
	function createCSS(sel, decl, media, newStyle) {
		if (ua.ie && ua.mac) { return; }
		var h = doc.getElementsByTagName("head")[0];
		if (!h) { return; } // to also support badly authored HTML pages that lack a head element
		var m = (media && typeof media == "string") ? media : "screen";
		if (newStyle) {
			dynamicStylesheet = null;
			dynamicStylesheetMedia = null;
		}
		if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
			// create dynamic stylesheet + get a global reference to it
			var s = createElement("style");
			s.setAttribute("type", "text/css");
			s.setAttribute("media", m);
			dynamicStylesheet = h.appendChild(s);
			if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
				dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
			}
			dynamicStylesheetMedia = m;
		}
		// add style rule
		if (ua.ie && ua.win) {
			if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
				dynamicStylesheet.addRule(sel, decl);
			}
		}
		else {
			if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
				dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
			}
		}
	}
	
	function setVisibility(id, isVisible) {
	    if(navigator.userAgent.indexOf('iPad') == -1)
	    {
		    if (!autoHideShow) { return; }
		    var v = isVisible ? "visible" : "hidden";
		    if (isDomLoaded && getElementById(id)) {
			    getElementById(id).style.visibility = v;
		    }
		    else {
			    createCSS("#" + id, "visibility:" + v);
		    }
        }
        else
        {
            getElementById(id).style.visibility = "hidden";
        }
	}

	/* Filter to avoid XSS attacks
	*/
	function urlEncodeIfNecessary(s) {
		var regex = /[\\\"<>\.;]/;
		var hasBadChars = regex.exec(s) != null;
		return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
	}
	
	/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
	*/
	var cleanup = function() {
		if (ua.ie && ua.win) {
			window.attachEvent("onunload", function() {
				// remove listeners to avoid memory leaks
				var ll = listenersArr.length;
				for (var i = 0; i < ll; i++) {
					listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
				}
				// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
				var il = objIdArr.length;
				for (var j = 0; j < il; j++) {
					removeSWF(objIdArr[j]);
				}
				// cleanup library's main closures to avoid memory leaks
				for (var k in ua) {
					ua[k] = null;
				}
				ua = null;
				for (var l in swfobject) {
					swfobject[l] = null;
				}
				swfobject = null;
			});
		}
	}();
	
	return {
		/* Public API
			- Reference: http://code.google.com/p/swfobject/wiki/documentation
		*/ 
		registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
			if (ua.w3 && objectIdStr && swfVersionStr) {
				var regObj = {};
				regObj.id = objectIdStr;
				regObj.swfVersion = swfVersionStr;
				regObj.expressInstall = xiSwfUrlStr;
				regObj.callbackFn = callbackFn;
				regObjArr[regObjArr.length] = regObj;
				setVisibility(objectIdStr, false);
			}
			else if (callbackFn) {
				callbackFn({success:false, id:objectIdStr});
			}
		},
		
		getObjectById: function(objectIdStr) {
			if (ua.w3) {
				return getObjectById(objectIdStr);
			}
		},
		
		embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
			var callbackObj = {success:false, id:replaceElemIdStr};
			if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
				setVisibility(replaceElemIdStr, false);
				addDomLoadEvent(function() {
					widthStr += ""; // auto-convert to string
					heightStr += "";
					var att = {};
					if (attObj && typeof attObj === OBJECT) {
						for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
							att[i] = attObj[i];
						}
					}
					att.data = swfUrlStr;
					att.width = widthStr;
					att.height = heightStr;
					var par = {}; 
					if (parObj && typeof parObj === OBJECT) {
						for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
							par[j] = parObj[j];
						}
					}
					if (flashvarsObj && typeof flashvarsObj === OBJECT) {
						for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
							if (typeof par.flashvars != UNDEF) {
								par.flashvars += "&" + k + "=" + flashvarsObj[k];
							}
							else {
								par.flashvars = k + "=" + flashvarsObj[k];
							}
						}
					}
					if (hasPlayerVersion(swfVersionStr)) { // create SWF
						var obj = createSWF(att, par, replaceElemIdStr);
						if (att.id == replaceElemIdStr) {
							setVisibility(replaceElemIdStr, true);
						}
						callbackObj.success = true;
						callbackObj.ref = obj;
					}
					else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
						att.data = xiSwfUrlStr;
						showExpressInstall(att, par, replaceElemIdStr, callbackFn);
						return;
					}
					else { // show alternative content
						setVisibility(replaceElemIdStr, true);
					}
					if (callbackFn) { callbackFn(callbackObj); }
				});
			}
			else if (callbackFn) { callbackFn(callbackObj);	}
		},
		
		switchOffAutoHideShow: function() {
			autoHideShow = false;
		},
		
		ua: ua,
		
		getFlashPlayerVersion: function() {
			return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
		},
		
		hasFlashPlayerVersion: hasPlayerVersion,
		
		createSWF: function(attObj, parObj, replaceElemIdStr) {
			if (ua.w3) {
				return createSWF(attObj, parObj, replaceElemIdStr);
			}
			else {
				return undefined;
			}
		},
		
		showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
			if (ua.w3 && canExpressInstall()) {
				showExpressInstall(att, par, replaceElemIdStr, callbackFn);
			}
		},
		
		removeSWF: function(objElemIdStr) {
			if (ua.w3) {
				removeSWF(objElemIdStr);
			}
		},
		
		createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
			if (ua.w3) {
				createCSS(selStr, declStr, mediaStr, newStyleBoolean);
			}
		},
		
		addDomLoadEvent: addDomLoadEvent,
		
		addLoadEvent: addLoadEvent,
		
		getQueryParamValue: function(param) {
			var q = doc.location.search || doc.location.hash;
			if (q) {
				if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
				if (param == null) {
					return urlEncodeIfNecessary(q);
				}
				var pairs = q.split("&");
				for (var i = 0; i < pairs.length; i++) {
					if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
						return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
					}
				}
			}
			return "";
		},
		
		// For internal usage only
		expressInstallCallback: function() {
			if (isExpressInstallActive) {
				var obj = getElementById(EXPRESS_INSTALL_ID);
				if (obj && storedAltContent) {
					obj.parentNode.replaceChild(storedAltContent, obj);
					if (storedAltContentId) {
						setVisibility(storedAltContentId, true);
						if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
					}
					if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
				}
				isExpressInstallActive = false;
			} 
		}
	};
}();

var Validation = Class.create({
	initialize: function(form, options){
		this.options = Object.extend({
			onSubmit: true,
			stopOnFirst: true,
			immediate: false,
			focusOnError: true,
			useTitles: true,
			onFormValidate: function(result, form){
			},
			onElementValidate: function(result, elm){
			},
			useExtendedError: false
		}, options ||
		{});
		this.form = $(form);

		if (this.options.onSubmit) {
			Event.observe(this.form, 'submit', this.onSubmit.bindAsEventListener(this));
		}
		if (this.options.immediate) {
			Form.getElements(this.form).each(function(input){
				Event.observe(input, 'blur', function(ev){
					this.validate(Event.element(ev));
				}.bindAsEventListener(this));
			}, this);
		}
	},
	test: function(name, elm) {
		var formalName = name;
		name = name.split('_')[0];
		var v = this.get(name);
		var prop = '__advice'+name.camelize();
		var advice;

		if (!this.isVisible(elm) && formalName.indexOf('_hidden') === -1) {
			return true;
		}
		if(!v.test($F(elm), elm, formalName)) {
			/* Test fallito. Campo non valido */
			advice = this.getAdvice(name, elm);
			if (advice !== null) {
				Element.remove(advice);
			}
			var errorMsg = this.options.useTitles ?  this.options.useTitles[name] : v.error;
			advice = '<div class="validation-advice ' + name + '" id="advice-' + this.getElmID(elm) +'" style="display:none">';
			if (this.options.useExtendedError){
				advice += '<div class="top"> </div>';
				advice += '<div class="cnt">' + errorMsg + '</div>';
				advice += '<div class="bottom"> </div>';
			} else {
				advice += errorMsg;
			}
			advice += '</div>';
			switch (elm.type.toLowerCase()) {
				case 'checkbox':
				case 'radio':
					var p = elm.parentNode;
					if(p) {
						Element.insert(elm.up(this.options.fieldContainerSelector), {bottom: advice});
					} else {
						Element.insert(elm.up(this.options.fieldContainerSelector), {bottom: advice});
					}
				break;
				default:
					Element.insert(elm.up(this.options.fieldContainerSelector), {bottom: advice});
			}
			advice = this.getAdvice(name, elm);
			if(typeof Effect === 'undefined') {
				advice.style.display = 'block';
			} else {
				new Effect.Appear(advice, {duration : .4 });
			}
			Element.removeClassName(elm, 'validation-passed');
			Element.addClassName(elm, 'validation-failed');
			return false;
		} else {
			/* Test ok. Nascondo precedenti errori */
			advice = this.getAdvice(name, elm);
			if (advice !== null) {
				Element.hide(advice);
			}
			Element.removeClassName(elm, 'validation-failed');
			if (!(Element.hasClassName(elm, 'no-required') && elm.value=='')) {
				Element.addClassName(elm, 'validation-passed');
			} else {
				Element.removeClassName(elm, 'validation-passed');
			}
			return true;
		}
	},
	addField: function(inputElm){
		var evtObs = inputElm.tagName.toLowerCase() === 'select' ? 'change' : 'blur';
		Event.observe(inputElm, evtObs, function(ev){
			// Event.element(ev)
			// inputElm
			this.validate(Event.element(ev), {
				useTitle: this.options.useTitles,
				onElementValidate: this.options.onElementValidate,
				useExtendedError: this.options.useExtendedError
			});
		}.bindAsEventListener(this));
	},
	onSubmit: function(ev){
		if(!this.validateAll()){
			Event.stop(ev);
		}
	},
	reset: function() {
		Form.getElements(this.form).each(this.reset);
	},
	validateAll: function() {
		var result = false;
		var formElements = Form.getElements(this.form);
		if(this.options.stopOnFirst) {
			result = formElements.all(function(elm) {
				return this.validate(elm);
			}, this);
		} else {
			result = formElements.collect(function(elm) {
				return this.validate(elm);
			}, this).all();
		}
		if(!result && this.options.focusOnError) {
			formElements.findAll(function(elm){
				return $(elm).hasClassName('validation-failed')
			}).first().focus();
		}
		this.options.onFormValidate(result, this.form);
		return result;
	},
	validate: function(elm){
		elm = $(elm);
		var cn = elm.classNames();
		var result = cn.all(function(value) {
			var test = this.test(value, elm);
			this.options.onElementValidate(test, elm);
			return test;
		}, this);

		return result;
	},
	isVisible: function(elm) {
		while(elm.tagName != 'BODY') {
			if(!$(elm).visible()) return false;
			elm = elm.parentNode;
		}
		return true;
	},
	getAdvice: function(name, elm) {
		return $('advice-' + name + '-' + this.getElmID(elm)) || $('advice-' + this.getElmID(elm));
	},
	getElmID: function(elm) {
		return elm.id ? elm.id : elm.name;
	},
	reset: function(elm) {
		elm = $(elm);
		var cn = elm.classNames();
		cn.each(function(value) {
			var prop = '__advice'+value.camelize();
			if(elm[prop]) {
				var advice = this.getAdvice(value, elm);
				advice.hide();
				elm[prop] = '';
			}
			elm.removeClassName('validation-failed');
			elm.removeClassName('validation-passed');
		}, this);
	},
	get: function(name) {
		return ValidatorRules.get(name);
	}
});

/*
 * Classe per la validazione. Raccolte nella classe ValidatorRules.
 */
var Validator = Class.create({
	initialize: function(className, error, test, options) {
		if(typeof(test) === 'function'){
			this.options = $H(options);
			this._test = test;
		} else {
			this.options = $H(test);
			this._test = function(){return true};
		}
		this.error = error || 'Validation failed.';
		this.className = className;
	},
	test: function(v, elm) {
		return (this._test(v,elm) && this.options.all(function(p){
			return ValidatorRules[p.key] ? ValidatorRules[p.key](v,elm,p.value) : true;
		}));
	}
});
/*
 * Classe che raccoglie tutte le tipologie di validazione.
 */
var ValidatorRules = Class.create({
	initialize: function(){

	},
	get: function(name) {
		return this[name] ? this[name] : this._LikeNoIDIEverSaw_;
	},
	set: function(name, test){
		if (!this[name]){
			this[name] = new Validator(name, '', test);
		}
	},
	pattern : function(v, elm, opt) {
		return (ValidatorRules.get('IsEmpty').test(v) || opt.test(v));
	},
	minLength : function(v, elm, opt) {
		return (v.length >= opt);
	},
	maxLength : function(v, elm, opt) {
		return (v.length <= opt);
	},
	min : function(v, elm, opt) {
		return (v >= parseFloat(opt));
	},
	max : function(v, elm, opt) {
		return (v <= parseFloat(opt));
	},
	notOneOf : function(v, elm, opt) {
		return $A(opt).all(function(value) {
			return v != value;
		});
	},
	oneOf : function(v, elm, opt) {
		return $A(opt).any(function(value) {
			return v == value;
		});
	},
	is: function(v,elm,opt) {
		return (v == opt);
	},
	isNot: function(v,elm,opt) {
		return (v != opt);
	},
	equalToField: function(v,elm,opt) {
		return (v == $F(opt));
	},
	notEqualToField: function(v,elm,opt) {
		return (v != $F(opt));
	},
	include: function(v,elm,opt) {
		return $A(opt).all(function(value) {
			return ValidatorRules.get(value).test(v, elm);
		});
	},
	'_LikeNoIDIEverSaw_': new Validator('_LikeNoIDIEverSaw_','',{}),
	'class-name': new Validator('class-name', 'Error message text', {
		pattern : new RegExp("[^\d]","g"), // only letter allowed
		minLength : 3,
		maxLength : 3
	}),
	'validate-email-confirm': new Validator('validate-email-confirm', '', {
		equalToField : 'email'
	}),
	'validate-password-confirm': new Validator('validate-password-confirm', '', {
		equalToField : 'password'
	}),
	'IsEmpty': new Validator('IsEmpty', '', function(v) {
		return  ((v == null) || (v.length == 0)); // || /^\s+$/.test(v));
	}),
	'required': new Validator('required', 'This is a required field.', function(v) {
		return !ValidatorRules.get('IsEmpty').test(v);
	}),
	'validate-number': new Validator('validate-number', 'Please enter a valid number in this field.', function(v) {
		return ValidatorRules.get('IsEmpty').test(v) || (!isNaN(v) && !/^\s+$/.test(v));
	}),
	'validate-digits': new Validator('validate-digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) {
		return ValidatorRules.get('IsEmpty').test(v) ||  !/[^\d]/.test(v);
	}),
	'validate-alpha': new Validator('validate-alpha', 'Please use letters only (a-z) in this field.', function (v) {
		return ValidatorRules.get('IsEmpty').test(v) ||  /^[a-zA-Z]+$/.test(v)
	}),
	'validate-alphanum': new Validator('validate-alphanum', 'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', function(v) {
		return ValidatorRules.get('IsEmpty').test(v) ||  !/\W/.test(v)
	}),
	'validate-date': new Validator('validate-date', 'Please enter a valid date.', function(v) {
		var test = new Date(v);
		return ValidatorRules.get('IsEmpty').test(v) || !isNaN(test);
	}),
	'validate-email': new Validator('validate-email', 'Please enter a valid email address. For example fred@domain.com .', function (v) {
		return ValidatorRules.get('IsEmpty').test(v) || /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2,3}|(aero|name|coop|info|museum|asia|travel|jobs|mobi))$/.test(v)
	}),
	'validate-email-no-required': new Validator('validate-email-no-required', 'Please enter a valid email address. For example fred@domain.com .', function (v) {
		return ValidatorRules.get('IsEmpty').test(v) || /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2,3}|(aero|name|coop|info|museum|asia|travel|jobs|mobi))$/.test(v)
	}),
	'validate-url': new Validator('validate-url', 'Please enter a valid URL.', function (v) {
		return ValidatorRules.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v)
	}),
	'validate-selection': new Validator('validate-selection', 'Please make a selection', function(v,elm){
		return elm.options ? elm.selectedIndex > 0 : !ValidatorRules.get('IsEmpty').test(v);
	}),
	'validate-one-required': new Validator('validate-one-required', 'Please select one of the above options.', function (v,elm) {
		var p = elm.parentNode;
		var options = p.getElementsByTagName('INPUT');
		return $A(options).any(function(elm) {
			return $F(elm);
		});
	}),
	'validate-phone': new Validator('validate-phone', 'Error message text', function(v) {
		//digits, -, +, spazio. Massimo 20 caratteri
		return ValidatorRules.get('IsEmpty').test(v) || (!/[^\d\-\u002B\u0020\u0028\u0029]/.test(v) && (v.length>3) && (v.length<21));
	}),
	'validate-phone-no-required': new Validator('validate-phone-no-required', 'Error message text', function(v) {
		//digits, -, +, spazio. Massimo 20 caratteri
		return  (!/[^\d\-\u002B\u0020\u0028\u0029]/.test(v) && (v.length>3) && (v.length<21)) || v.length == 0;
	}),
	'validate-phone-jp': new Validator('validate-phone-jp', 'Error message text', function(v) {
		//digits, -, +. Massimo 14 caratteri
		return  ValidatorRules.get('IsEmpty').test(v) || (!/[^\d\-\u002B]/.test(v) && (v.length>3) && (v.length<15));
	}),
	'validate-3-digit': new Validator('validate-3-digit', 'Error message text', function(v) {
		//digits, -, +, spazio
		return  /[0-9]{3}/.test(v);
	}),
	'validate-4-digit': new Validator('validate-4-digit', 'Error message text', function(v) {
		return  /[0-9]{4}/.test(v);
	}),
	'validate-5-digit': new Validator('validate-5-digit', 'Error message text', function(v) {
		return  /[0-9]{5}/.test(v);
	}),
	'validate-alphanum-withSpacesAndDashes': new Validator('validate-alphanum-withSpacesAndDashes', 'Error message text', function(v) {
		return  /^[0-9a-zA-Z\-\_\ ]+$/.test(v);
	}),
	'validate-alpha-withSpacesAndDashes': new Validator('validate-alpha-withSpacesAndDashes', 'Error message text', function(v) {
		return  /^[a-zA-Z\-\_\ ]+$/.test(v);
	}),
	'validate-name': new Validator('validate-name', 'Error message text', function(v) {
		return  /^['a-zA-Z- \u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u017F]+$/.test(v);
	}),
	'validate-name-jp': new Validator('validate-name-jp', 'Error message text', function(v) {
		return  /^[^\.\&\%\$\?]*$/.test(v);
	}),
	'validate-name-cn': new Validator('validate-name-cn', 'Error message text', function(v) {
		return  /^[^\.\&\%\$\?]*$/.test(v);
	}),
	'validate-japanese': new Validator('validate-japanese', 'Error message text', function(v) {
		return  /^[0-9\-\_\u2E80-\uA63F\uFF00-\uFFEF\ ]+$/.test(v);
	})
});

var ValidationInit = Class.create({
	initialize: function(jsonProps){
		this.errors = jsonProps.errors;

		jsonProps.forms.each(function(jsonElm){
			if ($(jsonElm.form) !== null){
				this.setup(jsonElm.form, jsonElm.submit, jsonElm.callback, jsonProps);
			}
		}, this);
		
		jsonProps.fieldContainerSelector = (typeof(jsonProps.fieldContainerSelector)!=='undefined') ? jsonProps.fieldContainerSelector : 'div';
	},
	setup: function(form, submitElm, callbackFunct, options){
		var opts = Object.extend({
			immediate : true,
			useTitles: this.errors,
			useExtendedError: options.useExtendedError || false
		}, options ||
		{});

		if (!Validation[form]){
			Validation[form] = new Validation($(form), opts);
		}
		this.setupSubmitObservers(form, submitElm, callbackFunct);
	},
	setupSubmitObservers: function(form, submitElm, callbackFunct){
		callbackFunct = (callbackFunct!='') ? callbackFunct : null;

		Event.observe(submitElm, 'click', this.submit.bindAsEventListener(this, form, callbackFunct));
	},
	submit: function(e, form, callback){
		var eventElement = $(e.element());
		var eventElementName = eventElement.tagName.toLowerCase();
		var eventParentForm = e.findElement('form');
		(eventParentForm)?void(0):eventParentForm=$(form);
		var data = $A(arguments);
		data.shift();

		var validForm = Validation[form].validateAll();
		if (validForm) {
			(callback)?eval(''+callback+'()'):eventParentForm.submit();
		}
	}
});

Event.observe(window, 'load', function(){
	if (typeof(jsinit_forms) === 'undefined') {
		return;
	}
	try {
		ValidatorRules = new ValidatorRules();
		ValidationInit = new ValidationInit(jsinit_forms);
	} catch (e){
		ErrorManager.error(e, (Links.SITE_CONTEXT || 'OS.Site'));
	}
});

/*!
* Copyright (c) 2009 Simo Kinnunen.
* Licensed under the MIT license.
*
* @version ${Version}
*/
var Cufon = (function () {
	var api = function () {
		return api.replace.apply(null, arguments);
	};
	var DOM = api.DOM = {
		ready: (function () {
			var complete = false,
			readyStatus = {
				loaded: 1,
				complete: 1
			};
			var queue = [],
			perform = function () {
				if (complete) return;
				complete = true;
				for (var fn; fn = queue.shift(); fn());
			}; // Gecko, Opera, WebKit r26101+
			if (document.addEventListener) {
				document.addEventListener('DOMContentLoaded', perform, false);
				window.addEventListener('pageshow', perform, false); // For cached Gecko pages
			} // Old WebKit, Internet Explorer
			if (!window.opera && document.readyState)(function () {
				readyStatus[document.readyState] ? perform() : setTimeout(arguments.callee, 10);
			})(); // Internet Explorer
			if (document.readyState && document.createStyleSheet)(function () {
				try {
					document.body.doScroll('left');
					perform();
				} catch(e) {
					setTimeout(arguments.callee, 1);
				}
			})();
			addEvent(window, 'load', perform); // Fallback
			return function (listener) {
				if (!arguments.length) perform();
				else complete ? listener() : queue.push(listener);
			};
		})(),
		root: function () {
			return document.documentElement || document.body;
		}
	};
	var CSS = api.CSS = {
		Size: function (value, base) {
			this.value = parseFloat(value);
			this.unit = String(value).match(/[a-z%]*$/)[0] || 'px';
			this.convert = function (value) {
				return value / base * this.value;
			};
			this.convertFrom = function (value) {
				return value / this.value * base;
			};
			this.toString = function () {
				return this.value + this.unit;
			};
		},
		addClass: function (el, className) {
			var current = el.className;
			el.className = current + (current && ' ') + className;
			return el;
		},
		color: cached(function (value) {
			var parsed = {};
			parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function ($0, $1, $2) {
				parsed.opacity = parseFloat($2);
				return 'rgb(' + $1 + ')';
			});
			return parsed;
		}),
		// has no direct CSS equivalent.
		// @see http://msdn.microsoft.com/en-us/library/system.windows.fontstretches.aspx
		fontStretch: cached(function (value) {
			if (typeof value == 'number') return value;
			if (/%$/.test(value)) return parseFloat(value) / 100;
			return {
				'ultra-condensed': 0.5,
				'extra-condensed': 0.625,
				condensed: 0.75,
				'semi-condensed': 0.875,
				'semi-expanded': 1.125,
				expanded: 1.25,
				'extra-expanded': 1.5,
				'ultra-expanded': 2
			} [value] || 1;
		}),
		getStyle: function (el) {
			var view = document.defaultView;
			if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null));
			if (el.currentStyle) return new Style(el.currentStyle);
			return new Style(el.style);
		},
		gradient: cached(function (value) {
			var gradient = {
				id: value,
				type: value.match(/^-([a-z]+)-gradient\(/)[1],
				stops: []
			},
			colors = value.substr(value.indexOf('(')).match(/([\d.]+=)?(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)/ig);
			for (var i = 0, l = colors.length, stop; i < l; ++i) {
				stop = colors[i].split('=', 2).reverse();
				gradient.stops.push([stop[1] || i / (l - 1), stop[0]]);
			}
			return gradient;
		}),
		hasClass: function (el, className) {
			return RegExp('(?:^|\\s)' + className + '(?=\\s|$)').test(el.className);
		},
		quotedList: cached(function (value) {
			
			var list = [],
			/* 
			
			var re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s[[[[[[[[asterisco]]]]]]]]/g,
			match; 
			
			*/
			
			re = new RegExp("\\s*(([\"'])([\\s\\S]*?[^\\\\])\\2|[^,]+)\\s*", "g"), 
			match;
			
			while (match = re.exec(value)) list.push(match[3] || match[1]);
			return list;
		}),
		recognizesMedia: cached(function (media) {
			var el = document.createElement('style'),
			sheet,
			container,
			supported;
			el.type = 'text/css';
			el.media = media;
			try { // this is cached anyway
				el.appendChild(document.createTextNode('/**/'));
			} catch(e) {}
			container = elementsByTagName('head')[0];
			container.insertBefore(el, container.firstChild);
			sheet = (el.sheet || el.styleSheet);
			supported = sheet && !sheet.disabled;
			container.removeChild(el);
			return supported;
		}),
		removeClass: function (el, className) {
			var re = RegExp('(?:^|\\s+)' + className + '(?=\\s|$)', 'g');
			el.className = el.className.replace(re, '');
			return el;
		},
		supports: function (property, value) {
			var checker = document.createElement('span').style;
			if (checker[property] === undefined) return false;
			checker[property] = value;
			return checker[property] === value;
		},
		textAlign: function (word, style, position, wordCount) {
			if (style.get('textAlign') == 'right') {
				if (position > 0) word = ' ' + word;
			} else if (position < wordCount - 1) word += ' ';
			return word;
		},
		textDecoration: function (el, style) {
			if (!style) style = this.getStyle(el);
			var types = {
				underline: null,
				overline: null,
				'line-through': null
			};
			for (var search = el; search.parentNode && search.parentNode.nodeType == 1;) {
				var foundAll = true;
				for (var type in types) {
					if (!hasOwnProperty(types, type) || types[type]) continue;
					if (style.get('textDecoration').indexOf(type) != -1) types[type] = style.get('color');
					foundAll = false;
				}
				if (foundAll) break; // this is rather unlikely to happen
				style = this.getStyle(search = search.parentNode);
			}
			return types;
		},
		textShadow: cached(function (value) {
			if (value == 'none') return null;
			var shadows = [],
			currentShadow = {},
			result,
			offCount = 0;
			var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
			while (result = re.exec(value)) {
				if (result[0] == ',') {
					shadows.push(currentShadow);
					currentShadow = {};
					offCount = 0;
				} else if (result[1]) {
					currentShadow.color = result[1];
				} else {
					currentShadow[
						['offX', 'offY', 'blur'][offCount++]] = result[2];
				}
			}
			shadows.push(currentShadow);
			return shadows;
		}),
		textTransform: (function () {
			var map = {
				uppercase: function (s) {
					return s.toUpperCase();
				},
				lowercase: function (s) {
					return s.toLowerCase();
				},
				capitalize: function (s) {
					return s.replace(/\b./g, function ($0) {
						return $0.toUpperCase();
					});
				}
			};
			return function (text, style) {
				var transform = map[style.get('textTransform')];
				return transform ? transform(text) : text;
			};
		})(),
		whiteSpace: (function () {
			var ignore = {
				inline: 1,
				'inline-block': 1,
				'run-in': 1
			};
			return function (text, style, node) {
				if (ignore[style.get('display')]) return text;
				if (!node.previousSibling) text = text.replace(/^\s+/, '');
				if (!node.nextSibling) text = text.replace(/\s+$/, '');
				return text;
			};
		})()
	};
	CSS.ready = (function () { // don't do anything in Safari 2 (it doesn't recognize any media type)
		var complete = !CSS.recognizesMedia('all'),
		hasLayout = false;
		var queue = [],
		perform = function () {
			complete = true;
			for (var fn; fn = queue.shift(); fn());
		};
		var links = elementsByTagName('link'),
		styles = elementsByTagName('style');
		function isContainerReady(el) {
			return el.disabled || isSheetReady(el.sheet, el.media || 'screen');
		}
		function isSheetReady(sheet, media) { // in Opera sheet.disabled is true when it's still loading,
			// even though link.disabled is false. they stay in sync if
			// set manually.
			if (!CSS.recognizesMedia(media || 'all')) return true;
			if (!sheet || sheet.disabled) return false;
			try {
				var rules = sheet.cssRules,
				rule;
				if (rules) { // needed for Safari 3 and Chrome 1.0.
					// in standards-conforming browsers cssRules contains @-rules.
					// Chrome 1.0 weirdness: rules[<number larger than .length - 1>]
					// returns the last rule, so a for loop is the only option.
					search: for (var i = 0, l = rules.length; rule = rules[i], i < l; ++i) {
						switch (rule.type) {
						case 2:
							// @charset
							break;
						case 3:
							// @import
							if (!isSheetReady(rule.styleSheet, rule.media.mediaText)) return false;
							break;
						default:
							// only @charset can precede @import
							break search;
						}
					}
				}
			} catch(e) {} // probably a style sheet from another domain
			return true;
		}
		function allStylesLoaded() { // Internet Explorer's style sheet model, there's no need to do anything
			if (document.createStyleSheet) return true; // standards-compliant browsers
			var el, i;
			for (i = 0; el = links[i]; ++i) {
				if (el.rel.toLowerCase() == 'stylesheet' && !isContainerReady(el)) return false;
			}
			for (i = 0; el = styles[i]; ++i) {
				if (!isContainerReady(el)) return false;
			}
			return true;
		}
		DOM.ready(function () { // getComputedStyle returns null in Gecko if used in an iframe with display: none
			if (!hasLayout) hasLayout = CSS.getStyle(document.body).isUsable();
			if (complete || (hasLayout && allStylesLoaded())) perform();
			else setTimeout(arguments.callee, 10);
		});
		return function (listener) {
			if (complete) listener();
			else queue.push(listener);
		};
	})();
	function Font(data) {
		var face = this.face = data.face;
		this.glyphs = data.glyphs;
		this.w = data.w;
		this.baseSize = parseInt(face['units-per-em'], 10);
		this.family = face['font-family'].toLowerCase();
		this.weight = face['font-weight'];
		this.style = face['font-style'] || 'normal';
		this.viewBox = (function () {
			var parts = face.bbox.split(/\s+/);
			var box = {
				minX: parseInt(parts[0], 10),
				minY: parseInt(parts[1], 10),
				maxX: parseInt(parts[2], 10),
				maxY: parseInt(parts[3], 10)
			};
			box.width = box.maxX - box.minX;
			box.height = box.maxY - box.minY;
			box.toString = function () {
				return [this.minX, this.minY, this.width, this.height].join(' ');
			};
			return box;
		})();
		this.ascent = -parseInt(face.ascent, 10);
		this.descent = -parseInt(face.descent, 10);
		this.height = -this.ascent + this.descent;
	}
	function FontFamily() {
		var styles = {},
		mapping = {
			oblique: 'italic',
			italic: 'oblique'
		};
		this.add = function (font) {
			(styles[font.style] || (styles[font.style] = {}))[font.weight] = font;
		};
		this.get = function (style, weight) {
			var weights = styles[style] || styles[mapping[style]] || styles.normal || styles.italic || styles.oblique;
			if (!weights) return null; // we don't have to worry about "bolder" and "lighter"
			// because IE's currentStyle returns a numeric value for it,
			// and other browsers use the computed value anyway
			weight = {
				normal: 400,
				bold: 700
			} [weight] || parseInt(weight, 10);
			if (weights[weight]) return weights[weight]; // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
			// Gecko uses x99/x01 for lighter/bolder
			var up = {
				1: 1,
				99: 0
			} [weight % 100],
			alts = [],
			min,
			max;
			if (up === undefined) up = weight > 400;
			if (weight == 500) weight = 400;
			for (var alt in weights) {
				if (!hasOwnProperty(weights, alt)) continue;
				alt = parseInt(alt, 10);
				if (!min || alt < min) min = alt;
				if (!max || alt > max) max = alt;
				alts.push(alt);
			}
			if (weight < min) weight = min;
			if (weight > max) weight = max;
			alts.sort(function (a, b) {
				return (up ? (a > weight && b > weight) ? a < b : a > b : (a < weight && b < weight) ? a > b : a < b) ? -1 : 1;
			});
			return weights[alts[0]];
		};
	}
	function HoverHandler() {
		function contains(node, anotherNode) {
			if (node.contains) return node.contains(anotherNode);
			return node.compareDocumentPosition(anotherNode) & 16;
		}
		function onOverOut(e) {
			var related = e.relatedTarget;
			if (!related || contains(this, related)) return;
			trigger(this);
		}
		function onEnterLeave(e) {
			trigger(this);
		}
		function trigger(el) { // A timeout is needed so that the event can actually "happen"
			// before replace is triggered. This ensures that styles are up
			// to date.
			setTimeout(function () {
				api.replace(el, sharedStorage.get(el).options, true);
			},
			10);
		}
		this.attach = function (el) {
			if (el.onmouseenter === undefined) {
				addEvent(el, 'mouseover', onOverOut);
				addEvent(el, 'mouseout', onOverOut);
			} else {
				addEvent(el, 'mouseenter', onEnterLeave);
				addEvent(el, 'mouseleave', onEnterLeave);
			}
		};
	}
	function ReplaceHistory() {
		var list = [],
		map = {};
		function filter(keys) {
			var values = [],
			key;
			for (var i = 0; key = keys[i]; ++i) values[i] = list[map[key]];
			return values;
		}
		this.add = function (key, args) {
			map[key] = list.push(args) - 1;
		};
		this.repeat = function () {
			var snapshot = arguments.length ? filter(arguments) : list,
			args;
			for (var i = 0; args = snapshot[i++];) api.replace(args[0], args[1], true);
		};
	}
	function Storage() {
		var map = {},
		at = 0;
		function identify(el) {
			return el.cufid || (el.cufid = ++at);
		}
		this.get = function (el) {
			var id = identify(el);
			return map[id] || (map[id] = {});
		};
	}
	function Style(style) {
		var custom = {},
		sizes = {};
		this.extend = function (styles) {
			for (var property in styles) {
				if (hasOwnProperty(styles, property)) custom[property] = styles[property];
			}
			return this;
		};
		this.get = function (property) {
			return custom[property] != undefined ? custom[property] : style[property];
		};
		this.getSize = function (property, base) {
			return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base));
		};
		this.isUsable = function () {
			return !! style;
		};
	}
	function addEvent(el, type, listener) {
		if (el.addEventListener) {
			el.addEventListener(type, listener, false);
		} else if (el.attachEvent) {
			el.attachEvent('on' + type, function () {
				return listener.call(el, window.event);
			});
		}
	}
	function attach(el, options) {
		var storage = sharedStorage.get(el);
		if (storage.options) return el;
		if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) {
			hoverHandler.attach(el);
		}
		storage.options = options;
		return el;
	}
	function cached(fun) {
		var cache = {};
		return function (key) {
			if (!hasOwnProperty(cache, key)) cache[key] = fun.apply(null, arguments);
			return cache[key];
		};
	}
	function getFont(el, style) {
		var families = CSS.quotedList(style.get('fontFamily').toLowerCase()),
		family;
		for (var i = 0; family = families[i]; ++i) {
			if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight'));
		}
		return null;
	}
	function elementsByTagName(query) {
		return document.getElementsByTagName(query);
	}
	function hasOwnProperty(obj, property) {
		return obj.hasOwnProperty(property);
	}
	function merge() {
		var merged = {},
		args, key;
		for (var i = 0, l = arguments.length; args = arguments[i], i < l; ++i) {
			for (key in args) {
				if (hasOwnProperty(args, key)) merged[key] = args[key];
			}
		}
		return merged;
	}
	function process(font, text, style, options, node, el) {
		var fragment = document.createDocumentFragment(),
		processed;
		if (text === '') return fragment;
		var separate = options.separate;
		var parts = text.split(separators[separate]),
		needsAligning = (separate == 'words');
		if (needsAligning && HAS_BROKEN_REGEXP) { // @todo figure out a better way to do this
			if (/^\s/.test(text)) parts.unshift('');
			if (/\s$/.test(text)) parts.push('');
		}
		for (var i = 0, l = parts.length; i < l; ++i) {
			processed = engines[options.engine](font, needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i], style, options, node, el, i < l - 1);
			if (processed) fragment.appendChild(processed);
		}
		return fragment;
	}
	function replaceElement(el, options) {
		var style = CSS.getStyle(attach(el, options)).extend(options);
		var font = getFont(el, style),
		node,
		type,
		next,
		anchor,
		text;
		for (node = el.firstChild; node; node = next) {
			type = node.nodeType;
			next = node.nextSibling;
			if (type == 3) { // Node.normalize() is broken in IE 6, 7, 8
				if (anchor) {
					anchor.appendData(node.data);
					el.removeChild(node);
				} else anchor = node;
				if (next) continue;
			}
			if (anchor) {
				el.replaceChild(process(font, CSS.whiteSpace(anchor.data, style, anchor), style, options, node, el), anchor);
				anchor = null;
			}
			if (type == 1 && node.firstChild) {
				if (CSS.hasClass(node, 'cufon')) {
					engines[options.engine](font, null, style, options, node, el);
				} else arguments.callee(node, options);
			}
		}
	}
	var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0;
	var sharedStorage = new Storage();
	var hoverHandler = new HoverHandler();
	var replaceHistory = new ReplaceHistory();
	var initialized = false;
	var engines = {},
	fonts = {},
	defaultOptions = {
		enableTextDecoration: false,
		engine: null,
		//fontScale: 1,
		//fontScaling: false,
		forceHitArea: false,
		hover: false,
		hoverables: {
			a: true
		},
		printable: true,
		//rotation: 0,
		//selectable: false,
		selector: (window.Sizzle || (window.jQuery &&
		function (query) {
			return jQuery(query);
		}) // avoid noConflict issues
		|| (window.dojo && dojo.query) || (window.Ext && Ext.query) || (window.$$ &&
		function (query) {
			return $$(query);
		}) || (window.$ &&
		function (query) {
			return $(query);
		}) || (document.querySelectorAll &&
		function (query) {
			return document.querySelectorAll(query);
		}) || elementsByTagName),
		separate: 'words',
		// 'none' and 'characters' are also accepted
		textShadow: 'none'
	};
	var separators = {
		words: /[^\S\u00a0]+/,
		characters: '',
		none: /^/
	};
	api.now = function () {
		DOM.ready();
		return api;
	};
	api.refresh = function () {
		replaceHistory.repeat.apply(replaceHistory, arguments);
		return api;
	};
	api.registerEngine = function (id, engine) {
		if (!engine) return api;
		engines[id] = engine;
		return api.set('engine', id);
	};
	api.registerFont = function (data) {
		var font = new Font(data),
		family = font.family;
		if (!fonts[family]) fonts[family] = new FontFamily();
		fonts[family].add(font);
		return api.set('fontFamily', '"' + family + '"');
	};
	api.replace = function (elements, options, ignoreHistory) {
		options = merge(defaultOptions, options);
		if (!options.engine) return api; // there's no browser support so we'll just stop here
		if (!initialized) {
			CSS.addClass(DOM.root(), 'cufon-active cufon-loading');
			CSS.ready(function () { // fires before any replace() calls, but it doesn't really matter
				CSS.addClass(CSS.removeClass(DOM.root(), 'cufon-loading'), 'cufon-ready');
			});
			initialized = true;
		}
		if (options.hover) options.forceHitArea = true;
		if (typeof options.textShadow == 'string') options.textShadow = CSS.textShadow(options.textShadow);
		if (typeof options.color == 'string' && /^-/.test(options.color)) options.textGradient = CSS.gradient(options.color);
		if (!ignoreHistory) replaceHistory.add(elements, arguments);
		if (elements.nodeType || typeof elements == 'string') elements = [elements];
		CSS.ready(function () {
			for (var i = 0, l = elements.length; i < l; ++i) {
				var el = elements[i];
				if (typeof el == 'string') api.replace(options.selector(el), options, true);
				else replaceElement(el, options);
			}
		});
		return api;
	};
	api.set = function (option, value) {
		defaultOptions[option] = value;
		return api;
	};
	return api;
})();
Cufon.registerEngine('canvas', (function () { // Safari 2 doesn't support .apply() on native methods
	var check = document.createElement('canvas');
	if (!check || !check.getContext || !check.getContext.apply) return;
	check = null;
	var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block'); // Firefox 2 w/ non-strict doctype (almost standards mode)
	var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId));
	var styleSheet = document.createElement('style');
	styleSheet.type = 'text/css';
	styleSheet.appendChild(document.createTextNode(('.cufon-canvas{text-indent:0;}' + '@media screen,projection{' + '.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle;' + (HAS_BROKEN_LINEHEIGHT ? '' : 'font-size:1px;line-height:1px;') + '}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden;text-indent:-10000in;}' + (HAS_INLINE_BLOCK ? '.cufon-canvas canvas{position:relative;}' : '.cufon-canvas canvas{position:absolute;}') + '}' + '@media print{' + '.cufon-canvas{padding:0;}' + '.cufon-canvas canvas{display:none;}' + '.cufon-canvas .cufon-alt{display:inline;}' + '}').replace(/;/g, '!important;')));
	document.getElementsByTagName('head')[0].appendChild(styleSheet);
	function generateFromVML(path, context) {
		var atX = 0,
		atY = 0;
		var code = [],
		re = /([mrvxe])([^a-z]*)/g,
		match;
		generate: for (var i = 0; match = re.exec(path); ++i) {
			var c = match[2].split(',');
			switch (match[1]) {
			case 'v':
				code[i] = {
					m: 'bezierCurveTo',
					a: [atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5]]
				};
				break;
			case 'r':
				code[i] = {
					m: 'lineTo',
					a: [atX += ~~c[0], atY += ~~c[1]]
				};
				break;
			case 'm':
				code[i] = {
					m: 'moveTo',
					a: [atX = ~~c[0], atY = ~~c[1]]
				};
				break;
			case 'x':
				code[i] = {
					m: 'closePath'
				};
				break;
			case 'e':
				break generate;
			}
			context[code[i].m].apply(context, code[i].a);
		}
		return code;
	}
	function interpret(code, context) {
		for (var i = 0, l = code.length; i < l; ++i) {
			var line = code[i];
			context[line.m].apply(context, line.a);
		}
	}
	return function (font, text, style, options, node, el) {
		var redraw = (text === null);
		if (redraw) text = node.alt;
		var viewBox = font.viewBox;
		var size = style.getSize('fontSize', font.baseSize);
		var letterSpacing = style.get('letterSpacing');
		letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
		var expandTop = 0,
		expandRight = 0,
		expandBottom = 0,
		expandLeft = 0;
		var shadows = options.textShadow,
		shadowOffsets = [];
		if (shadows) {
			for (var i = shadows.length; i--;) {
				var shadow = shadows[i];
				var x = size.convertFrom(parseFloat(shadow.offX));
				var y = size.convertFrom(parseFloat(shadow.offY));
				shadowOffsets[i] = [x, y];
				if (y < expandTop) expandTop = y;
				if (x > expandRight) expandRight = x;
				if (y > expandBottom) expandBottom = y;
				if (x < expandLeft) expandLeft = x;
			}
		}
		var chars = Cufon.CSS.textTransform(text, style).split(''),
		chr;
		var glyphs = font.glyphs,
		glyph, kerning, k;
		var width = 0,
		advance, jumps = [];
		for (var i = 0, j = 0, l = chars.length; i < l; ++i) {
			glyph = glyphs[chr = chars[i]] || font.missingGlyph;
			if (!glyph) continue;
			if (kerning) {
				width -= k = kerning[chr] || 0;
				jumps[j - 1] -= k;
			}
			width += advance = jumps[j++] = ~~ (glyph.w || font.w) + letterSpacing;
			kerning = glyph.k;
		}
		if (advance === undefined) return null; // there's nothing to render
		expandRight += viewBox.width - advance;
		expandLeft += viewBox.minX;
		var wrapper, canvas;
		if (redraw) {
			wrapper = node;
			canvas = node.firstChild;
		} else {
			wrapper = document.createElement('span');
			wrapper.className = 'cufon cufon-canvas';
			wrapper.alt = text;
			canvas = document.createElement('canvas');
			wrapper.appendChild(canvas);
			if (options.printable) {
				var print = document.createElement('span');
				print.className = 'cufon-alt';
				print.appendChild(document.createTextNode(text));
				wrapper.appendChild(print);
			}
		}
		var wStyle = wrapper.style;
		var cStyle = canvas.style;
		var height = size.convert(viewBox.height);
		var roundedHeight = Math.ceil(height);
		var roundingFactor = roundedHeight / height;
		var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch'));
		var stretchedWidth = width * stretchFactor;
		var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft));
		var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom));
		canvas.width = canvasWidth;
		canvas.height = canvasHeight; // needed for WebKit and full page zoom
		cStyle.width = canvasWidth + 'px';
		cStyle.height = canvasHeight + 'px'; // minY has no part in canvas.height
		expandTop += viewBox.minY;
		cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
		cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
		var wrapperWidth = Math.ceil(size.convert(stretchedWidth)) + 'px';
		if (HAS_INLINE_BLOCK) {
			wStyle.width = wrapperWidth;
			wStyle.height = size.convert(font.height) + 'px';
		} else {
			wStyle.paddingLeft = wrapperWidth;
			wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px';
		}
		var g = canvas.getContext('2d'),
		scale = height / viewBox.height; // proper horizontal scaling is performed later
		g.scale(scale, scale * roundingFactor);
		g.translate(-expandLeft, -expandTop);
		g.lineWidth = font.face['underline-thickness'];
		g.save();
		function line(y, color) {
			g.strokeStyle = color;
			g.beginPath();
			g.moveTo(0, y);
			g.lineTo(width, y);
			g.stroke();
		}
		var textDecoration = options.enableTextDecoration ? Cufon.CSS.textDecoration(el, style) : {};
		if (textDecoration.underline) line(-font.face['underline-position'], textDecoration.underline);
		if (textDecoration.overline) line(font.ascent, textDecoration.overline);
		function renderText() {
			g.scale(stretchFactor, 1);
			for (var i = 0, j = 0, l = chars.length; i < l; ++i) {
				var glyph = glyphs[chars[i]] || font.missingGlyph;
				if (!glyph) continue;
				if (glyph.d) {
					g.beginPath();
					if (glyph.code) interpret(glyph.code, g);
					else glyph.code = generateFromVML('m' + glyph.d, g);
					g.fill();
				}
				g.translate(jumps[j++], 0);
			}
			g.restore();
		}
		if (shadows) {
			for (var i = shadows.length; i--;) {
				var shadow = shadows[i];
				g.save();
				g.fillStyle = shadow.color;
				g.translate.apply(g, shadowOffsets[i]);
				renderText();
			}
		}
		var gradient = options.textGradient;
		if (gradient) {
			var stops = gradient.stops,
			fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY);
			for (var i = 0, l = stops.length; i < l; ++i) {
				fill.addColorStop.apply(fill, stops[i]);
			}
			g.fillStyle = fill;
		} else g.fillStyle = style.get('color');
		renderText();
		if (textDecoration['line-through']) line(-font.descent, textDecoration['line-through']);
		return wrapper;
	};
})());
Cufon.registerEngine('vml', (function () {
	var ns = document.namespaces;
	if (!ns) return;
	ns.add('cvml', 'urn:schemas-microsoft-com:vml');
	ns = null;
	var check = document.createElement('cvml:shape');
	check.style.behavior = 'url(#default#VML)';
	if (!check.coordsize) return; // VML isn't supported
	check = null;
	var HAS_BROKEN_LINEHEIGHT = (document.documentMode || 0) < 8;
	document.write(('<style type="text/css">' + '.cufon-vml-canvas{text-indent:0;}' + '@media screen{' + 'cvml\\:shape,cvml\\:rect,cvml\\:fill,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute;}' + '.cufon-vml-canvas{position:absolute;text-align:left;}' + '.cufon-vml{display:inline-block;position:relative;vertical-align:' + (HAS_BROKEN_LINEHEIGHT ? 'middle' : 'text-bottom') + ';}' + '.cufon-vml .cufon-alt{position:absolute;left:-10000in;font-size:1px;}' + 'a .cufon-vml{cursor:pointer}' + // ignore !important here
	'}' + '@media print{' + '.cufon-vml *{display:none;}' + '.cufon-vml .cufon-alt{display:inline;}' + '}' + '</style>').replace(/;/g, '!important;'));
	function getFontSizeInPixels(el, value) {
		return getSizeInPixels(el, /(?:em|ex|%)$|^[a-z-]+$/i.test(value) ? '1em' : value);
	} // Original by Dead Edwards.
	// Combined with getFontSizeInPixels it also works with relative units.
	function getSizeInPixels(el, value) {
		if (/px$/i.test(value)) return parseFloat(value);
		var style = el.style.left,
		runtimeStyle = el.runtimeStyle.left;
		el.runtimeStyle.left = el.currentStyle.left;
		el.style.left = value.replace('%', 'em');
		var result = el.style.pixelLeft;
		el.style.left = style;
		el.runtimeStyle.left = runtimeStyle;
		return result;
	}
	var fills = {};
	function gradientFill(gradient) {
		var id = gradient.id;
		if (!fills[id]) {
			var stops = gradient.stops,
			fill = document.createElement('cvml:fill'),
			colors = [];
			fill.type = 'gradient';
			fill.angle = 180;
			fill.focus = '0';
			fill.method = 'sigma';
			fill.color = stops[0][1];
			for (var j = 1, k = stops.length - 1; j < k; ++j) {
				colors.push(stops[j][0] * 100 + '% ' + stops[j][1]);
			}
			fill.colors = colors.join(',');
			fill.color2 = stops[k][1];
			fills[id] = fill;
		}
		return fills[id];
	}
	return function (font, text, style, options, node, el, hasNext) {
		var redraw = (text === null);
		if (redraw) text = node.alt; // @todo word-spacing, text-decoration
		var viewBox = font.viewBox;
		var size = style.computedFontSize || (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
		var letterSpacing = style.computedLSpacing;
		if (letterSpacing == undefined) {
			letterSpacing = style.get('letterSpacing');
			style.computedLSpacing = letterSpacing = (letterSpacing == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, letterSpacing));
		}
		var wrapper, canvas;
		if (redraw) {
			wrapper = node;
			canvas = node.firstChild;
		} else {
			wrapper = document.createElement('span');
			wrapper.className = 'cufon cufon-vml';
			wrapper.alt = text;
			canvas = document.createElement('span');
			canvas.className = 'cufon-vml-canvas';
			wrapper.appendChild(canvas);
			if (options.printable) {
				var print = document.createElement('span');
				print.className = 'cufon-alt';
				print.appendChild(document.createTextNode(text));
				wrapper.appendChild(print);
			} // ie6, for some reason, has trouble rendering the last VML element in the document.
			// we can work around this by injecting a dummy element where needed.
			// @todo find a better solution
			if (!hasNext) wrapper.appendChild(document.createElement('cvml:shape'));
		}
		var wStyle = wrapper.style;
		var cStyle = canvas.style;
		var height = size.convert(viewBox.height),
		roundedHeight = Math.ceil(height);
		var roundingFactor = roundedHeight / height;
		var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch'));
		var minX = viewBox.minX,
		minY = viewBox.minY;
		cStyle.height = roundedHeight;
		cStyle.top = Math.round(size.convert(minY - font.ascent));
		cStyle.left = Math.round(size.convert(minX));
		wStyle.height = size.convert(font.height) + 'px';
		var textDecoration = options.enableTextDecoration ? Cufon.CSS.textDecoration(el, style) : {};
		var color = style.get('color');
		var chars = Cufon.CSS.textTransform(text, style).split(''),
		chr;
		var glyphs = font.glyphs,
		glyph, kerning, k;
		var width = 0,
		jumps = [],
		offsetX = 0,
		advance;
		var shape, shadows = options.textShadow; // pre-calculate width
		for (var i = 0, j = 0, l = chars.length; i < l; ++i) {
			glyph = glyphs[chr = chars[i]] || font.missingGlyph;
			if (!glyph) continue;
			if (kerning) {
				width -= k = kerning[chr] || 0;
				jumps[j - 1] -= k;
			}
			width += advance = jumps[j++] = ~~ (glyph.w || font.w) + letterSpacing;
			kerning = glyph.k;
		}
		if (advance === undefined) return null;
		var fullWidth = -minX + width + (viewBox.width - advance);
		var shapeWidth = size.convert(fullWidth * stretchFactor),
		roundedShapeWidth = Math.round(shapeWidth);
		var coordSize = fullWidth + ',' + viewBox.height,
		coordOrigin;
		var stretch = 'r' + coordSize + 'ns';
		var fill = options.textGradient && gradientFill(options.textGradient);
		for (i = 0, j = 0; i < l; ++i) {
			glyph = glyphs[chars[i]] || font.missingGlyph;
			if (!glyph) continue;
			if (redraw) { // some glyphs may be missing so we can't use i
				shape = canvas.childNodes[j];
				while (shape.firstChild) shape.removeChild(shape.firstChild); // shadow, fill
			} else {
				shape = document.createElement('cvml:shape');
				canvas.appendChild(shape);
			}
			shape.stroked = 'f';
			shape.coordsize = coordSize;
			shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
			shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
			shape.fillcolor = color;
			if (fill) shape.appendChild(fill.cloneNode(false)); // it's important to not set top/left or IE8 will grind to a halt
			var sStyle = shape.style;
			sStyle.width = roundedShapeWidth;
			sStyle.height = roundedHeight;
			if (shadows) { // due to the limitations of the VML shadow element there
				// can only be two visible shadows. opacity is shared
				// for all shadows.
				var shadow1 = shadows[0],
				shadow2 = shadows[1];
				var color1 = Cufon.CSS.color(shadow1.color),
				color2;
				var shadow = document.createElement('cvml:shadow');
				shadow.on = 't';
				shadow.color = color1.color;
				shadow.offset = shadow1.offX + ',' + shadow1.offY;
				if (shadow2) {
					color2 = Cufon.CSS.color(shadow2.color);
					shadow.type = 'double';
					shadow.color2 = color2.color;
					shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
				}
				shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
				shape.appendChild(shadow);
			}
			offsetX += jumps[j++];
		} // addresses flickering issues on :hover
		var cover = shape.nextSibling,
		coverFill, vStyle;
		if (options.forceHitArea) {
			if (!cover) {
				cover = document.createElement('cvml:rect');
				cover.stroked = 'f';
				cover.className = 'cufon-vml-cover';
				coverFill = document.createElement('cvml:fill');
				coverFill.opacity = 0;
				cover.appendChild(coverFill);
				canvas.appendChild(cover);
			}
			vStyle = cover.style;
			vStyle.width = roundedShapeWidth;
			vStyle.height = roundedHeight;
		} else if (cover) canvas.removeChild(cover);
		wStyle.width = Math.max(Math.ceil(size.convert(width * stretchFactor)), 0);
		if (HAS_BROKEN_LINEHEIGHT) {
			var yAdjust = style.computedYAdjust;
			if (yAdjust === undefined) {
				var lineHeight = style.get('lineHeight');
				if (lineHeight == 'normal') lineHeight = '1em';
				else if (!isNaN(lineHeight)) lineHeight += 'em'; // no unit
				style.computedYAdjust = yAdjust = 0.5 * (getSizeInPixels(el, lineHeight) - parseFloat(wStyle.height));
			}
			if (yAdjust) {
				wStyle.marginTop = Math.ceil(yAdjust) + 'px';
				wStyle.marginBottom = yAdjust + 'px';
			}
		}
		return wrapper;
	};
})());
Cufon.registerFont({"w":162,"face":{"font-family":"DaxCondensed","font-weight":400,"font-stretch":"condensed","units-per-em":"360","panose-1":"2 0 5 6 0 0 0 0 0 0","ascent":"288","descent":"-72","x-height":"4","bbox":"-13 -321 274 82","underline-thickness":"18","underline-position":"-36","stemh":"22","stemv":"31","unicode-range":"U+0020-U+2122"},"glyphs":{" ":{"w":95},"\u00f0":{"d":"144,-96v0,58,-18,100,-62,100v-42,0,-66,-38,-66,-100v0,-56,22,-115,82,-98v-5,-16,-11,-32,-19,-46r-27,10v-1,-1,-7,-13,-8,-14r27,-10v-6,-10,-10,-15,-16,-23r26,-10v6,8,9,13,15,23r24,-9v1,1,7,13,8,14r-24,9v22,42,40,98,40,154xm82,-20v46,0,36,-110,22,-153v-45,-15,-59,24,-58,77v0,41,9,76,36,76","w":161},"\u00dd":{"d":"115,-314r-34,48r-15,0r19,-48v11,-8,28,-8,30,0xm150,-248r-57,137r0,111r-32,0r0,-111r-57,-137r34,0r41,108r41,-108r30,0","w":154},"\u00fd":{"d":"114,-264r-34,49r-15,0r20,-48v10,-8,27,-9,29,-1xm145,-194r-80,271r-30,0r28,-77r-53,-194r32,0r35,154r40,-154r28,0","w":154},"\u00de":{"d":"151,-132v0,47,-32,78,-94,73r0,59r-32,0r0,-248r32,0r0,44v52,-16,94,21,94,72xm119,-130v0,-44,-26,-64,-62,-53r0,101v42,4,62,-18,62,-48","w":163},"\u00fe":{"d":"146,-99v-2,73,-37,115,-95,98r0,78r-30,0r0,-360r30,0r0,93v55,-24,97,20,95,91xm68,-19v66,1,64,-157,5,-157v-8,0,-17,2,-22,6r0,148v5,1,10,3,17,3"},"\u00bd":{"d":"233,0r-84,0v18,-35,52,-69,61,-110v4,-24,-33,-31,-48,-19r-7,-15v27,-18,80,-9,77,32v-3,32,-36,67,-51,93r52,0r0,19xm179,-244r-102,248r-20,-8r102,-248xm72,-97r-22,0r0,-122v-9,4,-21,6,-34,7r-2,-12v21,-3,38,-14,43,-24r15,0r0,151","w":247},"\u00bc":{"d":"233,-33r-11,0r0,33r-20,0r0,-33r-53,0r0,-16r54,-102r19,0r0,102r11,0r0,16xm189,-244r-102,248r-20,-8r102,-248xm72,-97r-22,0r0,-122v-9,4,-21,6,-34,7r-2,-12v21,-3,38,-14,43,-24r15,0r0,151xm202,-49r0,-71r-36,71r36,0","w":247},"\u00b9":{"d":"79,-97r-22,0r0,-122v-9,4,-21,6,-34,7r-2,-12v21,-3,38,-14,43,-24r15,0r0,151","w":112},"\u00be":{"d":"233,-33r-11,0r0,33r-20,0r0,-33r-53,0r0,-16r54,-102r19,0r0,102r11,0r0,16xm195,-244r-101,248r-20,-8r102,-248xm72,-182v37,11,28,87,-21,87v-17,0,-28,-3,-37,-9r7,-16v16,13,53,7,53,-21v0,-18,-13,-31,-37,-31r0,-14v36,3,46,-43,11,-46v-9,0,-16,2,-21,6r-8,-14v23,-20,73,-8,73,26v0,18,-11,27,-20,32xm202,-49r0,-71r-36,71r36,0","w":247},"\u00b3":{"d":"96,-142v0,42,-52,59,-82,38r7,-16v16,13,53,7,53,-21v0,-18,-13,-31,-37,-31r0,-14v36,3,46,-43,11,-46v-9,0,-16,2,-21,6r-8,-14v24,-20,73,-8,73,26v0,18,-11,27,-20,32v11,5,24,17,24,40","w":112},"\u00b2":{"d":"96,-97r-84,0v18,-35,53,-67,61,-110v4,-24,-32,-33,-47,-19r-8,-15v28,-18,81,-8,78,32v-3,31,-37,67,-52,93r52,0r0,19","w":112},"\u00a6":{"d":"48,-117r-24,0r0,-126r24,0r0,126xm48,63r-24,0r0,-126r24,0r0,126","w":72},"\u00d7":{"d":"141,-136r-120,50r0,-23r99,-38r-99,-37r0,-24r120,51r0,21xm141,-39r-120,0r0,-21r120,0r0,21"},"!":{"d":"73,-248r-8,167r-23,0r-8,-167r39,0xm74,-18v0,12,-9,21,-21,21v-11,0,-21,-9,-21,-21v0,-12,10,-22,21,-22v12,0,21,10,21,22","w":98},"\"":{"d":"108,-242r-8,89r-14,0r-8,-89v6,-11,25,-13,30,0xm57,-242r-8,89r-14,0r-8,-89v6,-12,24,-12,30,0","w":129},"#":{"d":"155,-152r-35,0r-8,50r33,0r0,18r-36,0r-12,84r-18,0r13,-84r-38,0r-14,84r-17,0r13,-84r-32,0r0,-18r35,0r7,-50r-32,0r0,-18r35,0r12,-78r17,0r-11,78r38,0r12,-78r18,0r-12,78r32,0r0,18xm102,-152r-38,0r-8,50r39,0"},"$":{"d":"55,-191v0,46,82,73,82,127v0,37,-22,63,-55,67r4,42r-22,0r3,-41v-21,-1,-35,-7,-45,-15r8,-25v19,19,74,17,74,-24v0,-23,-11,-36,-38,-57v-27,-22,-42,-44,-42,-71v0,-35,22,-59,51,-63r-4,-40r22,0r-4,40v18,1,32,6,40,14r-8,22v-17,-17,-66,-15,-66,24"},"%":{"d":"235,-81v0,38,-6,85,-41,85v-35,0,-42,-47,-42,-85v0,-37,7,-85,42,-85v35,0,41,48,41,85xm169,-246r-70,248r-16,-4r70,-248xm100,-167v0,37,-6,85,-41,85v-35,0,-41,-48,-41,-85v0,-38,6,-85,41,-85v35,0,41,47,41,85xm215,-81v0,-28,-1,-71,-21,-71v-20,0,-22,43,-22,71v0,27,2,68,22,68v20,0,21,-41,21,-68xm81,-167v0,-28,-2,-70,-22,-70v-20,0,-22,42,-22,70v0,27,2,68,22,68v20,0,22,-41,22,-68","w":252},"&":{"d":"188,-161v-7,5,-24,11,-35,14r0,136v-10,8,-30,15,-57,15v-46,0,-78,-27,-78,-72v-1,-37,25,-58,50,-72v-52,-15,-51,-111,21,-111v21,0,35,5,45,14r-9,20v-18,-17,-68,-15,-64,25v6,62,85,34,117,10xm123,-23r0,-116v-53,15,-73,31,-73,68v0,41,37,64,73,48","w":189},"'":{"d":"62,-242r-9,89r-17,0r-9,-89v7,-12,28,-11,35,0","w":83},"(":{"d":"103,72r-16,10v-90,-134,-90,-236,0,-369r16,10v-67,97,-67,253,0,349","w":111},")":{"d":"25,-287v91,133,90,235,0,369r-16,-10v66,-96,67,-252,0,-349","w":111},"*":{"d":"125,-245v0,8,-6,14,-14,14v-8,0,-15,-6,-15,-14v0,-8,7,-14,15,-14v8,0,14,6,14,14xm87,-273v0,8,-6,15,-14,15v-8,0,-14,-7,-14,-15v0,-8,6,-14,14,-14v8,0,14,6,14,14xm84,-233v0,6,-5,11,-11,11v-6,0,-11,-5,-11,-11v0,-6,5,-11,11,-11v6,0,11,5,11,11xm110,-201v0,8,-6,15,-14,15v-8,0,-14,-7,-14,-15v0,-8,6,-14,14,-14v8,0,14,6,14,14xm49,-245v0,8,-6,14,-14,14v-8,0,-14,-6,-14,-14v0,-8,6,-14,14,-14v8,0,14,6,14,14xm64,-201v0,8,-7,15,-15,15v-8,0,-14,-7,-14,-15v0,-8,6,-14,14,-14v8,0,15,6,15,14","w":139},"+":{"d":"141,-110r-49,0r0,63r-22,0r0,-63r-49,0r0,-21r49,0r0,-63r21,0r0,63r50,0r0,21"},",":{"d":"64,-29r-45,91r-18,0r27,-90v4,-12,34,-19,36,-1","w":84},"-":{"d":"89,-106r-71,0r0,-28r71,0r0,28","w":106},".":{"d":"62,-19v0,12,-9,21,-21,21v-12,0,-22,-9,-22,-21v0,-12,10,-21,21,-21v12,0,22,10,22,21","w":81},"\/":{"d":"169,-278r-144,359r-20,-8r144,-359","w":174},"0":{"d":"145,-124v0,62,-11,128,-64,128v-53,0,-64,-66,-64,-128v0,-62,11,-127,64,-127v53,0,64,65,64,127xm115,-123v0,-59,-4,-106,-34,-106v-30,0,-35,47,-35,106v0,59,5,102,35,102v30,0,34,-43,34,-102"},"1":{"d":"110,0r-30,0r0,-204v-13,6,-35,11,-54,12r-3,-17v32,-4,58,-22,66,-39r21,0r0,248"},"2":{"d":"139,0r-127,0r76,-127v15,-24,18,-38,18,-54v0,-39,-49,-56,-75,-34r-9,-22v41,-29,122,-11,116,53v-5,52,-59,112,-82,156r83,0r0,28"},"3":{"d":"138,-73v0,66,-77,98,-124,62r9,-23v27,22,84,10,84,-38v0,-32,-22,-54,-58,-54r0,-19v31,-2,52,-19,52,-45v0,-35,-48,-45,-69,-26r-9,-21v34,-29,108,-11,108,43v0,31,-19,47,-33,54v17,8,40,28,40,67"},"4":{"d":"145,-55r-17,0r0,55r-28,0r0,-55r-83,0r0,-23r84,-170r27,0r0,171r17,0r0,22xm99,-77r0,-126r-60,126r60,0"},"5":{"d":"139,-73v0,64,-76,99,-124,62r9,-23v8,6,22,12,41,12v24,0,43,-24,43,-50v0,-36,-20,-65,-81,-52r9,-124r98,0r0,28r-76,0r-5,72v55,-7,86,25,86,75"},"6":{"d":"145,-73v0,38,-20,77,-63,77v-41,0,-65,-36,-65,-94v0,-116,83,-154,104,-160r8,22v-24,9,-60,33,-75,83v50,-16,91,17,91,72xm82,-20v48,-6,46,-113,-9,-108v-11,0,-18,1,-24,4v-9,41,1,108,33,104"},"7":{"d":"151,-248v-27,68,-73,197,-82,248r-38,0r82,-220r-94,0r0,-28r132,0"},"8":{"d":"145,-59v0,38,-29,63,-64,63v-35,0,-64,-24,-64,-63v0,-31,16,-50,44,-72v-26,-22,-37,-37,-37,-64v0,-33,26,-56,57,-56v31,0,57,23,57,56v0,26,-14,42,-40,63v29,24,47,42,47,73xm112,-195v0,-19,-14,-34,-31,-34v-43,0,-40,58,-1,82v23,-18,32,-29,32,-48xm116,-59v0,-22,-12,-37,-37,-57v-23,18,-35,34,-35,56v0,22,15,39,36,39v21,0,36,-15,36,-38"},"9":{"d":"145,-157v0,116,-83,154,-104,160r-8,-23v24,-9,60,-34,75,-83v-48,18,-91,-16,-91,-71v0,-38,19,-77,63,-77v41,0,65,36,65,94xm113,-123v9,-41,0,-105,-33,-105v-24,0,-34,25,-34,54v0,43,30,65,67,51"},":":{"d":"61,-129v0,12,-9,21,-21,21v-11,0,-21,-9,-21,-21v0,-12,10,-21,21,-21v12,0,21,9,21,21xm61,-18v0,12,-9,21,-21,21v-11,0,-21,-9,-21,-21v0,-12,10,-22,21,-22v12,0,21,10,21,22","w":81},";":{"d":"64,-129v0,12,-8,21,-20,21v-12,0,-21,-9,-21,-21v0,-12,9,-22,21,-22v12,0,20,10,20,22xm64,-29r-45,91r-18,0r27,-90v4,-12,34,-19,36,-1","w":84},"<":{"d":"141,-58r-120,-51r0,-22r120,-52r0,25r-99,38r99,38r0,24"},"=":{"d":"141,-140r-120,0r0,-21r120,0r0,21xm141,-80r-120,0r0,-21r120,0r0,21"},">":{"d":"141,-109r-120,51r0,-24r99,-38r-99,-38r0,-25r120,51r0,23"},"?":{"d":"124,-192v0,31,-27,50,-42,56r-3,56r-23,0r-3,-67v38,-1,61,-78,7,-78v-15,0,-26,5,-32,10r-10,-22v35,-28,106,-12,106,45xm89,-18v0,12,-10,21,-22,21v-11,0,-21,-9,-21,-21v0,-12,10,-21,21,-21v12,0,22,9,22,21","w":137},"@":{"d":"239,-148v0,44,-21,126,-65,126v-14,0,-22,-10,-23,-32v-13,20,-25,32,-44,32v-25,0,-36,-28,-36,-57v0,-38,21,-100,60,-100v19,0,25,11,34,32r4,-25r17,0v-6,40,-17,62,-17,111v0,18,2,22,11,22v29,0,45,-83,45,-109v0,-54,-29,-89,-72,-89v-68,0,-118,77,-118,158v0,70,41,113,96,113v58,0,83,-35,97,-53r8,12v-22,27,-45,57,-105,57v-63,0,-111,-48,-111,-129v0,-92,59,-172,133,-172v50,0,86,38,86,103xm114,-40v42,0,64,-121,19,-121v-29,0,-42,53,-42,81v0,21,6,40,23,40","w":262},"A":{"d":"174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"B":{"d":"161,-68v0,76,-88,86,-136,57r0,-226v41,-28,128,-13,128,49v0,33,-23,49,-39,57v18,5,47,22,47,63xm122,-185v0,-39,-34,-51,-66,-40r0,85v42,1,66,-7,66,-45xm129,-68v0,-40,-24,-54,-73,-52r0,96v33,11,73,0,73,-44","w":176},"C":{"d":"142,-11v-64,45,-123,-20,-123,-113v0,-87,54,-158,122,-113r-7,21v-6,-5,-17,-10,-31,-10v-71,0,-68,203,0,203v14,0,25,-5,31,-10","w":151},"D":{"d":"169,-126v0,76,-27,130,-87,130v-26,0,-47,-6,-57,-15r0,-226v12,-8,32,-14,56,-14v59,0,88,49,88,125xm136,-127v-1,-77,-24,-113,-79,-97r0,198v56,17,80,-21,79,-101","w":187},"E":{"d":"125,0r-100,0r0,-248r100,0r0,25r-68,0r0,83r61,0r0,24r-61,0r0,91r68,0r0,25","w":140},"F":{"d":"125,-223r-68,0r0,83r61,0r0,24r-61,0r0,116r-32,0r0,-248r100,0r0,25","w":136},"G":{"d":"163,-11v-71,39,-144,0,-144,-114v0,-105,70,-151,140,-112r-9,22v-52,-29,-101,2,-99,92v2,66,25,117,80,97r0,-90r-33,0r0,-24r65,0r0,129","w":185},"H":{"d":"162,0r-32,0r0,-116r-73,0r0,116r-32,0r0,-248r32,0r0,108r73,0r0,-108r32,0r0,248","w":187},"I":{"d":"58,0r-32,0r0,-248r32,0r0,248","w":83},"J":{"d":"60,-11v0,53,-36,87,-70,91r0,-24v18,-5,37,-30,37,-65r0,-239r33,0r0,237","w":84},"K":{"d":"162,0r-36,0r-68,-129r0,129r-32,0r0,-248r32,0r0,117r68,-117r31,0r-70,116","w":166},"L":{"d":"121,0r-95,0r0,-248r32,0r0,223r63,0r0,25","w":127},"M":{"d":"223,0r-30,0r-16,-206r-48,206r-20,0r-50,-206r-15,206r-28,0r24,-248r33,0r47,198r46,-198r33,0","w":239},"N":{"d":"175,0r-27,0r-98,-204r1,204r-26,0r0,-248r33,0r92,193r-1,-193r26,0r0,248","w":199},"O":{"d":"174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"P":{"d":"151,-175v1,47,-30,78,-94,73r0,102r-32,0r0,-237v12,-8,32,-14,54,-14v50,0,72,31,72,76xm120,-176v0,-41,-28,-62,-63,-49r0,100v45,4,63,-18,63,-51"},"Q":{"d":"96,-251v96,0,99,197,39,243r32,45r-26,24r-25,-60v-71,17,-97,-49,-97,-125v0,-69,21,-127,77,-127xm141,-125v0,-50,-8,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"R":{"d":"159,0r-36,0r-66,-118r0,118r-32,0r0,-237v12,-9,30,-14,53,-14v93,0,93,136,16,140xm120,-175v3,-45,-29,-65,-64,-50r0,100v32,4,61,-7,64,-50","w":169},"S":{"d":"48,-191v-4,47,91,75,82,127v6,62,-75,86,-116,53r8,-25v19,19,74,17,74,-24v0,-23,-11,-36,-38,-57v-27,-22,-42,-44,-42,-71v0,-59,68,-79,105,-49r-7,22v-18,-17,-71,-16,-66,24","w":145},"T":{"d":"122,-223r-42,0r0,223r-31,0r0,-223r-42,0r0,-25r115,0r0,25","w":128},"U":{"d":"159,-69v0,45,-28,73,-67,73v-38,0,-67,-27,-67,-73r0,-179r32,0r0,183v0,24,15,41,36,41v22,0,37,-17,37,-41r0,-183r29,0r0,179","w":183},"V":{"d":"157,-248r-61,248r-27,0r-61,-248r33,0r42,203r44,-203r30,0","w":164},"W":{"d":"238,-248r-49,248r-27,0r-38,-188r-39,188r-27,0r-49,-248r32,0r33,200r40,-200r22,0r39,200r35,-200r28,0","w":246},"X":{"d":"153,0r-36,0r-40,-99r-41,99r-32,0r58,-127r-51,-121r33,0r36,91r37,-91r30,0r-52,117","w":156},"Y":{"d":"151,-248r-58,137r0,111r-31,0r0,-111r-58,-137r34,0r41,108r42,-108r30,0","w":154},"Z":{"d":"127,0r-118,0r77,-223r-72,0r0,-25r113,0r-76,223r76,0r0,25","w":139},"[":{"d":"93,77r-68,0r0,-360r68,0r0,21r-41,0r0,319r41,0r0,20","w":104},"\\":{"d":"171,73r-20,8r-144,-359r20,-8","w":176},"]":{"d":"80,77r-68,0r0,-20r41,0r0,-319r-41,0r0,-21r68,0r0,360","w":104},"^":{"d":"142,-174r-11,14r-49,-64r-49,64r-11,-14r45,-67v9,-13,21,-13,30,0","w":164},"_":{"d":"166,74r-170,0r0,-24r170,0r0,24"},"`":{"d":"46,-216r-15,0r-35,-48v2,-8,20,-8,30,0","w":60},"a":{"d":"134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"b":{"d":"147,-101v0,62,-25,105,-75,105v-25,0,-40,-7,-50,-15r0,-272r30,0r0,94v52,-26,95,16,95,88xm117,-100v0,-53,-22,-92,-65,-70r0,147v45,16,65,-20,65,-77","w":163},"c":{"d":"120,-11v-47,38,-103,0,-103,-86v0,-84,54,-125,102,-86r-6,19v-35,-27,-65,6,-65,66v0,59,28,94,64,66","w":129},"d":{"d":"142,-11v-11,8,-29,15,-52,15v-48,0,-73,-39,-73,-99v1,-73,37,-115,95,-98r0,-90r30,0r0,272xm112,-23r0,-150v-46,-15,-65,29,-65,76v0,55,20,90,65,74","w":163},"e":{"d":"147,-98r-101,13v-3,64,52,76,88,52r8,22v-57,35,-125,8,-125,-87v0,-64,27,-100,67,-100v41,0,64,33,63,100xm117,-115v-1,-40,-12,-59,-34,-59v-22,0,-38,25,-38,69","w":163},"f":{"d":"90,-172r-29,0r0,172r-30,0r0,-172r-17,0r0,-22r17,0v-5,-53,12,-91,59,-89r0,23v-25,-5,-33,34,-29,66r29,0r0,22","w":99},"g":{"d":"142,3v7,74,-70,95,-117,64r10,-23v7,5,22,12,38,12v32,0,41,-27,39,-61v-53,27,-95,-15,-95,-88v0,-62,26,-105,76,-105v24,0,39,7,49,15r0,186xm112,-25r0,-146v-45,-16,-66,18,-65,77v1,56,21,92,65,69","w":163},"h":{"d":"143,0r-30,0r0,-140v2,-37,-37,-42,-61,-27r0,167r-30,0r0,-283r30,0r0,96v38,-24,91,-8,91,47r0,140","w":164},"i":{"d":"56,-249v0,10,-7,17,-17,17v-10,0,-18,-7,-18,-17v0,-10,8,-18,18,-18v10,0,17,8,17,18xm54,0r-31,0r0,-194r31,0r0,194","w":77},"j":{"d":"55,-249v0,10,-7,17,-17,17v-10,0,-18,-7,-18,-17v0,-10,8,-18,18,-18v10,0,17,8,17,18xm53,-12v0,57,-30,89,-60,92r0,-23v16,-6,30,-25,30,-67r0,-184r30,0r0,182","w":76},"k":{"d":"151,0r-33,0r-65,-102r0,102r-30,0r0,-283r30,0r0,179r64,-90r30,0r-66,90","w":152},"l":{"d":"54,0r-30,0r0,-283r30,0r0,283","w":78},"m":{"d":"233,0r-30,0r0,-140v2,-38,-42,-45,-60,-20r0,160r-30,0r0,-140v3,-31,-36,-43,-61,-30r0,170r-30,0r0,-183v21,-19,96,-21,108,7v30,-37,103,-28,103,36r0,140","w":255},"n":{"d":"143,0r-30,0r0,-140v5,-36,-38,-40,-61,-30r0,170r-30,0r0,-183v38,-24,121,-26,121,43r0,140","w":164},"o":{"d":"147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"p":{"d":"147,-99v-5,73,-37,115,-95,98r0,78r-30,0r0,-260v57,-37,130,-2,125,84xm117,-98v0,-52,-23,-92,-65,-73r0,149v44,18,65,-30,65,-76","w":163},"q":{"d":"142,77r-30,0r0,-82v-53,26,-95,-15,-95,-88v0,-86,65,-130,125,-90r0,260xm112,-25r0,-146v-44,-16,-66,18,-65,77v0,56,20,90,65,69","w":163},"r":{"d":"96,-198r-7,27v-13,-5,-27,-4,-37,2r0,169r-30,0r0,-183v17,-10,32,-16,74,-15","w":101},"s":{"d":"45,-149v3,42,68,48,68,100v0,49,-69,69,-101,38r8,-23v13,16,62,18,61,-14v-1,-43,-66,-51,-66,-99v0,-48,63,-66,92,-36r-8,20v-13,-16,-56,-15,-54,14","w":126},"t":{"d":"90,-1v-33,8,-60,-8,-59,-47r0,-124r-17,0r0,-22r17,0r0,-45r30,-11r0,56r29,0r0,22r-29,0r0,122v0,25,11,31,29,27r0,22","w":100},"u":{"d":"141,-12v-12,9,-32,16,-59,16v-87,0,-54,-120,-60,-198r30,0r0,137v-3,37,36,44,59,30r0,-167r30,0r0,182"},"v":{"d":"144,-194r-54,194r-26,0r-54,-194r31,0r37,151r38,-151r28,0","w":153},"w":{"d":"226,-194r-48,194r-24,0r-35,-147r-37,147r-23,0r-47,-194r29,0r32,151r35,-151r24,0r35,151r32,-151r27,0","w":237},"x":{"d":"140,0r-32,0r-35,-76r-35,76r-30,0r51,-99r-46,-95r31,0r31,69r33,-69r27,0r-46,91","w":147},"y":{"d":"144,-194r-80,271r-30,0r28,-77r-52,-194r31,0r35,154r40,-154r28,0","w":154},"z":{"d":"117,0r-108,0r68,-172r-63,0r0,-22r102,0r-67,172r68,0r0,22","w":127},"{":{"d":"101,46v-34,0,-61,-14,-61,-54v0,-34,8,-106,-23,-100r0,-21v31,6,23,-67,23,-100v0,-39,27,-54,61,-54r0,19v-66,-5,-5,132,-61,146v56,2,-7,148,61,145r0,19","w":117},"|":{"d":"48,77r-24,0r0,-360r24,0r0,360","w":72},"}":{"d":"101,-108v-31,-6,-23,66,-24,100v0,40,-26,54,-60,54r0,-19v66,6,3,-133,60,-146v-55,-3,6,-147,-60,-145r0,-19v34,0,60,15,60,54v1,34,-7,106,24,100r0,21","w":117},"~":{"d":"158,-128v-4,15,-14,26,-33,29v-24,4,-39,-23,-61,-19v-15,2,-21,15,-24,23r-12,-19v5,-15,13,-26,31,-29v27,-4,40,22,61,19v15,-3,22,-14,25,-23","w":185},"\u00c4":{"d":"132,-284v0,9,-6,15,-15,15v-9,0,-16,-6,-16,-15v0,-9,7,-16,16,-16v9,0,15,7,15,16xm79,-284v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"\u00c5":{"d":"119,-293v0,15,-14,29,-29,29v-16,0,-28,-14,-28,-29v0,-16,12,-28,28,-28v15,0,29,12,29,28xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm108,-293v0,-10,-9,-17,-18,-17v-10,0,-17,7,-17,17v0,10,7,18,17,18v9,0,18,-8,18,-18xm117,-94r-28,-117r-30,117r58,0","w":179},"\u00c7":{"d":"142,-11v-64,45,-123,-20,-123,-113v0,-87,54,-158,122,-113r-7,21v-6,-5,-17,-10,-31,-10v-71,0,-68,203,0,203v14,0,25,-5,31,-10xm124,38v0,20,-33,30,-69,25r6,-12v39,8,64,-25,11,-22r6,-11v32,-3,46,4,46,20","w":151},"\u00c9":{"d":"108,-314r-35,48r-14,0r19,-48v10,-8,28,-8,30,0xm125,0r-100,0r0,-248r100,0r0,25r-68,0r0,83r61,0r0,24r-61,0r0,91r68,0r0,25","w":140},"\u00d1":{"d":"143,-288v-3,10,-8,17,-21,19v-16,3,-26,-15,-41,-13v-10,1,-15,11,-17,16r-10,-16v3,-10,9,-18,21,-19v18,-3,27,15,41,13v11,-2,15,-10,16,-16xm175,0r-27,0r-98,-204r1,204r-26,0r0,-248r33,0r92,193r-1,-193r26,0r0,248","w":199},"\u00d6":{"d":"138,-284v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm85,-284v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"\u00dc":{"d":"135,-284v0,9,-7,15,-15,15v-9,0,-16,-6,-16,-15v0,-9,7,-16,16,-16v8,0,15,7,15,16xm82,-284v0,9,-6,15,-15,15v-9,0,-16,-6,-16,-15v0,-9,7,-16,16,-16v9,0,15,7,15,16xm159,-69v0,45,-28,73,-67,73v-38,0,-67,-27,-67,-73r0,-179r32,0r0,183v0,24,15,41,36,41v22,0,37,-17,37,-41r0,-183r29,0r0,179","w":183},"\u00e1":{"d":"118,-264r-34,49r-15,0r20,-48v10,-8,27,-9,29,-1xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e0":{"d":"93,-216r-14,0r-35,-48v2,-8,19,-8,30,0xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e2":{"d":"119,-221r-8,7r-32,-26r-33,26r-7,-7v13,-13,22,-33,39,-42v19,7,27,29,41,42xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e4":{"d":"122,-234v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-8,7,-16,15,-16v9,0,16,8,16,16xm69,-234v0,9,-7,15,-16,15v-9,0,-15,-6,-15,-15v0,-8,6,-16,15,-16v9,0,16,8,16,16xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e3":{"d":"123,-238v-3,10,-9,19,-22,19v-20,0,-48,-26,-57,3r-10,-15v3,-10,9,-18,21,-20v18,-3,27,15,41,13v10,-2,14,-9,16,-15xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e5":{"d":"108,-243v0,15,-12,28,-28,28v-15,0,-29,-13,-29,-28v0,-16,14,-29,29,-29v16,0,28,13,28,29xm134,-11v-37,29,-119,16,-119,-44v0,-51,42,-66,90,-72v9,-53,-44,-56,-73,-36r-7,-20v36,-23,109,-26,109,49r0,123xm97,-243v0,-10,-7,-18,-17,-18v-10,0,-18,8,-18,18v0,10,8,17,18,17v10,0,17,-7,17,-17xm105,-22r0,-86v-40,6,-61,17,-61,53v0,36,35,45,61,33","w":155},"\u00e7":{"d":"120,-11v-47,38,-103,0,-103,-86v0,-84,54,-125,102,-86r-6,19v-35,-27,-65,6,-65,66v0,59,28,94,64,66xm104,38v0,20,-32,30,-68,25r5,-12v22,3,41,-3,41,-12v0,-7,-6,-11,-29,-10r6,-11v32,-3,45,4,45,20","w":129},"\u00e9":{"d":"122,-264r-34,49r-15,0r20,-48v11,-8,27,-9,29,-1xm147,-98r-101,13v-3,64,52,76,88,52r8,22v-57,35,-125,8,-125,-87v0,-64,27,-100,67,-100v41,0,64,33,63,100xm117,-115v-1,-40,-12,-59,-34,-59v-22,0,-38,25,-38,69","w":163},"\u00e8":{"d":"95,-216r-14,0r-35,-48v2,-8,19,-8,29,0xm147,-98r-101,13v-3,64,52,76,88,52r8,22v-57,35,-125,8,-125,-87v0,-64,27,-100,67,-100v41,0,64,33,63,100xm117,-115v-1,-40,-12,-59,-34,-59v-22,0,-38,25,-38,69","w":163},"\u00ea":{"d":"124,-221r-7,7r-33,-26r-33,26r-7,-7v14,-13,22,-34,40,-42v18,8,26,29,40,42xm147,-98r-101,13v-3,64,52,76,88,52r8,22v-57,35,-125,8,-125,-87v0,-64,27,-100,67,-100v41,0,64,33,63,100xm117,-115v-1,-40,-12,-59,-34,-59v-22,0,-38,25,-38,69","w":163},"\u00eb":{"d":"127,-234v0,9,-7,15,-16,15v-9,0,-15,-6,-15,-15v0,-8,6,-16,15,-16v9,0,16,8,16,16xm74,-234v0,9,-7,15,-16,15v-9,0,-15,-6,-15,-15v0,-8,6,-16,15,-16v9,0,16,8,16,16xm147,-98r-101,13v-3,64,52,76,88,52r8,22v-57,35,-125,8,-125,-87v0,-64,27,-100,67,-100v41,0,64,33,63,100xm117,-115v-1,-40,-12,-59,-34,-59v-22,0,-38,25,-38,69","w":163},"\u00ed":{"d":"72,-264r-34,49r-15,0r19,-48v10,-8,28,-9,30,-1xm53,0r-30,0r0,-194r30,0r0,194","w":76},"\u00ec":{"d":"54,-216r-15,0r-35,-48v2,-8,19,-8,30,0xm55,0r-29,0r0,-194r29,0r0,194","w":76},"\u00ee":{"d":"79,-221r-7,7r-33,-26r-33,26r-7,-7v14,-13,22,-34,40,-42v18,8,26,29,40,42xm54,0r-30,0r0,-194r30,0r0,194","w":77},"\u00ef":{"d":"83,-234v0,9,-6,15,-15,15v-8,0,-16,-6,-16,-15v0,-8,8,-16,16,-16v9,0,15,8,15,16xm30,-234v0,9,-7,15,-15,15v-9,0,-16,-6,-16,-15v0,-8,7,-16,16,-16v8,0,15,8,15,16xm56,0r-30,0r0,-194r30,0r0,194","w":82},"\u00f1":{"d":"125,-238v-3,10,-9,17,-22,19v-16,3,-25,-14,-40,-12v-10,1,-15,10,-17,15r-10,-15v3,-10,9,-18,21,-20v18,-3,27,15,41,13v10,-2,14,-9,16,-15xm143,0r-30,0r0,-140v5,-36,-37,-40,-61,-30r0,170r-30,0r0,-183v38,-23,121,-27,121,43r0,140","w":164},"\u00f3":{"d":"120,-264r-34,49r-15,0r19,-48v10,-8,28,-9,30,-1xm147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"\u00f2":{"d":"94,-216r-14,0r-35,-48v2,-8,19,-8,29,0xm147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"\u00f4":{"d":"122,-221r-7,7r-33,-26r-32,26r-8,-7v14,-13,22,-34,40,-42v18,8,26,29,40,42xm147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"\u00f6":{"d":"124,-234v0,8,-7,15,-16,15v-8,0,-15,-7,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm71,-234v0,8,-7,15,-16,15v-8,0,-15,-7,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"\u00f5":{"d":"125,-238v-3,10,-8,19,-21,19v-20,0,-49,-25,-58,3v-12,-12,-9,-32,12,-35v18,-3,26,15,40,13v10,-2,15,-9,17,-15xm147,-97v0,56,-19,101,-65,101v-46,0,-65,-45,-65,-101v0,-57,19,-101,65,-101v46,0,65,44,65,101xm117,-97v0,-39,-6,-79,-35,-79v-29,0,-35,40,-35,79v0,39,7,76,35,76v29,0,35,-37,35,-76","w":163},"\u00fa":{"d":"117,-264r-34,49r-15,0r19,-48v11,-8,28,-9,30,-1xm141,-12v-12,9,-32,16,-59,16v-87,0,-54,-120,-60,-198r30,0r0,137v-3,37,36,44,59,30r0,-167r30,0r0,182"},"\u00f9":{"d":"94,-216r-14,0r-35,-48v2,-8,19,-8,30,0xm141,-12v-12,9,-32,16,-59,16v-87,0,-54,-120,-60,-198r30,0r0,137v-3,37,36,44,59,30r0,-167r30,0r0,182"},"\u00fb":{"d":"122,-221r-8,7r-32,-26r-33,26r-8,-7v14,-13,23,-33,40,-42v18,8,27,29,41,42xm141,-12v-12,9,-32,16,-59,16v-87,0,-54,-120,-60,-198r30,0r0,137v-3,37,36,44,59,30r0,-167r30,0r0,182"},"\u00fc":{"d":"123,-234v0,9,-6,15,-15,15v-8,0,-15,-6,-15,-15v0,-8,7,-16,15,-16v9,0,15,8,15,16xm71,-234v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-8,7,-16,15,-16v9,0,16,8,16,16xm141,-12v-12,9,-32,16,-59,16v-87,0,-54,-120,-60,-198r30,0r0,137v-3,37,36,44,59,30r0,-167r30,0r0,182"},"\u00b0":{"d":"126,-198v0,30,-23,53,-53,53v-30,0,-54,-23,-54,-53v0,-30,24,-53,54,-53v30,0,53,23,53,53xm107,-198v0,-19,-15,-34,-34,-34v-19,0,-34,15,-34,34v0,19,15,34,34,34v19,0,34,-15,34,-34","w":145},"\u00a2":{"d":"117,-46v-6,7,-15,11,-28,12r2,34r-20,0r2,-34v-29,-6,-47,-38,-47,-87v0,-49,18,-81,47,-86r-2,-34r20,0r-2,34v13,1,23,7,28,13r-7,19v-28,-23,-54,2,-54,53v0,52,25,80,54,54"},"\u00a3":{"d":"147,0r-129,0v20,-28,39,-72,28,-118r-29,0r0,-21r24,0v-16,-48,-1,-112,57,-112v24,0,40,7,47,14r-9,21v-6,-4,-17,-9,-33,-9v-40,-2,-43,52,-34,86r40,0r0,21r-37,0v7,32,0,66,-14,90r89,0r0,28"},"\u00a7":{"d":"112,-140v45,38,30,98,-7,123v38,30,23,98,-28,98v-21,0,-36,-5,-45,-14r8,-23v10,16,58,17,56,-11v-2,-51,-73,-72,-73,-131v0,-31,19,-49,33,-60v-30,-27,-21,-93,30,-93v21,0,34,7,41,14r-8,20v-10,-16,-58,-14,-52,14v-3,18,30,50,45,63xm90,-33v23,-14,37,-62,6,-85r-25,-26v-24,10,-27,65,-5,87"},"\u00b6":{"d":"149,-231r-9,0r0,308r-18,0r0,-308r-45,0r0,308r-18,0r0,-188v-31,-6,-52,-28,-52,-66v0,-79,68,-73,142,-71r0,17"},"\u00df":{"d":"107,-140v-1,27,58,56,49,89v5,44,-55,69,-86,44r6,-22v14,11,55,7,50,-22v4,-28,-53,-56,-46,-86v0,-24,13,-38,32,-44v6,-32,1,-82,-27,-82v-25,0,-32,31,-32,71r0,192r-30,0r0,-190v0,-57,21,-96,62,-96v51,0,69,65,54,115v-20,3,-32,12,-32,31","w":169},"\u00ae":{"d":"274,-123v0,70,-58,126,-128,126v-70,0,-127,-56,-127,-126v0,-70,57,-128,127,-128v70,0,128,58,128,128xm259,-123v0,-62,-50,-114,-113,-114v-62,0,-113,52,-113,114v0,62,51,112,113,112v63,0,113,-50,113,-112xm199,-50r-19,0r-51,-70r-6,0r-1,70r-15,0r0,-147v32,-16,84,-6,83,35v0,26,-18,40,-43,41xm173,-162v0,-31,-30,-35,-51,-26r0,56v27,1,51,-3,51,-30","w":292},"\u00a9":{"d":"274,-123v0,70,-58,126,-128,126v-70,0,-127,-56,-127,-126v0,-70,57,-128,127,-128v70,0,128,58,128,128xm259,-123v0,-62,-50,-114,-113,-114v-62,0,-113,52,-113,114v0,62,51,112,113,112v63,0,113,-50,113,-112xm206,-76v-39,54,-135,22,-135,-48v0,-70,96,-104,135,-49r-13,11v-31,-42,-105,-18,-105,38v0,54,73,79,105,38","w":292},"\u2122":{"d":"219,-128r-16,0r-11,-105r-27,105r-15,0r-28,-105r-10,105r-15,0r14,-120r21,0r26,100r25,-100r22,0xm90,-234r-28,0r0,106r-17,0r0,-106r-29,0r0,-14r74,0r0,14","w":243},"\u00b4":{"d":"63,-264r-34,49r-15,0r19,-48v10,-8,28,-9,30,-1","w":60},"\u00a8":{"d":"72,-234v0,9,-6,15,-15,15v-8,0,-16,-6,-16,-15v0,-8,8,-16,16,-16v9,0,15,8,15,16xm19,-234v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-8,7,-16,15,-16v9,0,16,8,16,16","w":60},"\u00c6":{"d":"220,0r-83,0r-14,-72r-69,0r-24,72r-30,0r89,-248r111,0r0,26r-79,0r18,82r61,0r0,25r-56,0r19,89r57,0r0,26xm120,-94r-22,-115r-37,115r59,0","w":226},"\u00d8":{"d":"161,-220v44,65,30,228,-59,224v-21,0,-36,-8,-50,-22r-22,41r-10,-6r24,-44v-44,-64,-31,-224,58,-224v21,0,37,7,51,21r21,-39r10,6xm140,-205v-9,-15,-21,-25,-38,-25v-63,0,-61,115,-42,173xm102,-20v63,0,60,-112,43,-171r-79,149v9,14,20,22,36,22","w":204},"\u00b1":{"d":"141,-131r-49,0r0,63r-22,0r0,-63r-49,0r0,-22r49,0r0,-62r21,0r0,62r50,0r0,22xm141,-24r-120,0r0,-22r120,0r0,22"},"\u00a5":{"d":"152,-248r-37,90r31,-3r0,16r-35,-3r-16,37r51,-3r0,16r-50,-2r0,100r-30,0r0,-100r-50,2r0,-16r51,3r-16,-37r-35,3r0,-16r31,3r-38,-90r35,0r39,111r40,-111r29,0"},"\u00aa":{"d":"94,-138v-23,16,-82,12,-82,-26v0,-31,29,-39,62,-43v6,-33,-32,-33,-50,-22r-5,-12v24,-13,75,-15,75,30r0,73xm74,-144r0,-51v-27,4,-42,9,-42,31v0,22,24,27,42,20","w":106},"\u00ba":{"d":"100,-190v0,33,-13,60,-45,60v-32,0,-44,-27,-44,-60v0,-34,13,-60,44,-60v32,0,45,26,45,60xm55,-144v36,0,34,-93,0,-93v-34,0,-33,93,0,93","w":110},"\u00e6":{"d":"232,-98r-100,13v-2,66,51,75,88,52r7,22v-24,22,-94,20,-105,-17v-8,19,-23,32,-47,32v-40,0,-60,-24,-60,-59v0,-48,30,-66,90,-72v8,-51,-44,-58,-72,-36r-7,-20v24,-18,85,-26,96,16v39,-62,120,-28,110,69xm202,-114v0,-40,-12,-61,-34,-61v-21,0,-37,26,-37,70xm76,-18v35,2,29,-53,29,-90v-45,5,-61,18,-61,52v0,24,12,38,32,38","w":248},"\u00f8":{"d":"128,-175v35,46,28,179,-45,179v-17,0,-29,-6,-39,-15r-18,35r-8,-5r19,-38v-34,-48,-25,-179,46,-179v17,0,28,6,38,15r18,-35r8,5xm110,-163v-5,-8,-14,-16,-27,-16v-50,0,-43,87,-32,134xm83,-18v49,0,44,-89,31,-134r-58,119v6,9,14,15,27,15","w":165},"\u00bf":{"d":"97,-152v0,12,-9,21,-21,21v-12,0,-21,-9,-21,-21v0,-12,9,-22,21,-22v12,0,21,10,21,22xm125,67v-34,27,-107,14,-107,-44v0,-32,28,-52,44,-58r2,-55r23,0r4,66v-21,5,-41,19,-41,45v0,35,46,42,66,25","w":138},"\u00a1":{"d":"74,-152v0,12,-9,21,-21,21v-11,0,-21,-9,-21,-21v0,-12,10,-21,21,-21v12,0,21,9,21,21xm73,77r-39,0r8,-167r23,0","w":98},"\u00ac":{"d":"141,-52r-22,0r0,-58r-98,1r0,-22r120,0r0,79"},"\u00ab":{"d":"139,-58r-15,14v-5,-8,-42,-67,-47,-76v5,-9,42,-67,47,-76r15,13r-38,63xm84,-58r-15,14v-5,-8,-42,-67,-47,-76v5,-9,42,-67,47,-76r15,13r-38,63","w":165},"\u00bb":{"d":"144,-120v-5,9,-43,68,-48,76r-15,-14r39,-62r-39,-63r15,-13v5,8,43,67,48,76xm88,-120v-5,9,-42,68,-47,76r-15,-14r38,-62r-38,-63r15,-13v5,8,42,67,47,76","w":165},"\u2026":{"d":"271,-18v0,12,-9,21,-21,21v-12,0,-21,-9,-21,-21v0,-12,9,-22,21,-22v12,0,21,10,21,22xm171,-18v0,12,-9,21,-21,21v-12,0,-21,-9,-21,-21v0,-12,9,-22,21,-22v12,0,21,10,21,22xm71,-18v0,12,-9,21,-21,21v-12,0,-21,-9,-21,-21v0,-12,9,-22,21,-22v12,0,21,10,21,22","w":300},"\u00a0":{"w":95},"\u00c0":{"d":"99,-266r-15,0r-35,-48v2,-8,19,-8,30,0xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"\u00c3":{"d":"136,-288v-3,10,-9,17,-22,19v-16,3,-25,-15,-40,-13v-10,1,-15,11,-17,16v-11,-12,-11,-32,11,-35v18,-2,27,15,41,13v10,-2,15,-10,17,-16xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"\u00d5":{"d":"140,-288v-3,10,-8,17,-21,19v-16,3,-26,-15,-41,-13v-10,1,-14,11,-16,16r-11,-16v3,-10,9,-18,21,-19v18,-3,27,15,41,13v10,-2,16,-10,17,-16xm174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"\u2013":{"d":"120,-110r-120,1r0,-22r120,0r0,21","w":119},"\u2014":{"d":"239,-111r-239,0r0,-18r239,0r0,18","w":239},"\u201c":{"d":"150,-283r-27,90v-5,13,-34,18,-36,0r45,-90r18,0xm89,-283r-27,90v-5,13,-34,18,-36,0r45,-90r18,0","w":174},"\u201d":{"d":"149,-273r-45,90r-18,0r27,-89v5,-13,34,-18,36,-1xm88,-273r-45,90r-18,0r27,-89v5,-13,34,-18,36,-1","w":174},"\u2018":{"d":"77,-283r-27,90v-5,13,-34,18,-36,0r45,-90r18,0","w":90},"\u2019":{"d":"76,-273r-44,90r-18,0r26,-89v5,-12,34,-19,36,-1","w":90},"\u00f7":{"d":"100,-177v0,10,-9,18,-19,18v-10,0,-19,-8,-19,-18v0,-10,9,-19,19,-19v10,0,19,9,19,19xm141,-111r-120,0r0,-19r120,0r0,19xm100,-63v0,10,-9,18,-19,18v-10,0,-19,-8,-19,-18v0,-11,9,-19,19,-19v10,0,19,8,19,19"},"\u00ff":{"d":"121,-234v0,9,-7,16,-16,16v-9,0,-15,-7,-15,-16v0,-9,6,-15,15,-15v9,0,16,6,16,15xm68,-234v0,9,-7,16,-16,16v-8,0,-15,-7,-15,-16v0,-9,7,-15,15,-15v9,0,16,6,16,15xm145,-194r-80,271r-30,0r28,-77r-53,-194r32,0r35,154r40,-154r28,0","w":154},"\u00a4":{"d":"153,-61r-12,13r-16,-16v-26,22,-62,21,-88,0r-16,16r-12,-13r16,-17v-20,-27,-20,-69,0,-95r-16,-17r12,-14r16,17v26,-22,62,-21,88,0r16,-17r12,14r-16,17v20,26,20,68,0,95xm132,-126v0,-31,-21,-57,-51,-57v-30,0,-51,26,-51,57v0,31,22,58,51,58v30,0,51,-27,51,-58"},"\u00b7":{"d":"59,-121v0,12,-9,21,-21,21v-12,0,-21,-9,-21,-21v0,-12,10,-21,21,-21v12,0,21,9,21,21","w":76},"\u00c2":{"d":"130,-272r-7,8r-33,-27r-32,27r-8,-8v15,-12,26,-51,50,-36xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"\u00ca":{"d":"113,-272r-7,8r-33,-27r-32,27r-8,-8v15,-12,26,-51,51,-36xm125,0r-100,0r0,-248r100,0r0,25r-68,0r0,83r61,0r0,25r-61,0r0,90r68,0r0,25","w":140},"\u00c1":{"d":"131,-314r-34,48r-15,0r19,-48v11,-8,28,-8,30,0xm174,0r-33,0r-18,-72r-69,0r-19,72r-30,0r72,-248r27,0xm117,-95r-28,-116r-30,116r58,0","w":179},"\u00cb":{"d":"117,-284v0,9,-6,15,-15,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,15,7,15,16xm64,-284v0,9,-6,15,-15,15v-9,0,-16,-6,-16,-15v0,-9,7,-16,16,-16v9,0,15,7,15,16xm126,0r-100,0r0,-248r100,0r0,25r-69,0r0,83r62,0r0,25r-62,0r0,90r69,0r0,25","w":140},"\u00c8":{"d":"87,-266r-15,0r-35,-48v2,-8,20,-8,30,0xm125,0r-100,0r0,-248r100,0r0,25r-68,0r0,83r61,0r0,25r-61,0r0,90r68,0r0,25","w":140},"\u00cd":{"d":"78,-314r-34,48r-15,0r19,-48v11,-8,28,-8,30,0xm57,0r-32,0r0,-248r32,0r0,248","w":82},"\u00ce":{"d":"82,-272r-7,8r-33,-27r-33,27r-7,-8v15,-11,26,-51,50,-36xm58,0r-32,0r0,-248r32,0r0,248","w":83},"\u00cf":{"d":"84,-284v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm31,-284v0,9,-7,15,-16,15v-8,0,-15,-6,-15,-15v0,-9,7,-16,15,-16v9,0,16,7,16,16xm58,0r-32,0r0,-248r32,0r0,248","w":83},"\u00cc":{"d":"57,-266r-15,0r-35,-48v2,-8,19,-8,30,0xm62,0r-32,0r0,-248r32,0r0,248","w":84},"\u00d3":{"d":"133,-314r-34,48r-15,0r20,-48v10,-8,27,-8,29,0xm174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"\u00d4":{"d":"136,-272r-7,8r-33,-27r-32,27r-8,-8v15,-11,26,-51,50,-36xm174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"\u00d2":{"d":"107,-266r-15,0r-34,-48v2,-8,18,-8,29,0xm174,-124v0,69,-22,128,-78,128v-56,0,-77,-59,-77,-128v0,-69,21,-127,77,-127v56,0,78,58,78,127xm141,-125v0,-50,-9,-101,-45,-101v-36,0,-45,51,-45,101v0,50,8,102,45,102v37,0,45,-52,45,-102","w":192},"\u00da":{"d":"127,-314r-34,48r-16,0r20,-48v10,-8,28,-8,30,0xm159,-69v0,45,-28,73,-67,73v-38,0,-67,-27,-67,-73r0,-179r32,0r0,183v0,24,15,41,36,41v22,0,37,-17,37,-41r0,-183r29,0r0,179","w":183},"\u00db":{"d":"132,-272r-7,8r-33,-27r-32,27r-8,-8v15,-11,26,-51,50,-36xm159,-69v0,45,-28,73,-67,73v-38,0,-67,-27,-67,-73r0,-179r32,0r0,183v0,24,15,41,36,41v22,0,37,-17,37,-41r0,-183r29,0r0,179","w":183},"\u00d9":{"d":"107,-266r-16,0r-34,-48v2,-8,19,-8,29,0xm159,-69v0,45,-28,73,-67,73v-38,0,-67,-27,-67,-73r0,-179r32,0r0,183v0,24,15,41,36,41v22,0,37,-17,37,-41r0,-183r29,0r0,179","w":183},"\u00af":{"d":"73,-225r-86,0r0,-19r86,0r0,19","w":60},"\u00b8":{"d":"65,38v0,21,-33,30,-69,25r5,-12v22,3,41,-3,41,-12v0,-7,-6,-11,-29,-10r6,-11v32,-3,46,5,46,20","w":60},"\u00d0":{"d":"175,-126v0,69,-24,130,-86,130v-25,0,-46,-6,-58,-15r0,-112r-18,0r0,-21r18,0r0,-93v74,-40,144,8,144,111xm143,-127v0,-76,-26,-113,-80,-97r0,80r35,0r0,21r-35,0r0,97v53,12,80,-9,80,-101","w":193}}});
Event.observe(document, 'dom:loaded', function(e){
	if (!$(document.body).hasClassName('western')) {
		return;
	}
	/*
	Cufon.replace('#home h3');
	
	Cufon.replace('#headerMenu a',{   
		hover: true,
		hoverables: { a:true }
	}) ;
	Cufon.replace('#menu li div',{   
		hover: true,
		hoverables: { a:true, div:true }
	}) ;
	
	Cufon.replace('.sMenu4 li',{   
		hover: true,
		hoverables: { div:true}
	}) ;
	
	
	Cufon.replace('#tabs h2',{   
		hover: true,
		hoverables: { a:true }
	});
		
	Cufon.replace('#tabMenu	a div',{   
		hover: true,
		hoverables: { a:true }
	});
	
	Cufon.replace('.titleBox',{   
		hover: true,
		hoverables: { a:true }
	});
	
	*/
});
/**
 * @author stumbog
 */
// accordion.js v2.0
//
// Copyright (c) 2007 stickmanlabs
// Author: Kevin P Miller | http://www.stickmanlabs.com
// 
// Accordion is freely distributable under the terms of an MIT-style license.
//
// I don't care what you think about the file size...
//   Be a pro: 
//        http://www.thinkvitamin.com/features/webapps/serving-javascript-fast
//      http://rakaz.nl/item/make_your_pages_load_faster_by_combining_and_compressing_javascript_and_css_files
//

/*-----------------------------------------------------------------------------------------------*/
 
if (typeof Effect == 'undefined') 
    throw("accordion.js requires including script.aculo.us' effects.js library!");

var accordion = Class.create();
accordion.prototype = {

    //
    //  Setup the Variables
    //
    showAccordion : null,
    currentAccordion : null,
    duration : null,
    effects : [],
    animating : false,
    
    //  
    //  Initialize the accordions
    //
    initialize: function(container, options) {
      if (!$(container)) {
        throw(container+" doesn't exist!");
        return false;
      }
      
        this.options = Object.extend({
            resizeSpeed : 7,
            classNames : {
                toggle : 'accordion_toggle',
                toggleActive : 'accordion_toggle_active',
                content : 'accordion_content'
            },
            defaultSize : {
                height : null,
                width : null
            },
            direction : 'vertical',
            onEvent : 'click'
        }, options || {});
        
        this.duration = ((11-this.options.resizeSpeed)*0.15);

        var accordions = $$('#'+container+' .'+this.options.classNames.toggle);
        accordions.each(function(accordion) {
            Event.observe(accordion, this.options.onEvent, this.activate.bind(this, accordion), false);
            if (this.options.onEvent == 'click') {
              accordion.onclick = function() {return false;};
            }
            
          	if (this.options.direction == 'horizontal') {
                var options = {width: '0px', display: 'none'};
            } else {
                var options = {height: '0px', display: 'none'};           
            }
			 
            this.currentAccordion = $(accordion.next(0)).setStyle(options);            
        }.bind(this));
		if (accordions.length == 1) {
			this.activate(accordions[0]);
		}
    },
    
    //
    //  Activate an accordion
    //
    activate : function(accordion) {
        if (this.animating) {
            return false;
        }
        
        this.effects = [];
    
        this.currentAccordion = $(accordion.next(0));
        this.currentAccordion.setStyle({
            display: 'block'
        });        
        
        this.currentAccordion.previous(0).addClassName(this.options.classNames.toggleActive);

        if (this.options.direction == 'horizontal') {
            this.scaling = $H({
                scaleX: true,
                scaleY: false
            });
        } else {
            this.scaling = $H({
                scaleX: false,
                scaleY: true
            });            
        }
            
        if (this.currentAccordion == this.showAccordion) {
          this.deactivate();
        } else {
          this._handleAccordion();
        }
    },
    // 
    // Deactivate an active accordion
    //
    deactivate : function() {
        var options = $H({
          duration: this.duration,
            scaleContent: false,
            transition: Effect.Transitions.sinoidal,
            queue: {
                position: 'end', 
                scope: 'accordionAnimation'
            },
            scaleMode: { 
                originalHeight: this.options.defaultSize.height ? this.options.defaultSize.height : this.currentAccordion.scrollHeight,
                originalWidth: this.options.defaultSize.width ? this.options.defaultSize.width : this.currentAccordion.scrollWidth
            },
            afterFinish: function() {
                this.showAccordion.setStyle({
          height: 'auto',
                    display: 'none'
                });                
                this.showAccordion = null;
                this.animating = false;
            }.bind(this)
        });    

    this.showAccordion.previous(0).removeClassName(this.options.classNames.toggleActive);
    
        new Effect.Scale(this.showAccordion, 0, options.merge(this.scaling).toObject())
    },

  //
  // Handle the open/close actions of the accordion
  //
    _handleAccordion : function() {
        var options = $H({
            sync: true,
            scaleFrom: 0,
            scaleContent: false,
            transition: Effect.Transitions.sinoidal,
            scaleMode: { 
                originalHeight: this.options.defaultSize.height ? this.options.defaultSize.height : this.currentAccordion.scrollHeight,
                originalWidth: this.options.defaultSize.width ? this.options.defaultSize.width : this.currentAccordion.scrollWidth
            }
        });
        
        this.effects.push(
            
            new Effect.Scale(this.currentAccordion, 100, options.merge(this.scaling).toObject())
        );

        if (this.showAccordion) {
            this.showAccordion.previous(0).removeClassName(this.options.classNames.toggleActive);
            
            options = $H({
                sync: true,
                scaleContent: false,
                transition: Effect.Transitions.sinoidal
            });
            
            this.effects.push(
                new Effect.Scale(this.showAccordion, 0, options.merge(this.scaling).toObject())
            );                
        }
        
    new Effect.Parallel(this.effects, {
            duration: this.duration, 
            queue: {
                position: 'end', 
                scope: 'accordionAnimation'
            },
            beforeStart: function() {
                this.animating = true;
            }.bind(this),
            afterFinish: function() {
                if (this.showAccordion) {
                    this.showAccordion.setStyle({
                        display: 'none'
                    });                
                }
                $(this.currentAccordion).setStyle({
                  height: 'auto'
                });
                this.showAccordion = this.currentAccordion;
                this.animating = false;
            }.bind(this)
        });
    }
};

// -----------------------------------------------------------------------------------
//
//	Lightbox v2.04
//	by Lokesh Dhakar - http://www.lokeshdhakar.com
//	Last Modification: 2/9/08
//
//	For more information, visit:
//	http://lokeshdhakar.com/projects/lightbox2/
//
//	Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
//  	- Free for use in both personal and commercial projects
//		- Attribution requires leaving author name, author link, and the license info intact.
//	
//  Thanks: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.com), and Thomas Fuchs(mir.aculo.us) for ideas, libs, and snippets.
//  		Artemy Tregubenko (arty.name) for cleanup and help in updating to latest ver of proto-aculous.
//
// -----------------------------------------------------------------------------------
/*

    Table of Contents
    -----------------
    Configuration

    Lightbox Class Declaration
    - initialize()
    - updateImageList()
    - start()
    - changeImage()
    - resizeImageContainer()
    - showImage()
    - updateDetails()
    - updateNav()
    - enableKeyboardNav()
    - disableKeyboardNav()
    - keyboardAction()
    - preloadNeighborImages()
    - end()
    
    Function Calls
    - document.observe()
   
*/
// -----------------------------------------------------------------------------------

//
//  Configurationl
//
LightboxOptions = Object.extend({
    fileLoadingImage:        'http://cdn2.yoox.biz/yooxgroup/img/loadyoox.gif',     
    fileBottomNavCloseImage: 'http://cdn2.yoox.biz/yooxgroup/img/closelabel.jpg',

    overlayOpacity: 0.8,   // controls transparency of shadow overlay

    animate: false,         // toggles resizing animations
    resizeSpeed: 7,        // controls the speed of the image resizing animations (1=slowest and 10=fastest)

    borderSize: 10,         //if you adjust the padding in the CSS, you will need to update this variable

	// When grouping images this is used to write: Image # of #.
	// Change it for non-english localization
	labelImage: "Image",
	labelOf: "of"
}, window.LightboxOptions || {});

// -----------------------------------------------------------------------------------

var Lightbox = Class.create();

Lightbox.prototype = {
    imageArray: [],
    activeImage: undefined,
    
    // initialize()
    // Constructor runs on completion of the DOM loading. Calls updateImageList and then
    // the function inserts html at the bottom of the page which is used to display the shadow 
    // overlay and the image container.
    //
    initialize: function() {    
        
        this.updateImageList();
        
        this.keyboardAction = this.keyboardAction.bindAsEventListener(this);

        if (LightboxOptions.resizeSpeed > 10) LightboxOptions.resizeSpeed = 10;
        if (LightboxOptions.resizeSpeed < 1)  LightboxOptions.resizeSpeed = 1;

	    this.resizeDuration = LightboxOptions.animate ? ((11 - LightboxOptions.resizeSpeed) * 0.15) : 0;
	    this.overlayDuration = LightboxOptions.animate ? 0.2 : 0;  // shadow fade in/out duration

        // When Lightbox starts it will resize itself from 250 by 250 to the current image dimension.
        // If animations are turned off, it will be hidden as to prevent a flicker of a
        // white 250 by 250 box.
        var size = (LightboxOptions.animate ? 250 : 1) + 'px';
        

        // Code inserts html at the bottom of the page that looks similar to this:
        //
        //  <div id="overlay"></div>
        //  <div id="lightbox">
        //      <div id="outerImageContainer">
        //          <div id="imageContainer">
        //              <img id="lightboxImage">
        //              <div style="" id="hoverNav">
        //                  <a href="#" id="prevLink"></a>
        //                  <a href="#" id="nextLink"></a>
        //              </div>
        //              <div id="loading">
        //                  <a href="#" id="loadingLink">
        //                      <img src="images/loading.gif">
        //                  </a>
        //              </div>
        //          </div>
        //      </div>
        //      <div id="imageDataContainer">
        //          <div id="imageData">
        //              <div id="imageDetails">
        //                  <span id="caption"></span>
        //                  <span id="numberDisplay"></span>
        //              </div>
        //              <div id="bottomNav">
        //                  <a href="#" id="bottomNavClose">
        //                      <img src="images/close.gif">
        //                  </a>
        //              </div>
        //          </div>
        //      </div>
        //  </div>


        var objBody = $$('body')[0];

		objBody.appendChild(Builder.node('div',{id:'overlay'}));
	
        objBody.appendChild(Builder.node('div',{id:'lightbox'}, [
            Builder.node('div',{id:'outerImageContainer'}, 
                Builder.node('div',{id:'imageContainer'}, [
                    Builder.node('img',{id:'lightboxImage'}), 
                    Builder.node('div',{id:'hoverNav'}, [
                        Builder.node('a',{id:'prevLink', href: '#' }),
                        Builder.node('a',{id:'nextLink', href: '#' })
                    ]),
                    Builder.node('div',{id:'loading'}, 
                        Builder.node('a',{id:'loadingLink', href: '#' }, 
                            Builder.node('img', {src: LightboxOptions.fileLoadingImage})
                        )
                    )
                ])
            ),
            Builder.node('div', {id:'imageDataContainer'},
                Builder.node('div',{id:'imageData'}, [
                    Builder.node('div',{id:'imageDetails'}, [
                        Builder.node('span',{id:'caption'}),
                        Builder.node('span',{id:'numberDisplay'})
                    ]),
                    Builder.node('div',{id:'bottomNav'},
                        Builder.node('a',{id:'bottomNavClose', href: '#' },
                            Builder.node('img', { src: LightboxOptions.fileBottomNavCloseImage })
                        )
                    )
                ])
            )
        ]));


		$('overlay').hide().observe('click', (function() { this.end(); }).bind(this));
		$('lightbox').hide().observe('click', (function(event) { if (event.element().id == 'lightbox') this.end(); }).bind(this));
		$('outerImageContainer').setStyle({ width: size, height: size });
		$('prevLink').observe('click', (function(event) { event.stop(); this.changeImage(this.activeImage - 1); }).bindAsEventListener(this));
		$('nextLink').observe('click', (function(event) { event.stop(); this.changeImage(this.activeImage + 1); }).bindAsEventListener(this));
		$('loadingLink').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
		$('bottomNavClose').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));

        var th = this;
        (function(){
            var ids = 
                'overlay lightbox outerImageContainer imageContainer lightboxImage hoverNav prevLink nextLink loading loadingLink ' + 
                'imageDataContainer imageData imageDetails caption numberDisplay bottomNav bottomNavClose';   
            $w(ids).each(function(id){ th[id] = $(id); });
        }).defer();
    },

    //
    // updateImageList()
    // Loops through anchor tags looking for 'lightbox' references and applies onclick
    // events to appropriate links. You can rerun after dynamically adding images w/ajax.
    //
    updateImageList: function() {   
        this.updateImageList = Prototype.emptyFunction;

	  Event.observe(document, 'click', function(evt){
		var clickedElm = $(Event.element(evt));
		var target = (new RegExp("^(lightbox)+", "").test(clickedElm.readAttribute("rel"))) || clickedElm.up('a[rel^=lightbox], area[rel^=lightbox]');
            if (target) {
                Event.stop(evt);
                this.start(target);
            }
	  }.bindAsEventListener(this));
    },
    
    //
    //  start()
    //  Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
    //
    start: function(imageLink) {    

        $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'hidden' });

        // stretch overlay to fill page and fade in
        var arrayPageSize = this.getPageSize();
        $('overlay').setStyle({ width: arrayPageSize[0] + 'px', height: arrayPageSize[1] + 'px' });

        new Effect.Appear(this.overlay, { duration: this.overlayDuration, from: 0.0, to: LightboxOptions.overlayOpacity });

        this.imageArray = [];
        var imageNum = 0;       

        if ((imageLink.rel == 'lightbox')){
            // if image is NOT part of a set, add single image to imageArray
            this.imageArray.push([imageLink.href, imageLink.title]);         
        } else {
            // if image is part of a set..
            this.imageArray = 
                $$(imageLink.tagName + '[href][rel="' + imageLink.rel + '"]').
                collect(function(anchor){ return [anchor.href, anchor.title]; }).
                uniq();
            
            while (this.imageArray[imageNum][0] != imageLink.href) { imageNum++; }
        }

        // calculate top and left offset for the lightbox 
        var arrayPageScroll = document.viewport.getScrollOffsets();
        var lightboxTop = arrayPageScroll[1] + (document.viewport.getHeight() / 10);
        var lightboxLeft = arrayPageScroll[0];
        this.lightbox.setStyle({ top: lightboxTop + 'px', left: lightboxLeft + 'px' }).show();
        
        this.changeImage(imageNum);
    },

    //
    //  changeImage()
    //  Hide most elements and preload image in preparation for resizing image container.
    //
    changeImage: function(imageNum) {   
        
        this.activeImage = imageNum; // update global var

        // hide elements during transition
        if (LightboxOptions.animate) this.loading.show();
        this.lightboxImage.hide();
        this.hoverNav.hide();
        this.prevLink.hide();
        this.nextLink.hide();
		// HACK: Opera9 does not currently support scriptaculous opacity and appear fx
        this.imageDataContainer.setStyle({opacity: .0001});
        this.numberDisplay.hide();      
        
        var imgPreloader = new Image();
        
        // once image is preloaded, resize image container


        imgPreloader.onload = (function(){
            this.lightboxImage.src = this.imageArray[this.activeImage][0];
            this.resizeImageContainer(imgPreloader.width, imgPreloader.height);
        }).bind(this);
        imgPreloader.src = this.imageArray[this.activeImage][0];
    },

    //
    //  resizeImageContainer()
    //
    resizeImageContainer: function(imgWidth, imgHeight) {

        // get current width and height
        var widthCurrent  = this.outerImageContainer.getWidth();
        var heightCurrent = this.outerImageContainer.getHeight();

        // get new width and height
        var widthNew  = (imgWidth  + LightboxOptions.borderSize * 2);
        var heightNew = (imgHeight + LightboxOptions.borderSize * 2);

        // scalars based on change from old to new
        var xScale = (widthNew  / widthCurrent)  * 100;
        var yScale = (heightNew / heightCurrent) * 100;

        // calculate size difference between new and old image, and resize if necessary
        var wDiff = widthCurrent - widthNew;
        var hDiff = heightCurrent - heightNew;

        if (hDiff != 0) new Effect.Scale(this.outerImageContainer, yScale, {scaleX: false, duration: this.resizeDuration, queue: 'front'}); 
        if (wDiff != 0) new Effect.Scale(this.outerImageContainer, xScale, {scaleY: false, duration: this.resizeDuration, delay: this.resizeDuration}); 

        // if new and old image are same size and no scaling transition is necessary, 
        // do a quick pause to prevent image flicker.
        var timeout = 0;
        if ((hDiff == 0) && (wDiff == 0)){
            timeout = 100;
            if (Prototype.Browser.IE) timeout = 250;   
        }

        (function(){
            this.prevLink.setStyle({ height: imgHeight + 'px' });
            this.nextLink.setStyle({ height: imgHeight + 'px' });
            this.imageDataContainer.setStyle({ width: widthNew + 'px' });

            this.showImage();
        }).bind(this).delay(timeout / 1000);
    },
    
    //
    //  showImage()
    //  Display image and begin preloading neighbors.
    //
    showImage: function(){
        this.loading.hide();
        new Effect.Appear(this.lightboxImage, { 
            duration: this.resizeDuration, 
            queue: 'end', 
            afterFinish: (function(){ this.updateDetails(); }).bind(this) 
        });
        this.preloadNeighborImages();
    },

    //
    //  updateDetails()
    //  Display caption, image number, and bottom nav.
    //
    updateDetails: function() {
    
        // if caption is not null
        if (this.imageArray[this.activeImage][1] != ""){
            this.caption.update(this.imageArray[this.activeImage][1]).show();
        }
        
        // if image is part of set display 'Image x of x' 
        if (this.imageArray.length > 1){
            this.numberDisplay.update( LightboxOptions.labelImage + ' ' + (this.activeImage + 1) + ' ' + LightboxOptions.labelOf + '  ' + this.imageArray.length).show();
        }

        new Effect.Parallel(
            [ 
                new Effect.SlideDown(this.imageDataContainer, { sync: true, duration: this.resizeDuration, from: 0.0, to: 1.0 }), 
                new Effect.Appear(this.imageDataContainer, { sync: true, duration: this.resizeDuration }) 
            ], 
            { 
                duration: this.resizeDuration, 
                afterFinish: (function() {
	                // update overlay size and update nav
	                var arrayPageSize = this.getPageSize();
	                this.overlay.setStyle({ height: arrayPageSize[1] + 'px' });
	                this.updateNav();
                }).bind(this)
            } 
        );
    },

    //
    //  updateNav()
    //  Display appropriate previous and next hover navigation.
    //
    updateNav: function() {

        this.hoverNav.show();               

        // if not first image in set, display prev image button
        if (this.activeImage > 0) this.prevLink.show();

        // if not last image in set, display next image button
        if (this.activeImage < (this.imageArray.length - 1)) this.nextLink.show();
        
        this.enableKeyboardNav();
    },

    //
    //  enableKeyboardNav()
    //
    enableKeyboardNav: function() {
        document.observe('keydown', this.keyboardAction); 
    },

    //
    //  disableKeyboardNav()
    //
    disableKeyboardNav: function() {
        document.stopObserving('keydown', this.keyboardAction); 
    },

    //
    //  keyboardAction()
    //
    keyboardAction: function(event) {
        var keycode = event.keyCode;

        var escapeKey;
        if (event.DOM_VK_ESCAPE) {  // mozilla
            escapeKey = event.DOM_VK_ESCAPE;
        } else { // ie
            escapeKey = 27;
        }

        var key = String.fromCharCode(keycode).toLowerCase();
        
        if (key.match(/x|o|c/) || (keycode == escapeKey)){ // close lightbox
            this.end();
        } else if ((key == 'p') || (keycode == 37)){ // display previous image
            if (this.activeImage != 0){
                this.disableKeyboardNav();
                this.changeImage(this.activeImage - 1);
            }
        } else if ((key == 'n') || (keycode == 39)){ // display next image
            if (this.activeImage != (this.imageArray.length - 1)){
                this.disableKeyboardNav();
                this.changeImage(this.activeImage + 1);
            }
        }
    },

    //
    //  preloadNeighborImages()
    //  Preload previous and next images.
    //
    preloadNeighborImages: function(){
        var preloadNextImage, preloadPrevImage;
        if (this.imageArray.length > this.activeImage + 1){
            preloadNextImage = new Image();
            preloadNextImage.src = this.imageArray[this.activeImage + 1][0];
        }
        if (this.activeImage > 0){
            preloadPrevImage = new Image();
            preloadPrevImage.src = this.imageArray[this.activeImage - 1][0];
        }
    
    },

    //
    //  end()
    //
    end: function() {
        this.disableKeyboardNav();
        this.lightbox.hide();
        new Effect.Fade(this.overlay, { duration: this.overlayDuration });
        $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'visible' });
    },

    //
    //  getPageSize()
    //
    getPageSize: function() {
	        
	     var xScroll, yScroll;
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = window.innerWidth + window.scrollMaxX;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}
		
		var windowWidth, windowHeight;
		
		if (self.innerHeight) {	// all except Explorer
			if(document.documentElement.clientWidth){
				windowWidth = document.documentElement.clientWidth; 
			} else {
				windowWidth = self.innerWidth;
			}
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	
		
		// for small pages with total height less then height of the viewport
		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}
	
		// for small pages with total width less then width of the viewport
		if(xScroll < windowWidth){	
			pageWidth = xScroll;		
		} else {
			pageWidth = windowWidth;
		}

		return [pageWidth,pageHeight];
	}
}

document.observe('dom:loaded', function () { new Lightbox(); });

var _os = null;

var OS = Class.create({
    'initialize': function() {
        this.appVersion = '0';
        this.sources = [];
        this.scriptsToLoad = [];    //stack to load
        this.loadedScripts = [];    //loaded by onloadcomplete
        this.completedScripts = []; //fired the oncomplete events
        this.completeCallbacks = []; //oncomplete events
        this.scriptLoadListeners = [];
        this.pageLoading = true;
    },
    'addScriptLoadListener': function(listenerCallback, associatedScripts) {
        var loadListener = new Object();
        loadListener.callback = listenerCallback;
        loadListener.associatedScripts = associatedScripts;
        this.scriptLoadListeners.push(loadListener);
    },
    'requireAll': function(caller, scripts, options) {
        options = options || {};
        if(options.onComplete) {
            this.scriptsToLoad.push(options.onComplete);
        }            
        var loadScripts = scripts;
        if(options.extendedWatchScripts) {
            var extendedScripts = options.extendedWatchScripts;
            for(var i = 0; i < extendedScripts.length; i++) {
                loadScripts.push(extendedScripts[i]);
            }
        }
        if(options.scriptLoadListener) this.addScriptLoadListener(options.scriptLoadListener, loadScripts);
        if(typeof scripts == 'string') {
            this.scriptsToLoad.push(scripts);
        } else {
            $A(scripts.reverse()).each(function(script) { //turn stack into queue so order is preserved
                if(!this.sources[script]) this.scriptsToLoad.push(script);
            }.bind(this));
        }        
        this.load();
    },
    'scriptLoaded': function(script) {
        var scripts = [];
        $A(this.scriptsToLoad).each(function(src) {
            if(src != script) scripts.push(src);
        });
        this.scriptsToLoad = scripts;
        if(this.completeCallbacks[script]) {
            this.completeCallbacks[script]();
            this.completeCallbacks[script] = false;
        }
        //broadcast to load listeners
        $A(this.scriptLoadListeners).each(function(listener) {
            var iTotal = 0;
            var iComplete = 0;
            $A(listener.associatedScripts).each(function(script) {
                if(this.completedScripts[script]) iComplete++;
                iTotal++;
            }.bind(this));
            try { listener.callback(iComplete, iTotal);} catch(ex){}
        }.bind(this));
    },
    'loadNext': function() {
        if(!this.queueLoading) return;
        this.requireNext();
        setTimeout(function() { this.loadNext(); }.bind(this), 50);
    },
    'load': function() {
        if(this.queueLoading) return;
        this.queueLoading = true;
        this.loadNext();
    },
    'loadComplete': function() {
        this.queueLoading = false;
    },
    'requireNext': function() {
        if(this.scriptLoadListener) this.scriptLoadListener(this.scriptsToLoad.length);
        if(this.scriptsToLoad.length == 0) { this.loadComplete(); return; }
        var next = this.scriptsToLoad[this.scriptsToLoad.length-1];
        if(typeof next == 'function') {
            next();
            this.scriptsToLoad.pop();
            this.load();
            return;
        } 
        this.require(next, function() { this.load(); }.bind(this));
    },
    'require':  function(src, onComplete) {
        if(this.sources[src]) {
            if(onComplete) setTimeout(onComplete, 10);
            return;   //do not load twice
        }
        this.sources[src] = true;
        
        //insert the script
        if(!OS.Path) { _os.showMessage('Error: Unknown JsOS'); return; }
        var headTag = document.getElementsByTagName("head").item(0); 
        var scriptTag = document.createElement("script"); 
	    scriptTag.setAttribute('type', 'text/javascript');
	    scriptTag.setAttribute('src', OS.Path() + src); 
        headTag.appendChild(scriptTag);
        
        this.waitForLoadComplete(src, onComplete);
    },
    'waitForLoadComplete':  function(script, onComplete) {
        if(this.isQueueLoading(script)) {
            setTimeout(function() { 
                this.waitForLoadComplete(script, onComplete); 
            }.bind(this), 10); 
            return;
        }
        if(this.completedScripts[script]) return;
        this.completedScripts[script] = true;
        if(onComplete) setTimeout(onComplete, 10);
    },
    'isQueueLoading':  function(script) {
        if (!this.loadedScripts[script]) return true;
        return false;
    },
    'onLoadComplete':  function(script) {
        this.loadedScripts[script] = true;
        this.scriptLoaded(script); 
    }
});

OS.Path = function() { return 'scripts/'; }
OS.Load = function() {
    _os = new OS();
    OS.OnLoadComplete();
};
OS.CreateElement = function(type, parent, id) {
    if(!$(parent)) return;
    var element = document.createElement(type);
    var eElement = $(parent).appendChild(element);
    if(id) eElement.id = id;
    return eElement;
};
OS.CreateDiv = function(parent, id) {
    var eDiv = OS.CreateElement('div', parent, id);
    return eDiv;
};
OS.CreateList = function(parent, id) {
    var eList = OS.CreateElement('ul', parent, id);
    return eList;
};
OS.AddListItem = function(parent, id) {
    var eItem = OS.CreateElement('li', parent, id);
    return eItem;
};
OS.OnLoadComplete = function() {
    if(!Prototype || !Element) {
        setTimeout(OS.OnLoadComplete, 300);
        return;
    }

    _os.onLoadComplete('os.js');
};
OS.PageLoadComplete = function() {
    _os.pageLoading = false;
    $A(OS.BehaviourQueue).each(function(selectors) {
        OS.ApplyBehaviour(selectors);
    });
    OS.BehaviourQueue = [];
};
Event.observe(document, 'dom:loaded', function() {
    OS.PageLoadComplete();
});
OS.BehaviourQueue = [];
OS.RegisterBehaviour = function(selectors) {
    if(!_os.pageLoading) { OS.ApplyBehaviour(selectors); return; }
    OS.BehaviourQueue.push(selectors);    
}
OS.ApplyBehaviour = function(selectors) {
    $H(selectors).each(function(item) {
        $$(item.key).each(function(element) {
            item.value(element);
        });
    });
};
OS.Load();


var ModelBehavior = Class.create();

ModelBehavior.SampleCarousel = null;
ModelBehavior.CarouselRules = {
    
    '#Carousel1': function(element) {
        //slideshow contenente solo immagini con apertura nel viewer
        ModelBehavior.ProfileCarousel = new Carousel('ProfileCarousel', element, 125, 125, ModelBehavior, {
            'setSize': 2,
            'nElements': 6,
            'duration': .5,
            'direction': 'horizontal',
            'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                 var sKey = item.down('.key').innerHTML;
                 var sCaption = item.down('.caption').innerHTML;
                 var sPictureHtml = item.down('.picture').innerHTML;
                 return { name:sCaption, pictureHtml:sPictureHtml };
            },
            'setItemEvents': function(carousel, itemElement, carouselItem, observer) {
                //This allows you to set events to the item like rollovers/mouse events
                Event.observe(itemElement, 'click', function(){                    
                    carousel.activate(carouselItem);
                });
                Event.observe(itemElement, 'mouseover', function(){                    
                    Element.addClassName(itemElement, 'hover');
                });
                Event.observe(itemElement, 'mouseout', function(){                    
                    Element.removeClassName(itemElement, 'hover');
                });
            }
        });
        ModelBehavior.ProfileCarousel.load();
    },
    '#Carousel2': function(element) {    
        //slideshow contenente immagini e testo con apertura nel viewer
        ModelBehavior.PictureCarousel = new Carousel('PictureCarousel', element, 150, 150, ModelBehavior, {
            'setSize': 2,
            'nElements': 5,
            'duration': .5,
            'direction': 'horizontal',
            'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                var sKey = item.down('.key').innerHTML;
                var sCaption = item.down('.caption').innerHTML;
                var sPictureHtml = item.down('.picture').innerHTML;
                return { name:sCaption, pictureHtml:sPictureHtml };
            },
            'setItemEvents': function(carousel, itemElement, carouselItem, observer) {
                //This allows you to set events to the item like rollovers/mouse events
                Event.observe(itemElement, 'click', function(){                    
                    carousel.activate(carouselItem);
                });
                Event.observe(itemElement, 'mouseover', function(){                    
                    Element.addClassName(itemElement, 'hover');
                });
                Event.observe(itemElement, 'mouseout', function(){                    
                    Element.removeClassName(itemElement, 'hover');
                });
            }
        });
        ModelBehavior.PictureCarousel.load();
    },
    
     '#Carousel3': function(element) {    
        //slideshow contenente immagini
        ModelBehavior.PictureCarousel = new Carousel('GroupCarousel', element, 803, 355, ModelBehavior, {
            'setSize': 2,
            'nElements': 1,
            'duration': .5,
            'direction': 'next',
            'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                var sKey = item.down('.key').innerHTML;
                var sCaption = item.down('.caption').innerHTML;
               // var sPictureHtml = item.down('.picture').innerHTML;
                return { name:sCaption };
            }
        });
        ModelBehavior.PictureCarousel.load();
    },
    '#Carousel4': function(element) {
        //Simple profiles
        ModelBehavior.PictureCarousel = new Carousel('Carousel4', element, 30, 206, ModelBehavior, {
            'setSize': 2,
            'nElements': 5,
            'duration': .5,
            'direction': 'vertical',
            'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                var sKey = item.down('.key').innerHTML;
                var sCaption = item.down('.caption').innerHTML;
                var sPictureHtml = item.down('.picture').innerHTML;
                return { name:sCaption, pictureHtml:sPictureHtml };
            },
            'setItemEvents': function(carousel, itemElement, carouselItem, observer) {
                //This allows you to set events to the item like rollovers/mouse events
                Event.observe(itemElement, 'click', function(){              
                    carousel.activate(carouselItem);
                });
                Event.observe(itemElement, 'mouseover', function(){                    
                    Element.addClassName(itemElement, 'hover');
                });
                Event.observe(itemElement, 'mouseout', function(){                    
                    Element.removeClassName(itemElement, 'hover');
                });
            }
        });
        ModelBehavior.PictureCarousel.load();
    },
    '#Carousel5': function(element) {    
        //slideshow contenente immagini,
        ModelBehavior.PictureCarousel = new Carousel('GroupCarousel', element, 660, 355, ModelBehavior, {
            'setSize': 2,
            'nElements': 5,
            'direction': 'horizontal',
			'moveOpacity': 1.0,
			'n4Page':4,
            'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                //var sKey = item.down('.key').innerHTML;
                //var sCaption = item.down('.caption').innerHTML;
               // var sPictureHtml = item.down('.picture').innerHTML;
                return;
            }
        });
        ModelBehavior.PictureCarousel.load();
    },
    '#Carousel6': function(element) {    
        //slideshow contenente immagini
        ModelBehavior.PictureCarousel = new Carousel('GroupCarousel', element, 640, 355, ModelBehavior, {
            'setSize': 2,
            'nElements': 1,
            'duration': .5,
            'direction': 'horizontal',
			'moveOpacity': 1.0,
			'n4Page':1,
			'itemParser': function(item) {
                //Given html element you can build a data object for the item if needed for later activation
                //var sKey = item.down('.key').innerHTML;
                //var sCaption = item.down('.caption').innerHTML;
               // var sPictureHtml = item.down('.picture').innerHTML;
                return;
            }
        });
        ModelBehavior.PictureCarousel.load();
    }
}

//EVENT OBSERVATION
ModelBehavior.fireActiveCarouselLoaded = function(carousel) {
}
ModelBehavior.fireActiveCarouselItem = function(carousel, element, item) {
    element.addClassName('selected');

    // Here we can update any part of the DOM to represent our data
    // In this sample we will use the same generic viewer element for all carousels
    switch(carousel.key) {
        case 'ProfileCarousel': 
            $('ViewerCaption').update(item.value.name);
            $('ViewerData').update(item.value.pictureHtml);
            Element.show('Viewer');
            break;
            
        case 'PictureCarousel':
            $('ViewerCaption').update(item.value.name);
            $('ViewerData').update(item.value.pictureHtml);
            Element.show('Viewer');
            break;

        case 'GroupCarousel':
            break;
        case 'Carousel4':
            $('ViewerCaption').update(item.value.name);
            $('ViewerData').update(item.value.pictureHtml);
            Element.show('Viewer2');
            break;
            
       case 'Carousel5':
           break;
		   
	   case 'Carousel6':
           break;
    }
}
ModelBehavior.fireDeactiveCarouselItem = function(carousel, element, item) {
    element.removeClassName('selected');

    switch(carousel.key) {
        case 'ProfileCarousel': 
            Element.hide('Viewer');
            break;

        case 'PictureCarousel': 
            Element.hide('Viewer');
            break;

        case 'GroupCarousel': 
            Element.hide('Viewer');
            break;
    }
}

/**** Load Dependencies ***/
ModelBehavior.Load = function(){
    var Script_Name = 'behaviors/model.js';
    document.observe("dom:loaded" ,function() {
                _os.onLoadComplete(Script_Name);
                OS.RegisterBehaviour(ModelBehavior.CarouselRules);
            }
    );
}
ModelBehavior.Load();


var CarouselItem = Class.create();
CarouselItem.prototype = {
    initialize: function() {
        this.key = null;		
        this.value = null;
        this.element = null;
    }
};
var Carousel = Class.create();
Carousel.prototype = {
    initialize: function(key, carouselElement, itemWidth, itemHeight, observer, options) {
        this.key = key;
        this.observer = observer;
        this.items = [];
        this.activeItem = null;
        this.activeIndex = 0;
        this.lastActiveIndex = 0;
        this.navScrollIndex = 0;
        this.duration = 1.0;
        if(options.duration) this.duration = options.duration;
        this.options = options;
        this.direction = 'vertical';
        if(options.direction) this.direction = options.direction;
        this.itemHeight = itemHeight;
        this.itemWidth = itemWidth;
	    this.moveOpacity = (options.moveOpacity)?options.moveOpacity : .6;
        this.setSize = 4;
        this.nElements = 5;
		if(options.setSize) this.setSize = options.setSize;
         if(options.nElements) this.nElements = options.nElements; 
        this.carouselElement = $(carouselElement);
        if(!this.carouselElement) { alert('Warning: Invalid carousel element: ' + carouselElement); return; }
        this.itemsElement = this.carouselElement.down('.items');
        if(!this.itemsElement) { alert('Warning: Class \'items\' does not exist as a child element in carousel: ' + carouselElement); return; }
        
        this.backElement = $('previous');
        this.forwardElement = $('next'); 
        this.linkNum = false;
      
		this.n4Page = (options.n4Page)?options.n4Page : .4;
		
        if(this.direction == 'next')
        {
          this.play = $('play');
          this.stop = $('stop');

          Event.observe(this.play, 'click', this.autoPlay.bindAsEventListener(this));
          Event.observe(this.stop, 'click', this.stopF.bindAsEventListener(this));
         
        }
        else
        {
            Event.observe(this.backElement, 'click', this.scrollBack.bindAsEventListener(this));
            Event.observe(this.forwardElement, 'click', this.scrollForward.bindAsEventListener(this));
            
        }  
        

    },
    goBack: function(){
	
		     new Effect.Fade(this.nodes[this.activeIndex], { duration: 0.5 ,queue: 'end' });
		
		    var prevNum = $$('.iconBox li')[this.activeIndex];
			Element.removeClassName(Element.firstDescendant(prevNum), 'selNumber');
				
		    //this.nodes.invoke('hide');
		    
			this.navScrollIndex = this.activeIndex-1;
			if(this.navScrollIndex < 0 )
			       this.navScrollIndex = this.nodes.length-1;		
			
			var activeNum = $$('.iconBox li')[this.navScrollIndex];
			Element.addClassName(Element.firstDescendant(activeNum), 'selNumber');
			
			new Effect.Appear(this.nodes[this.navScrollIndex], { duration: 1.0, queue: 'end'});
						
			this.activeIndex--;
						
			if(this.activeIndex < 0)
				this.activeIndex = this.nodes.length-1;				
    },
	
    goForward: function(){
            this.loop();					
    },
    stopF: function(){ 
 
        if(typeof(this.pe)!== 'undefined') this.pe.stop();
		
        Element.setStyle(this.stop, {'display':'none'});
        
        Event.stopObserving(this.backElement, 'click', this.goBack);
		Event.stopObserving(this.forwardElement, 'click', this.goForward);
		
        Event.observe(this.backElement, 'click', this.goBack.bindAsEventListener(this));
        Event.observe(this.forwardElement, 'click', this.goForward.bindAsEventListener(this));
          
        this.backElement.removeClassName("notOver");
        this.forwardElement.removeClassName("notOver"); 
         
    },
    
	autoPlay: function(){
		this.duration = 1.0;  
		if (this.direction == 'next') {
			Element.setStyle(this.play, {'display':'none'});
		} else {
			Element.setStyle(this.backElement, {'visibility':'visible'});
			Element.setStyle(this.forwardElement, {'visibility':'visible'});
		}

		Element.show(this.forwardElement);
		Element.show(this.backElement);
		if (this.items.length > 0) {
			Element.show(this.play);
		} else {
			Element.hide(this.play);
		}
		Element.show(this.stop);

		if (this.observer.fireCarouselAtIndex) {
			this.observer.fireCarouselAtIndex(this, index);
		}

		Event.stopObserving(this.backElement, 'click');
		Event.stopObserving(this.forwardElement, 'click');

		this.backElement.addClassName("notOver");
		this.forwardElement.addClassName("notOver");
        
        this.nodes[0].show();
        
        setTimeout("",3000);
        
		this.pe = new PeriodicalExecuter(function(){
			this.loop();
		}.bind(this), 3);
	},
	loop: function(){
		new Effect.Fade(this.nodes[this.activeIndex], {
			duration: 0.5, from: 1, to: 0,
			queue: 'end'
		});

        var prevNum = $$('.iconBox li')[this.activeIndex];
		Element.removeClassName(Element.firstDescendant(prevNum), 'selNumber');
		
		this.navScrollIndex = this.activeIndex+1;
		if(this.navScrollIndex >= this.nodes.length)
		this.navScrollIndex = 0;		

		
			
		var activeNum = $$('.iconBox li')[this.navScrollIndex];
		Element.addClassName(Element.firstDescendant(activeNum), 'selNumber');

		//this.nodes.invoke('hide');
		setTimeout("",3000);
		new Effect.Appear(this.nodes[this.navScrollIndex], { duration: 1.0, from: 0, to: 1, queue: 'end'});
		
		this.activeIndex++;
		if(this.activeIndex >= this.nodes.length) {
			this.activeIndex = 0;
		}
	},
	goToIndex:function(){
        
        /* Numero che dovrà apparire selezionato */
		var prevNum = $$('.iconBox li')[this.navScrollIndex];
		var activeNum = $$('.iconBox li')[this.activeIndex];
		
		Element.removeClassName(Element.firstDescendant(prevNum), 'selNumber');
		new Effect.Fade(this.nodes[this.navScrollIndex], {
			duration: 0.5,
			queue: 'end'
		});
		//this.nodes.invoke('hide');

		Element.addClassName(Element.firstDescendant(activeNum), 'selNumber');
		
		new Effect.Appear(this.nodes[this.activeIndex], {
			duration: 1.0,
			queue: 'end'
		});
		
	},
	load: function() {
		var eList = this.itemsElement;
		this.items.clear();

		var carousel = eList.select('.item');

		if (this.direction == 'next' && carousel.length > 1) {
			this.numBox= $('numBox');

			var ul = new Element('ul');
			$(ul).addClassName('iconBox');
			this.numBox.appendChild(ul);
		} else if (this.direction === 'horizontal') {
			this.pagination = $('pagination');

			if (this.pagination !== 'undefined') {
				var ul = new Element('ul');
				this.pagination.appendChild(ul);

				var list = $$('.item');
				this.nPage = parseInt(list.length / this.n4Page );
				 if(list.length % this.n4Page > 0)
					 this.nPage++;

				this.actualPage = 1;
				var li = new Element('li').update(this.actualPage);
				ul.appendChild(li);

				li = new Element('li').update('&nbsp;'+'-'+'&nbsp;');
				ul.appendChild(li);

				li = new Element('li').update(this.nPage);
				ul.appendChild(li);
			}
		}

		eList.select('.item').each(function(item, numBox) {
			item.carouselKey = null;
			var sKey = '';
			try {
				sKey = item.down('.key').innerHTML;
			} catch(e) {
				alert('Warning: Carousel Items require a child with classname [key]');
				return;
			}
			var oCarouselItem = new CarouselItem();
			if(this.options.itemParser) oCarouselItem.value = this.options.itemParser(item);
			oCarouselItem.index = this.items.length;
			oCarouselItem.key = sKey;
			oCarouselItem.element = item;
			this.items.push(oCarouselItem);
			//Store default selection
			if(item.hasClassName('selected')) {
				this.activeItem = oCarouselItem;
				this.activeIndex = this.items.size();
			}
			if(this.options.setItemEvents) {
				this.options.setItemEvents(this, item, oCarouselItem, this.observer);
			}
			if(this.direction === 'next' && carousel.length > 1) {
				var index = oCarouselItem.index;
				var li = new Element('li');
				$$('.iconBox')[0].appendChild(li);
				var a = new Element('a').update(index+1);
				
				if(numBox === 0) {
                    a.addClassName('selNumber');
		    }
				a.writeAttribute('href','javascript:void(0);' );
				li.appendChild(a);
		
				Event.observe(a, 'click',function(){
					   
					   Event.stopObserving(this.backElement, 'click');
				       Event.stopObserving(this.forwardElement, 'click');
				
					   this.stopF();
					   this.lastActiveIndex = this.activeIndex;
					   this.navScrollIndex = this.lastActiveIndex;
					   this.activeIndex = numBox;

					   this.goToIndex();
	
                       
                }.bind(this));
                
			}  
		}.bind(this));
		//Post processing        
		this.afterLoad();
	},

    afterLoad: function() {
        if(this.items.length == 0) {
            //console.log('Warning: No Carousel Items Exist');
            return;
        }
        
        //Change the following line to moveToIndex if you do 
        //not want the load animation on default selected items
        //this.moveToIndex(this.activeIndex);        
           
        if(this.direction == 'next')
        {
            
			this.nodes = $$('.items .item .icon img');
			
			if(this.nodes.length == 1)
			    this.nodes[0].show();
			
            if(this.nodes.length > 1)
				this.autoPlay();
            this.activate(this.activeItem);
			
			
        }
        else
        {    
            this.scrollToIndex(this.activeIndex); 
            this.activate(this.items[0]);
        }    
       
        if(this.observer.fireActiveCarouselLoaded) this.observer.fireActiveCarouselLoaded(this);
        
		var workspace = $('Workspace') == null ? $('Workspace2') : $('Workspace');
		
		if(workspace != null)
			workspace.setStyle({background:'none'});
    },
    scrollForward: function() {   
        //setsize-1 at a time scrolling 
        
        if(this.navScrollIndex > this.items.length - (this.setSize+this.nElements-1))
         //return;
        { 
           this.navScrollIndex=0;
            Element.setStyle(this.itemsElement, {'left':0});
        }
        var iIndex = this.navScrollIndex + (this.setSize-1);
		if(this.pagination !== 'undefined')
		 {
			this.actualPage++;
			var page = $$('#pagination ul li');
			page[0].update(this.actualPage);
			
		 }
        this.scrollToIndex(iIndex);
        this.activeIndex = iIndex;
        
    },
    scrollBack: function() {
        var iIndex = this.navScrollIndex - (this.setSize-1);
       
        if(iIndex < 0)
        { 
         iIndex  = this.items.length-2;  
         Element.setStyle(this.itemsElement, {'left':0});
		 
        }
		if(this.pagination !== 'undefined')
		 {
			this.actualPage--;
			var page = $$('#pagination ul li');
			page[0].update(this.actualPage);
			
		 }
        this.scrollToIndex(iIndex);
        this.activeIndex = iIndex;
		
		
    },
    getLeft: function(index) {
		 return index * (-this.itemWidth);
    },
    getTop: function(index) {
        var ua = window.navigator.userAgent;
        var msie = ua.indexOf ( "MSIE " );
        if(msie >0)
        {
			if(parseInt (ua.substring (msie+5, ua.indexOf (".", msie ))) == 6)
				return index * (-this.itemHeight-5); 
			return index * (-this.itemHeight-3);   
		}		
        return index * (-this.itemHeight);
    },
    activate: function(carouselItem) {
        if(this.activeItem) this.observer.fireDeactiveCarouselItem(this, this.activeItem.element, this.activeItem);
        if(carouselItem == null) return; 
        this.activeItem = carouselItem;
        if(this.observer.fireActiveCarouselItem) this.observer.fireActiveCarouselItem(this, carouselItem.element, carouselItem);
    },
    next: function() {
        if(this.activeItem == null) { this.activate(this.items[0]); return; }
        var iIndex = this.activeItem.index + 1;
        if(iIndex >= this.items.length) iIndex = 0;
        this.activate(this.items[iIndex]);
        this.activeIndex = iIndex;
    },
    previous: function() {
        if(this.activeItem == null) { this.activate(this.items[0]); return; }
        var iIndex = this.activeItem.index - 1;
        if(iIndex < 0) iIndex = 0;
        this.activate(this.items[iIndex]);
        this.activeIndex = iIndex;
    },
    scrollToIndex: function(index) {      
        if(this.direction == 'vertical') {
            var iPreviousTop = this.getTop(this.navScrollIndex);
            
            var iTop = this.getTop(index);
            
            var iCurrentTop = parseInt(Element.getStyle(this.itemsElement, 'top')) || 0;
            var offset = iPreviousTop-iCurrentTop;
            
            
            var move = iTop - iPreviousTop;
            
            if(move > 0) {
                move = move + offset;
            } else {
                move = move - offset;
            }
            Element.setOpacity(this.itemsElement, this.moveOpacity);
            var ef = new Effect.Move(this.itemsElement, {
                'duration': this.duration,
                'y': move, 
                'afterFinish': function() { 
                    Element.setStyle(this.itemsElement, {'top':iTop + 'px'});
                    Element.setOpacity(this.itemsElement, 1.0);
                }.bind(this)
            });
            ef = null;
            
            this.navScrollIndex = index;
           
            if (this.navScrollIndex <= this.items.length - (this.setSize+this.nElements-1)){
					Element.show(this.forwardElement);
				} else {
					Element.hide(this.forwardElement);
				}
				if ((parseInt(this.navScrollIndex) || 0) != 0){
					Element.show(this.backElement);
				} else {
					Element.hide(this.backElement);
				}
            if(this.observer.fireCarouselAtIndex) this.observer.fireCarouselAtIndex(this, index);
            
        } else  if(this.direction == 'horizontal') {
            var iPreviousLeft = this.getLeft(this.navScrollIndex);
            var iLeft = this.getLeft(index);
            var iCurrentLeft = parseInt(Element.getStyle(this.itemsElement, 'left')) || 0;
            var offset = iPreviousLeft - iCurrentLeft;
			
            var move = iLeft - iCurrentLeft;
            if(move > 0) {
                move = move + offset;	
            } else {
                move = move - offset;
            }
            
            Element.setOpacity(this.itemsElement, this.moveOpacity);
            var ef = new Effect.Move(this.itemsElement, {
                'duration': this.duration,
                'x': move, 
                'afterFinish': function() {
                    Element.setStyle(this.itemsElement, {'left':iLeft + 'px'});
                    Element.setOpacity(this.itemsElement, 1.0);
                }.bind(this)
            });
            ef = null;    
           
            this.navScrollIndex = index;

            if (this.actualPage <  this.nPage){
					Element.show(this.forwardElement);
				} else {
					Element.hide(this.forwardElement);
				}
				if ((parseInt(this.navScrollIndex) || 0) != 0){
					Element.show(this.backElement);
				} else {
					Element.hide(this.backElement);
				}
           
            if(this.observer.fireCarouselAtIndex) this.observer.fireCarouselAtIndex(this, index);
					 
			 
         
        }  
        else{
           
            if(this.observer.fireCarouselAtIndex) this.observer.fireCarouselAtIndex(this, index);

        }
        
    } ,
    moveToIndex: function(index) {
        if(this.direction == 'vertical') {
            var iTop = this.getTop(index);
            Element.setStyle(this.itemsElement, {'top':iTop + 'px'});
            Element.setOpacity(this.itemsElement, 1.0);
        } else {
            var iLeft = this.getLeft(index);
            Element.setStyle(this.itemsElement, {'left':iLeft + 'px'});
            Element.setOpacity(this.itemsElement, 1.0);
        }

        this.navScrollIndex = index;
	  
			if (this.navScrollIndex <= this.items.length - (this.setSize+1)) {
				Element.show(this.forwardElement);
			} else {
				Element.hide(this.forwardElement);
			}
        
			if ((parseInt(this.navScrollIndex) || 0) != 0) {
				Element.show(this.backElement);
			} else {
				Element.hide(this.backElement);
			}
        
    }
};

Carousel.Load = function() {
    var Script_Name = 'carousel.js';
    _os.onLoadComplete(Script_Name);    

};

Carousel.Load();

var Links = {
	openLayerPage: function(elm, page, width, height) {
	    var elm = $(elm);
		var options = {
			type: 'window',
			width: width || 410, 
			height: height || 230,
			enableClose: true,
			moveToMouse: true,
			draggable: true,
			pointer: {pointerX: elm.cumulativeOffset().left, pointerY: elm.cumulativeOffset().top}
		};
		return new LITBox(page, options);  
	},
	
	openLayerMsg: function(e, msg, width, height){
		var options = {
			type: 'alert',
			width: width || 410, 
			height: height || 230,
			enableClose: true,
			moveToMouse: true,
			draggable: true,
			pointer: {pointerX: Event.pointerX(e), pointerY: Event.pointerY(e)}
		};
		return new LITBox(msg, options);  
	}
};


