/*  Prototype JavaScript framework, version 1.6.1
 *  (c) 2005-2009 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.1',

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

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

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

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

      div = form = null;

      return isSupported;
    })()
  },

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

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

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


var Abstract = { };


var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

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

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

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

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

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

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

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

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

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

    if (!Object.keys({ toString: true }).length) {
      if (source.toString != Object.prototype.toString)
        properties.push("toString");
      if (source.valueOf != Object.prototype.valueOf)
        properties.push("valueOf");
    }

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

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

    return this;
  }

  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }

  };
})();
(function() {

  var _toString = Object.prototype.toString;

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

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

  function toJSON(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 (isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = toJSON(object[property]);
      if (!isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  }

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

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

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

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

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

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

  function isArray(object) {
    return _toString.call(object) == "[object Array]";
  }


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

  function isFunction(object) {
    return typeof object === "function";
  }

  function isString(object) {
    return _toString.call(object) == "[object String]";
  }

  function isNumber(object) {
    return _toString.call(object) == "[object Number]";
  }

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

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

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

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

Object.extend(String.prototype, (function() {

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

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

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

    if (!(pattern.length || pattern.source)) {
      replacement = replacement('');
      return replacement + source.split('').join(replacement) + replacement;
    }

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

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

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

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

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

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

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

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

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

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

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

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


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

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        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;
    });
  }

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

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

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

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

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

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

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

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

  function toJSON() {
    return this.inspect(true);
  }

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

  function isJSON() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  }

  function evalJSON(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var $break = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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









  return {
    each:       each,
    eachSlice:  eachSlice,
    all:        all,
    every:      all,
    any:        any,
    some:       any,
    collect:    collect,
    map:        collect,
    detect:     detect,
    findAll:    findAll,
    select:     findAll,
    filter:     findAll,
    grep:       grep,
    include:    include,
    member:     include,
    inGroupsOf: inGroupsOf,
    inject:     inject,
    invoke:     invoke,
    max:        max,
    min:        min,
    partition:  partition,
    pluck:      pluck,
    reject:     reject,
    sortBy:     sortBy,
    toArray:    toArray,
    entries:    toArray,
    zip:        zip,
    size:       size,
    inspect:    inspect,
    find:       detect
  };
})();
function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

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

Array.from = $A;


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

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

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

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

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

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

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

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

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

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

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


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

  function size() {
    return this.length;
  }

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

  function toJSON() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (!Object.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }

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

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

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

  Object.extend(arrayProto, Enumerable);

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

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

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

  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      if (values && typeof values == 'object') {
        if (Object.isArray(values))
          return results.concat(values.map(toQueryPair.curry(key)));
      } else results.push(toQueryPair(key, values));
      return results;
    }).join('&');
  }

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

  function toJSON() {
    return Object.toJSON(this.toObject());
  }

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

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

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

  function succ() {
    return this + 1;
  }

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

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

  function toJSON() {
    return isFinite(this) ? this.toString() : 'null';
  }

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

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

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

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

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

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

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

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

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

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



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

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

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

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

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

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

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});
Ajax.Base = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

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

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

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

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

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

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      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';
    }

    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 && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

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

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

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

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

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

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

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








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

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

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

  status:      0,

  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

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

  getHeader: Ajax.Request.prototype.getHeader,

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

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

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

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

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

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

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

    $super(url, options);
  },

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

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

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

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = { };
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

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

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});



function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

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

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

if (!window.Node) var Node = { };

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


(function(global) {

  var SETATTRIBUTE_IGNORES_NAME = (function(){
    var elForm = document.createElement("form");
    var elInput = document.createElement("input");
    var root = document.documentElement;
    elInput.setAttribute("name", "test");
    elForm.appendChild(elInput);
    root.appendChild(elForm);
    var isBuggy = elForm.elements
      ? (typeof elForm.elements.test == "undefined")
      : null;
    root.removeChild(elForm);
    elForm = elInput = null;
    return isBuggy;
  })();

  var element = global.Element;
  global.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (SETATTRIBUTE_IGNORES_NAME && 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(global.Element, element || { });
  if (element) global.Element.prototype = element.prototype;
})(this);

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

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

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


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

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

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

  update: (function(){

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

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

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

    function update(element, content) {
      element = $(element);

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

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

      content = Object.toHTML(content);

      var tagName = element.tagName.toUpperCase();

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

      if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
        if (tagName in Element._insertionTranslations.tags) {
          while (element.firstChild) {
            element.removeChild(element.firstChild);
          }
          Element._getContentFromAnonymousElement(tagName, content.stripScripts())
            .each(function(node) {
              element.appendChild(node)
            });
        }
        else {
          element.innerHTML = content.stripScripts();
        }
      }
      else {
        element.innerHTML = content.stripScripts();
      }

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

    return update;
  })(),

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

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

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

    var content, insert, tagName, childNodes;

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

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

      content = Object.toHTML(content);

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

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

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

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

    return element;
  },

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

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      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(element, 'parentNode');
  },

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

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

  immediateDescendants: function(element) {
    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(element, 'previousSibling');
  },

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

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

  match: function(element, selector) {
    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(element);
    return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  },

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

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = Element.previousSiblings(element);
    return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = Element.nextSiblings(element);
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },


  select: function(element) {
    var args = Array.prototype.slice.call(arguments, 1);
    return Selector.findChildElements(element, args);
  },

  adjacent: function(element) {
    var args = Array.prototype.slice.call(arguments, 1);
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

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

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

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

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

    return false;
  },

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

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

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

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

    return element;
  },

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

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

    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
      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';
      if (Prototype.Browser.Opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

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

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

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

  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.toUpperCase() == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (Element.getStyle(element, 'position') == 'absolute') return element;

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

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

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

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

    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;

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

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == '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] || { });

    source = $(source);
    var p = Element.viewportOffset(source);

    element = $(element);
    var delta = [0, 0];
    var parent = null;
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = Element.getOffsetParent(element);
      delta = Element.viewportOffset(parent);
    }

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

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

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

  childElements: Element.Methods.immediateDescendants
});

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

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

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

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

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

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

else if (Prototype.Browser.IE) {
  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);
      try { element.offsetParent }
      catch(e) { return $(document.body) }
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  $w('positionedOffset viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        try { element.offsetParent }
        catch(e) { return Element._returnOffset(0,0) }
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);
        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
    function(proceed, element) {
      try { element.offsetParent }
      catch(e) { return Element._returnOffset(0,0) }
      return proceed(element);
    }
  );

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

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

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

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

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

  Element._attributeTranslations = (function(){

    var classProp = 'className';
    var forProp = 'for';

    var el = document.createElement('div');

    el.setAttribute(classProp, 'x');

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

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

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

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

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

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

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

  Element._attributeTranslations.has = {};

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

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

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

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

}

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

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

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

    return element;
  };

  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 ('outerHTML' in document.documentElement) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

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

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

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next();
      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];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

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

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

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

(function(div) {

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

  div = null;

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

Element.extend = (function() {

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

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

  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

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

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

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

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

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

    extendElementWith(element, methods);

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

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

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

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

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

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

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

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

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

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


document.viewport = {

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

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

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

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

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

    return document.documentElement;
  }

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

    property[D] = 'client' + D;

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

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

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


Element.Storage = {
  UID: 1
};

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

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

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

    return Element.Storage[uid];
  },

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

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

    return element;
  },

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

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

    return value;
  },

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

    if (this.shouldUseSelectorsAPI()) {
      this.mode = 'selectorsAPI';
    } else if (this.shouldUseXPath()) {
      this.mode = 'xpath';
      this.compileXPathMatcher();
    } else {
      this.mode = "normal";
      this.compileMatcher();
    }

  },

  shouldUseXPath: (function() {

    var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
      var isBuggy = false;
      if (document.evaluate && window.XPathResult) {
        var el = document.createElement('div');
        el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';

        var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
          "//*[local-name()='li' or local-name()='LI']";

        var result = document.evaluate(xpath, el, null,
          XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

        isBuggy = (result.snapshotLength !== 2);
        el = null;
      }
      return isBuggy;
    })();

    return function() {
      if (!Prototype.BrowserFeatures.XPath) return false;

      var e = this.expression;

      if (Prototype.Browser.WebKit &&
       (e.include("-of-type") || e.include(":empty")))
        return false;

      if ((/(\[[\w-]*?:|:checked)/).test(e))
        return false;

      if (IS_DESCENDANT_SELECTOR_BUGGY) return false;

      return true;
    }

  })(),

  shouldUseSelectorsAPI: function() {
    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

    if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;

    if (!Selector._div) Selector._div = new Element('div');

    try {
      Selector._div.querySelector(this.expression);
    } catch(e) {
      return false;
    }

    return true;
  },

  compileMatcher: function() {
    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m, len = ps.length, name;

    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 = 0; i<len; i++) {
        p = ps[i].re;
        name = ps[i].name;
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
            new Template(c[name]).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, len = ps.length, name;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i = 0; i<len; i++) {
        name = ps[i].name;
        if (m = e.match(ps[i].re)) {
          this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
            new Template(x[name]).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;
    var e = this.expression, results;

    switch (this.mode) {
      case 'selectorsAPI':
        if (root !== document) {
          var oldId = root.id, id = $(root).identify();
          id = id.replace(/([\.:])/g, "\\$1");
          e = "#" + id + " " + e;
        }

        results = $A(root.querySelectorAll(e)).map(Element.extend);
        root.id = oldId;

        return results;
      case 'xpath':
        return document._getElementsByXPath(this.xpath, root);
      default:
       return this.matcher(root);
    }
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m, len = ps.length, name;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i = 0; i<len; i++) {
        p = ps[i].re;
        name = ps[i].name;
        if (m = e.match(p)) {
          if (as[name]) {
            this.tokens.push([name, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            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() + ">";
  }
});

if (Prototype.BrowserFeatures.SelectorsAPI &&
 document.compatMode === 'BackCompat') {
  Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
    var div = document.createElement('div'),
     span = document.createElement('span');

    div.id = "prototype_test_id";
    span.className = 'Test';
    div.appendChild(span);
    var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
    div = span = null;
    return isIgnored;
  })();
}

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: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      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)]",
      'checked':     "[@checked]",
      'disabled':    "[(@disabled) and (@type!='hidden')]",
      'enabled':     "[not(@disabled) and (@type!='hidden')]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v, len = p.length, name;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i = 0; i<len; i++) {
            name = p[i].name
            if (m = e.match(p[i].re)) {
              v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).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); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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: [
    { name: 'laterSibling', re: /^\s*~\s*/ },
    { name: 'child',        re: /^\s*>\s*/ },
    { name: 'adjacent',     re: /^\s*\+\s*/ },
    { name: 'descendant',   re: /^\s/ },

    { name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
    { name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
    { name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
    { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
    { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
    { name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
  ],

  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 nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    mark: function(nodes) {
      var _true = Prototype.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: (function(){

      var PROPERTIES_ATTRIBUTES_MAP = (function(){
        var el = document.createElement('div'),
            isBuggy = false,
            propName = '_countedByPrototype',
            value = 'x'
        el[propName] = value;
        isBuggy = (el.getAttribute(propName) === value);
        el = null;
        return isBuggy;
      })();

      return PROPERTIES_ATTRIBUTES_MAP ?
        function(nodes) {
          for (var i = 0, node; node = nodes[i]; i++)
            node.removeAttribute('_countedByPrototype');
          return nodes;
        } :
        function(nodes) {
          for (var i = 0, node; node = nodes[i]; i++)
            node._countedByPrototype = void 0;
          return nodes;
        }
    })(),

    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      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._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },

    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

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

    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          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() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;

      if (root == document) {
        if (!targetNode) return [];
        if (!nodes) return [targetNode];
      } else {
        if (!root.sourceIndex || root.sourceIndex < 1) {
          var nodes = root.getElementsByTagName('*');
          for (var j = 0, node; node = nodes[j]; j++) {
            if (node.id === id) return [node];
          }
        }
      }

      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, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      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, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      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);
    },

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

    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._countedByPrototype) {
          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++) {
        if (node.tagName == '!' || node.firstChild) 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._countedByPrototype) 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 && (!node.type || node.type !== 'hidden'))
          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 == v || nv && nv.startsWith(v); },
    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) 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) {
    expressions = Selector.split(expressions.join(','));
    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;
  }
});

if (Prototype.Browser.IE) {
  Object.extend(Selector.handlers, {
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    }
  });
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}

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

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

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            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) {
    var elements = $(form).getElementsByTagName('*'),
        element,
        arr = [ ],
        serializers = Form.Element.Serializers;
    for (var i = 0; element = elements[i]; i++) {
      arr.push(element);
    }
    return arr.inject([], function(elements, child) {
      if (serializers[child.tagName.toLowerCase()])
        elements.push(Element.extend(child));
      return elements;
    })
  },

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

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

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

    return matchingInputs;
  },

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

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

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

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

  focusFirstElement: function(form) {
    form = $(form);
    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)$/i.test(element.type))))
        element.select();
    } catch (e) { }
    return element;
  },

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

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

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

var Field = Form.Element;

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

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

Form.Element.Serializers = {
  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 (Object.isUndefined(value)) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

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

  select: function(element, value) {
    if (Object.isUndefined(value))
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, currentValue, single = !Object.isArray(value);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        currentValue = this.optionValue(opt);
        if (single) {
          if (currentValue == value) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = value.include(currentValue);
      }
    }
  },

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

  var Event = {
    KEY_BACKSPACE: 8,
    KEY_TAB:       9,
    KEY_RETURN:   13,
    KEY_ESC:      27,
    KEY_LEFT:     37,
    KEY_UP:       38,
    KEY_RIGHT:    39,
    KEY_DOWN:     40,
    KEY_DELETE:   46,
    KEY_HOME:     36,
    KEY_END:      35,
    KEY_PAGEUP:   33,
    KEY_PAGEDOWN: 34,
    KEY_INSERT:   45,

    cache: {}
  };

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

  var _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);
    };
  }

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

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

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

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

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

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

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

    return Element.extend(node);
  }

  function findElement(event, expression) {
    var element = Event.element(event);
    if (!expression) return element;
    var elements = [element].concat(element.ancestors());
    return Selector.findElement(elements, expression, 0);
  }

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

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

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

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

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


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

    event.stopped = true;
  }

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

    element: element,
    findElement: findElement,

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

    stop: stop
  };


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

  if (Prototype.Browser.IE) {
    function _relatedTarget(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);
    }

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

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

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);

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

      return Object.extend(event, methods);
    };
  } else {
    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
    Object.extend(Event.prototype, methods);
    Event.extend = Prototype.K;
  }

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

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

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

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

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

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

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

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

            if (parent === element) return;

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

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

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

  var CACHE = [];

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

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


  var _getDOMEventName = Prototype.K;

  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
    _getDOMEventName = function(eventName) {
      var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
      return eventName in translations ? translations[eventName] : eventName;
    };
  }

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

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

    if (!responder) return element;

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

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

    return element;
  }

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

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

    if (Object.isUndefined(registry)) return element;

    if (eventName && !handler) {
      var responders = registry.get(eventName);

      if (Object.isUndefined(responders)) return element;

      responders.each( function(r) {
        Element.stopObserving(element, eventName, r.handler);
      });
      return element;
    } else if (!eventName) {
      registry.each( function(pair) {
        var eventName = pair.key, responders = pair.value;

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

    var responders = registry.get(eventName);

    if (!responders) return;

    var responder = responders.find( function(r) { return r.handler === handler; });
    if (!responder) return element;

    var actualEventName = _getDOMEventName(eventName);

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

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

    return element;
  }

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

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

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

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

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

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

    return Event.extend(event);
  }


  Object.extend(Event, Event.Methods);

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

  Element.addMethods({
    fire:          fire,

    observe:       observe,

    stopObserving: stopObserving
  });

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

    observe:       observe.methodize(),

    stopObserving: stopObserving.methodize(),

    loaded:        false
  });

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

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

  var timer;

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

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

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

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

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

Element.addMethods();

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

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

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

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

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

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

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

var Position = {
  includeScrollOffsets: false,

  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },


  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

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

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

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

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

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

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

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

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

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

Object.extend(Element.ClassNames.prototype, Enumerable);

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


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

Scriptaculous.load();

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


;/**
	Prototype and script.aculo.us extensions by Gareth Hughes <garethh@3wmltd.com>
	Copyright 2009 3WM Ltd.
**/


/*********************************************************************************************************************
 *	@class:		HTMLTemplate
 *
 *	@example:	new HTMLTemplate("<div>#{test}</div>").evaluate({test: "<test>"});	// <div>&lt;test&gt;</div>
 *	@notes:		Works just like the Template class, except encodes all HTML elements before inserting the values.
 *********************************************************************************************************************/
var HTMLTemplate = Class.create(Template, {
	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).escapeHTML();
		});
	}
});


/*********************************************************************************************************************
 *	@function:	String.replaceAll(search, replace)
 *
 *	@example:	"Hello There".replaceAll(/[elo]/, "i"); //=> Hiiii Thiri
 *	@notes:		Replaces all occurances of a search pattern with replace string.
 *********************************************************************************************************************/
Object.extend(String.prototype, {
	replaceAll: function(search, replace) {
		var string = this;
		while(string.match(search))
			string = string.replace(search,replace);
		return string;
	}
});


/*********************************************************************************************************************
 *	@function:	Element.getText()
 *
 *	@example:	$("element").getText();
 *	@notes:		Returns the "textContent" or "innerText" of the element.
 *********************************************************************************************************************/
Element.addMethods({
	getText: function(element) {
		return element.textContent || element.innerText;
	}
});


/*********************************************************************************************************************
 *	@function:	String.moneyValue()
 *
 *	@example:	"£1,000.40".moneyValue();		// 1000.4
 *	@notes:		Parses the text for something looking like £1,000.40 and returns a float.
 *********************************************************************************************************************/
Object.extend(String.prototype, {
	moneyValue: function() {
		var value = this.replace(",","").match(/(-?)[^\d]{0,2}(\d[\d\.]*)/);
		if (!value)
			return 0;
		
		value = parseFloat(value[1]+value[2]);
		return isNaN(value) ? 0 : value;
	}
});


/*********************************************************************************************************************
 *	@function:	Number.roundTo(precision)
 *
 *	@example:	(1.2345).roundTo(2); //=> 1.23
 *	@notes:		Rounds the number to <precision> decimal places.
 *********************************************************************************************************************/
Object.extend(Number.prototype, {
	roundTo: function(places) {
		var multi = Math.pow(10, places);
		return Math.round(this*multi)/multi;
	}
});


/*********************************************************************************************************************
 *	@function:	Element.toggleBlind()
 *
 *	@example:	$('some_div').toggleBlind();
 *	@notes:		If hidden, will reveal itself with a blindDown effect. If visible, will hide with a blindUp effect.
 *				If it starts hidden, the "display:none" must be an inline style.
 *				Contains a couple of fixes over the traditional Effect.blind* functions.
 *	@returns:	true if now visible, false if now hidden
 *********************************************************************************************************************/
Element.addMethods({
	toggleBlind: function(element) {
		var status = element.getStyle('display') == "none" ? false : true;
		var EffectParams = {}

		if (status) {
			element.makeClipping();
			EffectParams = {
				scaleTo: 0,
				afterFinishInternal: function(effect) { effect.element.hide().undoClipping(); }
			}
		} else {
			element.show();
			var elementDimensions = element.getDimensions();
			element.hide();
			EffectParams = {
				scaleTo: 100,
				scaleFrom: 0,
				afterSetup: function(effect) { effect.element.makeClipping().setStyle({height: '0px'}).show(); },
				afterFinishInternal: function(effect) { effect.element.undoClipping(); },
				scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}
			}
		}

		new Effect.Scale(element, EffectParams.scaleTo, Object.extend({
			scaleContent: false, 
			scaleX: false,
			duration: 0.3,
			restoreAfterFinish: true
		}, EffectParams || {} ));
		
		return !status;
	}
});


/*********************************************************************************************************************
 *	@class:		PleaseWait
 *
 *	@example:	var pause = new PleaseWait('element_id'); pause.stop();
 *	@notes:		Creates a temporary shield over the element in question to prevent interaction and show a loading bar.
 *********************************************************************************************************************/
var PleaseWait = Class.create({
	initialize: function(element, options) {
		this.uid = Math.random()*100000000;
		this.element = $(element);
		this.options = Object.extend({
			opacity:			0.25,
			fadeDuration:		0.5,
			throbber:			"images/throbber_dark.gif",
			backgroundColor:	"black",
			zOrder:				1000
		}, options || {});
		this.start();
		Event.observe(window, 'resize', this.resize.bind(this));
	},
	
	start: function() {
		if (this.mask)
			return true;
		
		this.mask = new Element('div', {style: 'display: none; position: absolute; background-repeat: no-repeat; background-position: center;'});
		this.mask.setStyle({
			'zOrder':			this.options.zOrder,
			'backgroundColor':	this.options.backgroundColor,
			'backgroundImage':	"url('"+(this.options.throbber)+"')"
		});
		
		this.element.insert(this.mask);
		this.resize();
		new Effect.Appear(this.mask, {
			to: this.options.opacity,
			duration: this.options.fadeDuration,
			queue: {
				position: 'end',
				scope: 'PleaseWait'+this.uid
			}
		});
	},
	
	stop: function() {
		if (!this.mask || this.stopping)
			return true;
			
		this.stopping = true;
		new Effect.Fade(this.mask, {
			duration: this.options.fadeDuration,
			from: this.options.opacity,
			afterFinish: function() {
				this.mask.remove();
				this.mask = null;
				this.stopping = null;
			}.bind(this),
			queue: {
				position: 'end',
				scope: 'PleaseWait'+this.uid
			}
		});
	},
	
	resize: function() {
		if (!this.mask)
			return true;
			
		this.mask.setStyle({
			height:	this.element.getHeight()+"px",
			width:	this.element.getWidth()+"px",
			top:	this.element.cumulativeOffset()[1]+"px"
		});
	}
});

		function ni() {
			alert("We are sorry, but this feature has not been implemented yet.");
		}

		function ajax_request(form, options) {
			var callback = Object.extend({
				onSuccess:	Prototype.emptyFunction,
				onError:		Prototype.emptyFunction
			}, options || {})
			
			form = $(form);
			form.select('.blurred').each(function(e){e.value='';});
			form.request({onComplete: function(t){
				form.enable();
				
				form.select('p.field_error').each(function(ele){ ele.remove(); });
				form.select('.field_error').each(function(ele){ ele.removeClassName('field_error'); });
				
				var r = t.responseJSON || {};
				if (r.error) {
					r.error.reverse().each(function(err){
						var ele = form.getElements().find(function(e){return (e.name==err.key);});
						ele.insert({after: new Element('p', {'class': 'field_error'}).update(err.value)})
						ele.addClassName('field_error');
						ele.focus(); 
					});
					callback.onError(r);
				} else {
					callback.onSuccess(r);
				}
			}});
			form.disable();
			return false;
		}

;//Redundant code, should not be used. Is only included here to prevent errors;
var bPageIsLoaded = false;
function SwapImage(){}
function RestoreImage(){}
function PreloadImages(){bPageIsLoaded=true;}

/************************************************************************
 *	@function	setCookie()
 *	@notes		Generic Set Cookie routine
 ***********************************************************************/
function setCookie(name, value, expires) {
    var cookie = name + "=" + escape(value) +"; path=/" + (expires ? "; expires=" + expires.toUTCString() : "");
	document.cookie = cookie;
}

/************************************************************************
 *	@function	getCookies()
 *	@notes		Returns the cookie jar as an object
 ***********************************************************************/
function getCookies() {
	var cookiejar = {};
    document.cookie.split("; ").each(function(cookie) {
		var crumbs = cookie.split("=");
		cookiejar[crumbs[0]] = unescape(crumbs[1]);
	});
	
	return cookiejar;
}

/************************************************************************
 *	@function	getCookie()
 *	@notes		Generic Get Cookie routine
 ***********************************************************************/
function getCookie(name) {
	var cookiejar = getCookies();
	return cookiejar[name] ? cookiejar[name] : null;
}

/************************************************************************
 *	@function	deleteCookie()
 *	@notes		Generic Delete Cookie routine
 ***********************************************************************/
function deleteCookie(name) {
	document.cookie = name + "=; path=/; expires=" + new Date().toUTCString();
}

/************************************************************************
 *	@function	saveReferrer()
 *	@notes		Saves the current page to a Cookie so that the next
 *				CGI request will treat it as HTTP Referrer.
 *				Some pretty poor emulation that is easily broken.
 ***********************************************************************/
function saveReferrer() {
	if (window.name == 'ActPopup')
		return false;
	
	var idx;
	var url = document.URL;
	if (window.location.hash && (idx = url.lastIndexOf(window.location.hash)))
		url = url.substr(0, idx);

	setCookie("ACTINIC_REFERRER", url);
}


/***********************************************************************
 *	@function	ShowPopUp
 *	@notes		creates pop up window
 * 	@input 		url		-	URL of the page to display
 *				width	-	Width of window
 *				height	-	Height of window
 ***********************************************************************/
function ShowPopUp(url, width, height) {  
	window.open(url, 'ActPopup', 'width=' + width + ',height=' + height + ',scrollbars, resizable');
}

/************************************************************************
 *	@function	DecodeMail()
 *	@notes		Just a rework of the original function by Zoltan Magyar
 *				Now uses Prototype library.
 *				Reduced from 17 lines to 7.
 *	@ZMnotes	decodes the obfuscated mail address in 'contactus' link
 ***********************************************************************/
function DecodeMail() {
	$$("a[name='contactus']").each(function(a) {
		a.href.replace(/\s*\[at\]\s*/, "@");
		while (a.href.match(/\s*\[dot\]\s*/))
			a.href.replace(/\s*\[dot\]\s*/, ".");
	});
};

/************************************************************************
 *	@function	HtmlInclude()
 *	@notes		Just a rework of the original function by Zoltan Magyar
 *				Now uses Prototype library.
 *				Reduced from 68 lines to 7.
 ***********************************************************************/
function HtmlInclude() { 
	$$("a[rel='fragment']").each(function(a) {
		new Ajax.Request(a.href, { method: 'get', onComplete: function(t) {
				a.replace(t.responseText);
			}
		});
	});
}

document.observe("dom:loaded", HtmlInclude);

/************************************************************************
 *	@function	get_cart()
 *	@input		nothing
 *	@output		{ total : string, count : int }
 ***********************************************************************/
function get_cart() {
	var cart = { total: "0.00z\u0142", count: 0, value: 0 };
	var cookie = getCookie("CART_CONTENT");
	if (!cookie) return cart;
		
	cookie = cookie.split("\t");

	for (var i = 0; i < cookie.length; i+=2) {
		var key = cookie[i].match(/^CART_(.+)$/)[1].toLowerCase().strip();
		var value = cookie[i+1].strip();
		cart[key] = value;
	}
	
	cart.count = parseInt(cart.count);
	if (cart.total == "0") cart.total = "0.00zl";
	
	// extra: get the value of the cart as a float
	if (cart.total.match(/&#\d+;/g)) {
		var value = cart.total;
		value.match(/&#\d+;/g).each(function(ent){
			var char = parseInt(ent.match(/&#(\d+);/)[1]);
			value = value.replace(ent, String.fromCharCode(char));
		});
		cart.value = value.moneyValue();
	}
	
	return cart;
}

/************************************************************************
 *	@function	get_business()
 *	@input		nothing
 *	@output		object: ACTINIC_BUSINESS
 ***********************************************************************/
function get_business() {
	var business = {};
	var cookie = getCookie("ACTINIC_BUSINESS");
	if (!cookie || cookie == "-")
		return false;
		
	cookie.split("\n").each(function(pair){
		pair = pair.split("\t");
		business[pair[0].toLowerCase()] = pair[1];
	});

	return business;
}

/************************************************************************
 *	@function	toggle_attribute_lock()
 *	@input		event, element
 *	@purpose	locks attribute options for attributes which aren't selected
 ***********************************************************************/
function toggle_attribute_lock(event, element) {
	var ele = element ? element : Event.element(event);
	var table = ele.up("fieldset").down("table.attributes");
	table.select("input, select, textarea, button").each(function(i){
		  i.disabled = !ele.checked;
	});
}

/************************************************************************
 *	@function	line_up_elements()
 *	@input		CSS selector, columns
 *	@purpose	lines up the height of horizontal list items to look like a table.
 ***********************************************************************/
function line_up_elements(selector, columns) {
	$$(selector).inGroupsOf(columns).each(function(ele_group) {
		var max_height = ele_group.inject(0, function(acc, e) { return Math.max(acc, (e ? e.getHeight() : 0)); });
		ele_group.each(function(e) {
			if (!e) return;
			var padding = 0;
			if (parseInt(e.getStyle("border-top-width"))) padding += parseInt(e.getStyle("border-top-width"));
			if (parseInt(e.getStyle("border-bottom-width"))) padding += parseInt(e.getStyle("border-bottom-width"));
			if (parseInt(e.getStyle("padding-bottom"))) padding += parseInt(e.getStyle("padding-bottom"));
			if (parseInt(e.getStyle("padding-top"))) padding += parseInt(e.getStyle("padding-top"));
			e.style.height = (max_height - padding)+"px";
		});
	});
}

/************************************************************************
 *	@function	get_page_query()
 *	@purpose	returns an assoc array of the query string for the current page.
 ***********************************************************************/
function get_page_query() {
	var querystr = (window.location.search || window.location.href).match(/^\?(.*)$/);
	if (!querystr)
		return {};
		
	return querystr[1].split("&").inject({}, function(assoc, arg){
		var args = arg.split("=");
		assoc[unescape(args[0])] = unescape(args[1]) || "";
		return assoc;
	});
}

;var ShoppingBasket = Class.create({
	initialize: function(basket_id) {
		this.update_cart(basket_id);
		this.draw_mini_basket();
	},

	update_cart: function(basket_id) {
		
		// first, let's try and scrape some fresh information from a shopping basket table (if one is around)
		this.shopping_basket = $(basket_id);
		if (this.shopping_basket) {
			var tmp;
			this.products			= [];
			this.adjustments		= this.get_basket_value_pairs("adjustment");
			this.taxes				= this.get_basket_value_pairs("tax");
			this.exempted_taxes		= this.get_basket_value_pairs("exempted_tax");
			this.including_taxes	= this.get_basket_value_pairs("including_tax");
			this.subtotal			= ((tmp = this.shopping_basket.select("tr.sub_total td")).length)	? tmp[0].getText().moneyValue() : 0;
			this.shipping			= ((tmp = this.shopping_basket.select("tr.shipping td")).length)	? tmp[0].getText().moneyValue() : 0;
			this.handling			= ((tmp = this.shopping_basket.select("tr.handling td")).length)	? tmp[0].getText().moneyValue() : 0;
			this.total				= ((tmp = this.shopping_basket.select("tr.total th")).length > 1)	? tmp[1].getText().moneyValue() : 0;
			
			// add products
			this.shopping_basket.select(".product4028, .component4094").each(function(e){
				var product_row = e.up("tr.product_line");
				
				var qty = product_row.down("td.basket_quantity");
				var qty_input = qty.down("input");
				
				var item = {
					reference:		Element.getText(product_row.down("td.basket_reference div")),
					description:	Element.getText(e),
					quantity:		qty_input ? qty_input.value : Element.getText(qty),
					price:			product_row.down("td.basket_price").getText().moneyValue(),
					cost:			product_row.down("td.basket_cost").getText().moneyValue()
				};
				
				this.products.push(item);
			}.bind(this));
			
			// remove association with shopping_basket element -- don't need it in the cookie
			this.shopping_basket = null;
			setCookie(basket_id, Object.toJSON(this));
			return true;
		}

		// if we reached here, there's no cart on the page, so let's try to recall the data collected on the last save.
		var cookie = getCookie(basket_id);
		var data = cookie ? cookie.evalJSON() : {total: 0};

		// if our sums match, we should be ok to assume that the copy of the cart in our cookie is the same as what's in the actual cart.
		if (parseInt(get_cart().value*100) === parseInt(data.total*100)) {
			Object.extend(this, data);
			return true;
		}
			
		return false;
	},

	get_basket_value_pairs: function(key) {
		var pairs = [];
		this.shopping_basket.select("tr."+key).each(function(e){
			pairs.push({
				name:	e.down("th").getText(),
				value:	e.down("td").getText().moneyValue()
			});
		});
		return pairs;
	},
	
	draw_mini_basket: function() {
		var mini = $("mini_basket");
		if (!mini) return false;
		
		if (!getCookies().CART_CONTENT) {
			mini.hide();
			var mini_container = mini.up('.widget_container');
			if (mini_container) { mini_container.hide(); }
			return false;
		}
		
		var table_template 			= new 		Template('<table><thead>#{head}</thead><tbody>#{body}</tbody><tfoot>#{footer}</tfoot></table>');
		var table_head_template		= new 	HTMLTemplate('<tr><th class="left">#{description_caption}</th><th class="right">#{quantity_caption}</th></tr>');
		var product_row_template	= new 	HTMLTemplate('<tr><td class="left">#{description}</td><td class="right">x#{quantity}</td></tr>');
		var empty_row_template		= new 	HTMLTemplate('<tr><td colspan="2" class="center">#{message}</td></tr>');
		var table_footer_template	= new 		Template('<tr class="total"><td class="left">#{total_caption}</td><td class="right">#{total}</td></tr>');
		
		var table_head = table_head_template.evaluate({
			description_caption	: "Produkt",
			quantity_caption	: "szt"
		});
		
		var table_body = (this.products && this.products.length)
				? this.products.inject("", function(sofar, product){return sofar+product_row_template.evaluate(product);}.bind(this))
				: empty_row_template.evaluate({message: "Tw\u00F3j koszyk jest pusty."});
		
		var table_footer = table_footer_template.evaluate({
			total_caption	: "Suma w koszyku",
			total			: get_cart().total
		});
		
		var table = table_template.evaluate({
			head	: table_head,
			body	: table_body,
			footer	: table_footer
		});
		
		mini.update(table);
	}
});

;var StateRestriction = Class.create({
	initialize: function(country_select, region_select) {
		this.country_select	= $(country_select);
		this.region_select	= $(region_select);
		if (!this.country_select || !this.region_select || this.country_select.tagName.toLowerCase() == "input" || this.region_select.tagName.toLowerCase() == "input")
			return;
		
		this.selected_country = null;
		this.countries = [];
		
		this.country_select.select('option').each(function(option){
			this.countries.push({
				name: option.text,
				abbr: option.value,
				element: option,
				regions: []
			});
		}.bind(this));
		
		// Add hidden field to do the job of region_select when it isn't around. Only if required.
		this.no_region = null;
		var undefined_region = this.region_select.down("option[value='UndefinedRegion']");
		if (undefined_region && undefined_region.parentNode) {
			undefined_region.remove();
			this.no_region = new Element('input', {'type':'hidden','name':this.region_select.name,'value':'UndefinedRegion'});
		}
		
		// Catalogue all of the valid region options
		this.region_select.select('option').each(function(option){
			var place = option.value.split('.');
			var country = this.countries.find(function(country){ return (country.abbr == place[0]); });
			
			if (!country)
				return false;
			
			country.regions.push({
				name: option.text,
				abbr: option.value,
				element: option
			});
		}.bind(this));

		this.country_select.observe('change', this.performLock.bind(this));
		this.country_select.observe('click', this.performLock.bind(this));
		this.performLock();
	},
	
	performLock: function() {
		var country = this.countries.find(function(country){ return (country.abbr == this.country_select.value); }.bind(this));
		
		// Don't redraw list unless another country is selected
		if (this.selected_country && country.abbr == this.selected_country)
			return;
			
		this.selected_country = country.abbr;

		this.region_select.select('option').each( function(o){ if (o && o.parentNode) o.remove();} );
		if (country.regions.length) {
			country.regions.each(function(region){ this.region_select.insert(region.element); }.bind(this));
			this.region_select.selectedIndex = 0;
			if (this.no_region && this.no_region.parentNode) this.no_region.remove();
			this.region_select.show();
		} else {
			this.region_select.hide();
			if (this.no_region) this.region_select.insert({after:this.no_region});
		}
	}
});

;ColourPicker = new Class.create({
	choiceTable: {
		'White'			:	{colour:'#fff',		image:null},
		'Biały'			:	{colour:'#fff',		image:null},
		'Biay'			:	{colour:'#fff',		image:null},

		'Brown'			:	{colour:'#c59e6b',	image:null},
		'Brązowy'		:	{colour:'#c59e6b',	image:null},
		'Brzowy'		:	{colour:'#c59e6b',	image:null},

		'Blue'			:	{colour:'#5a78ce',	image:null},

		'Pink'			:	{colour:'#ea96c3',	image:null},
		'Różowy'		:	{colour:'#ea96c3',	image:null},
		'Rowy'			:	{colour:'#ea96c3',	image:null},

		'Silver'		:	{colour:'#b4b4b4',	image:null},
		'Srebrny'		:	{colour:'#b4b4b4',	image:null},

		'Grey'			:	{colour:'#b4b4b4',	image:null},

		'Silver Grey'		:	{colour:'#b4b4b4',	image:null},

		'Pink & Silver'		:	{colour:'#b4b4b4',	image:'swatch-pink-silver.png'}

	},

	determinePriceChange: function(buttonPressed) {
		var colour = buttonPressed.retrieve('inputpair').value;
		var permutation = this.permutations.find(function(p){ return p.Kolor.text == colour; });
		if (permutation) { document.fire('colourpicker:pricechange', {'price':permutation.Cena,'picker':this}); }
	},

	initialize: function(fieldset) {
		// Sanity Checks
		var table = fieldset.down('table.attributes');
		if (!table) {return;}

		// Currently, only drop-down component layouts can be converted into a colour picker.
		// Radio buttons do not need to be supported and are not used by default. If used, I assume that it is intentional and a colour picker is not desired.
		var sel = table.down('select');
		if (!sel) {return;}
		
		// Grab the key&value pairs from the drop down box
		this.options = sel.select('option').inject({}, function(acc, option){
			var txt = option.getText().strip();
			acc[option.value || txt] = txt;
			return acc;
		});
		
		// Grab the permutations table and interpret it as an array of objects
		this.permutations = this.getPermutations(fieldset.down('table.permutation_grid'));
		
		// Add a hidden field for tricking the browser into thinking that our colour picker is a proper form element.
		this.hiddenfield = new Element('input', {'type':'hidden', 'value':sel.getValue(), 'name':sel.name});
		table.insert({after: this.hiddenfield});
		
		// Create our colour picker interface and add it to the layout
		var ui = this.createInterface(this.options);
		table.insert({after: ui.container});
		
		// Add a float clear after the interface - prevents badness in webkit and trident
		var clear = new Element('div', {'class':'clear'});
		ui.container.insert({'after':clear});
		
		// Remove the old drop-down list. It is no longer used.
		table.remove();
	},
	
	getPermutations: function(permutationsGrid) {
		if (!permutationsGrid) {return [];}
		
		var rows = permutationsGrid.select('tr');
		var headings = rows.shift().select('th').inject([], function(headings, th){
			headings.push(th.getText().strip());
			return headings;
		});
		
		return rows.inject([], function(permutations, row){
			permutations.push(row.select('td').inject({}, function(permutation, td, idx){
				permutation[headings[idx]] = { html: td.innerHTML, text: td.getText().strip() }
				return permutation;
			}));
			return permutations;
		});
	},
	
	createInterface: function(kvpairs) {
		var ui = {
			picker		: this,
			buttons		: [],
			container	: new Element('div', {'class':'colourpicker'}),
			onclick		: function(e) {
				var clickedButton = e.tagName ? e : e.findElement('div.colourpicker_button');
				this.buttons.invoke('removeClassName', 'colourpicker_selected');
				clickedButton.addClassName('colourpicker_selected');
				this.picker.hiddenfield.value = clickedButton.retrieve('inputpair').key;
				document.fire('colourpicker:select', {'button':clickedButton,'ui':this});
				if (this.picker.permutations.length) {this.picker.determinePriceChange(clickedButton);}
			}
		};
		
		$H(kvpairs).each(function(pair){
			var choiceData = this.choiceTable[pair.value] || this.choiceTable[pair.value.replace(/[^\w\d]+/,'')] || this.choiceTable['White'];
			var button = new Element('div', {'class':'colourpicker_button','title':pair[1]});
			button.setStyle({backgroundColor: choiceData.colour || 'white', backgroundImage: 'url(\''+choiceData.image+'\')' || 'none'});
			
			button.store('inputpair',pair);
			ui.buttons.push(button);
			ui.container.insert(button);
			button.insert(new Element('span'));
			button.observe('click', ui.onclick.bindAsEventListener(ui));
		}.bind(this));
		
		var lead = ui.buttons.find(function(button){
			return (button.retrieve('inputpair').key == this.hiddenfield.getValue());
		}.bind(this));
		
		if (lead) {ui.onclick(lead);}
		
		return ui;
	}
});

;ProductThumbnails = Class.create({
	initialize: function(options) {
		this.options = Object.extend({
			'$container'	:	'.product_images',
			'$mainimage'	:	'.product_image',
			'$thumbnails'	:	'.product_thumbnails li',
			'mainFormat'	:	'r255x255',
			'thumbFormat'	:	'r110x110'
		}, {} || options);
		
		try {
			$$(this.options.$container).each(this.applymagic.bind(this));
		} catch (e) {}
	},
	
	applymagic: function(container) {
		var mainimage	=	container.down(this.options.$mainimage),
			thumbnails	=	container.select(this.options.$thumbnails);
		
		mainimage.store({'backgroundImageURLTemplate':new Template(mainimage.getStyle('backgroundImage').replace(/thumbnails\/[^\/]+\//, 'thumbnails/#{format}/'))});
		
		thumbnails.each(function(thumbnail){
			thumbnail.setStyle({'cursor':'pointer'});
			
			var url = thumbnail.getStyle('backgroundImage').replace(/thumbnails\/[^\/]+\//, 'thumbnails/#{format}/');
			thumbnail.store({'backgroundImageURLTemplate':new Template(url)});
			
			thumbnail.observe('click', function(){
				// Swap templates.
				var tmpUrlTemplate = thumbnail.retrieve('backgroundImageURLTemplate');
				thumbnail.store({'backgroundImageURLTemplate':mainimage.retrieve('backgroundImageURLTemplate')});
				mainimage.store({'backgroundImageURLTemplate':tmpUrlTemplate});
				
				// Update own image URLs
				mainimage.setStyle({'backgroundImage':mainimage.retrieve('backgroundImageURLTemplate').evaluate({'format':this.options.mainFormat})})
				thumbnail.setStyle({'backgroundImage':thumbnail.retrieve('backgroundImageURLTemplate').evaluate({'format':this.options.thumbFormat})})
			}.bind(this));
		}.bind(this));
	}
});

;Slideshow = Class.create({
	initialize: function(options) {
		this.options = Object.extend({
			'data'			:	[],
			'container'		:	'slideshow',
			'pathPattern'	:	'#{image}',
			'slideInterval'	:	3,
			'slideTime'		:	1
		}, options || {});
		
		this.options.pathPattern = new Template(this.options.pathPattern);
		this.options.container = $(this.options.container);
		if (!this.options.container) { throw new Exception("The container for the slideshow could not be found."); }
		if (!this.options.data.length) { throw new Exception("There are no slides for this slideshow."); }
		
		this.setupSlideshow();
		
		firstimage = this.options.container.down('img');
		if (firstimage) {
			firstimage.setStyle({'position':'absolute'});
			this.showNextSlideWhenReady.bind(this).delay(this.options.slideInterval);
		} else {
			this.showNextSlideWhenReady();
		}
	},
	
	cacheReadyCheck: function(ev, slide) {
		if (ev.type == "error") { slide.image = ""; }
		if (this.waitingToShowNextSlide) { this.showNextSlideWhenReady(); }
	},
	
	setupSlideshow: function() {
		this.currentSlide = 0;
		this.slides = this.options.data.length;
		this.title = this.options.container.down('h1,h2,h3,h4,h5,h6') || new Element('h1');
		var titleheight = 0,
			imageheight = 0,
			totalheight = 0;
		if (!this.title.parentNode) { this.options.container.insert(this.title); }
		this.options.data.reverse().each(function(slide){
			this.title.update(slide.text.escapeHTML());
			titleheight = Math.max(titleheight, this.title.getHeight());
			imageheight = Math.max(imageheight, slide.size[1] || 0);
			if (slide.image) {
				slide.cache = new Image();
				Event.observe(slide.cache, 'load', this.cacheReadyCheck.bindAsEventListener(this, slide));
				Event.observe(slide.cache, 'error', this.cacheReadyCheck.bindAsEventListener(this, slide));
				slide.cache.src = this.options.pathPattern.evaluate(slide);
			}
		}.bind(this));
		this.options.data.reverse();
		this.title.setStyle({'height':titleheight+'px'});
		totalheight += imageheight + titleheight;
		totalheight += parseFloat(this.title.getStyle("margin-bottom"));
		totalheight += parseFloat(this.title.getStyle("margin-top"));
		totalheight += parseFloat(this.title.getStyle("padding-bottom"));
		totalheight += parseFloat(this.title.getStyle("padding-top"));
		if (Prototype.Browser.IE) totalheight += 10;
		this.options.container.setStyle({'height':totalheight+'px'});
	},
	
	showNextSlideWhenReady: function() {
		this.waitingToShowNextSlide = true;
		// Are we ready?
		var nextSlide = this.options.data[this.currentSlide+1 >= this.slides ? 0 : this.currentSlide+1];
		if (nextSlide.image && !nextSlide.cache.complete) { return; }
		this.currentSlide = this.currentSlide+1 >= this.slides ? 0 : this.currentSlide+1;
		
		// Remove the old image
		var currentImage = this.options.container.down('img');
		if (currentImage) { (function(){ currentImage.remove(); }).delay(this.options.slideTime); }
		
		if (nextSlide.image) {
			this.options.container.insert(nextSlide.cache);
			nextSlide.cache.setStyle({'position':'absolute','display':'none'});
			nextSlide.cache.appear(this.options.slideTime);
		}
		this.title.update(nextSlide.text.escapeHTML());
		
		this.waitingToShowNextSlide = false;
		this.showNextSlideWhenReady.bind(this).delay(this.options.slideInterval);
	}
});

;Carousel = new Class.create({
	initialize: function(id, options) {
	
		this.options = Object.extend({
			'itemCount'			:	5,
			'itemWidth'			:	100,
			'scrollPPS'			:	220,					/*	Speed: Pixels per Second	*/
			'scrollFPS'			:	50						/*	Speed: Frames per Second	*/
		}, options || {});
	
		this.elements = {
			'outerContainer'	:	$(id),
			'innerContainer'	:	$(id).down('.inner_carousel'),
			'reel'				:	$(id).down('.carousel_reel'),
			'buttonLt'			:	new Element('div', {'class':'carousel_button carousel_button_left'}),
			'buttonRt'			:	new Element('div', {'class':'carousel_button carousel_button_right'})
		};

		with (this.elements) {
			outerContainer.addClassName('carousel_js');
			outerContainer.insert({'top'	:	buttonLt});
			outerContainer.insert({'bottom'	:	buttonRt});
			
			buttonLt.observe('mousedown',	this.startScrolling.bind(this, false));
			buttonRt.observe('mousedown',	this.startScrolling.bind(this, true));
			document.observe('mouseup',		this.stopScrolling.bind(this));
		}
		
		this.reelPosition	= parseFloat(this.elements.reel.getStyle('marginLeft'));
		this.reelWidth		= this.elements.innerContainer.getWidth();
		this.updateButtonStates();
	},

	scrolling: function(direction) {
		var pixelsCanTravel = direction ?
			(this.options.itemCount * this.options.itemWidth) - this.reelWidth + this.reelPosition:
			-this.reelPosition;

		if (!pixelsCanTravel) { return; }

		var pixelsToTravel = (1 / this.options.scrollFPS) * this.options.scrollPPS;
		
		// Move the smaller amount
		this.reelPosition += Math.min(pixelsToTravel, pixelsCanTravel) * (direction ? -1 : 1);
		this.updateReelPosition();
	},
	
	startScrolling: function(direction) {
		this.stopScrolling();
		this.pe = setInterval(
			this.scrolling.bind(this, direction),
			1000 / this.options.scrollFPS
		);
		setTimeout(
			this.updateButtonStates.bind(this),
			1000 / this.options.scrollFPS
		);
	},
	
	stopScrolling: function() {
		if (this.pe) {
			clearInterval(this.pe);
			delete this.pe;
		}
		this.updateButtonStates();
	},
	
	/* Move the reel to show image number <index> */
	goto: function(index) {
		this.reelPosition = -((((index-1) * this.options.itemWidth) - (this.reelWidth / 2)) + (this.options.itemWidth / 2));
		this.reelPosition = Math.max(this.reelPosition, -((this.options.itemWidth * this.options.itemCount) - this.reelWidth));
		this.reelPosition = Math.min(this.reelPosition, 0);
		this.updateReelPosition();
		this.updateButtonStates();
	},
	
	updateReelPosition: function() {
		this.elements.reel.setStyle({'marginLeft': this.reelPosition + 'px'});
	},
	
	updateButtonStates: function() {
		var end = -((this.options.itemWidth * this.options.itemCount) - this.reelWidth);
		(this.reelPosition >= 0		? Element.addClassName : Element.removeClassName)(this.elements.buttonLt, 'gallery_button_disabled_left');
		(this.reelPosition <= end	? Element.addClassName : Element.removeClassName)(this.elements.buttonRt, 'gallery_button_disabled_right');
	}
});

;//  Lightview 2.5.1 - 05-09-2009
//  Copyright (c) 2008-2009 Nick Stakenburg (http://www.nickstakenburg.com)
//
//  Licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License
//  http://creativecommons.org/licenses/by-nc-nd/3.0/

//  More information on this project:
//  http://www.nickstakenburg.com/projects/lightview/

Lightview = {
  Version: '2.5.1',

  // Configuration
  options: {
    backgroundColor: '#ffffff',                            // Background color of the view
    border: 12,                                            // Size of the border
    buttons: {
      opacity: {                                           // Opacity of inner buttons
        disabled: 0.4,
        normal: 0.75,
        hover: 1
      },
      side: { display: true },                             // Toggle side buttons
      innerPreviousNext: { display: true },                // Toggle the inner previous and next button
      slideshow: { display: true },                        // Toggle slideshow button
      topclose: { side: 'right' }                          // 'right' or 'left'                    
    },
    controller: {                                          // The controller is used on sets
      backgroundColor: '#4d4d4d',
      border: 6,
      buttons: {
        innerPreviousNext: true,
        side: false
      },
      margin: 18,
      opacity: 0.7,
      radius: 6,
      setNumberTemplate: '#{position} of #{total}'
    },
    cyclic: false,                                         // Makes galleries cyclic, no end/begin
    images: '',						                       // The directory of the images, from this file
    imgNumberTemplate: 'Image #{position} of #{total}',    // Want a different language? change it here
    keyboard: true,                                        // Toggle keyboard buttons
    menubarPadding: 6,                                     // Space between menubar and content in px
    overlay: {                                             // Overlay
      background: '#000',                                  // Background color, Mac Firefox & Mac Safari use overlay.png
      close: true,
      opacity: 0.85,
      display: true
    },
    preloadHover: false,                                   // Preload images on mouseover
    radius: 12,                                            // Corner radius of the border
    removeTitles: true,                                    // Set to false if you want to keep title attributes intact
    resizeDuration: 0.45,                                  // The duration of the resize effect in seconds
    slideshowDelay: 5,                                     // Delay in seconds before showing the next slide
    titleSplit: '::',                                      // The characters you want to split title with
    transition: function(pos) {                            // Or your own transition
      return ((pos/=0.5) < 1 ? 0.5 * Math.pow(pos, 4) :
        -0.5 * ((pos-=2) * Math.pow(pos,3) - 2));
    },
    viewport: true,                                        // Stay within the viewport, true is recommended
    zIndex: 5000,                                          // zIndex of #lightview, #overlay is this -1

    startDimensions: {                                     // Dimensions Lightview starts at
      width: 100,
      height: 100
    },
    closeDimensions: {                                     // Modify if you've changed the close button images
      large: { width: 77, height: 22 },
      small: { width: 25, height: 22 }
    },
    sideDimensions: {                                      // Modify if you've changed the side button images
      width: 16,
      height: 22
    },

    defaultOptions: {                                      // Default options for each type of view
      image: {
        menubar: 'bottom',
        closeButton: 'large'
      },
      gallery: {
        menubar: 'bottom',
        closeButton: 'large'
      },
      ajax:   {
        width: 400,
        height: 300,
        menubar: 'top',
        closeButton: 'small',
        overflow: 'auto'
      },
      iframe: {
        width: 400,
        height: 300,
        menubar: 'top',
        scrolling: true,
        closeButton: 'small'
      },
      inline: {
        width: 400,
        height: 300,
        menubar: 'top',
        closeButton: 'small',
        overflow: 'auto'
      },
      flash: {
        width: 400,
        height: 300,
        menubar: 'bottom',
        closeButton: 'large'
      },
      quicktime: {
        width: 480,
        height: 220,
        autoplay: true,
        controls: true,
        closeButton: 'large'
      }
    }
  },
  classids: {
    quicktime: 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B',
    flash: 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
  },
  codebases: {
    quicktime: 'http://www.apple.com/qtactivex/qtplugin.cab#version=7,5,5,0',
    flash: 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0'
  },
  errors: {
    requiresPlugin: "<div class='message'> The content your are attempting to view requires the <span class='type'>#{type}</span> plugin.</div><div class='pluginspage'><p>Please download and install the required plugin from:</p><a href='#{pluginspage}' target='_blank'>#{pluginspage}</a></div>"
  },
  mimetypes: {
    quicktime: 'video/quicktime',
    flash: 'application/x-shockwave-flash'
  },
  pluginspages: {
    quicktime: 'http://www.apple.com/quicktime/download',
    flash: 'http://www.adobe.com/go/getflashplayer'
  },
  // used with auto detection
  typeExtensions: {
    flash: 'swf',
    image: 'bmp gif jpeg jpg png',
    iframe: 'asp aspx cgi cfm htm html jsp php pl php3 php4 php5 phtml rb rhtml shtml txt',
    quicktime: 'avi mov mpg mpeg movie'
  }
};

(function () {
    var l = !!document.createElement("canvas").getContext,
    BROWSER_IS_IE_LT7 = Prototype.Browser.IE && (function (a) {
        var b = new RegExp("MSIE ([\\d.]+)").exec(a);
        return b ? parseFloat(b[1]) : -1
    })(navigator.userAgent) < 7,
    BROWSER_IS_WEBKIT_419 = (Prototype.Browser.WebKit && !document.evaluate),
    BROWSER_IS_FIREFOX_LT3 = navigator.userAgent.indexOf("Firefox") > -1 && parseFloat(navigator.userAgent.match(/Firefox[\/\s](\d+)/)[1]) < 3,
    FIX_OVERLAY_WITH_PNG = !!navigator.userAgent.match(/mac/i) && (BROWSER_IS_WEBKIT_419 || BROWSER_IS_FIREFOX_LT3);
    Object.extend(Lightview, {
        REQUIRED_Prototype: "1.6.1",
        REQUIRED_Scriptaculous: "1.8.2",
        queue: {
            position: "end",
            scope: "lightview"
        },
        require: function (a) {
            if ((typeof window[a] == "undefined") || (this.convertVersionString(window[a].Version) < this.convertVersionString(this["REQUIRED_" + a]))) {
                throw ("Lightview requires " + a + " >= " + this["REQUIRED_" + a])
            }
        },
        convertVersionString: function (a) {
            var v = a.replace(/_.*|\./g, "");
            v = parseInt(v + "0".times(4 - v.length));
            return a.indexOf("_") > -1 ? v - 1 : v
        },
        load: function () {
            this.require("Prototype");
            if ( !! window.Effect && !window.Scriptaculous) {
                this.require("Scriptaculous")
            }
            if (/^(https?:\/\/|\/)/.test(this.options.images)) {
                this.images = this.options.images
            } else {
                var b = /lightview(?:-[\w\d.]+)?\.js(.*)/;
                this.images = (($$("head script[src]").find(function (s) {
                    return s.src.match(b)
                }) || {}).src || "").replace(b, "") + this.options.images
            }
            if (!l) {
                if (document.documentMode >= 8 && !document.namespaces.ns_vml) {
                    document.namespaces.add("ns_vml", "urn:schemas-microsoft-com:vml", "#default#VML")
                } else {
                    document.observe("dom:loaded", function () {
                        var a = document.createStyleSheet();
                        a.cssText = "ns_vml\\:*{behavior:url(#default#VML)}"
                    })
                }
            }
        },
        start: function () {
            this.radius = this.options.radius;
            this.border = (this.radius > this.options.border) ? this.radius : this.options.border;
            this.closeDimensions = this.options.closeDimensions;
            this.sideDimensions = this.options.sideDimensions;
            this.build()
        }
    });
    Object.extend(Lightview, {
        _lightviewLoadedEvents: 14,
        _lightviewLoadedEvent: function () {
            var a = arguments.callee;
            a.counter++;
            if (a.counter == this._lightviewLoadedEvents) {
                $(document.body).fire("lightview:loaded")
            }
        }
    });
    Lightview._lightviewLoadedEvent.counter = 0;
    Object.extend(Lightview, {
        build: function () {
            this.lightview = new Element("div", {
                id: "lightview"
            });
            var d, sideNegativeMargin, sideStyle = pixelClone(this.sideDimensions);
            if (BROWSER_IS_WEBKIT_419) {
                this.lightview.hide = function () {
                    this.setStyle("left:-9500px;top:-9500px;visibility:hidden;");
                    return this
                };
                this.lightview.show = function () {
                    this.setStyle("visibility:visible");
                    return this
                };
                this.lightview.visible = function () {
                    return (this.getStyle("visibility") == "visible" && parseFloat(this.getStyle("top").replace("px", "")) > -9500)
                }
            }
            $(document.body).insert(this.overlay = new Element("div", {
                id: "lv_overlay"
            }).setStyle({
                zIndex: this.options.zIndex - 1,
                position: (!(BROWSER_IS_FIREFOX_LT3 || BROWSER_IS_IE_LT7)) ? "fixed" : "absolute",
                background: FIX_OVERLAY_WITH_PNG ? "url(" + this.images + "overlay.png) top left repeat" : this.options.overlay.background
            }).setOpacity(FIX_OVERLAY_WITH_PNG ? 1 : this.options.overlay.opacity).hide()).insert(this.lightview.setStyle({
                zIndex: this.options.zIndex,
                top: "-9500px",
                left: "-9500px"
            }).setOpacity(0).insert(this.container = new Element("div", {
                className: "lv_Container"
            }).insert(this.sideButtons = new Element("ul", {
                className: "lv_Sides"
            }).insert(this.prevSide = new Element("li", {
                className: "lv_PrevSide"
            }).setStyle(sideNegativeMargin = Object.extend({
                marginLeft: -1 * this.sideDimensions.width + "px"
            },
            sideStyle)).insert(this.prevButtonImage = new Element("div", {
                className: "lv_Wrapper"
            }).setStyle(Object.extend({
                marginLeft: this.sideDimensions.width + "px"
            },
            sideStyle)).insert(new Element("div", {
                className: "lv_Button"
            })))).insert(this.nextSide = new Element("li", {
                className: "lv_NextSide"
            }).setStyle(Object.extend({
                marginRight: -1 * this.sideDimensions.width + "px"
            },
            sideStyle)).insert(this.nextButtonImage = new Element("div", {
                className: "lv_Wrapper"
            }).setStyle(sideNegativeMargin).insert(new Element("div", {
                className: "lv_Button"
            }))))).insert(this.topButtons = new Element("div", {
                className: "lv_topButtons"
            }).insert(this.topcloseButtonImage = new Element("div", {
                className: "lv_Wrapper lv_topcloseButtonImage"
            }).insert(this.topcloseButton = new Element("div", {
                className: "lv_Button"
            })))).insert(new Element("ul", {
                className: "lv_Frames"
            }).insert(new Element("li", {
                className: "lv_Frame lv_FrameTop"
            }).insert(d = new Element("div", {
                className: "lv_Liquid"
            }).setStyle({
                height: this.border + "px"
            }).insert(new Element("ul", {
                className: "lv_Half lv_HalfLeft"
            }).insert(new Element("li", {
                className: "lv_CornerWrapper"
            }).insert(new Element("div", {
                className: "lv_Corner"
            })).insert(new Element("div", {
                className: "lv_Fill"
            }).setStyle({
                left: this.border + "px"
            })))).insert(new Element("div", {
                className: "lv_Filler"
            })).insert(new Element("ul", {
                className: "lv_Half lv_HalfRight"
            }).insert(new Element("li", {
                className: "lv_CornerWrapper"
            }).setStyle("margin-top: " + (-1 * this.border) + "px").insert(new Element("div", {
                className: "lv_Corner"
            })).insert(new Element("div", {
                className: "lv_Fill"
            }).setStyle("left: " + (-1 * this.border) + "px")))))).insert(this.resizeCenter = new Element("li", {
                className: "lv_Center"
            }).setStyle("height: " + (150 - this.border) + "px").insert(new Element("div", {
                className: "lv_WrapUp"
            }).insert(new Element("div", {
                className: "lv_WrapDown"
            }).setStyle("margin-top: " + this.border + "px").insert(this.center = new Element("div", {
                className: "lv_WrapCenter"
            }).setOpacity(0).setStyle("padding: 0 " + this.border + "px").insert(this.contentTop = new Element("div", {
                className: "lv_contentTop lv_Fill"
            })).insert(this.menubar = new Element("div", {
                className: "lv_MenuBar clearfix"
            }).insert(this.closeButton = new Element("div", {
                className: "lv_Button lv_Close"
            }).setStyle(pixelClone(this.options.closeDimensions.large)).setStyle({
                background: this.options.backgroundColor
            }).setOpacity(this.options.buttons.opacity.normal)).insert(this.data = new Element("ul", {
                className: "lv_Data"
            }).insert(this.dataText = new Element("li", {
                className: "lv_DataText"
            }).insert(this.title = new Element("div", {
                className: "lv_Title"
            })).insert(this.caption = new Element("div", {
                className: "lv_Caption"
            }))).insert(this.innerController = new Element("div", {
                className: "lv_innerController"
            }).insert(this.imgNumber = new Element("li", {
                className: "lv_ImgNumber"
            }).insert(new Element("div"))).insert(this.innerPrevNext = new Element("li", {
                className: "lv_innerPrevNext"
            }).insert(this.innerPrevButton = new Element("div", {
                className: "lv_Button"
            }).setOpacity(this.options.buttons.opacity.normal).setStyle({
                backgroundColor: this.options.backgroundColor
            }).setPngBackground(this.images + "inner_prev.png", {
                backgroundColor: this.options.backgroundColor
            })).insert(this.innerNextButton = new Element("div", {
                className: "lv_Button"
            }).setOpacity(this.options.buttons.opacity.normal).setStyle({
                backgroundColor: this.options.backgroundColor
            }).setPngBackground(this.images + "inner_next.png", {
                backgroundColor: this.options.backgroundColor
            }))).insert(this.slideshow = new Element("li", {
                className: "lv_Slideshow"
            }).insert(this.slideshowButton = new Element("div", {
                className: "lv_Button"
            }).setOpacity(this.options.buttons.opacity.normal).setStyle({
                backgroundColor: this.options.backgroundColor
            }).setPngBackground(this.images + "inner_slideshow_play.png", {
                backgroundColor: this.options.backgroundColor
            })))))).insert(this.contentBottom = new Element("div", {
                className: "lv_contentBottom "
            }))))).insert(this.loading = new Element("div", {
                className: "lv_Loading"
            }).insert(this.loadingButton = new Element("div", {
                className: "lv_Button"
            }).setStyle("background: url(" + this.images + "loading.gif) top left no-repeat")))).insert(new Element("li", {
                className: "lv_Frame lv_FrameBottom"
            }).insert(d.cloneNode(true))).insert(this.prevnext = new Element("li", {
                className: "lv_PrevNext"
            }).hide().setStyle("margin-top: " + this.border + "px; background: url(" + this.images + "blank.gif) top left repeat"))))).insert(new Element("div", {
                id: "lightviewError"
            }).hide());
            var f = new Image();
            f.onload = function () {
                f.onload = Prototype.emptyFunction;
                this.sideDimensions = {
                    width: f.width,
                    height: f.height
                };
                var a = pixelClone(this.sideDimensions),
                sideNegativeMargin;
                this.sideButtons.setStyle({
                    marginTop: 0 - (f.height / 2).round() + "px",
                    height: f.height + "px"
                });
                this.prevSide.setStyle(sideNegativeMargin = Object.extend({
                    marginLeft: -1 * this.sideDimensions.width + "px"
                },
                a));
                this.prevButtonImage.setStyle(Object.extend({
                    marginLeft: a.width
                },
                a));
                this.nextSide.setStyle(Object.extend({
                    marginRight: -1 * this.sideDimensions.width + "px"
                },
                a));
                this.nextButtonImage.setStyle(sideNegativeMargin);
                this._lightviewLoadedEvent()
            }.bind(this);
            f.src = this.images + "prev.png";
            $w("center title caption imgNumber")._each(function (e) {
                this[e].setStyle({
                    backgroundColor: this.options.backgroundColor
                })
            }.bind(this));
            var g = this.container.select(".lv_Corner");
            $w("tl tr bl br").each(function (a, i) {
                if (this.radius > 0) {
                    this.createCorner(g[i], a)
                } else {
                    g[i].insert(new Element("div", {
                        className: "lv_Fill"
                    }))
                }
                g[i].setStyle({
                    width: this.border + "px",
                    height: this.border + "px"
                }).addClassName("lv_Corner" + a.capitalize());
                this._lightviewLoadedEvent()
            }.bind(this));
            this.lightview.select(".lv_Filler", ".lv_Fill", ".lv_WrapDown").invoke("setStyle", {
                backgroundColor: this.options.backgroundColor
            });
            var S = {};
            $w("prev next topclose").each(function (s) {
                this[s + "ButtonImage"].side = s;
                var b = this.images + s + ".png";
                if (s == "topclose") {
                    S[s] = new Image();
                    S[s].onload = function () {
                        S[s].onload = Prototype.emptyFunction;
                        this.closeDimensions[s] = {
                            width: S[s].width,
                            height: S[s].height
                        };
                        var a = this.options.buttons.topclose.side,
                        style = Object.extend({
                            "float": a,
                            marginTop: this.closeDimensions[s].height + "px"
                        },
                        pixelClone(this.closeDimensions[s]));
                        style["padding" + a.capitalize()] = this.border + "px";
                        this[s + "ButtonImage"].setStyle(style);
                        this.topButtons.setStyle({
                            height: S[s].height + "px",
                            top: -1 * this.closeDimensions[s].height + "px"
                        });
                        this[s + "ButtonImage"].down().setPngBackground(b).setStyle(pixelClone(this.closeDimensions[s]));
                        this._lightviewLoadedEvent()
                    }.bind(this);
                    S[s].src = this.images + s + ".png"
                } else {
                    this[s + "ButtonImage"].setPngBackground(b)
                }
            },
            this);
            var C = {};
            $w("large small").each(function (a) {
                C[a] = new Image();
                C[a].onload = function () {
                    C[a].onload = Prototype.emptyFunction;
                    this.closeDimensions[a] = {
                        width: C[a].width,
                        height: C[a].height
                    };
                    this._lightviewLoadedEvent()
                }.bind(this);
                C[a].src = this.images + "close_" + a + ".png"
            },
            this);
            var L = new Image();
            L.onload = function () {
                L.onload = Prototype.emptyFunction;
                this.loading.setStyle({
                    width: L.width + "px",
                    height: L.height + "px",
                    marginTop: -0.5 * L.height + 0.5 * this.border + "px",
                    marginLeft: -0.5 * L.width + "px"
                });
                this._lightviewLoadedEvent()
            }.bind(this);
            L.src = this.images + "loading.gif";
            var h = new Image();
            h.onload = function (a) {
                h.onload = Prototype.emptyFunction;
                var b = {
                    width: h.width + "px",
                    height: h.height + "px"
                };
                this.slideshow.setStyle(b);
                this.slideshowButton.setStyle(b);
                this._lightviewLoadedEvent()
            }.bind(this);
            h.src = this.images + "inner_slideshow_stop.png";
            $w("prev next").each(function (s) {
                var S = s.capitalize(),
                i = new Image();
                i.onload = function () {
                    i.onload = Prototype.emptyFunction;
                    this["inner" + S + "Button"].setStyle({
                        width: i.width + "px",
                        height: i.height + "px"
                    });
                    this._lightviewLoadedEvent()
                }.bind(this);
                i.src = this.images + "inner_" + s + ".png";
                this["inner" + S + "Button"].prevnext = s
            },
            this);
            $w("slideshow innerPrevNext imgNumber").each(function (c) {
                this[c].hide = this[c].hide.wrap(function (a, b) {
                    this.style.position = "absolute";
                    a(b);
                    return this
                });
                this[c].show = this[c].show.wrap(function (a, b) {
                    this.style.position = "relative";
                    a(b);
                    return this
                })
            },
            this);
            this.lightview.select("*").invoke("setStyle", {
                zIndex: this.options.zIndex + 1
            });
            this.lightview.hide();
            this._lightviewLoadedEvent()
        },
        prepare: function () {
            Effect.Queues.get("lightview")._each(function (e) {
                e.cancel()
            });
            this.scaledInnerDimensions = null;
            if (this.view.isSet()) {
                this.controllerHeight = this._controllerHeight;
                if (this.controller && !this.controller.visible()) {
                    this.controller.setStyle("visibility:hidden").show();
                    this.controllerCenter.setOpacity(0)
                }
            } else {
                this.controllerHeight = null;
				if (this.controller)
	                this.controller.hide()
            }
            if (parseInt(this.topcloseButtonImage.getStyle("marginTop")) < this.closeDimensions.topclose.height) {
                this.toggleTopClose(false)
            }
            this.hideOverlapping();
            this.hideContent();
            new Effect.Event({
                queue: this.queue,
                afterFinish: function () {
                    $w("top bottom").each(function (a) {
                        var b = a.capitalize();
                        this["content" + b].remove();
                        var c = {};
                        this["content" + b] = new Element("div", {
                            className: "lv_content" + b
                        }).hide();
                        c[a] = this["content" + b];
                        this.center.insert(c)
                    }.bind(this))
                }.bind(this)
            });
            this.disableKeyboardNavigation();
            this.views = null
        },
        restoreInlineContent: function () {
            if (!this.inlineContent || !this.inlineMarker) {
                return
            }
            this.inlineMarker.insert({
                after: this.inlineContent.setStyle({
                    display: this.inlineContent._inlineDisplayRestore
                })
            });
            this.inlineMarker.remove();
            this.inlineMarker = null
        },

        show: function (b) {
            this.element = null;
            var c = Object.isString(b);
            if (Object.isElement(b) || c) {
                if (c && b.startsWith("#")) {
                    this.show({
                        href: b,
                        options: Object.extend({
                            autosize: true
                        },
                        arguments[1] || {})
                    });
                    return
                }
                this.element = $(b);
                if (!this.element) {
                    return
                }
                this.element.blur();
                this.view = this.element._view || new Lightview.View(this.element)
            } else {
                if (b.href) {
                    this.element = $(document.body);
                    this.view = new Lightview.View(b)
                } else {
                    if (Object.isNumber(b)) {
                        this.element = this.getSet(this.view.rel)[b];
                        this.view = this.element._view
                    }
                }
            }
            if (!this.view.href) {
                return
            }
            this.prepare();
            if (this.view.isGallery() || this.view.isSet()) {
                this.extendSet(this.view.rel);
                this.views = this.getViews(this.view.rel);
                if (this.view.isSet()) {
                    this.controllerOffset = this.views.length > 1 ? this._controllerOffset : 0;
                    this.isSetGallery = this.views.all(function (a) {
                        return a.isImage()
                    })
                }
            }
            this.restoreCenter();
            this.appear();
            if (this.view.href != "#lightviewError" && Object.keys(Lightview.Plugin).join(" ").indexOf(this.view.type) >= 0) {
                if (!Lightview.Plugin[this.view.type]) {
                    $("lightviewError").update(new Template(this.errors.requiresPlugin).evaluate({
                        type: this.view.type.capitalize(),
                        pluginspage: this.pluginspages[this.view.type]
                    }));
                    var d = $("lightviewError").getDimensions();
                    this.show({
                        href: "#lightviewError",
                        title: this.view.type.capitalize() + " plugin required",
                        options: d
                    });
                    return false
                }
            }
            var e = Object.extend({
                menubar: "bottom",
                topclose: false,
                wmode: "transparent",
                innerPreviousNext: this.view.isGallery() && this.options.buttons.innerPreviousNext.display,
                keyboard: this.options.keyboard,
                slideshow: (this.view.isGallery() && this.options.buttons.slideshow.display) || (this.isSetGallery),
                overflow: "hidden",
                overlayClose: this.options.overlay.close,
                viewport: this.options.viewport
            },
            this.options.defaultOptions[this.view.type] || {});
            this.view.options = Object.extend(e, this.view.options);
            if (this.view.isSet()) {
                this.view.options.topclose = (this.views.length <= 1)
            }
            if (! (this.view.title || this.view.caption || (this.views && this.views.length > 1)) && this.view.options.topclose) {
                this.view.options.menubar = false
            }
            this._contentPosition = "content" + (this.view.options.menubar == "top" ? "Bottom" : "Top");
            if (this.view.isImage()) {
                if (!l && !this.view._VMLPreloaded) {
                    this.view._VMLPreloaded = true;
                    var f = new Element("ns_vml:image", {
                        src: this.view.href,
                        display: "none"
                    }).setStyle("height:1px;width:1px;");
                    $(document.body).insert(f);
                    Element.remove.delay(0.1, f)
                }
                if (this.view.isGallery() || this.view.isSet()) {
                    this.position = this.views.indexOf(this.view);
                    this.preloadSurroundingImages()
                }
                this.innerDimensions = this.view.preloadedDimensions;
                if (this.innerDimensions) {
                    this.afterEffect()
                } else {
                    this.startLoading();
                    var f = new Image();
                    f.onload = function () {
                        f.onload = Prototype.emptyFunction;
                        this.stopLoading();
                        this.innerDimensions = {
                            width: f.width,
                            height: f.height
                        };
                        this.afterEffect()
                    }.bind(this);
                    f.src = this.view.href
                }
            } else {
                if (this.view.isSet()) {
                    this.position = this.views.indexOf(this.view)
                }
                this.innerDimensions = this.view.options.fullscreen ? m.getDimensions() : {
                    width: this.view.options.width,
                    height: this.view.options.height
                };
                this.afterEffect()
            }
        },
        insertContent: (function () {
            function insertImageUsingHTML(a, b, c) {
                a = $(a);
                var d = pixelClone(c);
                a.update(new Element("img", {
                    id: "lightviewContent",
                    src: b,
                    alt: "",
                    galleryimg: "no"
                }).setStyle(d))
            }
            var k = (function () {
                function insertImageUsingVML(a, b, c) {
                    a = $(a);
                    var d = Object.extend({
                        "float": "left"
                    },
                    pixelClone(c));
                    var e = new Element("ns_vml:image", {
                        src: b,
                        id: "lightviewContent"
                    }).setStyle(d);
                    a.update(e);
                    e.outerHTML = e.outerHTML
                }
                function insertImageUsingCanvas(b, c, d) {
                    b = $(b);
                    var f = pixelClone(d),
                    image = new Image();
                    image.onload = function () {
                        canvas = new Element("canvas", f);
                        b.update(canvas);
                        try {
                            var a = canvas.getContext("2d");
                            a.drawImage(image, 0, 0, d.width, d.height)
                        } catch(e) {
                            insertImageUsingHTML(b, c, d)
                        }
                    }.bind(this);
                    image.src = c
                }
                if (Prototype.Browser.IE) {
                    return insertImageUsingVML
                } else {
                    return insertImageUsingCanvas
                }
            })();
            return function () {
                var c = this.detectExtension(this.view.href),
                dimensions = this.scaledInnerDimensions || this.innerDimensions;
                if (this.view.isImage()) {
                    var d = pixelClone(dimensions);
                    this[this._contentPosition].setStyle(d);
                    if (this.scaledInnerDimensions) {
                        k(this[this._contentPosition], this.view.href, dimensions)
                    } else {
                        insertImageUsingHTML(this[this._contentPosition], this.view.href, dimensions)
                    }
                } else {
                    if (this.view.isExternal()) {
                        switch (this.view.type) {
                        case "ajax":
                            var f = Object.clone(this.view.options.ajax) || {};
                            var g = function () {
                                this.stopLoading();
                                if (this.view.options.autosize) {
                                    this[this._contentPosition].setStyle({
                                        width: "auto",
                                        height: "auto"
                                    });
                                    this.innerDimensions = this.getHiddenDimensions(this[this._contentPosition])
                                }
                                new Effect.Event({
                                    queue: this.queue,
                                    afterFinish: this.resizeWithinViewport.bind(this)
                                })
                            }.bind(this);
                            if (f.onComplete) {
                                f.onComplete = f.onComplete.wrap(function (a, b) {
                                    g();
                                    a(b)
                                })
                            } else {
                                f.onComplete = g
                            }
                            this.startLoading();
                            new Ajax.Updater(this[this._contentPosition], this.view.href, f);
                            break;
                        case "iframe":
                            if (this.scaledInnerDimensions) {
                                dimensions.height -= this.menubarDimensions.height
                            }
                            this[this._contentPosition].update(this.iframe = new Element("iframe", {
                                frameBorder: 0,
                                hspace: 0,
                                src: this.view.href,
                                id: "lightviewContent",
                                name: "lightviewContent_" + (Math.random() * 99999).round(),
                                scrolling: (this.view.options && this.view.options.scrolling) ? "auto" : "no"
                            }).setStyle(Object.extend({
                                border: 0,
                                margin: 0,
                                padding: 0
                            },
                            pixelClone(dimensions))));
                            break;
                        case "inline":
                            var h = this.view.href,
                            target = $(h.substr(h.indexOf("#") + 1));
                            if (!target || !target.tagName) {
                                return
                            }
                            var i = target.getDimensions();
                            target.insert({
                                before: this.inlineMarker = new Element(target.tagName).hide()
                            });
                            target._inlineDisplayRestore = target.getStyle("display");
                            this.inlineContent = target.show();
                            this[this._contentPosition].update(this.inlineContent);
                            this[this._contentPosition].select("select, object, embed").each(function (b) {
                                this.overlappingRestore.each(function (a) {
                                    if (a.element == b) {
                                        b.setStyle({
                                            visibility: a.visibility
                                        })
                                    }
                                })
                            }.bind(this));
                            if (this.view.options.autosize) {
                                this.innerDimensions = i;
                                new Effect.Event({
                                    queue: this.queue,
                                    afterFinish: this.resizeWithinViewport.bind(this)
                                })
                            }
                            break
                        }
                    } else {
                        var j = {
                            tag: "object",
                            id: "lightviewContent",
                            width: dimensions.width,
                            height: dimensions.height
                        };
                        switch (this.view.type) {
                        case "quicktime":
                            Object.extend(j, {
                                pluginspage: this.pluginspages[this.view.type],
                                children: [{
                                    tag: "param",
                                    name: "autoplay",
                                    value: this.view.options.autoplay
                                },
                                {
                                    tag: "param",
                                    name: "scale",
                                    value: "tofit"
                                },
                                {
                                    tag: "param",
                                    name: "controller",
                                    value: this.view.options.controls
                                },
                                {
                                    tag: "param",
                                    name: "enablejavascript",
                                    value: true
                                },
                                {
                                    tag: "param",
                                    name: "src",
                                    value: this.view.href
                                },
                                {
                                    tag: "param",
                                    name: "loop",
                                    value: this.view.options.loop || false
                                }]
                            });
                            Object.extend(j, Prototype.Browser.IE ? {
                                codebase: this.codebases[this.view.type],
                                classid: this.classids[this.view.type]
                            } : {
                                data: this.view.href,
                                type: this.mimetypes[this.view.type]
                            });
                            break;
                        case "flash":
                            Object.extend(j, {
                                data: this.view.href,
                                type: this.mimetypes[this.view.type],
                                quality: "high",
                                wmode: this.view.options.wmode,
                                pluginspage: this.pluginspages[this.view.type],
                                children: [{
                                    tag: "param",
                                    name: "movie",
                                    value: this.view.href
                                },
                                {
                                    tag: "param",
                                    name: "allowFullScreen",
                                    value: "true"
                                }]
                            });
                            if (this.view.options.flashvars) {
                                j.children.push({
                                    tag: "param",
                                    name: "FlashVars",
                                    value: this.view.options.flashvars
                                })
                            }
                            break
                        }
                        this[this._contentPosition].setStyle(pixelClone(dimensions)).update(this.createHTML(j)).show();
                        if (this.view.isQuicktime()) {
                            (function () {
                                try {
                                    if ("SetControllerVisible" in $("lightviewContent")) {
                                        $("lightviewContent").SetControllerVisible(this.view.options.controls)
                                    }
                                } catch(e) {}
                            }.bind(this)).defer()
                        }
                    }
                }
            }
        })(),
        getHiddenDimensions: function (b) {
            b = $(b);
            var d = b.ancestors(),
            restore = [],
            styles = [];
            d.push(b);
            d.each(function (c) {
                if (c != b && c.visible()) {
                    return
                }
                restore.push(c);
                styles.push({
                    display: c.getStyle("display"),
                    position: c.getStyle("position"),
                    visibility: c.getStyle("visibility")
                });
                c.setStyle({
                    display: "block",
                    position: "absolute",
                    visibility: "visible"
                })
            });
            var e = {
                width: b.clientWidth,
                height: b.clientHeight
            };
            restore.each(function (r, a) {
                r.setStyle(styles[a])
            });
            return e
        },
        clearContent: function () {
            var a = $("lightviewContent");
            if (a) {
                switch (a.tagName.toLowerCase()) {
                case "object":
                    if (Prototype.Browser.WebKit && this.view.isQuicktime()) {
                        try {
                            a.Stop()
                        } catch(e) {}
                        a.innerHTML = ""
                    }
                    if (a.parentNode) {
                        a.remove()
                    } else {
                        a = Prototype.emptyFunction
                    }
                    break;
                case "iframe":
                    a.remove();
                    if (Prototype.Browser.Gecko && window.frames.lightviewContent) {
                        delete window.frames.lightviewContent
                    }
                    break;
                default:
                    a.remove();
                    break
                }
            }
            $w("Top Bottom").each(function (S) {
                this["content" + S].setStyle("width:auto;height:auto;").update("").hide()
            },
            this)
        },
        adjustDimensionsToView: Prototype.K,
        afterEffect: function () {
            new Effect.Event({
                queue: this.queue,
                afterFinish: this.afterShow.bind(this)
            })
        },
        afterShow: function () {
            this.fillMenuBar();
            if (!this.view.isAjax()) {
                this.stopLoading()
            }
            if (! ((this.view.options.autosize && this.view.isInline()) || this.view.isAjax())) {
                this.resizeWithinViewport()
            }
            if (!this.view.isIframe()) {
                new Effect.Event({
                    queue: this.queue,
                    afterFinish: this.insertContent.bind(this)
                })
            }
            if (this.view.options.topclose) {
                new Effect.Event({
                    queue: this.queue,
                    afterFinish: this.toggleTopClose.bind(this, true)
                })
            }
        },
        finishShow: function () {
            new Effect.Event({
                queue: this.queue,
                afterFinish: this.showContent.bind(this)
            });
            if (this.view.isIframe()) {
                new Effect.Event({
                    delay: 0.2,
                    queue: this.queue,
                    afterFinish: this.insertContent.bind(this)
                })
            }
            if (this.sliding) {
                new Effect.Event({
                    queue: this.queue,
                    afterFinish: this.nextSlide.bind(this)
                })
            }
            if (this.view.isQuicktime()) {
                new Effect.Event({
                    queue: this.queue,
                    afterFinish: Element.setStyle.bind(this, this[this._contentPosition], "visibility:visible")
                })
            }
        },
        previous: function () {
            if (Effect.Queues.get(Lightview.queue.scope).effects.length) {
                return
            }
            this.show(this.getSurroundingIndexes().previous)
        },
        next: function () {
            if (Effect.Queues.get(Lightview.queue.scope).effects.length) {
                return
            }
            this.show(this.getSurroundingIndexes().next)
        },
        resizeWithinViewport: function () {
            this.adjustDimensionsToView();
            var a = this.getInnerDimensions(),
            bounds = this.getBounds();
            if (this.view.options.viewport && (a.width > bounds.width || a.height > bounds.height)) {
                if (this.view.options.fullscreen) {
                    this.scaledInnerDimensions = bounds;
                    this.fillMenuBar();
                    a = bounds
                } else {
                    var c = this.getOuterDimensions(),
                    b = bounds;
                    if (this.view.isMedia()) {
                        var d = [bounds.height / c.height, bounds.width / c.width, 1].min();
                        this.scaledInnerDimensions = {
                            width: (this.innerDimensions.width * d).round(),
                            height: (this.innerDimensions.height * d).round()
                        }
                    } else {
                        this.scaledInnerDimensions = {
                            width: c.width > b.width ? b.width : c.width,
                            height: c.height > b.height ? b.height : c.height
                        }
                    }
                    this.fillMenuBar();
                    a = Object.clone(this.scaledInnerDimensions);
                    if (this.view.isMedia()) {
                        a.height += this.menubarDimensions.height
                    }
                }
            } else {
                this.fillMenuBar();
                this.scaledInnerDimensions = null
            }
            this._resize(a)
        },
        resize: function (a) {
            this._resize(a, {
                duration: 0
            })
        },
        _resize: (function () {
            var e, wdiff, hdiff, mleft, mtop, controllerOffset, b;
            var f = (function () {
                var w, h;
                function init(p) {
                    w = (e.width + p * wdiff).toFixed(0);
                    h = (e.height + p * hdiff).toFixed(0)
                }
                var a;
                if (BROWSER_IS_IE_LT7) {
                    a = function (p) {
                        this.lightview.setStyle({
                            width: (e.width + p * wdiff).toFixed(0) + "px",
                            height: (e.height + p * hdiff).toFixed(0) + "px"
                        });
                        this.resizeCenter.setStyle({
                            height: h - 1 * this.border + "px"
                        })
                    }
                } else {
                    if (BROWSER_IS_FIREFOX_LT3) {
                        a = function (p) {
                            var v = this.getViewportDimensions(),
                            o = document.viewport.getScrollOffsets();
                            this.lightview.setStyle({
                                position: "absolute",
                                marginLeft: 0,
                                marginTop: 0,
                                width: w + "px",
                                height: h + "px",
                                left: (o[0] + (v.width / 2) - (w / 2)).floor() + "px",
                                top: (o[1] + (v.height / 2) - (h / 2)).floor() + "px"
                            });
                            this.resizeCenter.setStyle({
                                height: h - 1 * this.border + "px"
                            })
                        }
                    } else {
                        a = function (p) {
                            this.lightview.setStyle({
                                position: "fixed",
                                width: w + "px",
                                height: h + "px",
                                marginLeft: ((0 - w) / 2).round() + "px",
                                marginTop: ((0 - h) / 2 - controllerOffset).round() + "px"
                            });
                            this.resizeCenter.setStyle({
                                height: h - 1 * this.border + "px"
                            })
                        }
                    }
                }
                return function (p) {
                    init.call(this, p);
                    a.call(this, p)
                }
            })();
            return function (a) {
                var c = arguments[1] || {};
                e = this.lightview.getDimensions();
                b = 2 * this.border;
                width = a.width ? a.width + b : e.width;
                height = a.height ? a.height + b : e.height;
                this.hidePrevNext();
                if (e.width == width && e.height == height) {
                    new Effect.Event({
                        queue: this.queue,
                        afterFinish: this._afterResize.bind(this, a)
                    });
                    return
                }
                var d = {
                    width: width + "px",
                    height: height + "px"
                };
                wdiff = width - e.width;
                hdiff = height - e.height;
                mleft = parseInt(this.lightview.getStyle("marginLeft").replace("px", ""));
                mtop = parseInt(this.lightview.getStyle("marginTop").replace("px", ""));
                controllerOffset = this.controller.visible() ? (this.controllerOffset / 2) : 0;
                if (!BROWSER_IS_IE_LT7) {
                    Object.extend(d, {
                        marginLeft: 0 - width / 2 + "px",
                        marginTop: 0 - height / 2 + "px"
                    })
                }
                if (c.duration == 0) {
                    f.call(this, 1)
                } else {
                    this.resizing = new Effect.Tween(this.lightview, 0, 1, Object.extend({
                        duration: this.options.resizeDuration,
                        queue: this.queue,
                        transition: this.options.transition,
                        afterFinish: this._afterResize.bind(this, a)
                    },
                    c), f.bind(this))
                }
            }
        })(),
        _afterResize: function (a) {
            if (!this.menubarDimensions) {
                return
            }
            var b = this[this._contentPosition],
            contentDimensions;
            if (this.view.options.overflow == "auto") {
                contentDimensions = b.getDimensions()
            }
            b.setStyle({
                height: (a.height - this.menubarDimensions.height) + "px",
                width: a.width + "px"
            });
            if (this.view.options.overflow != "hidden" && (this.view.isAjax() || this.view.isInline())) {
                if (Prototype.Browser.IE) {
                    if (this.view.options.overflow == "auto") {
                        var c = b.getDimensions();
                        b.setStyle("overflow:visible");
                        var d = {
                            overflowX: "hidden",
                            overflowY: "hidden"
                        },
                        corrected = 0,
                        scrollbarWidth = 15;
                        if (contentDimensions.height > a.height) {
                            d.overflowY = "auto";
                            d.width = c.width - scrollbarWidth;
                            d.paddingRight = "15px";
                            corrected = scrollbarWidth
                        }
                        if (contentDimensions.width - corrected > a.width) {
                            d.overflowX = "auto";
                            d.height = c.height - scrollbarWidth;
                            d.paddingBottom = "15px"
                        }
                        b.setStyle(d)
                    } else {
                        b.setStyle({
                            overflow: this.view.options.overflow
                        })
                    }
                } else {
                    b.setStyle({
                        overflow: this.view.options.overflow
                    })
                }
            } else {
                b.setStyle("overflow:hidden")
            }
            this.restoreCenter();
            this.resizing = null;
            this.finishShow()
        },
        showContent: function () {
            new Effect.Event({
                queue: this.queue,
                afterFinish: this.hidePrevNext.bind(this)
            });
            new Effect.Event({
                queue: this.queue,
                afterFinish: function () {
                    this[this._contentPosition].show();
                    this.fillMenuBar();
                    if (this.menubar.visible()) {
                        this.menubar.setStyle("visibility:visible").setOpacity(1)
                    }
                }.bind(this)
            });
            new Effect.Parallel([new Effect.Opacity(this.center, {
                sync: true,
                from: 0,
                to: 1
            }), new Effect.Appear(this.sideButtons, {
                sync: true
            })], {
                queue: this.queue,
                duration: 0.25,
                afterFinish: function () {
                    if (this.element) {
                        this.element.fire("lightview:opened")
                    }
                }.bind(this)
            });
            if (this.view.isGallery() || (this.isSetGallery && this.options.controller.buttons.side)) {
                new Effect.Event({
                    queue: this.queue,
                    afterFinish: this.showPrevNext.bind(this)
                })
            }
        },
        hideContent: (function () {
            function after() {
                this.clearContent();
                this.topcloseButtonImage.setStyle({
                    marginTop: this.closeDimensions.topclose.height + "px"
                });
                this.restoreInlineContent();
                if (this.view.isQuicktime()) {
                    this[this._contentPosition].setStyle("visibility:hidden")
                }
            }
            function tween(p) {
                this.center.setOpacity(p);
                this.sideButtons.setOpacity(p)
            }
            return function () {
                if (!this.lightview.visible()) {
                    this.center.setOpacity(0);
                    this.sideButtons.setOpacity(0);
                    this.clearContent();
                    return
                }
                new Effect.Tween(this.lightview, 1, 0, {
                    duration: 0.2,
                    queue: this.queue,
                    afterFinish: after.bind(this)
                },
                tween.bind(this))
            }
        })(),
        hideData: function () {
            $w("innerController data dataText title caption imgNumber innerPrevNext slideshow closeButton").each(function (a) {
                Element.hide(this[a])
            },
            this);
            this.menubar.setStyle("visibility:hidden").setOpacity(0)
        },
        fillMenuBar: function () {
            this.hideData();
            if (!this.view.options.menubar) {
                this.menubarDimensions = {
                    width: 0,
                    height: 0
                };
                this.closeButtonWidth = 0;
                this.menubar.hide()
            } else {
                this.menubar.show()
            }
            if (this.view.title || this.view.caption) {
                this.dataText.show();
                this.data.show()
            }
            if (this.view.title) {
                this.title.update(this.view.title).show()
            }
            if (this.view.caption) {
                this.caption.update(this.view.caption).show()
            }
            if (this.views && this.views.length > 1) {
                if (this.view.isSet()) {
                    this.setNumber.update(new Template(this.options.controller.setNumberTemplate).evaluate({
                        position: this.position + 1,
                        total: this.views.length
                    }));
                    if (this.controller.getStyle("visibility") == "hidden") {
                        this.controller.setStyle("visibility:visible");
                        if (this._controllerCenterEffect) {
                            Effect.Queues.get("lightview").remove(this._controllerCenterEffect)
                        }
                        this._controllerCenterEffect = new Effect.Appear(this.controllerCenter, {
                            queue: this.queue,
                            duration: 0.1
                        })
                    }
                } else {
                    this.data.show();
                    if (this.view.isImage()) {
                        this.innerController.show();
                        this.imgNumber.show().down().update(new Template(this.options.imgNumberTemplate).evaluate({
                            position: this.position + 1,
                            total: this.views.length
                        }));
                        if (this.view.options.slideshow) {
                            this.slideshowButton.show();
                            this.slideshow.show()
                        }
                    }
                }
            }
            var a = this.view.isSet();
            if ((this.view.options.innerPreviousNext || a) && this.views.length > 1) {
                var b = {
                    prev: (this.options.cyclic || this.position != 0),
                    next: (this.options.cyclic || ((this.view.isGallery() || a) && this.getSurroundingIndexes().next != 0))
                };
                $w("prev next").each(function (z) {
                    var Z = z.capitalize(),
                    cursor = b[z] ? "pointer" : "auto";
                    if (a) {
                        this["controller" + Z].setStyle({
                            cursor: cursor
                        }).setOpacity(b[z] ? 1 : this.options.buttons.opacity.disabled)
                    } else {
                        this["inner" + Z + "Button"].setStyle({
                            cursor: cursor
                        }).setOpacity(b[z] ? this.options.buttons.opacity.normal : this.options.buttons.opacity.disabled)
                    }
                }.bind(this));
                if (this.view.options.innerPreviousNext || this.options.controller.innerPreviousNext) {
                    this.innerPrevNext.show()
                }
            }
            this.controllerSlideshow.setOpacity(this.isSetGallery ? 1 : this.options.buttons.opacity.disabled).setStyle({
                cursor: this.isSetGallery ? "pointer" : "auto"
            });
            this.setCloseButtons();
            if (!this.menubar.childElements().find(Element.visible)) {
                this.menubar.hide();
                this.view.options.menubar = false
            }
            this.setMenubarDimensions()
        },
        setCloseButtons: function () {
            var a = this.closeDimensions.small.width,
            large = this.closeDimensions.large.width,
            imgWidth = this.scaledInnerDimensions ? this.scaledInnerDimensions.width : this.innerDimensions.width,
            minimum = 180,
            width = 0,
            closeButton = this.view.options.closeButton || "large",
            background = this.options.borderColor;
            if (this.view.options.topclose || this.view.isSet() || !this.view.options.closeButton) {
                background = null
            } else {
                if (imgWidth >= minimum + a && imgWidth < minimum + large) {
                    background = "small";
                    width = a
                } else {
                    if (imgWidth >= minimum + large) {
                        background = closeButton;
                        width = this.closeDimensions[closeButton].width
                    }
                }
            }
            if (width > 0) {
                this.data.show();
                this.closeButton.setStyle({
                    width: width + "px"
                }).show()
            } else {
                this.closeButton.hide()
            }
            if (background) {
                this.closeButton.setPngBackground(this.images + "close_" + background + ".png", {
                    backgroundColor: this.options.backgroundColor
                })
            }
            this.closeButtonWidth = width
        },
        startLoading: function () {
            this.loadingEffect = new Effect.Appear(this.loading, {
                duration: 0.2,
                from: 0,
                to: 1,
                queue: this.queue
            })
        },
        stopLoading: function () {
            if (this.loadingEffect) {
                Effect.Queues.get("lightview").remove(this.loadingEffect)
            }
            new Effect.Fade(this.loading, {
                duration: 0.2,
                queue: this.queue,
                delay: 0.2
            })
        },
        setPrevNext: function () {
            if (!this.view.isImage()) {
                return
            }
            var a = (this.options.cyclic || this.position != 0),
            next = (this.options.cyclic || ((this.view.isGallery() || this.view.isSet()) && this.getSurroundingIndexes().next != 0));
            this.prevButtonImage[a ? "show" : "hide"]();
            this.nextButtonImage[next ? "show" : "hide"]();
            var b = this.scaledInnerDimensions || this.innerDimensions;
            this.prevnext.setStyle({
                height: b.height + "px",
                marginTop: this.border + (this.view.options.menubar == "top" ? this.menubar.getHeight() : 0) + "px"
            });
            var c = ((b.width / 2 - 1) + this.border).floor();
            if (a) {
                this.prevnext.insert(this.prevButton = new Element("div", {
                    className: "lv_Button lv_PrevButton"
                }).setStyle({
                    width: c + "px"
                }));
                this.prevButton.side = "prev"
            }
            if (next) {
                this.prevnext.insert(this.nextButton = new Element("div", {
                    className: "lv_Button lv_NextButton"
                }).setStyle({
                    width: c + "px"
                }));
                this.nextButton.side = "next"
            }
            if (a || next) {
                this.prevnext.show()
            }
        },
        showPrevNext: function () {
            if (!this.view || !this.options.buttons.side.display || !this.view.isImage()) {
                return
            }
            this.setPrevNext();
            this.prevnext.show()
        },
        hidePrevNext: function () {
            this.prevnext.update("").hide();
            this.prevButtonImage.hide().setStyle({
                marginLeft: this.sideDimensions.width + "px"
            });
            this.nextButtonImage.hide().setStyle({
                marginLeft: -1 * this.sideDimensions.width + "px"
            })
        },
        appear: (function () {
            function after() {
                this.lightview.setOpacity(1)
            }
            if (!BROWSER_IS_WEBKIT_419) {
                after = after.wrap(function (a, b) {
                    a(b);
                    this.lightview.show()
                })
            }
            return function () {
                if (this.lightview.getStyle("opacity") != 0) {
                    return
                }
                if (this.options.overlay.display) {
                    new Effect.Appear(this.overlay, {
                        duration: 0.2,
                        from: 0,
                        to: FIX_OVERLAY_WITH_PNG ? 1 : this.options.overlay.opacity,
                        queue: this.queue,
                        beforeStart: this.maxOverlay.bind(this),
                        afterFinish: after.bind(this)
                    })
                } else {
                    after.call(this)
                }
            }
        })(),
        hide: function () {
            if (Prototype.Browser.IE && this.iframe && this.view.isIframe()) {
                this.iframe.remove()
            }
            if (BROWSER_IS_WEBKIT_419 && this.view.isQuicktime()) {
                var a = $$("object#lightviewContent")[0];
                if (a) {
                    try {
                        a.Stop()
                    } catch(e) {}
                }
            }
            if (this.lightview.getStyle("opacity") == 0) {
                return
            }
            this.stopSlideshow();
            this.prevnext.hide();
            if (!Prototype.Browser.IE || !this.view.isIframe()) {
                this.center.hide()
            }
            if (Effect.Queues.get("lightview_hide").effects.length > 0) {
                return
            }
            Effect.Queues.get("lightview").each(function (e) {
                e.cancel()
            });
            new Effect.Event({
                queue: this.queue,
                afterFinish: this.restoreInlineContent.bind(this)
            });
            new Effect.Opacity(this.lightview, {
                duration: 0.1,
                from: 1,
                to: 0,
                queue: {
                    position: "end",
                    scope: "lightview_hide"
                }
            });
            new Effect.Fade(this.overlay, {
                duration: 0.16,
                queue: {
                    position: "end",
                    scope: "lightview_hide"
                },
                afterFinish: this.afterHide.bind(this)
            })
        },
        afterHide: function () {
            this.clearContent();
            this.lightview.hide();
            this.center.setOpacity(0).show();
            this.prevnext.update("").hide();
            this.contentTop.update("").hide();
            this.contentBottom.update("").hide();
            this.disableKeyboardNavigation();
            this.showOverlapping();
            new Effect.Event({
                queue: this.queue,
                afterFinish: this.resize.bind(this, this.options.startDimensions)
            });
            new Effect.Event({
                queue: this.queue,
                afterFinish: function () {
                    if (this.element) {
                        this.element.fire("lightview:hidden")
                    }
                    $w("element views view scaledInnerDimensions isSetGallery _openEffect content")._each(function (a) {
                        this[a] = null
                    }.bind(this))
                }.bind(this)
            })
        },
        setMenubarDimensions: function () {
            this.menubar.setStyle("padding:0;");
            var a = {},
            imgWidth = this[(this.scaledInnerDimensions ? "scaledI" : "i") + "nnerDimensions"].width;
            this.menubar.setStyle({
                width: imgWidth + "px"
            });
            this.data.setStyle({
                width: imgWidth - this.closeButtonWidth - 1 + "px"
            });
            a = this.getHiddenDimensions(this.menubar);
            if (this.view.options.menubar) {
                a.height += this.options.menubarPadding;
                switch (this.view.options.menubar) {
                case "bottom":
                    this.menubar.setStyle("padding:" + this.options.menubarPadding + "px 0 0 0");
                    break;
                case "top":
                    this.menubar.setStyle("padding: 0 0 " + this.options.menubarPadding + "px 0");
                    break
                }
            }
            this.menubar.setStyle({
                width: "100%"
            });
            this.menubarDimensions = this.view.options.menubar ? a : {
                width: a.width,
                height: 0
            }
        },
        restoreCenter: (function () {
            var a, controllerOffset;
            function init() {
                a = this.lightview.getDimensions();
                controllerOffset = this.controller.visible() ? (this.controllerOffset / 2) : 0
            }
            var b;
            if (BROWSER_IS_IE_LT7) {
                b = function () {
                    this.lightview.setStyle({
                        top: "50%",
                        left: "50%"
                    })
                }
            } else {
                if (BROWSER_IS_WEBKIT_419 || BROWSER_IS_FIREFOX_LT3) {
                    b = function () {
                        var v = this.getViewportDimensions(),
                        o = document.viewport.getScrollOffsets();
                        this.lightview.setStyle({
                            marginLeft: 0,
                            marginTop: 0,
                            left: (o[0] + (v.width / 2) - (a.width / 2)).floor() + "px",
                            top: (o[1] + (v.height / 2) - (a.height / 2)).floor() + "px"
                        })
                    }
                } else {
                    b = function () {
                        this.lightview.setStyle({
                            position: "fixed",
                            left: "50%",
                            top: "50%",
                            marginLeft: (0 - a.width / 2).round() + "px",
                            marginTop: (0 - a.height / 2 - controllerOffset).round() + "px"
                        })
                    }
                }
            }
            return function () {
                init.call(this);
                b.call(this)
            }
        })(),
        startSlideshow: function () {
            this.stopSlideshow();
            this.sliding = true;
            this.next.bind(this).delay(0.25);
            this.slideshowButton.setPngBackground(this.images + "inner_slideshow_stop.png", {
                backgroundColor: this.options.backgroundColor
            }).hide();
            this.controllerSlideshow.setPngBackground(this.images + "controller_slideshow_stop.png", {
                backgroundColor: this.options.controller.backgroundColor
            })
        },
        stopSlideshow: function () {
            if (this.sliding) {
                this.sliding = false
            }
            if (this.slideTimer) {
                clearTimeout(this.slideTimer)
            }
            this.slideshowButton.setPngBackground(this.images + "inner_slideshow_play.png", {
                backgroundColor: this.options.backgroundColor
            });
            this.controllerSlideshow.setPngBackground(this.images + "controller_slideshow_play.png", {
                backgroundColor: this.options.controller.backgroundColor
            })
        },
        toggleSlideshow: function () {
            if (this.view.isSet() && !this.isSetGallery) {
                return
            }
            this[(this.sliding ? "stop" : "start") + "Slideshow"]()
        },
        nextSlide: function () {
            if (this.sliding) {
                this.slideTimer = this.next.bind(this).delay(this.options.slideshowDelay)
            }
        },
        updateViews: function () {
            $$("a[class~=lightview], area[class~=lightview]").each(function (a) {
                var b = a._view;
                if (!b) {
                    return
                }
                if (b._title) {
                    a.writeAttribute("title", b._title)
                }
                a._view = null
            })
        },
        getSet: function (a) {
            var b = a.indexOf("][");
            if (b > -1) {
                a = a.substr(0, b + 1)
            }
            return $$('a[rel^="' + a + '"], area[rel^="' + a + '"]')
        },
        getViews: function (a) {
            return this.getSet(a).pluck("_view")
        },
        addObservers: function () {
            $(document.body).observe("click", this.delegateClose.bindAsEventListener(this));
            $w("mouseover mouseout").each(function (e) {
                this.prevnext.observe(e, function (a) {
                    var b = a.findElement("div");
                    if (!b) {
                        return
                    }
                    if (this.prevButton && this.prevButton == b || this.nextButton && this.nextButton == b) {
                        this.toggleSideButton(a)
                    }
                }.bindAsEventListener(this))
            }.bind(this));
            this.prevnext.observe("click", function (c) {
                var d = c.findElement("div");
                if (!d) {
                    return
                }
                var e = (this.prevButton && this.prevButton == d) ? "previous" : (this.nextButton && this.nextButton == d) ? "next" : null;
                if (e) {
                    this[e].wrap(function (a, b) {
                        this.stopSlideshow();
                        a(b)
                    }).bind(this)()
                }
            }.bindAsEventListener(this));
            $w("prev next").each(function (s) {
                var S = s.capitalize(),
                stopSlideshow = function (a, b) {
                    this.stopSlideshow();
                    a(b)
                },
                blockInnerPrevNext = function (a, b) {
                    var c = b.element().prevnext;
                    if ((c == "prev" && (this.options.cyclic || this.position != 0)) || (c == "next" && (this.options.cyclic || ((this.view.isGallery() || this.view.isSet()) && this.getSurroundingIndexes().next != 0)))) {
                        a(b)
                    }
                };
                this[s + "ButtonImage"].observe("mouseover", this.toggleSideButton.bindAsEventListener(this)).observe("mouseout", this.toggleSideButton.bindAsEventListener(this)).observe("click", this[s == "next" ? s : "previous"].wrap(stopSlideshow).bindAsEventListener(this));
                this["inner" + S + "Button"].observe("click", this[s == "next" ? s : "previous"].wrap(blockInnerPrevNext).wrap(stopSlideshow).bindAsEventListener(this)).observe("mouseover", Element.setOpacity.curry(this["inner" + S + "Button"], this.options.buttons.opacity.hover).wrap(blockInnerPrevNext).bindAsEventListener(this)).observe("mouseout", Element.setOpacity.curry(this["inner" + S + "Button"], this.options.buttons.opacity.normal).wrap(blockInnerPrevNext).bindAsEventListener(this));
                this["controller" + S].observe("click", this[s == "next" ? s : "previous"].wrap(blockInnerPrevNext).wrap(stopSlideshow).bindAsEventListener(this))
            },
            this);
            var f = [this.closeButton, this.slideshowButton];
            if (!BROWSER_IS_WEBKIT_419) {
                f.each(function (b) {
                    b.observe("mouseover", Element.setOpacity.bind(this, b, this.options.buttons.opacity.hover)).observe("mouseout", Element.setOpacity.bind(this, b, this.options.buttons.opacity.normal))
                },
                this)
            } else {
                f.invoke("setOpacity", 1)
            }
            this.slideshowButton.observe("click", this.toggleSlideshow.bindAsEventListener(this));
            this.controllerSlideshow.observe("click", this.toggleSlideshow.bindAsEventListener(this));
            if (BROWSER_IS_WEBKIT_419 || BROWSER_IS_FIREFOX_LT3) {
                var g = function (a, b) {
                    if (this.lightview.getStyle("top").charAt(0) == "-") {
                        return
                    }
                    a(b)
                };
                Event.observe(window, "scroll", this.restoreCenter.wrap(g).bindAsEventListener(this));
                Event.observe(window, "resize", this.restoreCenter.wrap(g).bindAsEventListener(this))
            }
            if (BROWSER_IS_FIREFOX_LT3) {
                Event.observe(window, "resize", this.maxOverlay.bindAsEventListener(this))
            }
            if (BROWSER_IS_IE_LT7) {
                function centerControllerIELT7() {
                    if (this.controller) {
                        this.controller.setStyle({
                            left: ((document.documentElement.scrollLeft || 0) + m.getWidth() / 2).round() + "px"
                        })
                    }
                }
                Event.observe(window, "scroll", centerControllerIELT7.bindAsEventListener(this));
                Event.observe(window, "resize", centerControllerIELT7.bindAsEventListener(this))
            }
            if (this.options.preloadHover) {
                this._preloadImageHover = function (a) {
                    var b = a.findElement("a[class~=lightview], area[class~=lightview]");
                    if (!b) {
                        return
                    }
                    a.stop();
                    if (!b._view) {
                        new Lightview.View(b)
                    }
                    this.preloadImageHover(b)
                }.bindAsEventListener(this);
                $(document.body).observe("mouseover", this._preloadImageHover)
            }
        },
        toggleTopClose: function (a) {
            if (this._topCloseEffect) {
                Effect.Queues.get("lightview_topCloseEffect").remove(this.topCloseEffect)
            }
            var b = {
                marginTop: (a ? 0 : this.closeDimensions.topclose.height) + "px"
            };
            this._topCloseEffect = new Effect.Morph(this.topcloseButtonImage, {
                style: b,
                duration: 0.16,
                queue: this.queue,
                delay: a ? 0.15 : 0
            })
        },
        getScrollDimensions: function () {
            var a = {};
            $w("width height").each(function (d) {
                var D = d.capitalize(),
                ddE = document.documentElement;
                a[d] = Prototype.Browser.IE ? [ddE["offset" + D], ddE["scroll" + D]].max() : Prototype.Browser.WebKit ? document.body["scroll" + D] : ddE["scroll" + D]
            });
            return a
        },
        maxOverlay: function () {
            if (!BROWSER_IS_FIREFOX_LT3) {
                return
            }
            this.overlay.setStyle(pixelClone(this.getScrollDimensions()))
        },
        delegateClose: (function () {
            var b = ".lv_Close, .lv_topButtons .lv_Button, .lv_Loading, .lv_controllerClose";
            return function (a) {
                if (this.view && this.view.options && a.findElement(b + (this.view.options.overlayClose ? ", #lv_overlay" : ""))) {
                    this.hide()
                }
            }
        })(),
        toggleSideButton: function (a) {
            var b = a.target,
            side = b.side,
            w = this.sideDimensions.width,
            offset = (a.type == "mouseover") ? 0 : side == "prev" ? w : -1 * w,
            style = {
                marginLeft: offset + "px"
            };
            if (!this.sideEffect) {
                this.sideEffect = {}
            }
            if (this.sideEffect[side]) {
                Effect.Queues.get("lightview_side" + side).remove(this.sideEffect[side])
            }
            this.sideEffect[side] = new Effect.Morph(this[side + "ButtonImage"], {
                style: style,
                duration: 0.2,
                queue: {
                    scope: "lightview_side" + side,
                    limit: 1
                },
                delay: (a.type == "mouseout") ? 0.1 : 0
            })
        },
        getSurroundingIndexes: function () {
            if (!this.views) {
                return
            }
            var a = this.position,
            length = this.views.length;
            var b = (a <= 0) ? length - 1 : a - 1,
            next = (a >= length - 1) ? 0 : a + 1;
            return {
                previous: b,
                next: next
            }
        },
        createCorner: function (a, b) {
            var c = arguments[2] || this.options,
            radius = c.radius,
            border = c.border;
            position = {
                top: (b.charAt(0) == "t"),
                left: (b.charAt(1) == "l")
            };
            if (l) {
                var d = new Element("canvas", {
                    className: "cornerCanvas" + b.capitalize(),
                    width: border + "px",
                    height: border + "px"
                });
                d.setStyle("float:left");
                a.insert(d);
                var e = d.getContext("2d");
                e.fillStyle = c.backgroundColor;
                e.arc((position.left ? radius : border - radius), (position.top ? radius : border - radius), radius, 0, Math.PI * 2, true);
                e.fill();
                e.fillRect((position.left ? radius : 0), 0, border - radius, border);
                e.fillRect(0, (position.top ? radius : 0), border, border - radius)
            } else {
                var f = new Element("ns_vml:roundrect", {
                    fillcolor: c.backgroundColor,
                    strokeWeight: "1px",
                    strokeColor: c.backgroundColor,
                    arcSize: (radius / border * 0.5).toFixed(2)
                }).setStyle({
                    width: 2 * border - 1 + "px",
                    height: 2 * border - 1 + "px",
                    position: "absolute",
                    left: (position.left ? 0 : (-1 * border)) + "px",
                    top: (position.top ? 0 : (-1 * border)) + "px"
                });
                a.insert(f);
                f.outerHTML = f.outerHTML
            }
        },
        hideOverlapping: (function () {
            function getOverlappingElements() {
                return $$("object, embed, select")
            }
            if (Prototype.Browser.IE && document.documentMode >= 8) {
                getOverlappingElements = function () {
                    return document.querySelectorAll("object, embed, select")
                }
            }
            return function () {
                if (this.preventingOverlap) {
                    return
                }
                var a = getOverlappingElements();
                this.overlappingRestore = [];
                for (var i = 0, length = a.length; i < length; i++) {
                    var b = a[i];
                    this.overlappingRestore.push({
                        element: b,
                        visibility: b.style.visibility
                    });
                    b.style.visibility = "hidden"
                }
                this.preventingOverlap = true
            }
        })(),
        showOverlapping: function () {
            this.overlappingRestore.each(function (a, i) {
                a.element.style.visibility = a.visibility
            });
            delete this.overlappingRestore;
            this.preventingOverlap = false
        },
        getInnerDimensions: function () {
            return {
                width: this.innerDimensions.width,
                height: this.innerDimensions.height + this.menubarDimensions.height
            }
        },
        getOuterDimensions: function () {
            var i = this.getInnerDimensions(),
            b = 2 * this.border;
            return {
                width: i.width + b,
                height: i.height + b
            }
        },
        getBounds: function () {
            var a = 21,
            safety = 2 * this.sideDimensions.height + a,
            v = this.getViewportDimensions();
            return {
                width: v.width - safety,
                height: v.height - safety
            }
        },
        getViewportDimensions: function () {
            var v = m.getDimensions();
            if (this.controller && this.controller.visible() && this.views && this.views.length > 1) {
                v.height -= this.controllerOffset
            }
            return v
        }
    });
    var m = {
        getDimensions: function () {
            return {
                width: this.getWidth(),
                height: this.getHeight()
            }
        }
    };
    (function (a) {
        var B = Prototype.Browser,
        doc = document,
        element, property = {};
        function getRootElement() {
            if (BROWSER_IS_WEBKIT_419) {
                return doc
            }
            if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) {
                return doc.body
            }
            return doc.documentElement
        }
        function define(D) {
            if (!element) {
                element = getRootElement()
            }
            property[D] = "client" + D;
            a["get" + D] = function () {
                return element[property[D]]
            };
            return a["get" + D]()
        }
        a.getWidth = define.curry("Width");
        a.getHeight = define.curry("Height")
    })(m);
    (function () {
        function guard(a, b) {
            if (!this.view) {
                return
            }
            a(b)
        }
        $w("fillMenuBar insertContent").each(function (a) {
            this[a] = this[a].wrap(guard)
        },
        Lightview)
    })();
    function pixelClone(b) {
        var c = {};
        Object.keys(b).each(function (a) {
            c[a] = b[a] + "px"
        });
        return c
    }
    Object.extend(Lightview, {
        enableKeyboardNavigation: function () {
            if (!this.view.options.keyboard) {
                return
            }
            this.keyboardEvent = this.keyboardDown.bindAsEventListener(this);
            document.observe("keydown", this.keyboardEvent)
        },
        disableKeyboardNavigation: function () {
            if (this.keyboardEvent) {
                document.stopObserving("keydown", this.keyboardEvent)
            }
        },
        keyboardDown: function (a) {
            var b = String.fromCharCode(a.keyCode).toLowerCase(),
            keyCode = a.keyCode,
            staticGallery = (this.view.isGallery() || this.isSetGallery) && !this.resizing,
            slideshow = this.view.options.slideshow,
            action;
            if (this.view.isMedia()) {
                a.stop();
                action = (keyCode == Event.KEY_ESC || ["x", "c"].member(b)) ? "hide" : (keyCode == 37 && staticGallery && (this.options.cyclic || this.position != 0)) ? "previous" : (keyCode == 39 && staticGallery && (this.options.cyclic || this.getSurroundingIndexes().next != 0)) ? "next" : (b == "p" && slideshow && staticGallery) ? "startSlideshow" : (b == "s" && slideshow && staticGallery) ? "stopSlideshow" : null;
                if (b != "s") {
                    this.stopSlideshow()
                }
            } else {
                action = (keyCode == Event.KEY_ESC) ? "hide" : null
            }
            if (action) {
                this[action]()
            }
            if (staticGallery) {
                if (keyCode == Event.KEY_HOME && this.views.first() != this.view) {
                    this.show(0)
                }
                if (keyCode == Event.KEY_END && this.views.last() != this.view) {
                    this.show(this.views.length - 1)
                }
            }
        }
    });
    Lightview.afterShow = Lightview.afterShow.wrap(function (a, b) {
        this.enableKeyboardNavigation();
        a(b)
    });
    Object.extend(Lightview, {
        extendSet: function (a) {
            var b = this.getSet(a);
            if (!b) {
                return
            }
            b._each(Lightview.Extend)
        },
        preloadSurroundingImages: function () {
            if (this.views.length == 0) {
                return
            }
            var a = this.getSurroundingIndexes();
            this.preloadFromSet([a.next, a.previous])
        },
        preloadFromSet: function (c) {
            var d = (this.views && this.views.member(c) || Object.isArray(c)) ? this.views : c.rel ? this.getViews(c.rel) : null;
            if (!d) {
                return
            }
            var e = $A(Object.isNumber(c) ? [c] : c.type ? [d.indexOf(c)] : c).uniq();
            e.each(function (a) {
                var b = d[a];
                this.preloadImageDimensions(b)
            },
            this)
        },
        setPreloadedDimensions: function (a, b) {
            a.preloadedDimensions = {
                width: b.width,
                height: b.height
            }
        },
        preloadImageDimensions: function (a) {
            if (a.preloadedDimensions || a.isPreloading || !a.href) {
                return
            }
            var P = new Image();
            P.onload = function () {
                P.onload = Prototype.emptyFunction;
                a.isPreloading = null;
                this.setPreloadedDimensions(a, P)
            }.bind(this);
            a.isPreloading = true;
            P.src = a.href
        },
        preloadImageHover: function (a) {
            var b = a._view;
            if (b && b.preloadedDimensions || b.isPreloading || !b.isImage()) {
                return
            }
            this.preloadImageDimensions(b)
        }
    });
    Element.addMethods({
        setPngBackground: function (a, b) {
            a = $(a);
            var c = Object.extend({
                align: "top left",
                repeat: "no-repeat",
                sizingMethod: "scale",
                backgroundColor: ""
            },
            arguments[2] || {});
            a.setStyle(BROWSER_IS_IE_LT7 ? {
                filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + b + "'', sizingMethod='" + c.sizingMethod + "')"
            } : {
                background: c.backgroundColor + " url(" + b + ") " + c.align + " " + c.repeat
            });
            return a
        }
    });
    Object.extend(Lightview, {
        detectType: function (a, b) {
            var c;
            $w("flash image iframe quicktime").each(function (t) {
                if (new RegExp("\\.(" + this.typeExtensions[t].replace(/\s+/g, "|") + ")(\\?.*)?", "i").test(a)) {
                    c = t
                }
            }.bind(this));
            if (c) {
                return c
            }
            if (a.startsWith("#")) {
                return "inline"
            }
            if (document.domain && document.domain != (a).replace(/(^.*\/\/)|(:.*)|(\/.*)/g, "")) {
                return "iframe"
            }
            return "image"
        },
        detectExtension: function (a) {
            var b = a.gsub(/\?.*/, "").match(/\.([^.]{3,4})$/);
            return b ? b[1] : null
        },
        createHTML: function (b) {
            var c = "<" + b.tag;
            for (var d in b) {
                if (! ["children", "html", "tag"].member(d)) {
                    c += " " + d + '="' + b[d] + '"'
                }
            }
            if (new RegExp("^(?:area|base|basefont|br|col|frame|hr|img|input|link|isindex|meta|param|range|spacer|wbr)$", "i").test(b.tag)) {
                c += "/>"
            } else {
                c += ">";
                if (b.children) {
                    b.children.each(function (a) {
                        c += this.createHTML(a)
                    }.bind(this))
                }
                if (b.html) {
                    c += b.html
                }
                c += "</" + b.tag + ">"
            }
            return c
        }
    });
    (function () {
        document.observe("dom:loaded", function () {
            var c = (navigator.plugins && navigator.plugins.length);
            function detectPlugin(a) {
                var b = false;
                if (c) {
                    b = ($A(navigator.plugins).pluck("name").join(",").indexOf(a) >= 0)
                } else {
                    try {
                        b = new ActiveXObject(a)
                    } catch(e) {}
                }
                return !! b
            }
            if (c) {
                window.Lightview.Plugin = {
                    flash: detectPlugin("Shockwave Flash"),
                    quicktime: detectPlugin("QuickTime")
                }
            } else {
                window.Lightview.Plugin = {
                    flash: detectPlugin("ShockwaveFlash.ShockwaveFlash"),
                    quicktime: detectPlugin("QuickTime.QuickTime")
                }
            }
        })
    })();
    Lightview.View = Class.create({
        initialize: function (b) {
            if (b._view) {
                return
            }
            var c = Object.isElement(b);
            if (c && !b._view) {
                b._view = this;
                if (b.title) {
                    b._view._title = b.title;
                    if (Lightview.options.removeTitles) {
                        b.setAttribute("title", "")
                    }
                }
            }
            this.href = c ? b.getAttribute("href") : b.href;
            if (this.href.indexOf("#") >= 0) {
                this.href = this.href.substr(this.href.indexOf("#"))
            }
            var d = b.rel;
            if (d) {
                this.rel = d;
                if (d.startsWith("gallery")) {
                    this.type = "gallery"
                } else {
                    if (d.startsWith("set")) {
                        if (d.include("][")) {
                            var e = d.split("]["),
                            relType = e[1].match(/([a-zA-Z]*)/)[1];
                            if (relType) {
                                this.type = relType;
                                var f = e[0] + "]";
                                b.writeAttribute("rel", f);
                                this.rel = f
                            }
                        } else {
                            this.type = Lightview.detectType(this.href)
                        }
                    } else {
                        this.type = d
                    }
                }
            } else {
                this.type = Lightview.detectType(this.href);
                this.rel = this.type
            }
            $w("ajax flash gallery iframe image inline quicktime external media set")._each(function (a) {
                var T = a.capitalize(),
                t = a.toLowerCase();
                if ("image gallery media external set".indexOf(a) < 0) {
                    this["is" + T] = function () {
                        return this.type == t
                    }.bind(this)
                }
            }.bind(this));
            if (c && b._view._title) {
                var g = b._view._title.split(Lightview.options.titleSplit).invoke("strip");
                if (g[0]) {
                    this.title = g[0]
                }
                if (g[1]) {
                    this.caption = g[1]
                }
                var h = g[2];
                this.options = (h && Object.isString(h)) ? eval("({" + h + "})") : {}
            } else {
                this.title = b.title;
                this.caption = b.caption;
                this.options = b.options || {}
            }
            if (this.options.ajaxOptions) {
                this.options.ajax = Object.clone(this.options.ajaxOptions);
                delete this.options.ajaxOptions
            }
        },
        isGallery: function () {
            return this.type.startsWith("gallery")
        },
        isSet: function () {
            return this.rel.startsWith("set")
        },
        isImage: function () {
            return (this.isGallery() || this.type == "image")
        },
        isExternal: function () {
            return "iframe inline ajax".indexOf(this.type) >= 0
        },
        isMedia: function () {
            return !this.isExternal()
        }
    });
    Lightview.Extend = function (a) {
        var b = $(a);
        new Lightview.View(a);
        return b
    };
    (function () {
        function handleClick(a) {
            var b = a.findElement("a[class~=lightview], area[class~=lightview]");
            if (!b) {
                return
            }
            a.stop();
            this.Extend(b);
            this.show(b)
        }
        function handleMouseOver(a) {
            var b = a.findElement("a[class~=lightview], area[class~=lightview]");
            if (!b) {
                return
            }
            this.Extend(b)
        }
        function elementIE8(a) {
            var b = a.target,
            type = a.type,
            currentTarget = a.currentTarget;
            if (currentTarget && currentTarget.tagName) {
                if (type === "load" || type === "error" || (type === "click" && currentTarget.tagName.toLowerCase() === "input" && currentTarget.type === "radio")) {
                    b = currentTarget
                }
            }
            if (b.nodeType == Node.TEXT_NODE) {
                b = b.parentNode
            }
            return b
        }
        function hasClassNameIE8(a, b) {
            if (!a) {
                return
            }
            var c = a.className;
            return (c.length > 0 && (c == b || new RegExp("(^|\\s)" + b + "(\\s|$)").test(c)))
        }
        function handleMouseOverIE8(a) {
            var b = elementIE8(a);
            if (b && hasClassNameIE8(b, "lightview")) {
                this.Extend(b)
            }
        }
        document.observe("lightview:loaded", function () {
            $(document.body).observe("click", handleClick.bindAsEventListener(Lightview));
            if (Lightview.options.removeTitles && Prototype.Browser.IE && document.documentMode >= 8) {
                $(document.body).observe("mouseover", handleMouseOverIE8.bindAsEventListener(Lightview))
            } else {
                $(document.body).observe("mouseover", handleMouseOver.bindAsEventListener(Lightview))
            }
        })
    })();
    Object.extend(Lightview, {
        buildController: function () {
            var b = this.options.controller,
            border = b.border;
            $(document.body).insert(this.controller = new Element("div", {
                id: "lightviewController"
            }).setStyle({
                zIndex: this.options.zIndex + 1,
                marginBottom: b.margin + "px",
                position: "absolute",

                visibility: "hidden"
            }).insert(this.controllerTop = new Element("div", {
                className: "lv_controllerTop"
            }).insert(new Element("div", {
                className: "lv_controllerCornerWrapper lv_controllerCornerWrapperTopLeft"
            }).setStyle("margin-left: " + border + "px").insert(new Element("div", {
                className: "lv_Corner"
            }))).insert(new Element("div", {
                className: "lv_controllerBetweenCorners"
            }).setStyle({
                margin: "0 " + border + "px",
                height: border + "px"
            })).insert(new Element("div", {
                className: "lv_controllerCornerWrapper lv_controllerCornerWrapperTopRight"
            }).setStyle("margin-left: -" + border + "px").insert(new Element("div", {
                className: "lv_Corner"
            })))).insert(this.controllerMiddle = new Element("div", {
                className: "lv_controllerMiddle clearfix"
            }).insert(this.controllerCenter = new Element("ul", {
                className: "lv_controllerCenter"
            }).setStyle("margin: 0 " + border + "px").insert(new Element("li", {
                className: "lv_controllerSetNumber"
            }).insert(this.setNumber = new Element("div"))).insert(new Element("li", {
                className: "lv_ButtonWrapper lv_controllerPrev"
            }).insert(this.controllerPrev = new Element("div", {
                className: "lv_Button"
            }).setPngBackground(this.images + "controller_prev.png", {
                backgroundColor: b.backgroundColor
            }))).insert(new Element("li", {
                className: "lv_ButtonWrapper lv_controllerNext"
            }).insert(this.controllerNext = new Element("div", {
                className: "lv_Button"
            }).setPngBackground(this.images + "controller_next.png", {
                backgroundColor: b.backgroundColor
            }))).insert(new Element("li", {
                className: "lv_ButtonWrapper lv_controllerSlideshow"
            }).insert(this.controllerSlideshow = new Element("div", {
                className: "lv_Button"
            }).setPngBackground(this.images + "controller_slideshow_play.png", {
                backgroundColor: b.backgroundColor
            }))).insert(new Element("li", {
                className: "lv_ButtonWrapper lv_controllerClose"
            }).insert(this.controllerClose = new Element("div", {
                className: "lv_Button"
            }).setPngBackground(this.images + "controller_close.png", {
                backgroundColor: b.backgroundColor
            }))))).insert(this.controllerBottom = new Element("div", {
                className: "lv_controllerBottom"
            }).insert(new Element("div", {
                className: "lv_controllerCornerWrapper lv_controllerCornerWrapperBottomLeft"
            }).setStyle("margin-left: " + border + "px").insert(new Element("div", {
                className: "lv_Corner"
            }))).insert(new Element("div", {
                className: "lv_controllerBetweenCorners"
            }).setStyle({
                margin: "0 " + border + "px",
                height: border + "px"
            })).insert(new Element("div", {
                className: "lv_controllerCornerWrapper lv_controllerCornerWrapperBottomRight"
            }).setStyle("margin-left: -" + border + "px").insert(new Element("div", {
                className: "lv_Corner"
            })))));
            $w("prev next").each(function (s) {
                var S = s.capitalize();
                this["controller" + S].prevnext = s
            },
            this);
            if (BROWSER_IS_WEBKIT_419) {
                this.controller.hide = function () {
                    this.setStyle("left:-9500px;top:-9500px;visibility:hidden;");
                    return this
                };
                this.controller.show = function () {
                    this.setStyle("visibility:visible");
                    return this
                };
                this.controller.visible = function () {
                    return (this.getStyle("visibility") == "visible" && parseFloat(this.getStyle("top").replace("px", "")) > -9500)
                }
            }
            this.controller.select(".lv_ButtonWrapper div").invoke("setStyle", pixelClone(this.controllerButtonDimensions));
            var c = this.controller.select(".lv_Corner");
            $w("tl tr bl br").each(function (a, i) {
                if (b.radius > 0) {
                    this.createCorner(c[i], a, b)
                } else {
                    c[i].insert(new Element("div", {
                        className: "lv_Fill"
                    }))
                }
                c[i].setStyle({
                    width: b.border + "px",
                    height: b.border + "px"
                }).addClassName("lv_Corner" + a.capitalize())
            },
            this);
            this.controller.down(".lv_controllerMiddle").setStyle("width:100%;");
            this.controller.setStyle(BROWSER_IS_IE_LT7 ? {
                position: "absolute",
                top: "auto",
                left: ""
            } : {
                position: "fixed",
                top: "auto",
                left: "50%"
            });
            this.controller.select(".lv_controllerBetweenCorners", ".lv_controllerMiddle", ".lv_Button", ".lv_Fill").invoke("setStyle", {
                backgroundColor: b.backgroundColor
            });
            this.setNumber.update(new Template(b.setNumberTemplate).evaluate({
                position: 999,
                total: 999
            }));
            this.setNumber.setStyle({
                width: this.setNumber.getWidth() + "px",
                height: this.controllerCenter.getHeight() + "px"
            });
            this._fixateController();
            this.setNumber.update("");
            this.controller.hide().setStyle("visibility:visible");
            this.addObservers();
            this._lightviewLoadedEvent()
        },
        _fixateController: function () {
            var b, finalWidth, controller = this.options.controller,
            border = controller.border;
            if (BROWSER_IS_IE_LT7) {
                b = this.controllerCenter.getDimensions(),
                finalWidth = b.width + 2 * border;
                this.controllerCenter.setStyle({
                    width: b.width + "px",
                    margin: 0
                });
                this.controllerMiddle.setStyle("width:auto;");
                this.controllerCenter.setStyle({
                    paddingLeft: border + "px"
                });
                this.controllerMiddle.setStyle({
                    width: finalWidth + "px"
                });
                $w("top bottom").each(function (a) {
                    this["controller" + a.capitalize()].setStyle({
                        width: finalWidth + "px"
                    })
                },
                this);
                this.controller.setStyle("margin-left:-" + (finalWidth / 2).round() + "px")
            } else {
                this.controllerMiddle.setStyle("width:auto");
                b = this.controllerMiddle.getDimensions();
                this.setNumber.up().setStyle({
                    lineHeight: b.height + "px",
                    width: this.setNumber.getDimensions().width + "px"
                });
                this.controller.setStyle({
                    width: b.width + "px",
                    marginLeft: (0 - (b.width / 2).round()) + "px"
                });
                this.controllerMiddle.setStyle({
                    width: b.width + "px"
                });
                $w("top bottom").each(function (a) {
                    this["controller" + a.capitalize()].setStyle({
                        width: b.width + "px"
                    })
                },
                this)
            }
            this._controllerOffset = controller.margin + b.height + 2 * border;
            this._controllerHeight = this.controller.getHeight();
            this.setNumber.setStyle({
                lineHeight: b.height + "px"
            })
        }
    });
    Lightview.buildController = Lightview.buildController.wrap(function (a, b) {
        var c = new Image();
        c.onload = function () {
            c.onload = Prototype.emptyFunction;
            this.controllerButtonDimensions = {
                width: c.width,
                height: c.height
            };
            a(b)
        }.bind(this);
        c.src = this.images + "controller_prev.png";
        var d = (new Image()).src = this.images + "controller_slideshow_stop.png"
    });
    Lightview.build = Lightview.build.wrap(function (a, b) {
        a(b);
        this.buildController()
    });
    Lightview.hide = Lightview.hide.wrap(function (a, b) {
        if (this.view && this.view.isSet()) {
            this.controller.hide();
            this.setNumber.update("")
        }
        a(b)
    })
})();
Lightview.load();
document.observe("dom:loaded", Lightview.start.bind(Lightview));

;GalleryThumbnails = Class.create({
	initialize: function(options) {
		this.options = Object.extend({
			'containerId'			:	'gallery_album',
			'mainImageSelector'		:	'.polaroid img',
			'mainImageFormat'		:	'r293x600',
			'mainTextSelector'		:	'.polaroid h3',
			'thumbnailsSelector'	:	'.thumbnails img'
		}, options || {});
		
		// Establish which HTML elements we will be using
		this.elements = {};
		this.elements.container		= $(this.options.containerId);
		this.elements.mainImage		= this.elements.container.down(this.options.mainImageSelector);
		this.elements.mainText		= this.elements.container.down(this.options.mainTextSelector);
		this.elements.thumbnails	= this.elements.container.select(this.options.thumbnailsSelector);
		this.fallbackText			= this.elements.mainText.getText();

		this.elements.thumbnails.each(this.addThumbnailBehaviour.bind(this));
		this.elements.mainImage.observe('click', this.lightview.bind(this));
	},
	
	addThumbnailBehaviour: function(thumb) {
		thumb.observe('click', this.thumbnailClick.bindAsEventListener(this));
	},
	
	thumbnailClick: function(event) {
		var thumb = event.findElement('img');
		var altText = thumb.getAttribute('alt');
		this.elements.mainImage.setAttribute('alt', altText || this.fallbackText);
		this.elements.mainText.update((altText || this.fallbackText).escapeHTML());
		this.elements.mainImage.setAttribute('src', thumb.getAttribute('src').replace(/thumbnails\/[^\/]+/, "thumbnails/"+this.options.mainImageFormat));
	},
	
	lightview: function() {
		if (!window.Lightview || !window.Lightview.show) { return false; }
		try {
		Lightview.show({
			href: this.elements.mainImage.getAttribute('src').replace(/thumbnails\/[^\/]+\//, "thumbnails/"),
			rel: 'image',
//			title: 'Login',
			caption: this.elements.mainText.getText()
		});
		} catch (e) {
			console.info(e);
		}
	}
});

;function PolishActinic() {
	var translationTable = {
		// Invalid Unicode range
		"128": "\u20AC",
		"130": "\u201A",
		"132": "\u201E",
		"133": "\u2026",
		"134": "\u2020",
		"135": "\u2021",
		"137": "\u2030",
		"138": "\u0160",
		"139": "\u2039",
		"140": "\u015A",
		"141": "\u0164",
		"142": "\u017D",
		"143": "\u0179",
		"145": "\u2018",
		"146": "\u2019",
		"147": "\u201C",
		"148": "\u201D",
		"149": "\u2022",
		"150": "\u2013",
		"151": "\u2014",
		"153": "\u2122",
		"154": "\u0161",
		"155": "\u203A",
		"156": "\u015B",
		"157": "\u0165",
		"158": "\u017E",
		"159": "\u017A",
		// Valid Unicode range
		"160": "\u00A0",
		"161": "\u02C7",
		"162": "\u02D8",
		"163": "\u0141",
		"164": "\u00A4",
		"165": "\u0104",
		"166": "\u00A6",
		"167": "\u00A7",
		"168": "\u00A8",
		"169": "\u00A9",
		"170": "\u015E",
		"171": "\u00AB",
		"172": "\u00AC",
		"173": "\u00AD",
		"174": "\u00AE",
		"175": "\u017B",
		"176": "\u00B0",
		"177": "\u00B1",
		"178": "\u02DB",
		"179": "\u0142",
		"180": "\u00B4",
		"181": "\u00B5",
		"182": "\u00B6",
		"183": "\u00B7",
		"184": "\u00B8",
		"185": "\u0105",
		"186": "\u015F",
		"187": "\u00BB",
		"188": "\u013D",
		"189": "\u02DD",
		"190": "\u013E",
		"191": "\u017C",
		"192": "\u0154",
		"193": "\u00C1",
		"194": "\u00C2",
		"195": "\u0102",
		"196": "\u00C4",
		"197": "\u0139",
		"198": "\u0106",
		"199": "\u00C7",
		"200": "\u010C",
		"201": "\u00C9",
		"202": "\u0118",
		"203": "\u00CB",
		"204": "\u011A",
		"205": "\u00CD",
		"206": "\u00CE",
		"207": "\u010E",
		"208": "\u0110",
		"209": "\u0143",
		"210": "\u0147",
		"211": "\u00D3",
		"212": "\u00D4",
		"213": "\u0150",
		"214": "\u00D6",
		"215": "\u00D7",
		"216": "\u0158",
		"217": "\u016E",
		"218": "\u00DA",
		"219": "\u0170",
		"220": "\u00DC",
		"221": "\u00DD",
		"222": "\u0162",
		"223": "\u00DF",
		"224": "\u0155",
		"225": "\u00E1",
		"226": "\u00E2",
		"227": "\u0103",
		"228": "\u00E4",
		"229": "\u013A",
		"230": "\u0107",
		"231": "\u00E7",
		"232": "\u010D",
		"233": "\u00E9",
		"234": "\u0119",
		"235": "\u00EB",
		"236": "\u011B",
		"237": "\u00ED",
		"238": "\u00EE",
		"239": "\u010F",
		"240": "\u0111",
		"241": "\u0144",
		"242": "\u0148",
		"243": "\u00F3",
		"244": "\u00F4",
		"245": "\u0151",
		"246": "\u00F6",
		"247": "\u00F7",
		"248": "\u0159",
		"249": "\u016F",
		"250": "\u00FA",
		"251": "\u0171",
		"252": "\u00FC",
		"253": "\u00FD",
		"254": "\u0163",
		"255": "\u02D9",
		// Windows-1252 fallback range
		"8364": "\u20AC",
		"8218": "\u201A",
		"8222": "\u201E",
		"8230": "\u2026",
		"8224": "\u2020",
		"8225": "\u2021",
		"8240": "\u2030",
		"352": "\u0160",
		"8249": "\u2039",
		"338": "\u015A",
	//	"141": "\u0164",
		"381": "\u017D",
	//	"143": "\u0179",
		"8216": "\u2018",
		"8217": "\u2019",
		"8220": "\u201C",
		"8221": "\u201D",
		"8226": "\u2022",
		"8211": "\u2013",
		"8212": "\u2014",
		"8482": "\u2122",
		"353": "\u0161",
		"8250": "\u203A",
		"339": "\u015B",
	//	"157": "\u0165",
		"382": "\u017E",
		"376": "\u017A"
	};

	function getTextNodes(element) {
		var text = [];
		$A(element.childNodes).each(function(child){
			switch (child.nodeType) {
				case 1:
					text.push(getTextNodes(child));
					break;
				case 3:
					text.push(child);
					break;
			}
		});
		return text.flatten();
	}

	getTextNodes(document.body).each(function(node){
		node.nodeValue = node.nodeValue.replace(/[\x7F-\xFF\u00FF-\uFFFF]/g, function(char){
			return translationTable[char.charCodeAt(0)] || char;
		});
	});

	$(document).fire('pl:loaded');
}

;document.observe("dom:loaded", function(){
	basket = new ShoppingBasket("shopping_basket");
	PolishActinic();
	
	new StateRestriction('lstDeliveryCountry', 'lstDeliveryRegion');
	new StateRestriction('lstInvoiceCountry', 'lstInvoiceRegion');
	
	// Automatically submit form on bounce page
	if (document.formOCC) document.formOCC.submit();
	
	// Save the current page URL, Actinic needs it to emulate HTTP_REFERER, poorly
	saveReferrer();
	
	// Add Product Thumbnail interactions for Product Pages
	new ProductThumbnails({
		'$container'	:	'.product_images',
		'$mainimage'	:	'.product_image',
		'$thumbnails'	:	'.product_thumbnails li',
		'mainFormat'	:	'r:255x255',
		'thumbFormat'	:	'r:110x110'
	});
	
	// Toggle default attribute locks
	$$("fieldset.product_component legend input[type='checkbox']").each(function(e){
		e.observe("click", toggle_attribute_lock);
		toggle_attribute_lock(null, e);
	});
	
	if ($('shopping_basket')) $('shopping_basket').select('td.remove_button input').each(function(input){
		var button = input.up().down('img');
		button.setStyle({display: 'inline'});
		var hi = new Element('input', {type: 'hidden', name: input.name, value: ''});
		input.insert({after: hi});
		input.remove();
		button.observe('click', function(){
			hi.value = 'on';
			hi.up('form').submit();
		});
	});
});

document.observe("pl:loaded", function(){
	// Colour picker
	$$('div.product_details fieldset legend label').each(function(label){
		if (label.getText().strip() != 'Kolor') return;
		new ColourPicker(label.up('fieldset'));
	});
	
	// Line up section links
	if ($('section_list')) try { line_up_elements('#section_list a span.section_name', 3); } catch (e) {}
	if ($('related_list')) try { line_up_elements('#related_list a span.section_name', 3); } catch (e) {}
});

// What to do when a product colour causes a price change
document.observe("colourpicker:pricechange", function(e){
	e.memo.picker.hiddenfield.up('.product_details').down('.purchase_info .price_list .pricecontainer').update(e.memo.price.html);
});

