/* 

javascript

prototype.js,scriptaculous.js,effects.js,controls.js,slider.js,cyberpodroze/cyber_scripts.js

 */

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

var Prototype = {
  Version: '1.6.0.3',

  Browser: {
    IE:     !!(window.attachEvent &&
      navigator.userAgent.indexOf('Opera') === -1),
    Opera:  navigator.userAgent.indexOf('Opera') > -1,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
      navigator.userAgent.indexOf('KHTML') === -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    SelectorsAPI: !!document.querySelector,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div')['__proto__'] &&
      document.createElement('div')['__proto__'] !==
        document.createElement('form')['__proto__']
  },

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

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

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


/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

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

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

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

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

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

    klass.prototype.constructor = klass;

    return klass;
  }
};

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

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

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (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;
  }
};

var Abstract = { };

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

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (Object.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;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

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

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

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

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

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

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

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

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

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

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

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

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

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

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

  bind: function() {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

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

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

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

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

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

    return returnValue;
  }
};

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

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

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

var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

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

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

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

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

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

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

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

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

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

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

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

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

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

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

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

  escapeHTML: function() {
    var self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

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

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

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

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

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

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

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

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

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

    return camelized;
  },

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

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

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

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

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

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

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

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

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

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

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

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

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

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

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

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

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

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);

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

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

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

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

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

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

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

var $break = { };

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

  eachSlice: function(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);
  },

  all: function(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;
  },

  any: function(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;
  },

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

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

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

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

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

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

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

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

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

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

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

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

  min: function(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;
  },

  partition: function(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];
  },

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

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

  sortBy: function(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');
  },

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

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

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

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

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

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
    // In Safari, only use the `toArray` method if it's not a NodeList.
    // A NodeList is a function, has an function `item` property, and a numeric
    // `length` property. Adapted from Google Doctype.
    if (!(typeof iterable === 'function' && typeof iterable.length ===
        'number' && typeof iterable.item === 'function') && iterable.toArray)
      return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

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

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

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

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

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

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

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

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

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

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

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

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

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

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

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

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

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

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

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

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

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

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

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

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

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

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {

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

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

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

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

    get: function(key) {
      // simulating poorly supported hasOwnProperty
      if (this._object[key] !== Object.prototype[key])
        return this._object[key];
    },

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

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

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

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

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

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

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

    toQueryString: function() {
      return this.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('&');
    },

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

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

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

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

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

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

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

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

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

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

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

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

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

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});

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

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

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    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)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

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

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

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

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

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && 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') {
      // avoid memory leak in MSIE: clean up
      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) {
  // DOM level 2 ECMAScript Language Binding
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
  if (element) this.Element.prototype = element.prototype;
}).call(window);

Element.cache = { };

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

  toggle: function(element) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('toggle element error: '+ old_element);
    }
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('hide element error: '+ old_element);
    }

    element.style.display = 'none';
    return element;
  },

  show: function(element) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('show element error: '+ old_element);
    }
    element.style.display = '';
    return element;
  },

  remove: function(element) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('remove element error: '+ old_element);
    }
    
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('update element error: '+ old_element);
    }
    
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('replace element error: '+ old_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) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('insert element error: '+ old_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) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('wrap element error: '+ old_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) {
    var old_element = element;

    element = $(element);

    if(typeof(debug) != "undefined" && debug != 0 && element == null) {
      alert('inspect element error: '+ old_element);
    }
    
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

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

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

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

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

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

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

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

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

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

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

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    return Object.isNumber(expression) ? element.descendants()[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();
    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();
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },

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

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

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

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

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

    if (typeof name == 'object') attributes = name;
    else attributes[name] = 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().height;
  },

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

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

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

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

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

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

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

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

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

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

    if (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();
    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('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (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('position') == 'absolute') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

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

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

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

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

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

  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

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

    return $(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

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

    } while (element = element.offsetParent);

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

    // find page position of source
    source = $(source);
    var p = source.viewportOffset();

    // find coordinate system to use
    element = $(element);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

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

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

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

if (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':
          // returns '0px' for hidden elements; we want it to return null
          if (!Element.visible(element)) return null;

          // returns the border-box dimensions rather than the content-box
          // dimensions, so we subtract padding and borders from the value
          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) {
  // IE doesn't report offsets correctly for static elements, so we change them
  // to "relative" to get the values, then change them back.
  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);
      // IE throws an error if element is not in document
      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);
        // Trigger hasLayout on the offset parent so that IE6 reports
        // accurate offsetTop and offsetLeft values for position: fixed.
        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 = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return $(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    names: Object.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._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);
}

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

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

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

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
  // KHTML/WebKit only.
  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  Element.Methods.update = function(element, content) {
    element = $(element);

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

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

    if (tagName in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

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

if ('outerHTML' in document.createElement('div')) {
  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() {
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

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

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

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

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

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

    // extend methods for specific tags
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

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

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

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

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

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

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

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

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

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

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

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

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName)['__proto__'];
    return window[klass];
  }

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

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

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

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

document.viewport = {
  getDimensions: function() {
    var dimensions = { }, B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      if (B.WebKit && !document.evaluate) {
        // Safari <3.0 needs self.innerWidth/Height
        dimensions[d] = self['inner' + D];
      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
        // Opera <9.5 needs document.body.clientWidth/Height
        dimensions[d] = document.body['client' + D]
      } else {
        dimensions[d] = document.documentElement['client' + D];
      }
    });
    return dimensions;
  },

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

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

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();

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

  },

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

    var e = this.expression;

    // Safari 3 chokes on :*-of-type and :empty
    if (Prototype.Browser.WebKit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;

    // XPath can't do namespaced attributes, nor can it read
    // the "checked" property from DOM nodes
    if ((/(\[[\w-]*?:|:checked)/).test(e))
      return false;

    return true;
  },

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

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

    // Make sure the browser treats the selector as valid. Test on an
    // isolated element to minimize cost of this check.
    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;

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

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
            new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

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

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    var e = this.expression, results;

    switch (this.mode) {
      case 'selectorsAPI':
        // querySelectorAll queries document-wide, then filters to descendants
        // of the context element. That's not what we want.
        // Add an explicit context to the selector if necessary.
        if (root !== document) {
          var oldId = root.id, id = $(root).identify();
          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;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the Selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

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

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: 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;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); 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: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

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

  // for Selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

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

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

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._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++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, 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);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._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++) {
        // IE treats comments as element nodes
        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.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

  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, {
    // IE returns comment nodes on getElementsByTagName("*").
    // Filter them out.
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    },

    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node.removeAttribute('_countedByPrototype');
      return nodes;
    }
  });
}

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

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (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) {
            // a key is already present; construct an array of values
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

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

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

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

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

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

    return matchingInputs;
  },

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

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

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

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  disable: function(element) {
    element = $(element);
    element.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) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

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

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

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

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

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

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

Abstract.EventObserver = Class.create({
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
});

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

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

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

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

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

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

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

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

      if (currentTarget && currentTarget.tagName) {
        // Firefox screws up the "click" event when moving between radio buttons
        // via arrow keys. It also screws up the "load" and "error" events on images,
        // reporting the document as the target instead of the original image.
        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);
    },

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

    pointer: function(event) {
      var docElement = document.documentElement,
      body = document.body || { scrollLeft: 0, scrollTop: 0 };
      return {
        x: event.pageX || (event.clientX +
          (docElement.scrollLeft || body.scrollLeft) -
          (docElement.clientLeft || 0)),
        y: event.pageY || (event.clientY +
          (docElement.scrollTop || body.scrollTop) -
          (docElement.clientTop || 0))
      };
    },

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

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

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

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

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

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

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }


  // Internet Explorer needs to remove event handlers on page unload
  // in order to avoid memory leaks.
  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  // Safari has a dummy event handler on page unload so that it won't
  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
  // object when page is returned to via the back button using its bfcache.
  if (Prototype.Browser.WebKit) {
    window.addEventListener('unload', Prototype.emptyFunction, false);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

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

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

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

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

  var timer;

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

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

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

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

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

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

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

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

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

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

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

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

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

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

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

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

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

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Element.addMethods();

// script.aculo.us scriptaculous.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// 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.2',
  require: function(libraryName) {
    // inserting via DOM fails in Safari 2.0, so brute force approach
    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) <
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('head script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js') });
    });
  }
};

Scriptaculous.load();

// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

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

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

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

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

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

Element.setContentZoom = function(element, percent) {
  element = $(element);
  element.setStyle({fontSize: (percent/100) + 'em'});
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

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

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

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

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    spring: function(pos) {
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';

    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character),
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') ||
        Object.isFunction(element)) &&
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;

    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || { });
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    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;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, 'afterUpdate');
        }
      };
    })();

    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if (timePos >= this.startOn) {
      if (timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) this.finish();
        this.event('afterFinish');
        return;
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

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

Effect.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(),
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

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

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

Effect.Move = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

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

Effect.Scale = Class.create(Effect.Base, {
  initialize: function(element, percent) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || { });
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');

    this.originalStyle = { };
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));

    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;

    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if (fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));

    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;

    this.dims = null;
    if (this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if (/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if (!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = { };
    if (this.options.scaleX) d.width = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + 'px';
    if (this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute') {
        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if (this.options.scaleY) d.top = -topd + 'px';
        if (this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

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

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
  scrollOffsets = document.viewport.getScrollOffsets(),
  elementOffsets = $(element).cumulativeOffset();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

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

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

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

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

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

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

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

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

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}); }}); }}); }}); }}); }});
};

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

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

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

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

  var dims = element.getDimensions();
  var initialMoveX, initialMoveY;
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0;
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }

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

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

  var dims = element.getDimensions();
  var moveX, moveY;

  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }

  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  return new Effect.Opacity(element,
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

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

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });

    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        };
      }
    }
    this.start(options);
  },

  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 );
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

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

      var originalValue = this.element.getStyle(property);
      return {
        style: property.camelize(),
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      );
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] =
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

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

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');

Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }

  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]);
  });

  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) {
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);

// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

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

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

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

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

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

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

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

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

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

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

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

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

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex)
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },

  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },

  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ?
          Element.addClassName(this.getEntry(i),"selected") :
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) {
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },

  markPrevious: function() {
    if(this.index > 0) this.index--;
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },

  markNext: function() {
    if(this.index < this.entryCount-1) this.index++;
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },

  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },

  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },

  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();

    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

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

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

      this.stopIndicator();
      this.index = 0;

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

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

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

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

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

  getUpdatedChoices: function() {
    this.startIndicator();

    var entry = encodeURIComponent(this.options.paramName) + '=' +
      encodeURIComponent(this.getToken());

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

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

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

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

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

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

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

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

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

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

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

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

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

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML.unescapeHTML();
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw('Server returned an invalid collection representation.');
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

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

// script.aculo.us slider.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Marty Haught, Thomas Fuchs
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if (!Control) var Control = { };

// options:
//  axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
//  onChange(value)
//  onSlide(value)
Control.Slider = Class.create({
  initialize: function(handle, track, options) {
    var slider = this;

    if (Object.isArray(handle)) {
      this.handles = handle.collect( function(e) { return $(e) });
    } else {
      this.handles = [$(handle)];
    }

    this.track   = $(track);
    this.options = options || { };

    this.axis      = this.options.axis || 'horizontal';
    this.increment = this.options.increment || 1;
    this.step      = parseInt(this.options.step || '1');
    this.range     = this.options.range || $R(0,1);

    this.value     = 0; // assure backwards compat
    this.values    = this.handles.map( function() { return 0 });
    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
    this.options.startSpan = $(this.options.startSpan || null);
    this.options.endSpan   = $(this.options.endSpan || null);

    this.restricted = this.options.restricted || false;

    this.maximum   = this.options.maximum || this.range.end;
    this.minimum   = this.options.minimum || this.range.start;

    // Will be used to align the handle onto the track, if necessary
    this.alignX = parseInt(this.options.alignX || '0');
    this.alignY = parseInt(this.options.alignY || '0');

    this.trackLength = this.maximumOffset() - this.minimumOffset();

    this.handleLength = this.isVertical() ?
      (this.handles[0].offsetHeight != 0 ?
        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
        this.handles[0].style.width.replace(/px$/,""));

    this.active   = false;
    this.dragging = false;
    this.disabled = false;

    if (this.options.disabled) this.setDisabled();

    // Allowed values array
    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
    if (this.allowedValues) {
      this.minimum = this.allowedValues.min();
      this.maximum = this.allowedValues.max();
    }

    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
    this.eventMouseMove = this.update.bindAsEventListener(this);

    // Initialize handles in reverse (make sure first handle is active)
    this.handles.each( function(h,i) {
      i = slider.handles.length-1-i;
      slider.setValue(parseFloat(
        (Object.isArray(slider.options.sliderValue) ?
          slider.options.sliderValue[i] : slider.options.sliderValue) ||
         slider.range.start), i);
      h.makePositioned().observe("mousedown", slider.eventMouseDown);
    });

    this.track.observe("mousedown", this.eventMouseDown);
    document.observe("mouseup", this.eventMouseUp);
    document.observe("mousemove", this.eventMouseMove);

    this.initialized = true;
  },
  dispose: function() {
    var slider = this;
    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
    Event.stopObserving(document, "mouseup", this.eventMouseUp);
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
    this.handles.each( function(h) {
      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
    });
  },
  setDisabled: function(){
    this.disabled = true;
  },
  setEnabled: function(){
    this.disabled = false;
  },
  getNearestValue: function(value){
    if (this.allowedValues){
      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
      if (value <= this.allowedValues.min()) return(this.allowedValues.min());

      var offset = Math.abs(this.allowedValues[0] - value);
      var newValue = this.allowedValues[0];
      this.allowedValues.each( function(v) {
        var currentOffset = Math.abs(v - value);
        if (currentOffset <= offset){
          newValue = v;
          offset = currentOffset;
        }
      });
      return newValue;
    }
    if (value > this.range.end) return this.range.end;
    if (value < this.range.start) return this.range.start;
    return value;
  },
  setValue: function(sliderValue, handleIdx){
    if (!this.active) {
      this.activeHandleIdx = handleIdx || 0;
      this.activeHandle    = this.handles[this.activeHandleIdx];
      this.updateStyles();
    }
    handleIdx = handleIdx || this.activeHandleIdx || 0;
    if (this.initialized && this.restricted) {
      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
        sliderValue = this.values[handleIdx-1];
      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
        sliderValue = this.values[handleIdx+1];
    }
    sliderValue = this.getNearestValue(sliderValue);

    this.values[handleIdx] = sliderValue;
    this.value = this.values[0]; // assure backwards compat

    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
      this.translateToPx(sliderValue);

    this.drawSpans();
    if (!this.dragging || !this.event) this.updateFinished();
  },
  setValueBy: function(delta, handleIdx) {
    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
      handleIdx || this.activeHandleIdx || 0);
  },
  translateToPx: function(value) {
    return Math.round(
      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
      (value - this.range.start)) + "px";
  },
  translateToValue: function(offset) {
    return ((offset/(this.trackLength-this.handleLength) *
      (this.range.end-this.range.start)) + this.range.start);
  },
  getRange: function(range) {
    var v = this.values.sortBy(Prototype.K);
    range = range || 0;
    return $R(v[range],v[range+1]);
  },
  minimumOffset: function(){
    return(this.isVertical() ? this.alignY : this.alignX);
  },
  maximumOffset: function(){
    return(this.isVertical() ?
      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
        this.track.style.height.replace(/px$/,"")) - this.alignY :
      (this.track.offsetWidth != 0 ? this.track.offsetWidth :
        this.track.style.width.replace(/px$/,"")) - this.alignX);
  },
  isVertical:  function(){
    return (this.axis == 'vertical');
  },
  drawSpans: function() {
    var slider = this;
    if (this.spans)
      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
    if (this.options.startSpan)
      this.setSpan(this.options.startSpan,
        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
    if (this.options.endSpan)
      this.setSpan(this.options.endSpan,
        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
  },
  setSpan: function(span, range) {
    if (this.isVertical()) {
      span.style.top = this.translateToPx(range.start);
      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
    } else {
      span.style.left = this.translateToPx(range.start);
      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
    }
  },
  updateStyles: function() {
    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
    Element.addClassName(this.activeHandle, 'selected');
  },
  startDrag: function(event) {
    if (Event.isLeftClick(event)) {
      if (!this.disabled){
        this.active = true;

        var handle = Event.element(event);
        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
        var track = handle;
        if (track==this.track) {
          var offsets  = Position.cumulativeOffset(this.track);
          this.event = event;
          this.setValue(this.translateToValue(
           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
          ));
          var offsets  = Position.cumulativeOffset(this.activeHandle);
          this.offsetX = (pointer[0] - offsets[0]);
          this.offsetY = (pointer[1] - offsets[1]);
        } else {
          // find the handle (prevents issues with Safari)
          while((this.handles.indexOf(handle) == -1) && handle.parentNode)
            handle = handle.parentNode;

          if (this.handles.indexOf(handle)!=-1) {
            this.activeHandle    = handle;
            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
            this.updateStyles();

            var offsets  = Position.cumulativeOffset(this.activeHandle);
            this.offsetX = (pointer[0] - offsets[0]);
            this.offsetY = (pointer[1] - offsets[1]);
          }
        }
      }
      Event.stop(event);
    }
  },
  update: function(event) {
   if (this.active) {
      if (!this.dragging) this.dragging = true;
      this.draw(event);
      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
      Event.stop(event);
   }
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.track);
    pointer[0] -= this.offsetX + offsets[0];
    pointer[1] -= this.offsetY + offsets[1];
    this.event = event;
    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
    if (this.initialized && this.options.onSlide)
      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  },
  endDrag: function(event) {
    if (this.active && this.dragging) {
      this.finishDrag(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.dragging = false;
  },
  finishDrag: function(event, success) {
    this.active = false;
    this.dragging = false;
    this.updateFinished();
  },
  updateFinished: function() {
    if (this.initialized && this.options.onChange)
      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
    this.event = null;
  }
});

/**** CYBER_SCRIPTS.JS */

function addLoadEvent(func) {
   var oldonload = window.onload;
   if (typeof window.onload != 'function') {
      window.onload = func;
   } else {
      window.onload = function() {
         if (oldonload) {
            oldonload();
         }
         func();
      }
   }
}

function wopen(url, name, w, h) {
   name = name.replace(/ /gi, '_');
   name = name.replace(/-/gi, '_');

   win = window.open(url, name, 'menubar=0, toolbar=0, location=0, scrollbars=1, resizable=0, status=0, width='+w+',height='+h);
   win.resizeTo(w, h);
   win.focus();
}


function ajax_query(url, form, div_id, x) {
   new Ajax.Request(url, {
     method: 'get',
     //onLoading: function(transport) {show_loading();},
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         if(document.getElementById(div_id)) {
            document.getElementById(div_id).innerHTML = transport.responseText;
         }
         hide_loading();
       }
     }
   });
}

function dump(arr,level,br) {
   var dumped_text = "";
   if(!level)
      level = 0;

   var level_padding = "";
   for(var j=0;j<level+1;j++)
      level_padding += "    ";

   if(typeof(arr) == 'object') {
      for(var item in arr) {
         var value = arr[item];

         if(typeof(value) == 'object') { //If it is an array,
            dumped_text += level_padding + "'" + item + "' ...\n";
            dumped_text += dump(value,level+1);
         } else {
            dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
         }
      }
   } else {
      dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
   }
   
   if(br == 1) {
      dumped_text = dumped_text.replace(/\n/g, '<br/>');
      dumped_text = dumped_text.replace(/ /g, '&nbsp;');
   }
   return dumped_text;
}


//pobiera rozmiar strony
function getPageSize(){

	var xScroll, yScroll;

	if (window.innerHeight && window.scrollMaxY) {
		xScroll = document.body.scrollWidth;
		yScroll = window.innerHeight + window.scrollMaxY;
	} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
		xScroll = document.body.scrollWidth;
		yScroll = document.body.scrollHeight;
	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
		xScroll = document.body.offsetWidth;
		yScroll = document.body.offsetHeight;
	}

	var windowWidth, windowHeight;
	if (self.innerHeight) {	// all except Explorer
		windowWidth = self.innerWidth;
		windowHeight = self.innerHeight;
	} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
		windowWidth = document.documentElement.clientWidth;
		windowHeight = document.documentElement.clientHeight;
	} else if (document.body) { // other Explorers
		windowWidth = document.body.clientWidth;
		windowHeight = document.body.clientHeight;
	}

	// for small pages with total height less then height of the viewport
	if(yScroll < windowHeight){
		pageHeight = windowHeight;
	} else {
		pageHeight = yScroll;
	}

	// for small pages with total width less then width of the viewport
	if(xScroll < windowWidth){
		pageWidth = windowWidth;
	} else {
		pageWidth = xScroll;
	}

	arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight)
	return arrayPageSize;
}


//*************************************************************
// funkcje pobierające pozycję myszy
// i blokujące przewijanie strony wheelem (przydatne przy Mapie Googla)
//*************************************************************

xMousePos = -10000;
yMousePos = -10000;

move_cyber_hint = false;
last_this_cyber_hint = null;

if (document.layers) { // Netscape
   document.captureEvents(Event.MOUSEMOVE);
   document.onmousemove = captureMousePosition;
   document.onload = captureMousePosition;
} else if (document.all) { // Internet Explorer
   document.onmousemove = captureMousePosition;
   document.onload = captureMousePosition;
} else if (document.getElementById) { // Netcsape 6
   document.onmousemove = captureMousePosition;
   document.onload = captureMousePosition;
}

function captureMousePosition(e) {
   if (document.layers) { //Netscape
      xMousePos = e.pageX;
      yMousePos = e.pageY;
   } else if (document.all) { //IE
      if(document && document.body) {
         if (document.documentElement)
      	{
      		theTop = document.documentElement.scrollTop;
      	}
      	else if (document.body)
      	{
      		theTop = document.body.scrollTop;
      	}
         xMousePos = event.clientX + document.body.scrollLeft;
         yMousePos = event.clientY + theTop;
      }
   } else if (document.getElementById) {
      xMousePos = e.pageX;
      yMousePos = e.pageY;
   }
   
   if(move_cyber_hint == true) {
      show_cyber_hint(last_this_cyber_hint);
   }
}

/** This is high-level function; REPLACE IT WITH YOUR CODE.
 * It must react to delta being more/less than zero.
 */
function handle(delta) {
	if (delta < 0)
		/* something. */;
	else
		/* something. */;
}

function wheel(event){
	var delta = 0;
	if (!event) event = window.event;
	if (event.wheelDelta) {
		delta = event.wheelDelta/120;
		if (window.opera) delta = -delta;
	} else if (event.detail) {
		delta = -event.detail/3;
	}
	if (delta)
		handle(delta);
        if (event.preventDefault)
                event.preventDefault();
        event.returnValue = false;
}

/* Initialization code. */
//if (window.addEventListener)
//	window.addEventListener('DOMMouseScroll', wheel, false);
//window.onmousewheel = document.onmousewheel = wheel;

//*************************************************************
// funkcje zmieniające wygląd check i radio -boxów
//*************************************************************

// zmienia wygląd checkboxów na dowolny obrazek
function change_checkbox (var_name) {

	if (!document.getElementById(var_name).checked)
	{
      Element.show(var_name +'_ch');
      Element.hide(var_name +'_uch');
		document.getElementById(var_name).checked = '1';
	}
	else
	{
      Element.show(var_name +'_uch');
      Element.hide(var_name +'_ch');
      document.getElementById(var_name).checked = false;
	}
}

// zmienia wygląd radioboxów na dowolny obrazek
function change_radio (var_name, radio_list, radio_input) {
   for(i = 0; i < radio_list.length; i++) {
      Element.show(radio_list[i] +'_uch');
      Element.hide(radio_list[i] +'_ch');
   }
   Element.show(var_name +'_ch');
   Element.hide(var_name +'_uch');
   if(radio_input != null) {
      document.getElementById(radio_input).value = var_name;
   }
}

//*************************************************************
// funkcje wyświetlające podpowiedzi na dodatkowych div'ach
//*************************************************************

// pobiera pozycję elementu naprowadzającego, którym jest najczęściej <span>
function set_pos_cyber_hint(div, left, top, width) {
   var page_width = document.getElementById('all').offsetLeft + document.getElementById('all').offsetWidth;
   width = width + 20;

   if(page_width < xMousePos + left + width) {
      left = left - width;
   }

   document.getElementById(div).style.left = xMousePos + left +'px';
   document.getElementById(div).style.top = yMousePos + top +'px';
}

// pokazuje warstwę z podpowiedzią
function show_cyber_hint(a, width) {

   var hint = a.getAttribute('title_hint');

   document.getElementById('cyber_hint').style.width = 'auto';
   document.getElementById('cyber_hint_window').style.width  = 'auto';
   document.getElementById('cyber_hint').innerHTML = hint;
   Element.show('cyber_hint_window');

   width = document.getElementById('cyber_hint').offsetWidth;
   document.getElementById('cyber_hint_window').style.width = width +'px';
   var width2 = width - 20;
   document.getElementById('cyber_hint').style.width = width2 +'px';

   set_pos_cyber_hint('cyber_hint_window', 15, 0, width);
   show_iframe('cyber_hint_window');
   
   move_cyber_hint = true;
   last_this_cyber_hint = a;
}

// ukrywa warstwę z podpowiedzią
function hide_cyber_hint() {
   Element.hide('cyber_hint_window');
   hide_iframe('cyber_hint_window');
   
   move_cyber_hint = false;
   last_this_cyber_hint = null;
}


//*************************************************************
// funkcje odpowiedzialne za wygląd elementów formularza
//*************************************************************

// czyści input i zamienia czcionkę z pochyłej na normalną
function make_editable(input, txt_default) {
   //input.style.fontStyle = 'normal';
   if(input.value == txt_default) {
      input.select();
   }
}

// sprawdza, czy w selekcie wybrano 'wpisz ponownie'
function rewrite_select_check(select_id, txt_default, button_submit_id) {
   select = document.getElementById(select_id);
   if(select.value == 'rewrite') {
      select.value = txt_default;
      document.getElementById(button_submit_id).click();
   }
}

//*************************************************************
// funkcje odpowiedzialne za formatowanie daty urodzenia
//*************************************************************

function create_born_date(input_Y,input_M,input_D,input_hidden) {
   document.getElementById(input_hidden).value = document.getElementById(input_Y).value +'-'+document.getElementById(input_M).value +'-'+document.getElementById(input_D).value;
}

function jump_to_next_input(curr_input, input_length, next_input) {
   if(curr_input != 'undefined') {
      if(document.getElementById(curr_input).value.length == input_length) {
         if (document.getElementById(next_input).value == 'RRRR' || document.getElementById(next_input).value == 'MM' || document.getElementById(next_input).value == 'DD' ) {
           document.getElementById(next_input).value = '';
         }
         document.getElementById(next_input).focus();
      }
   }
}

//*************************************************************
// funkcje odpowiedzialne za sprawdzanie czy wybrano
// przynajmniej jednego pasażera oraz czy podano wiek
// wszytskich dzieci
//*************************************************************

function check_pass(type) {
   var x = 0;
   var ad = 0;
   var yth = 0;
   if(!type){
      type = 'Data';
   }

   if(document.getElementById(type+'FlyPassAd')) { // wiemy że przelot
      x += parseInt(document.getElementById(type+'FlyPassAd').value);
      x += parseInt(document.getElementById(type+'FlyPassYo').value);
      x += parseInt(document.getElementById(type+'FlyPassCh').value);
      x += parseInt(document.getElementById(type+'FlyPassBa').value);
   }

   if(x == 0) {
      show_message (2, 'Musisz wybrać przynajmniej jednego pasażera');
      //alert("2");
      return false;
   }

   ad = parseInt(document.getElementById(type+'FlyPassAd').value);
   yth = parseInt(document.getElementById(type+'FlyPassYo').value);
   if(ad == 0 && yth == 0) {
     show_message (2, 'Musisz wybrać przynajmniej jednego pasażera dorosłego lub młodzież');
      //alert("2");
      return false;
   }
   //alert("3");
   return true;
}

function set_checkbox(id) {
  if (document.getElementById(id).checked == true) {
    document.getElementById(id).checked = false;
  } else {
    document.getElementById(id).checked = true;
  }
}

function pokaz_detale(ktory,i, flight_id)
{
  if (ktory == 1)
  {
     if (i == 1 )
     {
         Element.hide('data_flight_1_short_'+ flight_id);
         Element.show('data_flight_1_detailed_'+ flight_id);
     } else {
         Element.hide('data_flight_1_detailed_'+ flight_id);
         Element.show('data_flight_1_short_'+ flight_id);
     }
  }
  if (ktory == 2)
  {
     if (i == 1 )
     {
         Element.hide('data_flight_2_short_'+ flight_id);
         Element.show('data_flight_2_detailed_'+ flight_id);
     } else {
         Element.hide('data_flight_2_detailed_'+ flight_id);
         Element.show('data_flight_2_short_'+ flight_id);
     }
  }
  if (ktory == 3)
  {
     if (i == 1 )
     {
         Element.hide('data_flight_3_short_'+ flight_id);
         Element.show('data_flight_3_detailed_'+ flight_id);
     } else {
         Element.hide('data_flight_3_detailed_'+ flight_id);
         Element.show('data_flight_3_short_'+ flight_id);
     }
  }
}

function pokaz_detale_hotelu(i, hotel_id)
{
   if (i == 1 )
     {
         //Element.hide('data_hotel_short_'+ hotel_id);
         Element.show('data_hotel_detailed_'+ hotel_id);
     } else {
         Element.hide('data_hotel_detailed_'+ hotel_id);
         //Element.show('data_hotel_short_'+ hotel_id);
     }
}

function pokaz_detale_atrakcji(i, sightseeing_id)
{
   if (i == 1 )
     {
         Element.show('data_sightseeing_detailed_'+ sightseeing_id);
     } else {
         Element.hide('data_sightseeing_detailed_'+ sightseeing_id);

     }
}

function pokaz_detale_przelotu(i, flight_id)
{
   if (i == 1 )
     {
         //Element.hide('data_flight_short_1_'+ flight_id);
         Element.show('data_flight_detailed_1_'+ flight_id);
     } else {
         Element.hide('data_flight_detailed_1_'+ flight_id);
         //Element.show('data_flight_short_1_'+ flight_id);
     }
}


function show_car_detail(i, car_id)
{
   if (i == 1 )
     {
       //Element.hide('data_car_short_'+ car_id);
       Element.show('data_car_detailed_'+ car_id);
     } else {
       Element.hide('data_car_detailed_'+ car_id);
       //Element.show('data_car_short_'+ car_id);
     }
}

//*************************************************************
// Funkcje ładujące hotele, miasta, regiony
//*************************************************************
// wczytuje kraje

function load_ctr(URL, loading , model) {
   var fieldname;
   var divname;
   if (!model) {
      fieldname = 'SearchHotelCountry';
      divname   = 'ctr';
   } else {
      fieldname = model+'Country';
      divname = model+'ctr';
   }

   if(document.getElementById(fieldname)) {
      var x = document.getElementById(fieldname).selectedIndex;
      if(x == -1) {x = 0;}
      var id = document.getElementById(fieldname).options[x].value;
   } else {
      var id = '-1';
   }

   document.getElementById(divname).innerHTML = loading;
   new Ajax.Request(URL + 'ctr_select/' + id + '/'+ model, {
     method: 'get',
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         ctr_response(transport.responseText, URL, loading, model);
       }
     }
   });
}

// funkcja odpowiedzi na zapytanie AJAX o kraje
function ctr_response(str, URL, loading, model) {
   var divname;

   if (!model) {
      divname = 'ctr';
   } else {
      divname = model+'ctr';
   }

   document.getElementById(divname).innerHTML = str;
   load_cty(URL, loading, model);
}

// wczytuje miasta z wybranego kraju
function load_cty(URL, loading, model, ctr_id, mini) {
   if (!mini) {
      mini = 0;
   }

   if (!model) {
      fieldname1 = 'SearchHotelCountry';
      fieldname2 = 'SearchHotelCity';
      divname   = 'cty';

      var x = document.getElementById(fieldname1).selectedIndex;
      if(x == -1) {x = 0;}
      var cid = document.getElementById(fieldname1).options[x].value;
   } else if(model == 'from_arg') {
      fieldname2 = 'SearchHotelCity';
      divname   = 'cty';
      var cid = ctr_id;
      model = null;
   } else {
      fieldname1 = model+'Country';
      fieldname2 = model+'City';
      divname = model+'cty';

      var x = document.getElementById(fieldname1).selectedIndex;
      if(x == -1) {x = 0;}
      var cid = document.getElementById(fieldname1).options[x].value;
   }

   if(document.getElementById(fieldname2)) {
      var x = document.getElementById(fieldname2).selectedIndex;
      if(x == -1) {x = 0;}
      var id = document.getElementById(fieldname2).options[x].value;
   } else {
      var id = -1;
   }

   document.getElementById(divname).innerHTML = loading;

   new Ajax.Request(URL +'cty_select/'+ cid +'/'+ id +'/'+  model, {
     method: 'get',
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         cty_response(transport.responseText, URL, loading, model, mini);
       }
     }
   });
}
// funkcja odpowiedzi na zapytanie AJAX o miasta
function cty_response(str, URL, loading, model, mini) {
   if (!model) {
      divname = 'cty';
   } else {
      divname = model+'cty';
   }

   document.getElementById(divname).innerHTML = str;
   load_loc(URL, loading, model, mini);
}

// wczytuje lokalizacje z wybranego miasta
function load_loc(URL, loading, model, mini, cty_id) {
   return 1;

   if (!mini) {
      mini = 0;
   }

   if(!cty_id) {
      if (!model) {
         fieldname2 = 'SearchHotelCity';
         divname   = 'loc';
      } else {
         fieldname2 = model+'City';
         divname = model+'loc';
      }

      var x = document.getElementById(fieldname2).selectedIndex;
      if(x == -1) {x = 0;}
      var cid = document.getElementById(fieldname2).options[x].value;
      document.getElementById(divname).innerHTML = loading;
   } else {
      cid = cty_id;
      divname   = 'loc';
   }

   new Ajax.Updater(divname, URL + 'loc_select/' + cid + '/' + model +'/'+ mini, {
     method: 'get'
   });
}

// wczytuje kraje, a później regiony
function load_ctr_with_rgn(URL, loading) {
   var id = document.getElementById('TouristPackageCountry').options[document.getElementById('TouristPackageCountry').selectedIndex].value;
   document.getElementById('ctr').innerHTML = loading;

   new Ajax.Request(URL + 'ctr_select_with_rgn/' + id, {
     method: 'get',
     //onLoading: function(transport) {},
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         ctr_response_with_rgn(transport.responseText, URL, loading);
       }
     }
   });
}

// funkcja odpowiedzi na zapytanie AJAX o kraje z regionami
function ctr_response_with_rgn(str, URL, loading) {
   document.getElementById('ctr').innerHTML = str;
   load_rgn(URL, loading);
}

// wczytuje regiony z wybranego państwa
function load_rgn(URL, loading) {
   var x = document.getElementById('TouristPackageCountry').selectedIndex;
   if(x == -1) {x = 0;}
   var ctr_id = document.getElementById('TouristPackageCountry').options[x].value;

   document.getElementById('rgn').innerHTML = loading;

    new Ajax.Updater('rgn', URL + 'rgn_select/' + ctr_id, {
     method: 'get'
     //onLoading: function(transport) {},
     //onComplete: function(transport) {},
   });
}

/* Obsługa iframe (dot. IE6) do ukrycia pól select */
function show_iframe(div) {
   obj = document.getElementById(div);
   var left = obj.offsetLeft;
   var top = obj.offsetTop;
   var width = obj.offsetWidth;
   var height = obj.offsetHeight;
   left = left - 3;
   width = width + 3;

   if(document.getElementById("iframe_"+div+"_content")) {
      document.getElementById("iframe_"+div+"_content").style.left = left;
      document.getElementById("iframe_"+div+"_content").style.top = top;
      document.getElementById("iframe_"+div+"_content").style.width = width;
      document.getElementById("iframe_"+div+"_content").style.height = height;
   } else {
      var iFrame =
         '<iframe id="iframe_'+ div +'_content" style="position:absolute; filter:alpha(opacity=0); left:' + left + 'px; top:' + top +'px; width:'+width+'px; height:'+height+'px; z-index:100;" src="'+ page_url +'/img/transparent_1x1.gif" frameBorder="0" scrolling="no"></iframe>';

      document.getElementById("iframe_"+div).innerHTML = iFrame;
   }
   Element.show("iframe_"+div);
}

function hide_iframe(div) {
   if(!document.getElementById("iframe_"+div+"_content")) {
      document.getElementById("iframe_"+div).innerHTML = '';
   }
   Element.hide("iframe_"+div);
}

// pobiera pozycję elementu naprowadzającego, i ustawia wskazany obiekt wzgledem niego
function set_abs_pos(source, target, left, top) {
   var page_width = document.getElementById('all').offsetLeft + document.getElementById('all').offsetWidth;
   var width;
   var min_width;

   if(typeof(source) == 'object') {
      var obj = source;
   } else {
      var obj = document.getElementById(source);
   }

   tempX = obj.offsetLeft;
   tempY = obj.offsetTop;

   while (obj = obj.offsetParent) {
   	tempX += obj.offsetLeft;
   	tempY += obj.offsetTop;
	}

   if(typeof(target) == 'object') {
      width = target.offsetWidth;
      min_width = target.style.minWidth;

      if(parseInt(min_width) > width) {
         width = parseInt(min_width);
      }

      left = tempX + left;

      if(page_width < left + width) {
         if(typeof(source) == 'object') {
            var obj = source;
         } else {
            var obj = document.getElementById(source);
         }
         input_width = obj.offsetWidth;
         left = left - width + input_width;
         if(left < 0) {
            left = 0;
         }
      }

      target.style.left = left +'px';
      target.style.top = tempY + top +'px';
   } else {
      width = document.getElementById(target).offsetWidth;
      min_width = document.getElementById(target).style.minWidth;
      
      if(parseInt(min_width) > width) {
         width = parseInt(min_width);
      }
      
      left = tempX + left;

      if(page_width < left + width) {
         if(typeof(source) == 'object') {
            var obj = source;
         } else {
            var obj = document.getElementById(source);
         }
         input_width = obj.offsetWidth;
         left = left - width + input_width;
         if(left < 0) {
            left = 0;
         }
      }

      document.getElementById(target).style.left = left +'px';
      document.getElementById(target).style.top = tempY + top +'px';
   }
}

// zmiana wartości pola hidden o nazwie last_search w multi_searches, potrzebnego do kopiowania danych między wyszukiwarkami
function change_value(val)
{
   document.getElementById('last_search').value = val;
}

// sprawdzenie z której wyszukiwarki nastąpiło przejście do obecnej
function check_value(val)
{
   var lastSearch = document.getElementById('last_search').value;
   var presentSearch = val;

   if (lastSearch != presentSearch)
   {
     copy_data_between_searches(lastSearch);
   }
}

/*
   Kopiowanie danych pomiędzy wyszukiwarkami:
   ------------------------------------------
   1 - Przelot (SearchFlight)
   2 - Hotel (SearchHotel)
   3 - Hotel + Przelot (Data_hpf)
   5 - Samochód (SearchCar)
   6 - Przelot + Samochód (Data_fpc)
   7 - Hotel + Przelot + Samochód (Data_hpfpc)
   8 - Hotel + Samochód (Data_hpc)
   9 - Zwiedzanie i atrakcje (SearchSightseeing)
   ------------------------------------------
*/
function copy_data_between_searches(lastSearch)
{
   var presentPrefix;

   if(lastSearch == 1) {
      presentPrefix = 'SearchFlight';
   } else if(lastSearch == 2) {
      presentPrefix = 'SearchHotel';
   } else if(lastSearch == 3) {
      presentPrefix = 'Data_hpf';
   } else if(lastSearch == 5) {
      presentPrefix = 'SearchCar';
   } else if(lastSearch == 6) {
      presentPrefix = 'Data_fpc';
   } else if(lastSearch == 7) {
      presentPrefix = 'Data_hpfpc';
   } else if(lastSearch == 8) {
      presentPrefix = 'Data_hpc';
   } else if(lastSearch == 9) {
      presentPrefix = 'SearchSightseeing';
   }

   var from_city; var from_country; var from_city_id; var from_country_id;
   var to_city; var to_country; var to_city_id; var to_country_id;
   var from_date; var to_date;
   var from_hour; var to_hour;
   var ad; var chd; var src;
   var chd_age = new Array();
   var flight_class;

   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyFrom[1]')) {
      from_city = document.getElementById(presentPrefix+'FlyFrom[1]').value;
   }
   if(document.getElementById(presentPrefix+'Pickup')) {
      from_city = document.getElementById(presentPrefix+'Pickup').value;
   }
   if(document.getElementById(presentPrefix+'FlyFrom[1]_hidden')) {
      from_city_id = document.getElementById(presentPrefix+'FlyFrom[1]_hidden').value;
   }
   if(document.getElementById(presentPrefix+'Pickup_hidden')) {
      from_city_id = document.getElementById(presentPrefix+'Pickup_hidden').value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyTo[1]')) {
      to_city = document.getElementById(presentPrefix+'FlyTo[1]').value;
   }
   if(document.getElementById(presentPrefix+'Dropoff')) {
      to_city = document.getElementById(presentPrefix+'Dropoff').value;
   }
   if(document.getElementById(presentPrefix+'City') && !document.getElementById(presentPrefix+'City').options) {
      to_city = document.getElementById(presentPrefix+'City').value;
   }
   if(document.getElementById(presentPrefix+'FlyTo[1]_hidden')) {
      to_city_id = document.getElementById(presentPrefix+'FlyTo[1]_hidden').value;
   }
   if(document.getElementById(presentPrefix+'City_hidden')) {
      to_city_id = document.getElementById(presentPrefix+'City_hidden').value;
   }
   if(document.getElementById(presentPrefix+'Dropoff_hidden')) {
      to_city_id = document.getElementById(presentPrefix+'Dropoff_hidden').value;
   }
   if(document.getElementById(presentPrefix+'FlyTo[1]_hidden_c_id')) {
      to_country_id = document.getElementById(presentPrefix+'FlyTo[1]_hidden_c_id').value;
   }
   if(document.getElementById(presentPrefix+'Country')) {
      var x = document.getElementById(presentPrefix+'Country').selectedIndex;
      if(x == -1) {x = 0;}
      to_country_id = document.getElementById(presentPrefix+'Country').options[x].value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyDate[1]')) {
      from_date = document.getElementById(presentPrefix+'FlyDate[1]').value;
   }
   if(document.getElementById(presentPrefix+'InDate')) {
      from_date = document.getElementById(presentPrefix+'InDate').value;
   }
   if(document.getElementById(presentPrefix+'PickupDate')) {
      from_date = document.getElementById(presentPrefix+'PickupDate').value;
   }
   if(document.getElementById(presentPrefix+'StartDate')) {
      from_date = document.getElementById(presentPrefix+'StartDate').value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'BackDate')) {
      to_date = document.getElementById(presentPrefix+'BackDate').value;
   }
   if(document.getElementById(presentPrefix+'OutDate')) {
      to_date = document.getElementById(presentPrefix+'OutDate').value;
   }
   if(document.getElementById(presentPrefix+'DropoffDate')) {
      to_date = document.getElementById(presentPrefix+'DropoffDate').value;
   }
   if(document.getElementById(presentPrefix+'EndDate')) {
      to_date = document.getElementById(presentPrefix+'EndDate').value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyPassAd')) {
      ad = document.getElementById(presentPrefix+'FlyPassAd').value;
   }
   if(document.getElementById(presentPrefix+'Ad')) {
      ad = document.getElementById(presentPrefix+'Ad').value;
   }
   if(document.getElementById(presentPrefix+'ParticipantAd')) {
      ad = document.getElementById(presentPrefix+'ParticipantAd').value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyPassCh')) {
      chd = document.getElementById(presentPrefix+'FlyPassCh').value;

      for (var i = 1; i <= chd; i++)
      {
         if(document.getElementById(presentPrefix+'ChAge'+i)) {
            chd_age[i] = document.getElementById(presentPrefix+'ChAge'+i).value;
         }
      }
   }
   if(document.getElementById(presentPrefix+'Chd')) {
      chd = document.getElementById(presentPrefix+'Chd').value;

      for (var i = 1; i <= chd; i++)
      {
         if(document.getElementById(presentPrefix+'ChAge'+i)) {
            chd_age[i] = document.getElementById(presentPrefix+'ChAge'+i).value;
         }
      }
   }
   if(document.getElementById(presentPrefix+'ParticipantCh')) {
      chd = document.getElementById(presentPrefix+'ParticipantCh').value;

      for (var i = 1; i <= chd; i++)
      {
         if(document.getElementById(presentPrefix+'ChAge'+i)) {
            chd_age[i] = document.getElementById(presentPrefix+'ChAge'+i).value;
         }
      }
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyPassEl')) {
      src = document.getElementById(presentPrefix+'FlyPassEl').value;
   }
   ////////////////////////////////
   if(document.getElementById(presentPrefix+'FlyYourPrefClass')) {
      flight_class = document.getElementById(presentPrefix+'FlyYourPrefClass').value;
   }
   ////////////////////////////////

   //alert(from_city);
   //alert(from_city_id);

   //alert(to_city);
   //alert(to_city_id);

   // USTAWIANIE PRZELOTÓW
   if(presentPrefix != 'SearchFlight' || 1) {
      if(document.getElementById('SearchFlightFlyFrom[1]') && from_city != null) {
         document.getElementById('SearchFlightFlyFrom[1]').value = from_city;
         if(document.getElementById('SearchFlightFlyFrom[1]_hidden')) {
            document.getElementById('SearchFlightFlyFrom[1]_hidden').value = from_city_id;
         }
         if(document.getElementById('SearchFlightFlyFrom[1]_hidden_c_id')) {
            document.getElementById('SearchFlightFlyFrom[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchFlightFlyFrom[1]_hidden_ch_id')) {
            document.getElementById('SearchFlightFlyFrom[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchFlightFlyFrom[1]_additional')) {
            document.getElementById('input_SearchFlightFlyFrom[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchFlightFlyTo[1]') && to_city != null) {
         document.getElementById('SearchFlightFlyTo[1]').value = to_city;
         if(document.getElementById('SearchFlightFlyTo[1]_hidden')) {
            document.getElementById('SearchFlightFlyTo[1]_hidden').value = to_city_id;
         }
         if(document.getElementById('SearchFlightFlyTo[1]_hidden_c_id')) {
            document.getElementById('SearchFlightFlyTo[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchFlightFlyTo[1]_hidden_ch_id')) {
            document.getElementById('SearchFlightFlyTo[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchFlightFlyTo[1]_additional')) {
            document.getElementById('input_SearchFlightFlyTo[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchFlightFlyDate[1]') && from_date != null) {
         document.getElementById('SearchFlightFlyDate[1]').value = from_date;
      }
      if(document.getElementById('SearchFlightBackDate') && to_date != null) {
         document.getElementById('SearchFlightBackDate').value = to_date;
      }
      if(document.getElementById('SearchFlightFlyPassAd') && ad != null) {
         document.getElementById('SearchFlightFlyPassAd').value = ad;
      }
      if(document.getElementById('SearchFlightFlyPassCh') && chd != null) {
         document.getElementById('SearchFlightFlyPassCh').value = chd;
      }
      for (var i = 1; i <= chd; i++) {
         if(document.getElementById('SearchFlightChAge'+i) && chd_age[i] != null) {
            document.getElementById('SearchFlightChAge'+i).value = chd_age[i];
         }
      }
      if(document.getElementById('SearchFlightFlyYourPrefClass') && flight_class != null) {
         document.getElementById('SearchFlightFlyYourPrefClass').value = flight_class;
      }
   }

   // USTAWIANIE HOTELI
   if(presentPrefix != 'SearchHotel' || 1) {
      if(document.getElementById('SearchHotelCountry') && to_country_id != null) {
         document.getElementById('SearchHotelCountry').value = to_country_id;
      }
      if(document.getElementById('SearchHotelCity') && to_city != null) {
         document.getElementById('SearchHotelCity').value = to_city;
         if(document.getElementById('SearchHotelCity_hidden')) {
            document.getElementById('SearchHotelCity_hidden').value = to_city_id;
         }
         if(document.getElementById('SearchHotelCity_hidden_c_id')) {
            document.getElementById('SearchHotelCity_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchHotelCity_hidden_ch_id')) {
            document.getElementById('SearchHotelCity_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchHotelCity_additional')) {
            document.getElementById('input_SearchHotelCity_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchHotelInDate') && from_date != null) {
         document.getElementById('SearchHotelInDate').value = from_date;
      }
      if(document.getElementById('SearchHotelOutDate') && to_date != null) {
         document.getElementById('SearchHotelOutDate').value = to_date;
      }
      if(document.getElementById('SearchHotelAd') && ad != null) {
         document.getElementById('SearchHotelAd').value = ad;
      }
      if(document.getElementById('SearchHotelCh') && chd != null) {
         document.getElementById('SearchHotelCh').value = chd;
      }
   }

   // USTAWIANIE PRZELOT + HOTEL
   if(presentPrefix != 'Data_hpf' || 1) {
      if(document.getElementById('Data_hpfFlyFrom[1]') && from_city != null) {
         document.getElementById('Data_hpfFlyFrom[1]').value = from_city;
         if(document.getElementById('Data_hpfFlyFrom[1]_hidden')) {
            document.getElementById('Data_hpfFlyFrom[1]_hidden').value = from_city_id;
         }
         if(document.getElementById('Data_hpfFlyFrom[1]_hidden_c_id')) {
            document.getElementById('Data_hpfFlyFrom[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_hpfFlyFrom[1]_hidden_ch_id')) {
            document.getElementById('Data_hpfFlyFrom[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_hpfFlyFrom[1]_additional')) {
            document.getElementById('input_Data_hpfFlyFrom[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_hpfFlyTo[1]') && to_city != null) {
         document.getElementById('Data_hpfFlyTo[1]').value = to_city;
         if(document.getElementById('Data_hpfFlyTo[1]_hidden')) {
            document.getElementById('Data_hpfFlyTo[1]_hidden').value = to_city_id;
         }
         if(document.getElementById('Data_hpfFlyTo[1]_hidden_c_id')) {
            document.getElementById('Data_hpfFlyTo[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_hpfFlyTo[1]_hidden_ch_id')) {
            document.getElementById('Data_hpfFlyTo[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_hpfFlyTo[1]_additional')) {
            document.getElementById('input_Data_hpfFlyTo[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_hpfFlyDate[1]') && from_date != null) {
         document.getElementById('Data_hpfFlyDate[1]').value = from_date;
      }
      if(document.getElementById('Data_hpfBackDate') && to_date != null) {
         document.getElementById('Data_hpfBackDate').value = to_date;
      }
      if(document.getElementById('Data_hpfFlyPassAd') && ad != null) {
         document.getElementById('Data_hpfFlyPassAd').value = ad;
      }
      if(document.getElementById('Data_hpfFlyPassCh') && chd != null) {
         document.getElementById('Data_hpfFlyPassCh').value = chd;
      }
      for (var i = 1; i <= chd; i++) {
         if(document.getElementById('Data_hpfChAge'+i) && chd_age[i] != null) {
            document.getElementById('Data_hpfChAge'+i).value = chd_age[i];
         }
      }
      if(document.getElementById('Data_hpfFlyYourPrefClass') && flight_class != null) {
         document.getElementById('Data_hpfFlyYourPrefClass').value = flight_class;
      }
   }

   // USTAWIANIE SAMOCHODÓW
   if(presentPrefix != 'SearchCar' || 1) {
      if(document.getElementById('SearchCarPickup') && from_city != null) {
         document.getElementById('SearchCarPickup').value = from_city;
         if(document.getElementById('SearchCarPickup_hidden')) {
            document.getElementById('SearchCarPickup_hidden').value = from_city_id;
         }
         if(document.getElementById('SearchCarPickup_hidden_c_id')) {
            document.getElementById('SearchCarPickup_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchCarPickup_hidden_ch_id')) {
            document.getElementById('SearchCarPickup_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchCarPickup_additional')) {
            document.getElementById('input_SearchCarPickup_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchCarDropoff') && to_city != null) {
         document.getElementById('SearchCarDropoff').value = to_city;
         if(document.getElementById('SearchCarDropoff_hidden')) {
            document.getElementById('SearchCarDropoff_hidden').value = to_city_id;
         }
         if(document.getElementById('SearchCarDropoff_hidden_c_id')) {
            document.getElementById('SearchCarDropoff_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchCarDropoff_hidden_ch_id')) {
            document.getElementById('SearchCarDropoff_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchCarDropoff_additional')) {
            document.getElementById('input_SearchCarDropoff_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchCarPickupDate') && from_date != null) {
         document.getElementById('SearchCarPickupDate').value = from_date;
      }
      if(document.getElementById('SearchCarDropoffDate') && to_date != null) {
         document.getElementById('SearchCarDropoffDate').value = to_date;
      }
   }

   // USTAWIANIE PRZELOT + SAMOCHÓD
   if(presentPrefix != 'Data_fpc' || 1) {
      if(document.getElementById('Data_fpcFlyFrom[1]') && from_city != null) {
         document.getElementById('Data_fpcFlyFrom[1]').value = from_city;
         if(document.getElementById('Data_fpcFlyFrom[1]_hidden')) {
            document.getElementById('Data_fpcFlyFrom[1]_hidden').value = from_city_id;
         }
         if(document.getElementById('Data_fpcFlyFrom[1]_hidden_c_id')) {
            document.getElementById('Data_fpcFlyFrom[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_fpcFlyFrom[1]_hidden_ch_id')) {
            document.getElementById('Data_fpcFlyFrom[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_fpcFlyFrom[1]_additional')) {
            document.getElementById('input_Data_fpcFlyFrom[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_fpcFlyTo[1]') && to_city != null) {
         document.getElementById('Data_fpcFlyTo[1]').value = to_city;
         if(document.getElementById('Data_fpcFlyTo[1]_hidden')) {
            document.getElementById('Data_fpcFlyTo[1]_hidden').value = to_city_id;
         }
         if(document.getElementById('Data_fpcFlyTo[1]_hidden_c_id')) {
            document.getElementById('Data_fpcFlyTo[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_fpcFlyTo[1]_hidden_ch_id')) {
            document.getElementById('Data_fpcFlyTo[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_fpcFlyTo[1]_additional')) {
            document.getElementById('input_Data_fpcFlyTo[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_fpcFlyDate[1]') && from_date != null) {
         document.getElementById('Data_fpcFlyDate[1]').value = from_date;
      }
      if(document.getElementById('Data_fpcBackDate') && to_date != null) {
         document.getElementById('Data_fpcBackDate').value = to_date;
      }
      if(document.getElementById('Data_fpcFlyPassAd') && ad != null) {
         document.getElementById('Data_fpcFlyPassAd').value = ad;
      }
      if(document.getElementById('Data_fpcFlyPassCh') && chd != null) {
         document.getElementById('Data_fpcFlyPassCh').value = chd;
      }
      for (var i = 1; i <= chd; i++) {
         if(document.getElementById('Data_fpcChAge'+i) && chd_age[i] != null) {
            document.getElementById('Data_fpcChAge'+i).value = chd_age[i];
         }
      }
      if(document.getElementById('Data_fpcFlyYourPrefClass') && flight_class != null) {
         document.getElementById('Data_fpcFlyYourPrefClass').value = flight_class;
      }
   }

   // USTAWIANIE PRZELOT + HOTEL + SAMOCHÓD
   if(presentPrefix != 'Data_hpfpc' || 1) {
      if(document.getElementById('Data_hpfpcFlyFrom[1]') && from_city != null) {
         document.getElementById('Data_hpfpcFlyFrom[1]').value = from_city;
         if(document.getElementById('Data_hpfpcFlyFrom[1]_hidden')) {
            document.getElementById('Data_hpfpcFlyFrom[1]_hidden').value = from_city_id;
         }
         if(document.getElementById('Data_hpfpcFlyFrom[1]_hidden_c_id')) {
            document.getElementById('Data_hpfpcFlyFrom[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_hpfpcFlyFrom[1]_hidden_ch_id')) {
            document.getElementById('Data_hpfpcFlyFrom[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_hpfpcFlyFrom[1]_additional')) {
            document.getElementById('input_Data_hpfpcFlyFrom[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_hpfpcFlyTo[1]') && to_city != null) {
         document.getElementById('Data_hpfpcFlyTo[1]').value = to_city;
         if(document.getElementById('Data_hpfpcFlyTo[1]_hidden')) {
            document.getElementById('Data_hpfpcFlyTo[1]_hidden').value = to_city_id;
         }
         if(document.getElementById('Data_hpfpcFlyTo[1]_hidden_c_id')) {
            document.getElementById('Data_hpfpcFlyTo[1]_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_hpfpcFlyTo[1]_hidden_ch_id')) {
            document.getElementById('Data_hpfpcFlyTo[1]_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_hpfpcFlyTo[1]_additional')) {
            document.getElementById('input_Data_hpfpcFlyTo[1]_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_hpfpcFlyDate[1]') && from_date != null) {
         document.getElementById('Data_hpfpcFlyDate[1]').value = from_date;
      }
      if(document.getElementById('Data_hpfpcBackDate') && to_date != null) {
         document.getElementById('Data_hpfpcBackDate').value = to_date;
      }
      if(document.getElementById('Data_hpfpcFlyPassAd') && ad != null) {
         document.getElementById('Data_hpfpcFlyPassAd').value = ad;
      }
      if(document.getElementById('Data_hpfpcFlyPassCh') && chd != null) {
         document.getElementById('Data_hpfpcFlyPassCh').value = chd;
      }
      for (var i = 1; i <= chd; i++) {
         if(document.getElementById('Data_hpfpcChAge'+i) && chd_age[i] != null) {
            document.getElementById('Data_hpfpcChAge'+i).value = chd_age[i];
         }
      }
      if(document.getElementById('Data_hpfpcFlyYourPrefClass') && flight_class != null) {
         document.getElementById('Data_hpfpcFlyYourPrefClass').value = flight_class;
      }
   }

   // USTAWIANIE HOTEL + SAMOCHÓD
   if(presentPrefix != 'Data_hpc' || 1) {
      if(document.getElementById('Data_hpcCountry') && to_country_id != null) {
         document.getElementById('Data_hpcCountry').value = to_country_id;
      }
      if(document.getElementById('Data_hpcCity') && to_city != null) {
         document.getElementById('Data_hpcCity').value = to_city;
         if(document.getElementById('Data_hpcCity_hidden')) {
            document.getElementById('Data_hpcCity_hidden').value = to_city_id;
         }
         if(document.getElementById('Data_hpcCity_hidden_c_id')) {
            document.getElementById('Data_hpcCity_hidden_c_id').value = '';
         }
         if(document.getElementById('Data_hpcCity_hidden_ch_id')) {
            document.getElementById('Data_hpcCity_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_Data_hpcCity_additional')) {
            document.getElementById('input_Data_hpcCity_additional').innerHTML = '';
         }
      }
      if(document.getElementById('Data_hpcInDate') && from_date != null) {
         document.getElementById('Data_hpcInDate').value = from_date;
      }
      if(document.getElementById('Data_hpcOutDate') && to_date != null) {
         document.getElementById('Data_hpcOutDate').value = to_date;
      }
      if(document.getElementById('Data_hpcAd') && ad != null) {
         document.getElementById('Data_hpcAd').value = ad;
      }
      if(document.getElementById('Data_hpcCh') && chd != null) {
         document.getElementById('Data_hpcCh').value = chd;
      }
   }

   // USTAWIANIE SIGHTSEEING
   if(presentPrefix != 'SearchSightseeing' || 1) {
      if(document.getElementById('SearchSightseeingCity') && to_city != null) {
         document.getElementById('SearchSightseeingCity').value = to_city;
         if(document.getElementById('SearchSightseeingCity_hidden')) {
            document.getElementById('SearchSightseeingCity_hidden').value = to_city_id;
         }
         if(document.getElementById('SearchSightseeingCity_hidden_c_id')) {
            document.getElementById('SearchSightseeingCity_hidden_c_id').value = '';
         }
         if(document.getElementById('SearchSightseeingCity_hidden_ch_id')) {
            document.getElementById('SearchSightseeingCity_hidden_ch_id').value = '';
         }
         if(document.getElementById('input_SearchSightseeingCity_additional')) {
            document.getElementById('input_SearchSightseeingCity_additional').innerHTML = '';
         }
      }
      if(document.getElementById('SearchSightseeingStartDate') && from_date != null) {
         document.getElementById('SearchSightseeingStartDate').value = from_date;
      }
      if(document.getElementById('SearchSightseeingEndDate') && to_date != null) {
         document.getElementById('SearchSightseeingEndDate').value = to_date;
      }
      if(document.getElementById('SearchSightseeingParticipantAd') && ad != null) {
         document.getElementById('SearchSightseeingParticipantAd').value = ad;
      }
      if(document.getElementById('SearchSightseeingParticipantCh') && chd != null) {
         document.getElementById('SearchSightseeingParticipantCh').value = chd;
      }
      for (var i = 1; i <= chd; i++) {
         if(document.getElementById('SearchSightseeingChAge'+i) && chd_age[i] != null) {
            document.getElementById('SearchSightseeingChAge'+i).value = chd_age[i];
         }
      }
   }
}

// sprawdzanie czy w poprzedniej wyszukiwarce zmieniono ilość dzieci i odświeżenie odpowiedniej ilości pól do uzupelnienia wieku dzieci
function check_change_of_ch_number(SearchValue)
{
   var prefix;

   if (SearchValue == '1')
   {
      prefix = 'SearchFlight';
   }
   else if (SearchValue == '3')
   {
      prefix = 'Data_hpf';
   }
   else if (SearchValue == '6')
   {
      prefix = 'Data_fpc';
   }
   else if (SearchValue == '7')
   {
      prefix = 'Data_hpfpc';
   }

   get_child_age_multi_searches(document.getElementById(prefix+'FlyPassCh').value);
}



function isIE() {
   return /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent);
}

function isFF() {
   return /firefox/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent);
}

function isOpera() {
   return /opera/i.test(navigator.userAgent);
}

function isSafari() {
   return /safari/i.test(navigator.userAgent);
}

/* overlay loading */
var dot_time = 0;
var timerID = 0;

function dot_timer() {
   if(timerID) {
      clearTimeout(timerID);
   }

   dot_time++;

   if(dot_time == 10) {
      dot_time = 0;
   }

   var dot = dot_time % 10;

   for(var k = 0; k < 10; k++) {
      if(k < dot - 2 || k > dot) {
         if(document.getElementById('i'+ k) && document.getElementById('a'+ k)) {
            document.getElementById('i'+ k).style.display = "inline";
            document.getElementById('a'+ k).style.display = "none";
         }
      } else {
         if(document.getElementById('i'+ k) && document.getElementById('a'+ k)) {
            document.getElementById('i'+ k).style.display = "none";
            document.getElementById('a'+ k).style.display = "inline";
         }
      }
   }

   timerID = setTimeout("dot_timer()", 150);
}

/**** AUTOCOMPLETE_SCRIPTS.JS */
var aDivMOver = 0;
var adiv_id = -1;
var aopen_div = false;
var asel_pos = -1;
var last_keypress = 0;

// nie pozwala submitować po wciścnięciu entera na inpucie
function checkCR(evt) {
   var now = new Date();
   last_keypress = now.getTime();

   var evt  = (evt) ? evt : ((event) ? event : null);
   var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
   if ((evt.keyCode == 13) && (aDivMOver != 0)) {
      return false;
   }
}
document.onkeypress = checkCR;

function prepare_autocomplete_query ( argString ) {
    var string = (argString+'').replace(/\r\n/g, "\n").replace(/\r/g, "\n");

    var utftext = "";
    var start, end;
    var stringl = 0;

    start = end = 0;
    stringl = string.length;
    for (var n = 0; n < stringl; n++) {
        var c1 = string.charCodeAt(n);
        var enc = null;

        if (c1 < 128) {
            end++;
        } else if((c1 > 127) && (c1 < 2048)) {
            enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
        } else {
            enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
        }
        if (enc !== null) {
            if (end > start) {
                utftext += string.substring(start, end);
            }
            utftext += enc;
            start = end = n+1;
        }
    }

    if (end > start) {
        utftext += string.substring(start, string.length);
    }

    return Base64.encode(utftext);
}

///////////////////////////////////////////////////////////
function createAutocomplete (e, div_id, func, loading, search_kind, next_chance) {
   var keycode;
   if (window.event) {
      keycode = window.event.keyCode;
   } else if (e) {
      keycode = e.which;
   }

   if(aDivMOver != 0 && (keycode == 13 || keycode == 38 || keycode == 40) ) {
      onAutocompleteKeyPress(e, div_id);
      return false;
   } else if(keycode != 13) {
      document.getElementById(div_id +'_hidden').value = '';
      document.getElementById('input_'+ div_id +'_additional').innerHTML = '';

      var text = document.getElementById(div_id).value;

      if(text.length >= 3) {
         var now = new Date();
         now = now.getTime();

         if(last_keypress == 0 || now - last_keypress <= 295) {
            if(next_chance != 1) {
               setTimeout("createAutocomplete(this.event,'"+div_id+"','"+func+"','"+loading+"','"+search_kind+"',1)", 300);
            }
            return false;
         }

         //hideAutocomplete();
         //showAutocomplete(div_id);
         //document.getElementById('autocomplete_'+ div_id).innerHTML = loading;

         /*
         var length = 0;
         for(var k = 0; k < document.getElementById("autocomplete_"+div_id).getElementsByTagName('span').length; k++) {
            var temp_length = document.getElementById("autocomplete_"+div_id).getElementsByTagName('span')[k].innerHTML.length;
            if(temp_length > length) {
               length = temp_length;
            }
      	}

         length = (length * 6);
  	      document.getElementById('autocomplete_'+ div_id).style.width = length +'px';
  	      //alert(document.getElementById('autocomplete_'+ div_id).style.width);
         */

         show_iframe("autocomplete_"+div_id);

         if (isIE()) {
            set_abs_pos(div_id, "iframe_autocomplete_"+div_id, -2, -20);
         } else{
            set_abs_pos(div_id, "iframe_autocomplete_"+div_id, -2, -18);
         }

         var date = new Date();
         var timestamp = date.getTime();
         
         text = prepare_autocomplete_query( text );
         new Ajax.Request(func +'/'+ text +'/0/'+ search_kind +'/'+ timestamp, {
           method: 'get',
           //onLoading: function(transport) {},
           onComplete: function(transport) {
             if (transport.readyState == 4) {
               if(parseInt(transport.responseText) != 0) {
                  hideAutocomplete();

                  document.getElementById('autocomplete_'+ div_id).style.width = 'auto';
                  document.getElementById('autocomplete_'+ div_id).innerHTML = transport.responseText;

                  showAutocomplete(div_id);
         			    show_iframe("autocomplete_"+div_id);

         			    width = document.getElementById('autocomplete_'+ div_id).offsetWidth;
                  document.getElementById('autocomplete_'+ div_id).style.width = width +'px';
         			    if (isIE()) {
                     set_abs_pos(div_id, "iframe_autocomplete_"+div_id, -2, -20);
                  } else{
                     set_abs_pos(div_id, "iframe_autocomplete_"+div_id, -2, -18);
                  }

               } else {
                  hideAutocomplete();
               }
            }
           }
         });
      } else {
         hideAutocomplete();
      }
      return true;
   }
}

function set_ss_cat_types(id) {
   alert("yo");
   alert(id);
}

function autocompleteSelect() {
   if(asel_pos == -1) {
      asel_pos = 0;
   }

   if(document.getElementById("autocomplete_"+adiv_id)) {
      var text = document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].getAttribute('input_value');
      var hidden_value = document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].getAttribute('hidden_value');
      var country_value = document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].getAttribute('country_value');
      var city_hotel_value = document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].getAttribute('city_hotel_value');
      //var additional_value = document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].getAttribute('additional_value');


   	for(var k = 0; k < document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div').length; k++) {
         if(k == asel_pos) {
   			document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[k].className = 'sel';
   		} else {
   			document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[k].className = '';
   		}
   	}

   	document.getElementById(adiv_id).value = text;
   	document.getElementById(adiv_id +'_hidden').value = hidden_value;
   	document.getElementById(adiv_id +'_hidden_c_id').value = country_value;
   	document.getElementById(adiv_id +'_hidden_ch_id').value = city_hotel_value;

      /*
   	if(additional_value.length != 0) {
         document.getElementById('input_'+ adiv_id +'_additional').style.display = 'inline';
   	   document.getElementById('input_'+ adiv_id +'_additional').innerHTML = additional_value;
      } else {
         document.getElementById('input_'+ adiv_id +'_additional').style.display = 'none'
      }
      */
   }
}
// pokazuje DIV z możliwymi wyborami
function showAutocomplete(div) {
/*   if(adiv_id != div) {
      if(document.getElementById("autocomplete_"+adiv_id)) {
         hideAutocomplete();
      }

      elem = document.getElementById("autocomplete_"+div);
      elem.className = "optionsDivVisible";

      if (isIE()) {
         set_abs_pos(div, "autocomplete_"+div, -2, -20);
      } else{
         set_abs_pos(div, "autocomplete_"+div, -2, -18);
      }

      document.getElementById("autocomplete_"+div).style.minWidth = parseInt(document.getElementById(div).style.width) - 2 +'px';

      asel_pos = -1;

      aDivMOver = 1;
      aopen_div = true;
      adiv_id = div;
   } */

   if(adiv_id != div) {
      if(document.getElementById("autocomplete_"+adiv_id)) {
         hideAutocomplete();
      }

      elem = document.getElementById("autocomplete_"+div);
      elem.className = "optionsDivVisible";

      asel_pos = -1;

      aDivMOver = 1;
      aopen_div = true;
      adiv_id = div;

      document.getElementById("autocomplete_"+div).style.minWidth = parseInt(document.getElementById(div).style.width) - 0 +'px';

      //ukrywa div, jeśli kliknięcie poza nim
      document.getElementsByTagName('BODY')[0].onclick = function() {
         //alert(aDivMOver);
         //alert(aopen_div);

         if (aDivMOver == 2 && aopen_div == true) {
            hideAutocomplete();
         }
         if (aDivMOver == 1 && aopen_div == true) {
            aDivMOver = 2;
         }
      }
      
      if (isIE()) {
         set_abs_pos(div, "autocomplete_"+div, 0, 16);
      } else{
         set_abs_pos(div, "autocomplete_"+div, 0, 16);
      }
   }
}

// ukrywa DIV z możliwymi wyborami
function hideAutocomplete() {
	if(document.getElementById("autocomplete_"+ adiv_id) && aDivMOver != 0) {
      document.getElementById("autocomplete_"+ adiv_id).className = "optionsDivInvisible";

      hide_iframe("autocomplete_"+ adiv_id);

      aopen_div = false;
   	aDivMOver = 0;
   	adiv_id = -1;
   	search = false;

   	document.getElementsByTagName('BODY')[0].onclick = function() {}
   }
}

var select_text = '';
var timer_sek;
function onAutocompleteKeyPress(e, selectFieldId) {
   var keycode;
   if (window.event) {
      keycode = window.event.keyCode;
   } else if (e) {
      keycode = e.which;
   }

   //alert(keycode);

   if(keycode == 13) {
      if(asel_pos == -1) {
         asel_pos = 0;
      }

      if(document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div')[asel_pos]) {
         document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div')[asel_pos].focus()
         if(!document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div')[asel_pos].click) {
            document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div')[asel_pos].onClick();
         } else {
            document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div')[asel_pos].click();
         }
      }
   } else if(keycode == 38) {
      try {
         document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].className = '';
      } catch (e) {}

      asel_pos--;
      if(asel_pos < 0) {
         asel_pos = document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div').length - 1;
      }

      if(asel_pos != document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div').length - 1) {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) - 100;
      } else {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) + 13;
      }
      if(asel_pos != 0) {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) - 100;
      } else {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = -13;
      }

      document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].className = 'sel';
      //autocompleteSelect();
   } else if(keycode == 40) {
      try {
         document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].className = '';
      } catch (e) {}

      asel_pos++;
      if(asel_pos > document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div').length - 1) {
         asel_pos = 0;
      }

      if(asel_pos != document.getElementById("autocomplete_"+selectFieldId).getElementsByTagName('div').length - 1) {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) - 100;
      } else {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) + 13;
      }
      if(asel_pos != 0) {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = (asel_pos * 13) - 100;
      } else {
         document.getElementById("autocomplete_"+selectFieldId).scrollTop = -13;
      }

      document.getElementById("autocomplete_"+adiv_id).getElementsByTagName('div')[asel_pos].className = 'sel';
      //autocompleteSelect();
   }
}

/**** MESSAGE_SCRIPTS.JS */
//*************************************************************
// funkcje odpowiedzialne za wyświetlenie informacji
//*************************************************************
Object.extend(Element, {
	getWidth: function(element) {
	   	element = $(element);
	   	return element.offsetWidth;
	},
	setWidth: function(element,w) {
	   	element = $(element);
    	element.style.width = w +"px";
	},
	setHeight: function(element,h) {
   		element = $(element);
    	element.style.height = h +"px";
	},
	setTop: function(element,t) {
	   	element = $(element);
    	element.style.top = t +"px";
	},
	setLeft: function(element,l) {
	   	element = $(element);
    	element.style.left = l +"px";
	},
	setSrc: function(element,src) {
    	element = $(element);
    	element.src = src;
	},
	setHref: function(element,href) {
    	element = $(element);
    	element.href = href;
	},
	setInnerHTML: function(element,content) {
		element = $(element);
		element.innerHTML = content;
	}
});


/////////////////////
// type = 1 - Informacje potwierdzające wykonanie akcji użytkownika (bez okienka wyskakującego)
// type = 2 - Błędy, informacje o braku dostępności itp. (okienko wyskakujące)
// type = 3 - Błędy krytyczne, okna dialogowe
// type = 4 - Błędy w formularzach
// type = 5 - Informacje ostrzegające - warning (bez okienka wyskakującego)
////////////////////
function show_message (type, text)
{
   if(type == 1) {
      show_message_bar(text, 'confirmation');
   } else if(type == 2) {
      if(isIE() && document.location.protocol == "https:") {
         show_message_bar(text, 'warning');
      } else {
         show_error_window(text);
      }
   } else if(type == 3) {
      show_dialog_window(text);
   } else if(type == 4) {
      show_form_error(text);
   } else if(type == 5) {
      show_message_bar(text, 'warning');
   }

   return true;
}

function show_message_bar (text, class_name)
{
   document.getElementById('message_bar').className = class_name;

   text = text.split('||');
   if(text[1]) {
      text = '<span class="title">'+ text[0] +'</span><br/>'+ text[1];
   } else {
      text = text[0];
   }
   document.getElementById('content_mb').innerHTML = text;
   Element.show('message_bar');
   Effect.ScrollTo('message_bar', {duration: 0.1, offset: -30});
}

function show_error_window (text)
{
   show_window (1, text, 2);
}

function show_dialog_window (view_url)
{
   new Ajax.Request(view_url, {
     method: 'get',
     onLoading: function(transport) {show_loading();},
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         hide_loading();
         show_window (2, transport.responseText, 3);

         // ODPALANIE SKRYPTÓW KTÓRE PRZYSZŁY W ODPOWIEDZI
         var temp_script_start = transport.responseText.split("<sc" + "ript language=\"JavaScript\">");
         for(var i = 0; i < temp_script_start.length; i++) {

            if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
               continue;
            }

            var temp_script_end = temp_script_start[i].split("</sc" + "ript>");

            if(temp_script_end[0].indexOf("<script") != -1) {
               continue;
            }

            var temp_script = temp_script_end[0];

            if (window.execScript) {
               window.execScript(temp_script);
            } else {
               window.eval(temp_script);
            }
         }

         var temp_script_start = transport.responseText.split("<sc" + "ript type=\"text/javascript\">");
         for(var i = 0; i < temp_script_start.length; i++) {

            if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
               continue;
            }

            var temp_script_end = temp_script_start[i].split("</sc" + "ript>");

            if(temp_script_end[0].indexOf("<script") != -1) {
               continue;
            }

            var temp_script = temp_script_end[0];

            if (window.execScript) {
               window.execScript(temp_script);
            } else {
               window.eval(temp_script);
            }
         }
         ////////////////////////////////////////////////////////////////
       }
     }
   });
}

function show_form_error (text)
{
   document.getElementById('content_fe').innerHTML = text;
   Element.show('form_errors');
}

function set_window_pos() {
   var arrayPageSize = getPageSize();

   if (window.innerHeight && window.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.body.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.documentElement.scrollTop) {
      theTop = document.documentElement.scrollTop;
   } else {
		theTop = document.body.scrollTop;
	}

   Element.setTop('message_window', theTop + (arrayPageSize[3] / 3) - 100);
	Element.setLeft('message_window', (arrayPageSize[0] / 2) - 250);
}

var window_visible = false;
var loading_visible = false;
var overlay_visible = false;

/////////////////////
// close = 1 - pokaż przycisk zamknij
// close = 2 - ukryj przycisk zamknij
//
// type = 1 - powiadomienie,
// type = 2 - error,
// type = 3 - zapytanie,
//
////////////////////
function show_window (close, text, type)
{

   if(type == 1) {
      document.getElementById('message_window').className = 'confirmation';
      Element.show('ok_mw');
   } else if(type == 2) {
      document.getElementById('message_window').className = 'error';
      Element.show('ok_mw');
   } else if(type == 3) {
      document.getElementById('message_window').className = 'query';
      Element.hide('ok_mw');
   }

   text = text.split('||');

   if(text[1]) {
      document.getElementById('top_mw').innerHTML = text[0];
      document.getElementById('content_mw').innerHTML = text[1];
   } else {
      document.getElementById('content_mw').innerHTML = text[0];
   }

   set_window_pos();

   if(close == 2) {
      window_visible = true;
      overlay_visible = true;

      window.onscroll = set_window_pos;

      document.getElementById('overlay').attributes["onclick"].value = '';

      Element.hide('ok_mw');

      var arrayPageSize = getPageSize();

      Element.setHeight('overlay', arrayPageSize[1]);
      Element.setWidth('overlay', arrayPageSize[0]);

      Element.show('overlay');
      show_iframe('overlay');

      Element.show('message_window');
      show_iframe('message_window');
   } else {
      window_visible = true;
      overlay_visible = false;

      Element.show('ok_mw');

      Element.show('message_window');
      show_iframe('message_window');

   }

    var theHandle = document.getElementById("icon_mw");
    var theRoot = document.getElementById("message_window");
    Drag.init(theHandle, theRoot);

    var theHandle = document.getElementById("top_mw");
    var theRoot = document.getElementById("message_window");
    Drag.init(theHandle, theRoot);
}

function hide_window()
{
    if(window_visible) {
       window_visible = false;
       new Effect.Fade('message_window', { duration: 0.3});
       hide_iframe('message_window');
    }

    if(loading_visible) {
       loading_visible = false;
       new Effect.Fade('loading', { duration: 0.3});
    }

    if(overlay_visible) {
      overlay_visible = false;
      Element.hide('overlay');
      hide_iframe('overlay');
    }

    window.onscroll = '';
}

function hide_loading(x)
{
    if(x == 1) {
       if(document.getElementById('overlay_loading')) {
         Element.hide('overlay_loading');
         hide_iframe('overlay_loading');
       }
       if(document.getElementById('overlay')) {
         Element.hide('overlay');
         hide_iframe('overlay');
       }
       window.onscroll = '';
   } else {
      start_system_timer('hide_loading(1)', 2);
   }
}


function show_loading ()
{
   Element.hide('overlay_loading');
   document.getElementById('overlay_loading').attributes["onclick"].value = '';

   var arrayPageSize = getPageSize();

   Element.setHeight('overlay', arrayPageSize[1]);
   Element.setWidth('overlay', arrayPageSize[0]);

   Element.show('overlay');
   show_iframe('overlay');

   set_loading_pos();

   Element.show('overlay_loading');
   //show_iframe('overlay_loading');

   window.onscroll = set_loading_pos;
}

function set_loading_pos() {
   var arrayPageSize = getPageSize();

   if (window.innerHeight && window.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.body.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.documentElement.scrollTop) {
      theTop = document.documentElement.scrollTop;
   } else {
		theTop = document.body.scrollTop;
	}

   Element.setTop('searching_info', theTop + (arrayPageSize[3] / 2) - 100);
	Element.setLeft('searching_info', (arrayPageSize[2] / 2) - 250);
}

function set_pass_edit_window_pos() {
   var arrayPageSize = getPageSize();

   if (window.innerHeight && window.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.body.scrollTop) {
		theTop = document.body.scrollTop;
	} else if (document.documentElement.scrollTop) {
      theTop = document.documentElement.scrollTop;
   } else {
		theTop = document.body.scrollTop;
	}

   var new_top = theTop + (arrayPageSize[3] / 3);
   
   if(new_top < 400) {
      new_top = 400;
   }
   
   Element.setTop('edit_pass_window', new_top);
	Element.setLeft('edit_pass_window', (arrayPageSize[0] / 2) - 115);
}

function show_pass_edit_window () {

   set_pass_edit_window_pos();

   document.getElementById('pass_overlay').attributes["onclick"].value = '';

   //alert(document.getElementById('pass_form_div').offsetHeight);
   //alert(document.getElementById('pass_form_div').offsetWidth);

   Element.setHeight('pass_overlay', document.getElementById('pass_form_div').offsetHeight);
   Element.setWidth('pass_overlay', document.getElementById('pass_form_div').offsetWidth);
   Element.setTop('pass_overlay', document.getElementById('pass_form_div').offsetTop);
	Element.setLeft('pass_overlay', document.getElementById('pass_form_div').offsetLeft);

   Element.show('pass_overlay');
   show_iframe('pass_overlay');

   //Element.show('edit_pass_window');
   Effect.Appear('edit_pass_window', { duration: .5, afterFinish: function() {
         if(!isIE()) {
            document.getElementById('edit_title').focus();
         }
         show_iframe('edit_pass_window');
      }
   });

    //var theHandle = document.getElementById("icon_mw");
    //var theRoot = document.getElementById("message_window");
    //Drag.init(theHandle, theRoot);

    //var theHandle = document.getElementById("top_mw");
    //var theRoot = document.getElementById("message_window");
    //Drag.init(theHandle, theRoot);
}

function hide_pass_edit_window () {
    new Effect.Fade('edit_pass_window', { duration: 0.3});
    hide_iframe('edit_pass_window');

   overlay_visible = false;
   Element.hide('pass_overlay');
   hide_iframe('pass_overlay');
}

/*** CALENDAR.JS */
/**
* @brief Calendar
*
* @author Michał Buk,  mich.buk@gmail.com
* @copyright Copyright (c); 2007, Blue Sky Travel, Poznań, Poland
* @package BakeSale
* @version $Id: calendar.js 001 2007-05-17 09:39 mich.buk $
*
**/

var pole;
var warstwa;
var year_scroll;
var time_comp;
var from;
var to_date;
var cDivMOver = 0;

// nazwy miesięcy, które pokażą się w kalendarzu
var ARR_MONTHS = ["Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec",
		"Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"];
// nazwy dni tygodnia, które pokażą się w kalendarzu
var ARR_WEEKDAYS = ["N", "P", "W", "Ś", "C", "P", "S"];
// tydzień zaczyna się od: (0-Nd, 1-Pn, 2-Wt ...)
var NUM_WEEKSTART = 1;
// jeśli podano dwucyfrowy rok i jeśli jest on większy lub równy tej liczbie,
// to rok ustawiony zostanie na 19xx, a jeśli jest mniejszy na 20xx
var NUM_CENTYEAR = 50;

var RE_NUM = /^\-?\d+$/;

var dt_current;
var dt_ogr;
var dt_ogr_to;
var dt_firstday;
var dt_act_month;
var dt_next_month;
var dt_prev_month;
var today;
var org_date;
var org_birth_date;

function start_cal (div,input,from_ogr,year,time,to_ogr,birth_date) {
   pole = input;
   org_date = from_ogr;
   
   org_birth_date = birth_date;

   if(document.getElementById(warstwa)) {
      Element.hide(warstwa);
   }

   warstwa = div;
   
   // from_ogr parsuję date oryginalną z która zaczeliśmy

   from = prs_tsmp(from_ogr);
   dt_ogr = from;
   
   if(to_ogr != 0) {
      to_date = prs_tsmp(to_ogr);
   } else {
      to_date = prs_tsmp('6000-12-30');
   }
   dt_ogr_to = to_date;

   year_scroll = year;
   time_comp = time;

   // sprawdzamy czy w polu jest wpisana jakakolwiek data. Jeśli nie bierz oryginalna. Jeśli jest sprawdz
   // podaną przez użytkownika
   if(pole.value.length != 0) {
      dt_current = prs_tsmp(pole.value);
   } else {
      dt_current = from;
   }

   // wyznacza dzień dzisiejszy
   today = new Date();

   //aktualny miesiąc
   dt_act_month = new Date(dt_current);

   // miesiąc do tyłu
   dt_prev_month = new Date(dt_act_month);
   dt_prev_month.setMonth(dt_prev_month.getMonth() - 1);
   if (dt_prev_month.getDate() != dt_act_month.getDate())
   	dt_prev_month.setDate(0);

   // miesiąc do przodu
   dt_next_month = new Date(dt_act_month);
   dt_next_month.setMonth(dt_next_month.getMonth() + 1);
   if (dt_next_month.getDate() != dt_act_month.getDate())
   	dt_next_month.setDate(0);

   // wyznacza, który dzień jest 1 w aktualnym miesiącu
   dt_firstday = new Date(dt_act_month);
   dt_firstday.setDate(1);
   dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - NUM_WEEKSTART) % 7);

   if(org_birth_date != 1) {
      print_cal();
   } else {
      print_year();
   }
   cDivMOver = 1;
   Element.show(warstwa);

   if (isIE()) {
      set_abs_pos(pole, warstwa, 0, 16);
   } else{
      set_abs_pos(pole, warstwa, 0, 16);
   }

   // Wyświetlenie iframe o wymiarach względem przykrywającego go diva
   show_iframe(warstwa);

   if (isIE()) {
      set_abs_pos(pole, "iframe_"+warstwa, 0, 16);
   } else{
      set_abs_pos(pole, "iframe_"+warstwa, 0, 16);
   }
}

// zamyka kalendarz
function close_cal() {
  if(cDivMOver != 0) {
     hide_iframe(warstwa);
     Element.hide(warstwa);
  }
}

// wyświetla kalendarz
function print_cal() {

   var page =
   '<div class="clear"></div><div style="float:left; height:20px;width:248px;border-top:5px solid #C9DBED;z-index:990;"></div>'+
   '<div style="float:right;margin-top:0px; height:158px; width:5px; background-color:#C9DBED;"></div>'+
      '<div style="margin-top:0px; margin-right:-5px; float:right; valign:top; z-index:999;">'+
      '<div id="cal_close" class="hand sprite_bg_icon sprite_zamknij" onClick="cDivMOver = 2; close_cal();"></div>'+
      '</div>';

   page += '<div style="float:left;width:150px;margin-left:2px; height:133px; z-index:990; border-right:1px solid #DDE7F4;">'+

   '<div style="border-bottom:1px solid #DDE7F4;width:150px;" >';

   page += '<div style="float:left;width:18px; height:18px; z-index:990;">';

   if(dt_prev_month.getYear() < dt_ogr.getYear() || (dt_prev_month.getYear() == dt_ogr.getYear() && dt_prev_month.getMonth() < dt_ogr.getMonth())) {
      page += '';
   } else {
      page += '<div id="cal_prev" class="hand sprite_bg_icon sprite_lewy_btn" onClick="javascript:set_datetime(1,0);"></div>';
   }
   page += '</div>';


   page += '<div style="font-weight:bold; text-align:center; height:18px; color:#7CA8D4;">'+ARR_MONTHS[dt_act_month.getMonth()]+' '+dt_act_month.getFullYear() + '</div>'+
   '</div>'+
   '<div>';
   // wyświetla nazwy dni tygodnia
   for (var n=0; n<7; n++) {
      if((NUM_WEEKSTART+n)%7 == 0) {
         page += '<div style="float:left; text-align:center; width:21px; height:16px; font-size:9px; text-decoration:none;"><font color="#85AED7"><b>'+ARR_WEEKDAYS[(NUM_WEEKSTART+n)%7]+'</b></font></div>';
      } else {
         page += '<div style="float:left; text-align:center; width:21px; height:16px; font-size:9px; text-decoration:none;"><font color="#85AED7"><b>'+ARR_WEEKDAYS[(NUM_WEEKSTART+n)%7]+'</b></font></div>';
      }
   }
   page += '<div class="clear"></div></div>';
   // wyświetla kalendarz
   var dt_current_day = new Date(dt_firstday);

   while (dt_current_day.getMonth() == dt_act_month.getMonth() || dt_current_day.getMonth() == dt_firstday.getMonth()) {
   	// początek row
   	page += '<div>';
   	for (var n_current_wday=0; n_current_wday<7; n_current_wday++) {
         bgcolor = '#FFFFFF';
         fontcolor = '#000000';
         fontweight = 'bold';
         bgimg = '';

         if (dt_current_day.valueOf() == dt_current.valueOf()) {
            // jeśli wybrana data to tło szare
            bgimg = 'background-color:#DDE7F4';
            fontcolor = '#000000';
         } else if (dt_current_day.getDay() == 0) {
            // jeśli weekend to tło różowe
            bgcolor = '#ffffff';
         }


         if (dt_current_day.getDate() == dt_ogr.getDate() && dt_current_day.getMonth() == dt_ogr.getMonth() && dt_current_day.getYear() == dt_ogr.getYear()) {
            // jeśli aktualna data to czcionka czerwona
            fontcolor = '#000000';
         }
         if (dt_current_day.getMonth() == dt_act_month.getMonth()) {

            if(dt_current_day.valueOf() >= dt_ogr.valueOf() && dt_current_day.valueOf() < dt_ogr_to.valueOf()) {
               // jeśli dni większe lub równe postawionemu warunkowi to czcionka czarna
               if (dt_current_day.getMonth() != dt_act_month.getMonth()) {
                  // jeśli dni nie tego miesiąca to czcionka szara
                  fontcolor = '#8D8D8D';
               }
               page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight + bgimg +';">';
               page += '<a style="font-size:10px; text-decoration:none; color:'+ fontcolor +';" href="javascript:set_datetime(3,'+ dt_current_day.valueOf() +');">';
               page += dt_current_day.getDate()+'</a></div>';
            } else {
               // jeśli dni mniejsze od postawionego warunku to czcionka szara i bez linku
               fontcolor = '#8D8D8D';
               page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight +';">';
               page += dt_current_day.getDate() +'</div>';
               //page += dt_ogr.getDate() +'</div>';
            }
         } else {
            page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight +';">&nbsp;</div>';
         }
   		dt_current_day.setDate(dt_current_day.getDate()+1);
   	}
   	// koniec row
   	page += '<div class="clear"></div></div>';
   }
   page += '</div>'+

   '<div style="float:left;width:150px; margin-top:0px;margin-left:0px; height:133px; z-index:990;">';

   // prawy kalendarz

   page += '<div style="width:150px height:18px; border-bottom:1px solid #DDE7F4; z-index:990;">';

      page += '<div style="float:right;width:18px; height:18px; z-index:990;">';

      if(dt_next_month.getYear() > dt_ogr_to.getYear() || (dt_next_month.getYear() == dt_ogr_to.getYear() && dt_next_month.getMonth() >= dt_ogr_to.getMonth())) {
         page += '';
      } else {
         page += '<div id="cal_next" class="hand sprite_bg_icon sprite_prawy_btn" onClick="javascript:set_datetime(2,0);"></div>';
      }
      page += '</div>';

      page += '<div style="text-align:center; color:#7CA8D4; font-weight:bold;">'+ARR_MONTHS[dt_next_month.getMonth()]+' '+dt_next_month.getFullYear() + '</div>'+
      '<div class="clear"></div>'+

   '</div>'+

   '<div>';
   // wyświetla nazwy dni tygodnia
   for (var n=0; n<7; n++) {
   	if((NUM_WEEKSTART+n)%7 == 0) {
         page += '<div style="float:left; text-align:center; width:21px; height:16px; font-size:9px; text-decoration:none;"><font color="#7CA8D4"><b>'+ARR_WEEKDAYS[(NUM_WEEKSTART+n)%7]+'</b></font></div>';
      } else {
         page += '<div style="float:left; text-align:center; width:21px; height:16px; font-size:9px; text-decoration:none;"><font color="#7CA8D4"><b>'+ARR_WEEKDAYS[(NUM_WEEKSTART+n)%7]+'</b></font></div>';
      }
   }
   page += '<div class="clear"></div></div>';
   // wyświetla kalendarz

   dt_next_month.setDate(1);
   dt_next_month.setMonth(dt_next_month.getMonth() + 1);
   dt_prev_month.setDate(1);
   dt_prev_month.setMonth(dt_prev_month.getMonth() + 1);
   dt_act_month.setDate(1);
   dt_act_month.setMonth(dt_act_month.getMonth() + 1);

   // wyznacza, który dzień jest 1 w aktualnym miesiącu
   dt_firstday = new Date(dt_act_month);
   dt_firstday.setDate(1);
   dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - NUM_WEEKSTART) % 7);

   var dt_current_day = new Date(dt_firstday);

   while (dt_current_day.getMonth() == dt_act_month.getMonth() || dt_current_day.getMonth() == dt_firstday.getMonth()) {
   	// początek row
   	page += '<div>';
   	for (var n_current_wday=0; n_current_wday<7; n_current_wday++) {
         bgcolor = '#FFFFFF';
         fontcolor = '#000000';
         fontweight = '';
         bgimg = '';

         if (dt_current_day.valueOf() == dt_current.valueOf()) {
            // jeśli wybrana data to tło szare
            bgimg = 'background-color:#F7AB21';
            fontcolor = '#000000';
         } else if (dt_current_day.getDay() == 0) {
            // jeśli weekend to tło różowe
            bgcolor = '#ffffff';
         }


         if (dt_current_day.getDate() == dt_ogr.getDate() && dt_current_day.getMonth() == dt_ogr.getMonth() && dt_current_day.getYear() == dt_ogr.getYear()) {
            // jeśli aktualna data to czcionka czerwona
            fontcolor = '#000000';
         }
         if (dt_current_day.getMonth() == dt_act_month.getMonth()) {

            if(dt_current_day.valueOf() >= dt_ogr.valueOf() && dt_current_day.valueOf() < dt_ogr_to.valueOf()) {
               // jeśli dni większe lub równe postawionemu warunkowi to czcionka czarna
               if (dt_current_day.getMonth() != dt_act_month.getMonth()) {
                  // jeśli dni nie tego miesiąca to czcionka szara
                  fontcolor = '#8D8D8D';
               }
               page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight + bgimg +';">';
               page += '<a style="font-size:10px; text-decoration:none; color:'+ fontcolor +';" href="javascript:set_datetime(3,'+ dt_current_day.valueOf() +');">';
               page += dt_current_day.getDate()+'</a></div>';
            } else {
               // jeśli dni większe od postawionego warunku to czcionka szara i bez linku
               fontcolor = '#8D8D8D';
               page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight +';">';
               page += dt_current_day.getDate() +'</div>';
            }
         } else {
            page += '<div style="float:left; text-align:center; width:21px; height:16px; background-color:'+ bgcolor +'; color:'+ fontcolor +'; '+ fontweight +';">&nbsp;</div>';
         }
   		dt_current_day.setDate(dt_current_day.getDate()+1);
   	}
   	// koniec row
   	page += '<div class="clear"></div></div>';
   }
   page += '</div>';

   page +='</div>';

   page +='<div class="clear"></div>';

   document.getElementById(warstwa).innerHTML = page;
}

// wyświetla tylko rok
function print_year() {

   var page =
   '<div class="clear"></div>'+
   '<div style="float:left; height:20px;width:248px;border-top:5px solid #C9DBED;z-index:990;"></div>'+
   '<div style="float:right;margin-top:0px; height:158px; width:5px; background-color:#C9DBED;"></div>'+
      '<div style="margin-top:0px; margin-right:-5px; float:right; valign:top; z-index:999;">'+
         '<div id="cal_close" class="hand" onClick="cDivMOver = 2; close_cal();"></div>'+
      '</div>';

      page += '<div style="float:left;width:300px;margin-left:2px; height:133px; z-index:990;">';
         page +='asfasfasf';
      page +='</div>';
      
      page +='<div class="clear"></div>';
   page += '</div>';

   document.getElementById(warstwa).innerHTML = page;
}

// ustawia datę i wpisuje do pola, lub ustawia datę i przesuwa kalendarz
// i = 1 - miesiąc poprzedni
// i = 2 - miesiąc następny
// i = 3 - wpisz datę do pola
function set_datetime(i, n_datetime) {
   if(i == 3) {
      cDivMOver = 2;

      var dt_datetime = prs_time('', new Date(n_datetime));
      // przesunięcie daty powrotu o x dni od daty wyjazdu

      if(pole.id == 'SearchFlightFlyDate[1]') {
         if (document.getElementById('SearchFlightFlyDate[2]')) {
           var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchFlightFlyDate[2]').value);
         }
         if(document.getElementById('SearchFlightBackDate')) {
            var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchFlightBackDate').value);
         }

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         if (document.getElementById('SearchFlightFlyDate[2]')) {
           document.getElementById('SearchFlightFlyDate[2]').value = gen_date_back(dt_datetime, x);
         }
         if(document.getElementById('SearchFlightBackDate')) {
            document.getElementById('SearchFlightBackDate').value = gen_date_back(dt_datetime, y);
         }
      } else if(pole.id == 'SearchFlightFlyDate[2]') {
         var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchFlightFlyDate[3]').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchFlightFlyDate[3]').value = gen_date_back(dt_datetime, x);
      } else if(pole.id == 'SearchFlightFlyDate[3]') {
         var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchFlightFlyDate[4]').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchFlightFlyDate[4]').value = gen_date_back(dt_datetime, x);
      } else if(pole.id == 'SearchHotelInDate') {
         var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchHotelOutDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchHotelOutDate').value = gen_date_back(dt_datetime, x);
      } else if(pole.id == 'Data_hpcInDate') {
         var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('Data_hpcOutDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('Data_hpcOutDate').value = gen_date_back(dt_datetime, x);
      } else if(pole.id == 'Data_hpfFlyDate[1]') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('Data_hpfBackDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('Data_hpfBackDate').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'Data_hpfpcFlyDate[1]') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('Data_hpfpcBackDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('Data_hpfpcBackDate').value = gen_date_back(dt_datetime, y);
      }else if(pole.id == 'Data_fpcFlyDate[1]') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('Data_fpcBackDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('Data_fpcBackDate').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SearchCarPickupDate') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchCarDropoffDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchCarDropoffDate').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SearchInsuranceStartInsDate') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchInsuranceEndInsDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchInsuranceEndInsDate').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SearchSightseeingStartDate') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchSightseeingEndDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchSightseeingEndDate').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SpecialFlightOfferSellFrom') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SpecialFlightOfferSellTo').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SpecialFlightOfferSellTo').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SpecialFlightOfferFlyoutFrom') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SpecialFlightOfferFlyoutTo').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SpecialFlightOfferFlyoutTo').value = gen_date_back(dt_datetime, y);
      } else if(pole.id == 'SpecialFlightOfferReturnFrom') {
         var y = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SpecialFlightOfferReturnTo').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SpecialFlightOfferReturnTo').value = gen_date_back(dt_datetime, y);
      }  else if(pole.id == 'SearchTouristPackageInDate') {
         var x = days_between_dates(document.getElementById(pole.id).value,document.getElementById('SearchTouristPackageOutDate').value);

         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
         document.getElementById('SearchTouristPackageOutDate').value = gen_date_back(dt_datetime, x);
      }  else {
         document.getElementById(pole.id).value = (document.cal_time ? gen_tsmp(dt_datetime) : gen_date(dt_datetime));
      }

      // obliczenie długości pobytu

      if(pole.id == 'SearchHotelInDate' || pole.id == 'SearchHotelOutDate') {
         length_of_stay(document.getElementById('SearchHotelInDate').value,document.getElementById('SearchHotelOutDate').value,'SearchHotel');
      }
      if(pole.id == 'Data_hpcInDate' || pole.id == 'Data_hpcOutDate') {
         length_of_stay(document.getElementById('Data_hpcInDate').value,document.getElementById('Data_hpcOutDate').value,'Data_hpc');
      }

      close_cal();
   } else {
      if(i == 1) {
         dt_next_month.setDate(1);
         dt_next_month.setMonth(dt_next_month.getMonth() - 2);
         dt_prev_month.setDate(1);
         dt_prev_month.setMonth(dt_prev_month.getMonth() - 2);
         dt_act_month.setDate(1);
         dt_act_month.setMonth(dt_act_month.getMonth() - 2);

         // wyznacza, który dzień jest 1 w aktualnym miesiącu
         dt_firstday = new Date(dt_act_month);
         dt_firstday.setDate(1);
         dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - NUM_WEEKSTART) % 7);
      } else if(i == 2){
         //tego nie musi być, bo robi się w kalendarzu
         /*
         dt_next_month.setMonth(dt_next_month.getMonth() + 1);
         dt_prev_month.setMonth(dt_prev_month.getMonth() + 1);
         dt_act_month.setMonth(dt_act_month.getMonth() + 1);

         // wyznacza, który dzień jest 1 w aktualnym miesiącu
         dt_firstday = new Date(dt_act_month);
         dt_firstday.setDate(1);
         dt_firstday.setDate(1 - (7 + dt_firstday.getDay() - NUM_WEEKSTART) % 7);
         */
      }
      print_cal();
   }
}

function check_date_propriety(pole) {
   if(pole.id == 'SearchFlightFlyDate[1]') {
      if (document.getElementById('SearchFlightFlyDate[2]')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchFlightFlyDate[2]').value)) {
            document.getElementById('SearchFlightFlyDate[2]').value = pole.value;
         }
      }
      if (document.getElementById('SearchFlightBackDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchFlightBackDate').value)) {
            document.getElementById('SearchFlightBackDate').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchFlightFlyDate[2]') {
      if (document.getElementById('SearchFlightFlyDate[3]')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchFlightFlyDate[3]').value)) {
            document.getElementById('SearchFlightFlyDate[3]').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchFlightFlyDate[3]') {
      if (document.getElementById('SearchFlightFlyDate[4]')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchFlightFlyDate[4]').value)) {
            document.getElementById('SearchFlightFlyDate[4]').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchHotelInDate') {
      if (document.getElementById('SearchHotelOutDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchHotelOutDate').value)) {
            document.getElementById('SearchHotelOutDate').value = pole.value;
         }
      }
   } else if(pole.id == 'Data_hpcInDate') {
      if (document.getElementById('Data_hpcOutDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('Data_hpcOutDate').value)) {
            document.getElementById('Data_hpcOutDate').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchInsuranceStartInsDate') {
      if (document.getElementById('SearchInsuranceEndInsDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchInsuranceEndInsDate').value)) {
            document.getElementById('SearchInsuranceEndInsDate').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchSightseeingStartDate') {
      if (document.getElementById('SearchSightseeingEndDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchSightseeingEndDate').value)) {
            document.getElementById('SearchSightseeingEndDate').value = pole.value;
         }
      }
   } else if(pole.id == 'Data_hpfFlyDate[1]') {
      if (document.getElementById('Data_hpfBackDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('Data_hpfBackDate').value)) {
            document.getElementById('Data_hpfBackDate').value = pole.value;
         }
      }
   } else if(pole.id == 'Data_hpfpcFlyDate[1]') {
      if (document.getElementById('Data_hpfpcBackDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('Data_hpfpcBackDate').value)) {
            document.getElementById('Data_hpfpcBackDate').value = pole.value;
         }
      }
   }else if(pole.id == 'Data_fpcFlyDate[1]') {
      if (document.getElementById('Data_fpcBackDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('Data_fpcBackDate').value)) {
            document.getElementById('Data_fpcBackDate').value = pole.value;
         }
      }
   } else if(pole.id == 'SearchCarPickupDate') {
      if (document.getElementById('SearchCarDropoffDate')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SearchCarDropoffDate').value)) {
            document.getElementById('SearchCarDropoffDate').value = pole.value;
         }
      }
   } else if(pole.id == 'SpecialFlightOfferSellFrom') {
      if (document.getElementById('SpecialFlightOfferSellTo')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SpecialFlightOfferSellTo').value)) {
            document.getElementById('SpecialFlightOfferSellTo').value = pole.value;
         }
      }
   } else if(pole.id == 'SpecialFlightOfferFlyoutFrom') {
      if (document.getElementById('SpecialFlightOfferFlyoutTo')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SpecialFlightOfferFlyoutTo').value)) {
            document.getElementById('SpecialFlightOfferFlyoutTo').value = pole.value;
         }
      }
   } else if(pole.id == 'SpecialFlightOfferReturnFrom') {
      if (document.getElementById('SpecialFlightOfferReturnTo')) {
         if(prs_tsmp(pole.value) > prs_tsmp(document.getElementById('SpecialFlightOfferReturnTo').value)) {
            document.getElementById('SpecialFlightOfferReturnTo').value = pole.value;
         }
      }
   }
}

// oblicza ilość dni pobytu wg dat
function days_between_dates(d1,d2) {
   var one_day = 1000 * 60 * 60 * 24;
   var date1 = prs_tsmp(d1);
   var date2 = prs_tsmp(d2);

   return Math.abs(Math.ceil((date1.getTime() - date2.getTime()) / (one_day)));
}

function gen_tsmp (dt_datetime) {
	return(gen_date(dt_datetime) + ' ' + gen_time(dt_datetime));
}

// generuje datę
function gen_date (dt_datetime) {
	return (
		dt_datetime.getFullYear() + "-"
		+ (dt_datetime.getMonth() < 9 ? '0' : '') + (dt_datetime.getMonth() + 1) + "-"
		+ (dt_datetime.getDate() < 10 ? '0' : '') + dt_datetime.getDate()
	);
}
// generuje datę + x dni
function gen_date_back (dt_datetime, x) {
   dt_datetime.setDate(dt_datetime.getDate() + x);
   return (
      dt_datetime.getFullYear() + "-"
		+ (dt_datetime.getMonth() < 9 ? '0' : '') + (dt_datetime.getMonth() + 1) + "-"
		+ (dt_datetime.getDate() < 10 ? '0' : '') + dt_datetime.getDate()
	);
}
// generuje czas
function gen_time (dt_datetime) {
	return (
		(dt_datetime.getHours() < 10 ? '0' : '') + dt_datetime.getHours() + ":"
		+ (dt_datetime.getMinutes() < 10 ? '0' : '') + (dt_datetime.getMinutes()) + ":"
		+ (dt_datetime.getSeconds() < 10 ? '0' : '') + (dt_datetime.getSeconds())
	);
}

// parsuje timestamp
function prs_tsmp (str_datetime) {
	// jeśli nie ma parametru, zwraca aktualnądatę jako timestamp
	if (!str_datetime)
		return (new Date());

	// jeśli parametr jest dodatnim integerem to traktuj jako liczbę milisekund od 1 stycznia 1970
	if (RE_NUM.exec(str_datetime))
		return new Date(str_datetime);

	// jeśli żadne z powyższych to parametr jest datą jako tekst
	var a = str_datetime;
	var arr_datetime = a.split(' ');
	return prs_time(arr_datetime[1], prs_date(arr_datetime[0]));
}

// parsuje datę
function prs_date (str_date) {
   //alert("org"+org_date);
   //alert("podane"+str_date);

	var arr_date = str_date.split('-');

	if (arr_date.length != 3) {
      //return alert ("Nieprawidłowy format daty: '" + str_date + "'.\nAkceptowany format to: yyyy-mm-dd.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
      //alert(org_date);
      //alert(pole.id);
   }

	if (!arr_date[0]) {
      //return alert ("Nieprawidłowy format daty: '" + str_date + "'.\nNie podano roku.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

   if (!RE_NUM.exec(arr_date[0])) {
      //return alert ("Nieprawidłowa wartość: '" + arr_date[0] + "'.\nRok musi być liczbą całkowitą dodatnią.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

	if (!arr_date[1]) {
      //return alert ("Nieprawidłowy format daty: '" + str_date + "'.\nNie podano miesiąca.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

	if (!RE_NUM.exec(arr_date[1])){
      //return alert ("Nieprawidłowa wartość: '" + arr_date[1] + "'.\nMiesiąc musi być liczbą całkowitą dodatnią.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

	if (!arr_date[2]) {
      //return alert ("Nieprawidłowy format daty: '" + str_date + "'.\nNie podano dnia.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

	if (!RE_NUM.exec(arr_date[2])) {
      //return alert ("Nieprawidłowa wartość: '" + arr_date[2] + "'.\nDzień musi być liczbą całkowitą dodatnią.");
      arr_date = org_date.split('-');
      document.getElementById(pole.id).value = org_date;
   }

	var dt_date = new Date();
	dt_date.setDate(1);

	if (arr_date[0] < 100)
      arr_date[0] = Number(arr_date[0]) + (arr_date[0] < NUM_CENTYEAR ? 2000 : 1900);

	dt_date.setFullYear(arr_date[0]);

	if (arr_date[1] < 1 || arr_date[1] > 12) {
      //return alert ("Nieprawidłowa wartość miesiąca: '" + arr_date[1] + "'.\nDopuszczalne wartości to: 01-12.");
      document.getElementById(pole.id).value = org_date;
   }

	dt_date.setMonth(arr_date[1] - 1);

	var dt_numdays = new Date(arr_date[0], arr_date[1], 0);
	dt_date.setDate(arr_date[2]);
	if (dt_date.getMonth() != (arr_date[1]-1)) {
      //return alert ("Nieprawidłowa wartość dnia: '" + arr_date[2] + "'.\nDopuszczalne wartości to: 01-"+dt_numdays.getDate()+".");
      document.getElementById(pole.id).value = org_date;
   }

	return (dt_date)
}

// parsuje czas
function prs_time (str_time, dt_date) {

	if (!dt_date) return null;
	var arr_time = String(str_time ? str_time : '').split(':');

	if (!arr_time[0]) dt_date.setHours(0);
	else if (RE_NUM.exec(arr_time[0]))
		if (arr_time[0] < 24) dt_date.setHours(arr_time[0]);
		else return cal_error ("Nieprawidłowa wortość godziny: '" + arr_time[0] + "'.\nDopuszczalne wartości to: 00-23.");
	else return cal_error ("Nieprawidłowa wartość: '" + arr_time[0] + "'.\nGodzina musi być liczbą całkowitą dodatnią.");

	if (!arr_time[1]) dt_date.setMinutes(0);
	else if (RE_NUM.exec(arr_time[1]))
		if (arr_time[1] < 60) dt_date.setMinutes(arr_time[1]);
		else return cal_error ("Nieprawidłowa wortość minut: '" + arr_time[1] + "'.\nDopuszczalne wartości to: 00-59.");
	else return cal_error ("Nieprawidłowa wartość: '" + arr_time[1] + "'.\nMinuty muszą być liczbą całkowitą dodatnią.");

	if (!arr_time[2]) dt_date.setSeconds(0);
	else if (RE_NUM.exec(arr_time[2]))
		if (arr_time[2] < 60) dt_date.setSeconds(arr_time[2]);
		else return cal_error ("Nieprawidłowa wortość sekund: '" + arr_time[2] + "'.\nDopuszczalne wartości to: 00-59.");
	else return cal_error ("Nieprawidłowa wartość: '" + arr_time[2] + "'.\nSekundy muszą być liczbą całkowitą dodatnią.");

	dt_date.setMilliseconds(0);
	return dt_date;
}

// wyświetla błąd
function cal_error (str_message) {
	alert (str_message);
	return null;
}

function strtotime (str, now) {
    // http://kevin.vanzonneveld.net
    // +   original by: Caio Ariede (http://caioariede.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: David
    // +   improved by: Caio Ariede (http://caioariede.com)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Wagner B. Soares
    // +   bugfixed by: Artur Tchernychev
    // %        note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones)
    // *     example 1: strtotime('+1 day', 1129633200);
    // *     returns 1: 1129719600
    // *     example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200);
    // *     returns 2: 1130425202
    // *     example 3: strtotime('last month', 1129633200);
    // *     returns 3: 1127041200
    // *     example 4: strtotime('2009-05-04 08:30:00');
    // *     returns 4: 1241418600

    var i, match, s, strTmp = '', parse = '';

    strTmp = str;
    strTmp = strTmp.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces
    strTmp = strTmp.replace(/[\t\r\n]/g, ''); // unecessary chars

    if (strTmp == 'now') {
        return (new Date()).getTime()/1000; // Return seconds, not milli-seconds
    } else if (!isNaN(parse = Date.parse(strTmp))) {
        return (parse/1000);
    } else if (now) {
        now = new Date(now*1000); // Accept PHP-style seconds
    } else {
        now = new Date();
    }

    strTmp = strTmp.toLowerCase();

    var __is =
    {
        day:
        {
            'sun': 0,
            'mon': 1,
            'tue': 2,
            'wed': 3,
            'thu': 4,
            'fri': 5,
            'sat': 6
        },
        mon:
        {
            'jan': 0,
            'feb': 1,
            'mar': 2,
            'apr': 3,
            'may': 4,
            'jun': 5,
            'jul': 6,
            'aug': 7,
            'sep': 8,
            'oct': 9,
            'nov': 10,
            'dec': 11
        }
    };

    var process = function (m) {
        var ago = (m[2] && m[2] == 'ago');
        var num = (num = m[0] == 'last' ? -1 : 1) * (ago ? -1 : 1);

        switch (m[0]) {
            case 'last':
            case 'next':
                switch (m[1].substring(0, 3)) {
                    case 'yea':
                        now.setFullYear(now.getFullYear() + num);
                        break;
                    case 'mon':
                        now.setMonth(now.getMonth() + num);
                        break;
                    case 'wee':
                        now.setDate(now.getDate() + (num * 7));
                        break;
                    case 'day':
                        now.setDate(now.getDate() + num);
                        break;
                    case 'hou':
                        now.setHours(now.getHours() + num);
                        break;
                    case 'min':
                        now.setMinutes(now.getMinutes() + num);
                        break;
                    case 'sec':
                        now.setSeconds(now.getSeconds() + num);
                        break;
                    default:
                        var day;
                        if (typeof (day = __is.day[m[1].substring(0, 3)]) != 'undefined') {
                            var diff = day - now.getDay();
                            if (diff == 0) {
                                diff = 7 * num;
                            } else if (diff > 0) {
                                if (m[0] == 'last') {diff -= 7;}
                            } else {
                                if (m[0] == 'next') {diff += 7;}
                            }
                            now.setDate(now.getDate() + diff);
                        }
                }
                break;

            default:
                if (/\d+/.test(m[0])) {
                    num *= parseInt(m[0], 10);

                    switch (m[1].substring(0, 3)) {
                        case 'yea':
                            now.setFullYear(now.getFullYear() + num);
                            break;
                        case 'mon':
                            now.setMonth(now.getMonth() + num);
                            break;
                        case 'wee':
                            now.setDate(now.getDate() + (num * 7));
                            break;
                        case 'day':
                            now.setDate(now.getDate() + num);
                            break;
                        case 'hou':
                            now.setHours(now.getHours() + num);
                            break;
                        case 'min':
                            now.setMinutes(now.getMinutes() + num);
                            break;
                        case 'sec':
                            now.setSeconds(now.getSeconds() + num);
                            break;
                    }
                } else {
                    return false;
                }
                break;
        }
        return true;
    };

    match = strTmp.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);
    if (match != null) {
        if (!match[2]) {
            match[2] = '00:00:00';
        } else if (!match[3]) {
            match[2] += ':00';
        }

        s = match[1].split(/-/g);

        for (i in __is.mon) {
            if (__is.mon[i] == s[1] - 1) {
                s[1] = i;
            }
        }
        s[0] = parseInt(s[0], 10);

        s[0] = (s[0] >= 0 && s[0] <= 69) ? '20'+(s[0] < 10 ? '0'+s[0] : s[0]+'') : (s[0] >= 70 && s[0] <= 99) ? '19'+s[0] : s[0]+'';
        return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2])+(match[4] ? match[4]/1000 : ''), 10);
    }

    var regex = '([+-]?\\d+\\s'+
        '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+
        '|sun\.?|sunday|mon\.?|monday|tue\.?|tuesday|wed\.?|wednesday'+
        '|thu\.?|thursday|fri\.?|friday|sat\.?|saturday)'+
        '|(last|next)\\s'+
        '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+
        '|sun\.?|sunday|mon\.?|monday|tue\.?|tuesday|wed\.?|wednesday'+
        '|thu\.?|thursday|fri\.?|friday|sat\.?|saturday))'+
        '(\\sago)?';

    match = strTmp.match(new RegExp(regex, 'g'));
    if (match == null) {
        return false;
    }

    for (i = 0; i < match.length; i++) {
        if (!process(match[i].split(' '))) {
            return false;
        }
    }

    return (now.getTime()/1000);
}

/*** DOM-DRAG.JS */
var Drag = {

	obj : null,

	init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
	{
		o.onmousedown	= Drag.start;

		o.hmode			= bSwapHorzRef ? false : true ;
		o.vmode			= bSwapVertRef ? false : true ;

		o.root = oRoot && oRoot != null ? oRoot : o ;

		if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
		if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
		if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
		if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

		o.minX	= typeof minX != 'undefined' ? minX : null;
		o.minY	= typeof minY != 'undefined' ? minY : null;
		o.maxX	= typeof maxX != 'undefined' ? maxX : null;
		o.maxY	= typeof maxY != 'undefined' ? maxY : null;

		o.xMapper = fXMapper ? fXMapper : null;
		o.yMapper = fYMapper ? fYMapper : null;

		o.root.onDragStart	= new Function();
		o.root.onDragEnd	= new Function();
		o.root.onDrag		= new Function();
	},

	start : function(e)
	{
		var o = Drag.obj = this;
		e = Drag.fixE(e);
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
		o.root.onDragStart(x, y);

		o.lastMouseX	= e.clientX;
		o.lastMouseY	= e.clientY;

		if (o.hmode) {
			if (o.minX != null)	o.minMouseX	= e.clientX - x + o.minX;
			if (o.maxX != null)	o.maxMouseX	= o.minMouseX + o.maxX - o.minX;
		} else {
			if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
			if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
		}

		if (o.vmode) {
			if (o.minY != null)	o.minMouseY	= e.clientY - y + o.minY;
			if (o.maxY != null)	o.maxMouseY	= o.minMouseY + o.maxY - o.minY;
		} else {
			if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
			if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
		}

		document.onmousemove	= Drag.drag;
		document.onmouseup		= Drag.end;

		return false;
	},

	drag : function(e)
	{
		e = Drag.fixE(e);
		var o = Drag.obj;

		var ey	= e.clientY;
		var ex	= e.clientX;
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
		var nx, ny;

		if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
		if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
		if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
		if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

		nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
		ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

		if (o.xMapper)		nx = o.xMapper(y)
		else if (o.yMapper)	ny = o.yMapper(x)

		Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
		Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";

      if(document.getElementById('iframe_'+ o.root.id +'_content')) {
         document.getElementById('iframe_'+ o.root.id +'_content').style[o.hmode ? "left" : "right"] = nx - 2 + "px";
         document.getElementById('iframe_'+ o.root.id +'_content').style[o.vmode ? "top" : "bottom"] = ny - 2 + "px";
		}
		Drag.obj.lastMouseX	= ex;
		Drag.obj.lastMouseY	= ey;

		Drag.obj.root.onDrag(nx, ny);
		return false;
	},

	end : function()
	{
		document.onmousemove = null;
		document.onmouseup   = null;
		Drag.obj.root.onDragEnd(	parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]),
									parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
		Drag.obj = null;
	},

	fixE : function(e)
	{
		if (typeof e == 'undefined') e = window.event;
		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
		return e;
	}
};

var run_system_timer = false;
var system_time = 0;

function start_system_timer(function_name, stop_time) {
   run_system_timer = true;
   system_time = 0;
   system_timer(function_name, stop_time);
}

function system_timer(function_name, stop_time) {
   var format_time;
   system_time++;

   if(system_time == stop_time) {
      run_system_timer = false;
      if (window.execScript) {
         window.execScript(function_name);
      } else {
         window.eval(function_name);
      }
   }

   if(run_system_timer) {
      setTimeout("system_timer("+ function_name +", "+ stop_time +")", 1000);
   }
}

function show_login_layer_div() {
   if (isIE()) {
      set_abs_pos('top_right_bottom', 'login_layer_div', 218, 0);
   } else{
      set_abs_pos('top_right_bottom', 'login_layer_div', 218, -1);
   }

   //Effect.BlindDown('login_layer_div', { duration: 0.5 });
   Effect.Grow('login_layer_div',{ duration: 0.3, direction: 'top-left' });

   //Element.show('login_layer_div');
   show_iframe('login_layer_div');
}

function hide_login_layer_div() {
   hide_iframe('login_layer_div');
   //Element.hide('login_layer_div');

   //Effect.BlindUp('login_layer_div', { duration: 0.5 });
   Effect.Shrink('login_layer_div',{ duration: 0.3, direction: 'top-left' });
}

function substr_count( haystack, needle, offset, length ) {
    var pos = 0, cnt = 0;

    haystack += '';
    needle += '';
    if(isNaN(offset)) offset = 0;
    if(isNaN(length)) length = 0;
    offset--;

    while( (offset = haystack.indexOf(needle, offset+1)) != -1 ){
        if(length > 0 && (offset+needle.length) > length){
            return false;
        } else{
            cnt++;
        }
    }

    return cnt;
}

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/

var Base64 = {

	// private property
	_keyStr : new String('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+='),

	// public method for encoding
	encode : function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		input = Base64.utf8_encode(input);

		while (i < input.length) {

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}

			output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

		}

		return output;
	},

	// public method for decoding
	decode : function (input) {
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;

		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

		while (i < input.length) {

			enc1 = this._keyStr.indexOf(input.charAt(i++));
			enc2 = this._keyStr.indexOf(input.charAt(i++));
			enc3 = this._keyStr.indexOf(input.charAt(i++));
			enc4 = this._keyStr.indexOf(input.charAt(i++));

			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;

			output = output + String.fromCharCode(chr1);

			if (enc3 != 64) {
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) {
				output = output + String.fromCharCode(chr3);
			}

		}

		output = Base64.utf8_decode(output);

		return output;

	},

	// public method for UTF-8 encoding
	utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// public method for UTF-8 decoding
	utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while ( i < utftext.length ) {

			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}
}

// serializuje tablicę tak jak w php
function serialize (mixed_value) {
   var _getType = function (inp) {
      var type = typeof inp, match;
      var key;
      if (type == 'object' && !inp) {
         return 'null';
      }
      if (type == "object") {
         if (!inp.constructor) {
             return 'object';
         }
         var cons = inp.constructor.toString();
         match = cons.match(/(\w+)\(/);
         if (match) {
             cons = match[1].toLowerCase();
         }
         var types = ["boolean", "number", "string", "array"];
         for (key in types) {
             if (cons == types[key]) {
                 type = types[key];
                 break;
             }
         }
     }
     return type;
   };
    var type = _getType(mixed_value);
    var val, ktype = '';

    switch (type) {
        case "function":
            val = "";
            break;
        case "boolean":
            val = "b:" + (mixed_value ? "1" : "0");
            break;
        case "number":
            val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
            break;
        case "string":
            val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
            break;
        case "array":
        case "object":
            val = "a";
            /*
            if (type == "object") {
                var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/);
                if (objname == undefined) {
                    return;
                }
                objname[1] = this.serialize(objname[1]);
                val = "O" + objname[1].substring(1, objname[1].length - 1);
            }
            */
            var count = 0;
            var vals = "";
            var okey;
            var key;
            for (key in mixed_value) {
                ktype = _getType(mixed_value[key]);
                if (ktype == "function") {
                    continue;
                }

                okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
                vals += this.serialize(okey) +
                        this.serialize(mixed_value[key]);
                count++;
            }
            val += ":" + count + ":{" + vals + "}";
            break;
        case "undefined": // Fall-through
        default: // if the JS object has a property which contains a null value, the string cannot be unserialized by PHP
            val = "N";
            break;
    }
    if (type != "object" && type != "array") {
        val += ";";
    }
    return val;
}


/**
*
*  Odpalanie skryptów JavaScript z odpowiedzi AJAXa
*
**/

function run_ajax_script(text) {
   var temp_script_start = text.split("<sc" + "ript type=\"text/javascript\">");
   for(var i = 0; i < temp_script_start.length; i++) {
      if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
         continue;
      }

      var temp_script_end = temp_script_start[i].split("</sc" + "ript>");
      var temp_script = temp_script_end[0];

      try {
         //alert(temp_script);
         if (window.execScript) {
            window.execScript(temp_script);
         } else {
            window.eval(temp_script);
         }
      } catch(err) {
         txt="There was an error on this page.\n\n";
         txt+="Error description: " + err.description + "\n\n";
         txt+="Click OK to continue.\n\n";
         //alert(txt);
      }
   }

   var temp_script_start = text.split("<sc" + "ript language=\"JavaScript\">");
   for(var i = 0; i < temp_script_start.length; i++) {
      if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
         continue;
      }

      var temp_script_end = temp_script_start[i].split("</sc" + "ript>");
      var temp_script = temp_script_end[0];

      try {
         //alert(temp_script);
         if (window.execScript) {
            window.execScript(temp_script);
         } else {
            window.eval(temp_script);
         }
      } catch(err) {
         txt="There was an error on this page.\n\n";
         txt+="Error description: " + err.description + "\n\n";
         txt+="Click OK to continue.\n\n";
         //alert(txt);
      }
   }
   
   var temp_script_start = text.split("<sc" + "ript language=\"javascript\">");
   for(var i = 0; i < temp_script_start.length; i++) {
      if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
         continue;
      }

      var temp_script_end = temp_script_start[i].split("</sc" + "ript>");
      var temp_script = temp_script_end[0];

      try {
         //alert(temp_script);
         if (window.execScript) {
            window.execScript(temp_script);
         } else {
            window.eval(temp_script);
         }
      } catch(err) {
         txt="There was an error on this page.\n\n";
         txt+="Error description: " + err.description + "\n\n";
         txt+="Click OK to continue.\n\n";
         //alert(txt);
      }
   }
   
   var temp_script_start = text.split("<sc" + "ript type=\"text/javascript\" language=\"JavaScript\">");
   for(var i = 0; i < temp_script_start.length; i++) {
      if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
         continue;
      }

      var temp_script_end = temp_script_start[i].split("</sc" + "ript>");
      var temp_script = temp_script_end[0];

      try {
         //alert(temp_script);
         if (window.execScript) {
            window.execScript(temp_script);
         } else {
            window.eval(temp_script);
         }
      } catch(err) {
         txt="There was an error on this page.\n\n";
         txt+="Error description: " + err.description + "\n\n";
         txt+="Click OK to continue.\n\n";
         //alert(txt);
      }
   }
   
   var temp_script_start = text.split("<sc" + "ript type=\"text/javascript\" language=\"javascript\">");
   for(var i = 0; i < temp_script_start.length; i++) {
      if(temp_script_start[i].indexOf("</sc" + "ript>") == -1) {
         continue;
      }

      var temp_script_end = temp_script_start[i].split("</sc" + "ript>");
      var temp_script = temp_script_end[0];

      try {
         //alert(temp_script);
         if (window.execScript) {
            window.execScript(temp_script);
         } else {
            window.eval(temp_script);
         }
      } catch(err) {
         txt="There was an error on this page.\n\n";
         txt+="Error description: " + err.description + "\n\n";
         txt+="Click OK to continue.\n\n";
         //alert(txt);
      }
   }
}

/*
* REGEXP
*/

function check_forbidden_word(text) {
   text = text.toLowerCase();
   var patern = /^test+$/g;
   if(patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_string_pl(text, min_length) {
   if(!min_length) {
      min_length = 0;
   }

   var len = text.length;
   
   if(len < min_length) {
      return 0;
   }

   var patern = /^[A-Za-zŚĆĄĘŻŹŁŃÓśćąężźłńó\-\.\/\\ ]+$/;

   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_string_pl_with_numers(text, min_length) {
   if(!min_length) {
      min_length = 0;
   }

   var len = text.length;

   if(len < min_length) {
      return 0;
   }

   var patern = /^[A-Za-z0-9ŚĆĄĘŻŹŁŃÓśćąężźłńó\-\.\/\\ ]+$/;
   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_postcode(text) {
   var patern = /^[A-Za-z0-9ŚĆĄĘŻŹŁŃÓśćąężźłńó\-\.\/\\ ]+$/;
   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_date(text) {
   var patern = /([0-9]{4})-([0][1-9]|[1][0-2])-([0-2][0-9]|[3][0-1])/;
   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_numeric(text) {
   var patern = /^[0-9]+$/;
   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}

function is_numeric_with_plus(text) {
   var patern = /^\+?[0-9]+$/;
   if(!patern.test(text)) {
      return 0;
   }

   // 0 blad
   // 1 ok
   return 1;
}


/*****************************************************************************
RightContext v0.2.5
Author: Harel Malka
http://www.harelmalka.com
harel@harelmalka.com
Bugs: Leave comment on the site

February 2007 - Initial Revision
----------------------------------------------------------------------------

RightContext is a supercharged context menu. I created it to
answer a few requirements I had of context menus which I could not find
anywhere else; mainly provide the *correct* contextualized links depending
on what was triggering the menu. Originaly this was a Right Click menu only,
though this was changed in 0.2.4 to allow menu triggering by Left/Right Click,
or Mouse over actions.
RightContext generates unique context menus that are built based on the DOM
element clicked using special attributes embedded in the element's html.
The attributes are custom generated and can be anything. Menu items can
include [tags] referencing those attributes, which will cause them to be
transformed to contain the actual values when the menu is constructed
(i.e., when an element is left/right clicked or moused over, depending on
the menu's setting.)

Some of the key features of RightClick:
	* Trigger items via Right click, Left click or Mouse over (new in 0.2.4)
	* Menu items that link somewhere
	* Menu items that perform a custom javascript function
	* Menu items that display hardcoded text
	* Menu items that retrieve text via a remote 'ajax' call
	* Menu item separators
	* Supports multiple different menus that can be called depending on
		the element clicked
	* All menu items can contain [tags] which are transformed at runtime to
		the values embedded in the clicked element
	* Conditional evaluation of menu items. An item can show or not show depending
		on a specified condition in the menu template (new in v0.2.3)
	* Css based look and feel
	* Unobtrusive standalone javascript: no additional js framework required.

RightContext is the menu object. It contains a collection of context menu
definitions which can be retrieved by unique names. A context menu will
receive a set of arguments from any calling Context that can be used to
construct the links or text in it.
This is a singleton object that should have one instance per page.

A few of the object's properties that you can edit:
	menuTriggerEvent -> Can be either RIGHT (default), LEFT, or MOVE to
						determine what triggers the menus generated (left/right
						click or a mouse over event). Value is in UPPER case.
	allowedContexts  -> A String array of tag names that can have a menu attached
						to them. The default list covers A, DIV, SPAN, INPUT tags.

A menu definition is an object with the following spec:

	menu1 = { attributes: "attr1,attr2,attr3" ,

				items: [
					{type:RightContext.TYPE_MENU,
						text:"Search for [attr2] on Google",
						url:"http://www.goole.com?q=[attr1]",
						image: "icon.gif", align:"right" },

					{type:RightContext.TYPE_MENU,
						text:"The second item in the menu. it will show if attr3=Y (and its: [attr3])",
						requires: ["attr3", "Y"],
						onclick:function() { alert('This is a custom javascript')} },

					{type: RightContext.TYPE_SEPERATOR },

					{type: RightContext.TYPE_TEXT,
						text: "This is hardcoded, yet dynamic text: attr1=[attr1], attr2=[attr2], attr3=[attr3]"} ,

					{type: RightContext.TYPE_TEXT_EXT,
						url: "external.html"}   ]
			};

Each menu definition object contains two properties:

	attributes : a comma delimited list of attributes that this menu will
				look for in the elements its bound to
	items      : an array of objects, each describes a menu item of a given
				type. The example above covers all
				currently supported types and their respective properties.

A couple of notes about item properties:
	* Use either url or onclick for TYPE_MENU items - url will take precedence
	over onclick.
	* For url type menu items, you can add an optional frame property to redirect
	the link to a specific frame, or use _blank to open a new window
	* The image attribute for TYPE_MENU are optional.
	* For TYPE_MENU, you can use the optional 'requires' property. It accepts
	an array with two elements; the first one is an attribute to evaluate and
	the second is what it should evaluate to if this menu item is to be displayed.

After creating some menu definitions, you need to add them to the menu
collection within RightContext by doing:

	RightContext.addMenu("menuOne", menu1);

This adds menu1 and tags it with unique name: menuOne. The unique name is how
we'll refer to this menu later.

Not all html tags are used to allow context menus. By default, only
A, DIV, SPAN and INPUT tags are considered 'contextualizable' but you can add
additional tags by editing the allowedContexts property of the RightContext
object.

To generate a context menu for a particular html tag, first add the context
attribute to it, and then any additional attributes that you'd like it to
have. The context attribute holds which menu to call up.
The additional ones are the attributes defined in the attributes property of
the menu definition.
For the menu definition above, here's an A tag example:

	<a href="http://www.freecrm.com" context="menuOne" attr1="freecrm"
						attr2="FreeCRM.com" attr3="Hi mum!">Right Click Me</a>

	The above link will use the menuOne (mapped to menu1) defintion. When
	triggered, the first menu item will a link titled
	"Search for freecrm on Google" ([attr1] is replaced by the tag's
	attribute value - freecrm)
	The second item will pop a javascript alert and will be titled
	"The second item in the menu. attr3=Hi Mum!".
	It will be followed by a separator and then the following text:
	"This is hardcoded, yet dynamic text: attr1=freecrm, attr2=FreeCRM.com,
	attr3=Hi mum!"
	The last item will retrieve the contents of the external.html file
	provided. Obviously, you can also embed url parameters in the url as tags.
	Note that the link above will cause the second menu item to not display
	because the attr3 attribute is equal to "Hi mum!" while the menu template
	defines that the second item will only show if the attr3 is equal to Y.

You can create as many menu definitions as needed for different elements on
your page. Simple define which menu to call in the tag's context attribute.

Finally, to kick of the party and start some context menu action going, you
need to bind the tags that contain the context attribute to their respectful
menus by doing:

	RightContext.initialize();

Please see the example file provided (index.html). An example is worth
a thousand comments...

CSS Notes:
The RightContext object makes use of some CSS classes defined in the
rightcontext.css file. You need to include that in your html, or make use
of these classes yourself.

Some credits:
I was always on the lookout for the right context menu but was too busy (or
lazy) to make my own. What finally broke the camel's back was a ContextMenu
script using prototype that i found here:
http://www.ajaxline.com/node/338 (note - last i checked the link was not
active). But, it wasn't stand alone, and it wasn't really "contextualize"
to what's clicked. I wasn't a happy puppy, So i rolled up my sleeves.
I used the getDimensions method from prototype since it worked great and
saved me time doing my own. (http://prototype.conio.net)
I also adapted the logic in Alessandro Fulciniti great bubble tooltip
script's Locate method for the locateMousePos (slightly modified)
method for the same reason (http://pro.html.it  and
http://web-graphics.com )
Also thanks to all the good people who bothered to comment about this script.
Thanks to them I added the left click and mouse over trigger options.
In 0.2.5 I also incorporated fixes provided by Ian, CraigD, David Zhang and fanno.
So thanks guys!

LICENSE:
This script is FREE to use and modify for personal or commercial purposes.
I only ask that you either leave this commented message intact, OR leave
credit where credit is due, with all URLs provided (not just to myself,
but also to prototype and Alessandro).
As well, if you make any modifications to this script that enhances its
functionality, please DO give them back to the world and please email them
to me so that I can consider including them into my own version here.
I'd also love to hear of any good use you've made with this script!

WARRANTY:
This code comes with NO Warranty whatsoever. I cannot be help responsible
for any damage of any kind to any thing resulting from using this code.
Do NOT use this code for NASA shuttle launches! It won't work.

Please send bug reports to rightcontext@googlemail.com

Have fun

CHANGE LOG -----------------------------------------------------------------

0.2   : 12 Feb 2007  Added support for tags in menu onclick event handlers.
					Added a couple of fixes provided by JDG including a Safari
					fix.
0.2.1 : 17 Feb 2007  Bug fix: event handler parsing was overwriting the template
					with the first parsed item.
0.2.2 : 20 Feb 2007  Bug fix: IE6-7 issue with onlclick events containing tags
					and a menu positioning fix.
0.2.3 : 20 Feb 2007  Added conditional display of menu items based on [tag] values
0.2.4 : 26 Feb 2007  Added menu triggering via LEFT click and mouse over (MOVE)
					events.
0.2.5 : 04 Oct 2008  Fixes tags not passing properly on FF3 with onclick events.
					Also incorporates all fixes proposed by users.
					Thanks to Ian, CraigD, David Zhang and fanno

****************************************************************************/

var RightContext = {
	//some final vars:
	TYPE_MENU: 0,       // menu item
	TYPE_TEXT: 1,       // inline text (non-mutable hard coded)
	TYPE_TEXT_EXT: 2,   // external text (retrived via rpc call)
	TYPE_SEPERATOR:3,   // separator line
	TYPE_ATTRIBUTES:4,  // menu attributes.

	// some simple browser detection
	browser: null,

	// set the event to trigger the menus: RIGHT,LEFT (right/left click) or mouse MOVE
	menuTriggerEvent: "RIGHT",

	// object to hold temp mouse position
	mousePos: {x:0, y:0},

	// offset for menu from mouse pointer
	rightOffset: 5,

	// kill menu timeout - sets the timeout from mouse out to menu dissapearing
	killMenuTimeout: 5,

	// type of html tags that can have context menus. You can edit this to
	// allow more tags into the party.
	allowedContexts: ["a","div","span","input","button"],

	// object to hold a collection of menus indexed by name
	menuCollection: new Object(),

	// the currently visible context menu DIV element
	contextMenu: null,

	// some state machine: is the menu showing (LEFT), and should killing it be aborted (MOVE)
	isShowing: false,
	abortKill: true,

	// image cache
	images: new Object(),

	// var to hold external requests
	req: null,

	// initialize RightContext object
	initialize: function () {
		this.browser = RightContext.detectBrowser();
		this.attachContextEvents();
	},

	// adds a menu to the menuCollection
	addMenu: function (n, m) {
		this.menuCollection[n] = m;
	},

	// return a menu from the menu collection
	getMenu: function (n) {
		return this.menuCollection[n];
	},

	// loop all context allowed tags in the document and attach menu events to
	// those that contain the menu attribute
	attachContextEvents: function () {
		var tagContext, thisTag;
		for (var t=0; t<this.allowedContexts.length; t++) {
			tags = document.getElementsByTagName(this.allowedContexts[t]);

			for (e=0; e<tags.length; e++) {
				thisTag = tags[e];
				tagContext = thisTag.getAttribute("context");
				if (tagContext!=null && tagContext != "undefined") {
					this.bindEvent('mousemove', tags[e], function(e) { return RightContext.locateMousePos(e); });
					if (this.menuTriggerEvent=="RIGHT") {
						tags[e].oncontextmenu = function() {   return RightContext.render(this);   };
					} else if (this.menuTriggerEvent=="LEFT") {
						//this.bindEvent('click', tags[e],  function() {  return RightContext.render(this, tagContext);  });
						tags[e].onclick = function(e) {
														RightContext.killBubble(e);
														return RightContext.render(this)
													};
						tags[e].onmouseout = function(e) { setTimeout("RightContext.killMenu()", 5000);};
					} else if (this.menuTriggerEvent=="MOVE") {
						if (!document.all) {
							this.bindEvent('mouseover', tags[e], function(e) { RightContext.locateMousePos(e); return RightContext.render(this); });
							this.bindEvent('mouseout',  tags[e], function(e) { setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); });
						} else {
							tags[e].onmouseover =  function(e) { RightContext.locateMousePos(e); return RightContext.render(this); };
							tags[e].onmouseout = function(e) { setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); };
						}
					}
				}
			}
		}
	},

	killBubble: function(e) {
		if (!e) var e = window.event;
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	},

	// binds an event handler to an object
	bindEvent: function (evt, obj, act, bubble) {
		if (!bubble) bubble = false;
		if (obj.addEventListener) {
			obj.addEventListener(evt, act, bubble);
		} else if (obj.attachEvent) {
			obj.attachEvent('on'+evt, act);
		}
	},


	/*
	renders a given menu and attaches it to the caller object.
	The caller is responsible to contain a few extra attributes
	that will help construct the links for this menu (i.e., provide the Context)
	*/
	render: function (caller, name) {
		var url, title;
		// if name was not specified, grab it from the caller
		// v0.2 - changed to getAttribute (used direct nodeValue access before my mistake). pointed out by JDG.
		var name = name || caller.getAttribute("context");

		// get the requested menu
		var thisMenu = this.getMenu(name);

		// extracts this menus attributes list and items
		var attributes = thisMenu["attributes"].split(',');
		var items = thisMenu.items;

		// constructs a map from the callers attributes
		var objMap = this.buildAttributeMap(attributes, caller);

		// start building the menu itself, but first remove menu if visible
		this.mykillMenu();
		this.buildMenu(caller);

		// create a table to build the menu items in
		tbl = document.createElement("TABLE");
		tbl.id = "rcRightContextTable";

		// loop the menu items and render each according to its type
		for (var m=0; m<items.length; m++) {
			switch (items[m]["type"]) {
				case this.TYPE_MENU:
					// add the menu item
					if (this.isDisplayed(items[m], objMap)) {
						this.addMenuItem(items[m], objMap, tbl);
					}
					break;

				case this.TYPE_TEXT:
					// add fixed text
					text = this.transform(items[m]["text"], objMap);
					cell = this.addTableCell(tbl, "rcMenuItemText", text);
					break;

				case this.TYPE_TEXT_EXT:
					cell = this.addTableCell(tbl, "rcMenuItemTextExt");
					url = this.transform(items[m]["url"], objMap);
					this.request(url, function() { if (RightContext.req.readyState == 4 && RightContext.req.status == 200) { cell.innerHTML = RightContext.req.responseText } });
					break;

				case this.TYPE_SEPERATOR:
					cell = this.addTableCell(tbl);
					cell.appendChild(this.getSeparator());
					break;

				default:
					// no default behaviour
					break;
			}

		}
		// append the menu item table to the menu itself
		this.contextMenu.appendChild(tbl);
		// make sure we're not overflowed to the edge of the screen.
		this.repositionMenu();

		if (this.menuTriggerEvent=="MOVE") {
			this.bindEvent('mouseout',  this.contextMenu, function(e) { RightContext.abortKill = false; setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); });
			this.bindEvent('mouseover', this.contextMenu, function(e) { RightContext.abortKill = true;  });
		} else if (this.menuTriggerEvent=="LEFT" || this.menuTriggerEvent=="RIGHT") {
			this.bindEvent('click', document.body, function(e) { setTimeout("RightContext.killMenu();", RightContext.killMenuTimeout); }, false);
		}
		this.isShowing = true;

		return false;
	},

	isDisplayed : function(item, objMap) {
		var reqVar, reqVal;
		var shown = true; // by default all items are shown, unless they require something
		// lets make sure this item does not require any condition to be true in order to display
		if (item["requires"] != null && item["requires"] != "undefined") {
			// yep, this one has a requirement...
			reqVar = item["requires"][0];
			reqVal = item["requires"][1];
			if (objMap[reqVar] != null && objMap[reqVar] != "undefined") {
				// if the condition is not met, do not show this item.
				if (objMap[reqVar] != reqVal) {
					shown = false;
				}
			} else {
				// if the condition is not defined do not show the item
				shown = false;
			}
		}
		return shown;
	},

	// check if the menu goes outside the window boundries and adjust its
	// location if so
	repositionMenu: function() {
		var mPos = this.findPosition(this.contextMenu);
		var mDim = this.getDimensions(this.contextMenu);
		var winHeight = this.getWindowHeight(); // window.innerHeight || document.body.clientHeight;
		var winWidth = window.innerWidth || document.body.clientWidth;
		if (mPos.y + mDim.height > winHeight-30 ) {
			this.position(this.contextMenu, mPos.x, mPos.y - mDim.height);
			mPos = this.findPosition(this.contextMenu);
		}
		if (mPos.x + mDim.width > winWidth - 30 ) {
			this.position(this.contextMenu, mPos.x-mDim.width, mPos.y);
		}
	},

	// returns an HR sepearator which uses the rcMenuSeparator style
	getSeparator: function () {
		var sep = document.createElement("HR");
		sep.className = "rcMenuSeparator";
		return sep;
	},

	// adds a table cell to the provided table and returns it.
	// attached a class if provided and initializes the cell with some content
	// where applicable
	addTableCell: function (table, className, content) {
		row = table.insertRow(-1);
		cell = row.insertCell(0);
		if (className) {
			cell.className = className;
			if (content) {
				cell.innerHTML = content;
			}
		}
		return cell;
	},

	// adds a menu item to the provided table. transforms all data as defined
	// in the objMap argument
	addMenuItem: function (item, objMap, tbl) {
		var title = this.transform(item["text"], objMap);
		var url, frame, img, imgAlign, itemSrc, tmp, itemAction;
		var cell = this.addTableCell(tbl, "rcMenuItem", title);
		cell.style.cursor = document.all?'hand':'pointer';
		this.bindEvent('mouseover', cell, function(e) { cell.className="rcMenuItemHover";});
		this.bindEvent('mouseout',  cell, function(e) { cell.className="rcMenuItem";     });

		// deal with image if applicable
		if (item["image"]!=null && item["image"]!="undefined") {
			// get image alignment or default to absmiddle
			imgAlign = (item["align"]!=null && item["align"]!="undefined") ? item["align"] : "absmiddle";
			// load the image from the cache, or from disk (and then cache it)
			if (this.images[item["image"]] != null && this.images[item["image"]] != "undefined") {
				img = this.images[item["image"]];
			} else {
				img = this.loadImage(item["image"]);
			}
			// set image alignment
			img.align=imgAlign;
			// insert the image as first child of the cell
			cell.insertBefore(this.images[item["image"]], cell.childNodes[0]);
		}

		if (item["url"]!=null && item["url"] != "undefined") {
			url   = this.transform(item["url"],  objMap);
			frame = false;
			if (item["frame"] != null && item["frame"] != "undefined") {
				frame = item["frame"];
			}
			cell.onclick = function () { RightContext.redirect(url, frame); }
		} else {
			// we first need to find out if the event handler contains a potential
			// tag. if so, we grab its source, transform it and re-evaluate it.
			// if this fails, the value reverts back to its original function
			itemAction = item["onclick"];
			try {

				itemSrc = item["onclick"].toString();
				if (itemSrc.indexOf('[')>-1) {
						itemSrc = this.transform(itemSrc, objMap);
						eval('itemAction = ' + itemSrc);
				}

			} catch (e) {
				// nothing...
			}

			// set the cell onclick event handler.
			cell.onclick=itemAction;
		}

	},

	// transforms a string based on the provided map
	transform: function (str, map) {
		var tStr, tmp;
		tStr = str;
		for (p in map) {
			tmp = "[" + p + "]";
			while (tStr.indexOf(tmp) > -1) {
				tStr = tStr.replace(tmp, map[p]);
			}
		}
		return tStr;
	},

	// returns the menu's attributes collection that will be used to construct
	// the transformation map
	getMenuAttributeArray: function (menu) {
		for (var i=0; i<menu.length; i++) {
			if (menu[i].type == this.TYPE_ATTRIBUTES) {
				return menu[i]["attributes"].split(',');
			}
		}
		return new Array(0);
	},

	// construct the transformation map for a given object based on the tags in
	// attribs
	buildAttributeMap: function (attribs, obj) {
		var thisAttr, thisValue;
		var attrMap = new Object();

		for (var a=0; a<attribs.length; a++) {
			thisAttr = attribs[a];
			thisValue = obj.getAttribute(attribs[a]);
			if (typeof thisValue != "undefined") {
				attrMap[thisAttr] = thisValue;
			}
		}
		return attrMap;
	},

	// find the position of an element on the screen and returns an array of [x,y]
	findPosition: function (obj) {
		var lft = 0;
		var top = 0;
		if (obj.offsetParent) {
			lft = obj.offsetLeft
			top = obj.offsetTop
			while (obj = obj.offsetParent) {
				lft += obj.offsetLeft
				top += obj.offsetTop
			}
		}
		return {x:lft,y:top};
	},

	getWindowHeight: function() {
		if (this.browser.khtml || this.browser.safari) {
			return this.innerHeight;
		} else if (this.browser.opera) {
			return document.body.clientHeight;
		} else {
			return document.documentElement.clientHeight;
		}
	},

	// Returns the dimensions of an element on screen. Lifted from the wonderful
	// prototype framework
	getDimensions: function(obj) {
		//var display = obj.getStyle('display');
		//if (display != 'none' && display != null) // Safari bug
		//  return {width: element.offsetWidth, height: element.offsetHeight};

		// All *Width and *Height properties give 0 on elements with display none,
		// so enable the element temporarily
		var objStyle = obj.style;
		var originalVisibility = objStyle.visibility;
		var originalPosition = objStyle.position;
		var originalDisplay = objStyle.display;
		objStyle.visibility = 'hidden';
		objStyle.position = 'absolute';
		objStyle.display = 'block';
		var originalWidth = obj.clientWidth;
		var originalHeight = obj.clientHeight;
		objStyle.display = originalDisplay;
		objStyle.position = originalPosition;
		objStyle.visibility = originalVisibility;
		return {width: originalWidth, height: originalHeight};
	},

	// positions object at x,y coordinates
	// v0.2 - added px to the position coordinate (provided by JDG)
	position: function (obj, x, y) {
		obj.style.left = x + 'px';
		obj.style.top  = y + 'px';
	},

	// builds a menu for parent object
	buildMenu: function (parent) {
		var pos, dim, tbl;
		//document.onmousemove  = RightContext.getMousePos;
		this.contextMenu = document.createElement("DIV");
		this.contextMenu.id = "rcRightContext";
		this.contextMenu.className = 'rcMenuContainer';

		// get the position and dimensions of the parent
		pos = this.findPosition(parent);
		dim = this.getDimensions(parent);

		// position the container to the bottom right of the element.
		this.position (this.contextMenu, this.mousePos.x + this.rightOffset, pos.y+dim.height);

		// set some event handlers
		// if the menu is triggered by a right click, disable the right click on the menu itself
		if (this.menuTriggerEvent == "RIGHT") {
			this.contextMenu.oncontextmenu = function () { return false; };
		}

		// add the container to the body of the document
		document.body.appendChild(this.contextMenu);

	},


	// kills the currently visible context menu
	killMenu: function () {
		if (!this.abortKill && this.isShowing) {
		try {
			rc = this.contextMenu;
			document.body.removeChild(rc);
		} catch (e) {
			// already removed?
		}
		this.contextMenu = null;
		this.isShowing = false;
		this.abortKill = false;
		}
	},
	
	// kills the currently visible context menu always
	mykillMenu: function () {
		try {
			rc = this.contextMenu;
			document.body.removeChild(rc);
		} catch (e) {
			// already removed?
		}
		this.contextMenu = null;
		this.isShowing = false;
	},

	// locate the mouse cursor position
	locateMousePos: function(e) {
		var posx = 0, posy =0;
		if(e==null) e=window.event;
		if(e.pageX || e.pageY) {
			posx=e.pageX; posy=e.pageY;
		} else if (e.clientX || e.clientY) {
			if(document.documentElement.scrollTop){
				posx=e.clientX+document.documentElement.scrollLeft;
				posy=e.clientY+document.documentElement.scrollTop;
			} else {
				posx=e.clientX+document.body.scrollLeft;
				posy=e.clientY+document.body.scrollTop;
			}
		}
		this.mousePos = {x:posx , y:posy};

	},

	// redirects the browser to given url
	// if frame!=false, it will open in provided frame (or new win if _blank)
	redirect: function (u, frame) {
		if (!frame) {
			document.location = u;
		} else {
			if (frame=="_blank") {
				w = window.open(u, 'w');
			} else {
				window.frames[frame].document.location = u;
			}
		}
	},

	// performs a request - ajax style
	request: function (url, callBack) {
		if (window.XMLHttpRequest) { // native XMLHttpRequest
			this.req = new XMLHttpRequest();
			this.req.onreadystatechange =  callBack;
			this.req.open("GET", url, true);
			this.req.send(null);
		} else if (window.ActiveXObject) { // The M$ 'standard'
			this.req = new ActiveXObject("Microsoft.XMLHTTP");
			if (this.req) {
				this.req.onreadystatechange =   callBack;
				this.req.open("GET", url, true);
				this.req.send();
			}
		}
	},

	loadImage: function (url) {
		var img = new Image();
		img.src = url;
		img.className = "rcImage";
		this.images[url] = img;
		return img;
	},

	detectBrowser: function() {
		var ua = navigator.userAgent.toUpperCase();
		var up = navigator.platform.toUpperCase().substr(0,3);
		var isSafari  = (ua.indexOf("SAFARI"   )>0);
		var isKHTML   = (ua.indexOf("KONQUEROR")>0 || isSafari);
		var isFirefox = (ua.indexOf("FIREFOX"  )>0);
		var isOpera   = (ua.indexOf("OPERA"    )>=0);
		return { safari: isSafari, khtml: isKHTML, opera: isOpera, firefox: isFirefox, platform: up }
	}

};

function submit_layout_design(name, value, attr, url) {
    new Ajax.Request(url + '/' + name + '/'+ value +'/'+ attr, {
     method: 'get',
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         var result = transport.responseText;

         var result_array = result.split('|||');
         var name = result_array[0];
         var attr = result_array[1];
         var value = result_array[2];

         try {
            var temp_script = 'for(i = 0; i < window.document.getElementsByName(\''+name+'\').length; i++) { window.document.getElementsByName(\''+name+'\')[i].setAttribute(\''+attr+'\', \''+value+'\'); }';

            if (window.execScript) {
               window.execScript(temp_script);
            } else {
               window.eval(temp_script);
            }
            
            RightContext.mykillMenu();
         } catch(err) {
            alert("Nie udało się podmienić źródła strony w locie. Strona zostanie odświeżona.\n\nERROR JS:\n\n"+ err.description +"\n\n"+ err);
            window.location.reload(true);
         }
       }
     }
   });
}

function print_before_nodes_tag(obj) {
   var text = '';
   var name = '';
   var text_name = '';
   
   var act_obj = obj;
   var tag_name = act_obj.tagName;
   name = act_obj.getAttribute('name');

   while (tag_name != 'BODY') {
      if(name && name.indexOf('LD_') > -1) {
         text_name = '['+name+']';
      } else {
         text_name = '';
      }

      text = '>'+ tag_name.toLowerCase() + text_name + text;
      
      act_obj = act_obj.parentNode
      tag_name = act_obj.tagName;
      name = act_obj.getAttribute('name');
   }
   
   text = 'body' + text;

   obj.setAttribute('title_hint', 'Edytuj Design:<br>'+ text);
   obj.setAttribute('attr2', text);
}

var old_elems_bckg = new Array();
function highlite_elems(name) {
   var obj = document.getElementsByName(name);
   
   for(var i = 0; i < obj.length; i++) {
      old_elems_bckg[name] = obj[i].style.backgroundColor;

      obj[i].style.backgroundColor = '#FF00FF';

      var act_obj = obj[i];

      Effect.Pulsate(act_obj, { pulses: 3, duration: 1, afterFinish: function(act_obj) {
            new Effect.Opacity(act_obj, { from: 1, to: 1, duration: 0.1 });
         }
       });
   }
}

function unhighlite_elems(name) {
   var obj = document.getElementsByName(name);

   for(var i = 0; i < obj.length; i++) {
      obj[i].style.backgroundColor = old_elems_bckg[name];
      new Effect.Opacity(obj[i], { from: 1, to: 1, duration: 0.1 });
   }
}

var act_map_div = -1;
function load_map(url, div_id) {
   new Ajax.Request(url, {
     method: 'get',
     //onLoading: function(transport) {show_loading();},
     onComplete: function(transport) {
       if (transport.readyState == 4) {
         if(document.getElementById(div_id)) {
            if(act_map_div != -1) {
               document.getElementById(act_map_div).innerHTML = '';
               Element.hide(act_map_div);
            }

            act_map_div = div_id;
            Element.show(act_map_div);
            
            // ODPALANIE SKRYPTÓW KTÓRE PRZYSZŁY W ODPOWIEDZI
            run_ajax_script(transport.responseText);
            ////////////////////////////////////////////////////////////////
            
            document.getElementById(act_map_div).innerHTML = transport.responseText;

         }
       }
     }
   });
}