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

var Prototype = {
  Version: '1.6.1',

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

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

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

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

      div = form = null;

      return isSupported;
    })()
  },

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

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

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


var Abstract = { };


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

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

    return returnValue;
  }
};

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

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

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

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

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

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

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

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

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

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

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

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

    return this;
  }

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

  var _toString = Object.prototype.toString;

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

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

    this.registerCallback();
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

    return camelized;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var $break = { };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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









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

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

Array.from = $A;


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

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

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

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

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

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

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

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

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

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

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


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

  function size() {
    return this.length;
  }

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

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

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

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

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

  Object.extend(arrayProto, Enumerable);

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

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

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

  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  function succ() {
    return this + 1;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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



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

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

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

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

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

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

Object.extend(Ajax.Responders, Enumerable);

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

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

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

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

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

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

    this.parameters = params;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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








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

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

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

  status:      0,

  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

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

  getHeader: Ajax.Request.prototype.getHeader,

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

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

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

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

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

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

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

    $super(url, options);
  },

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

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

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

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

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

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

    this.start();
  },

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

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

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

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

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



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

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

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

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

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


(function(global) {

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

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

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

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

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


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

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

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

  update: (function(){

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

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

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

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

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

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

      content = Object.toHTML(content);

      var tagName = element.tagName.toUpperCase();

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

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

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

    return update;
  })(),

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

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

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

    var content, insert, tagName, childNodes;

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

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

      content = Object.toHTML(content);

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

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

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

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

    return element;
  },

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

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return false;
  },

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

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

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

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

    return element;
  },

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

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

    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
      els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      if (Prototype.Browser.Opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

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

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

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

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName.toUpperCase() == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

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

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

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

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

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

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

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

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

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

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

    return $(document.body);
  },

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

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

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

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

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

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

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

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

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

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

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

  childElements: Element.Methods.immediateDescendants
});

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Element._attributeTranslations = (function(){

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

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

    el.setAttribute(classProp, 'x');

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

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

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

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

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

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

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

  Element._attributeTranslations.has = {};

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

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

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

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

}

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

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

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

    return element;
  };

  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

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

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

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

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

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

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

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

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

Element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

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

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

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

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

(function(div) {

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

  div = null;

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

Element.extend = (function() {

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

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

  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

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

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

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

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

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

    extendElementWith(element, methods);

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


document.viewport = {

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

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

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

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

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

    return document.documentElement;
  }

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

    property[D] = 'client' + D;

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

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

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


Element.Storage = {
  UID: 1
};

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

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

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

    return Element.Storage[uid];
  },

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

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

    return element;
  },

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

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

    return value;
  },

  clone: function(element, deep) {
    if (!(element = $(element))) return;
    var clone = element.cloneNode(deep);
    clone._prototypeUID = void 0;
    if (deep) {
      var descendants = Element.select(clone, '*'),
          i = descendants.length;
      while (i--) {
        descendants[i]._prototypeUID = void 0;
      }
    }
    return Element.extend(clone);
  }
});
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

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

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

  },

  shouldUseXPath: (function() {

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

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

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

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

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

      var e = this.expression;

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

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

      if (IS_DESCENDANT_SELECTOR_BUGGY) return false;

      return true;
    }

  })(),

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

    if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;

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

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

    return true;
  },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i = 0; i<len; i++) {
        p = ps[i].re;
        name = ps[i].name;
        if (m = e.match(p)) {
          if (as[name]) {
            this.tokens.push([name, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            return this.findElements(document).include(element);
          }
        }
      }
    }

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

    return match;
  },

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

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

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

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

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

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
      'checked':     "[@checked]",
      'disabled':    "[(@disabled) and (@type!='hidden')]",
      'enabled':     "[not(@disabled) and (@type!='hidden')]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v, len = p.length, name;

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

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: [
    { name: 'laterSibling', re: /^\s*~\s*/ },
    { name: 'child',        re: /^\s*>\s*/ },
    { name: 'adjacent',     re: /^\s*\+\s*/ },
    { name: 'descendant',   re: /^\s/ },

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

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

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

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

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

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

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

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

    unmark: (function(){

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

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

    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (node.tagName == '!' || node.firstChild) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled && (!node.type || node.type !== 'hidden'))
          results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

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

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = Selector.split(expressions.join(','));
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

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

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

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

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

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

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

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

  getElements: function(form) {
    var elements = $(form).getElementsByTagName('*'),
        element,
        arr = [ ],
        serializers = Form.Element.Serializers;
    for (var i = 0; element = elements[i]; i++) {
      arr.push(element);
    }
    return arr.inject([], function(elements, child) {
      if (serializers[child.tagName.toLowerCase()])
        elements.push(Element.extend(child));
      return elements;
    })
  },

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

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

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

    return matchingInputs;
  },

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

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

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

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

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

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

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

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

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

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

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


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

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

Form.Element.Methods = {

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

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

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

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

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

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

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

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

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

var Field = Form.Element;

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

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

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

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

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

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

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

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

    cache: {}
  };

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

  var _isButton;
  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    _isButton = function(event, code) {
      return event.button === buttonMap[code];
    };
  } else if (Prototype.Browser.WebKit) {
    _isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };
  } else {
    _isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

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

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

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

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

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

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

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

    return Element.extend(node);
  }

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

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

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

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

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

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


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

    event.stopped = true;
  }

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

    element: element,
    findElement: findElement,

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

    stop: stop
  };


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            if (parent === element) return;

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

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

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

  var CACHE = [];

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

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


  var _getDOMEventName = Prototype.K;

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

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

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

    if (!responder) return element;

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

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

    return element;
  }

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

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

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

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

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

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

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

    var responders = registry.get(eventName);

    if (!responders) return;

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

    var actualEventName = _getDOMEventName(eventName);

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

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

    return element;
  }

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

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

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

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

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

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

    return Event.extend(event);
  }


  Object.extend(Event, Event.Methods);

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

  Element.addMethods({
    fire:          fire,

    observe:       observe,

    stopObserving: stopObserving
  });

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

    observe:       observe.methodize(),

    stopObserving: stopObserving.methodize(),

    loaded:        false
  });

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

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

  var timer;

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

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

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

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

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

Element.addMethods();

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

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

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

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

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

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

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

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

var Position = {
  includeScrollOffsets: false,

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

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

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

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

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

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

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


  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

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

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

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*--------------------------------------------------------------------------*/
// script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

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

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

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

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

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

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

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

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

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

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

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

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

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect, options) {
    element = $(element);
    effect  = (effect || 'appear').toLowerCase();
    
    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, options || {}));
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable, {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();

    var position = Object.isString(effect.options.queue) ?
      effect.options.queue : effect.options.queue.position;

    switch(position) {
      case 'front':
        // move unstarted effects after this effect
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }

    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);

    if (!this.interval)
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if (this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++)
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;

    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return styleRules;
};

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

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

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

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

Element.addMethods(Effect.Methods);// script.aculo.us dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

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

if(Object.isUndefined(Effect))
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
    element = $(element);
    var options = Object.extend({
      greedy:     true,
      hoverclass: null,
      tree:       false
    }, arguments[1] || { });

    // cache containers
    if(options.containment) {
      options._containers = [];
      var containment = options.containment;
      if(Object.isArray(containment)) {
        containment.each( function(c) { options._containers.push($(c)) });
      } else {
        options._containers.push($(containment));
      }
    }

    if(options.accept) options.accept = [options.accept].flatten();

    Element.makePositioned(element); // fix IE
    options.element = element;

    this.drops.push(options);
  },

  findDeepestChild: function(drops) {
    deepest = drops[0];

    for (i = 1; i < drops.length; ++i)
      if (Element.isParent(drops[i].element, deepest.element))
        deepest = drops[i];

    return deepest;
  },

  isContained: function(element, drop) {
    var containmentNode;
    if(drop.tree) {
      containmentNode = element.treeNode;
    } else {
      containmentNode = element.parentNode;
    }
    return drop._containers.detect(function(c) { return containmentNode == c });
  },

  isAffected: function(point, element, drop) {
    return (
      (drop.element!=element) &&
      ((!drop._containers) ||
        this.isContained(element, drop)) &&
      ((!drop.accept) ||
        (Element.classNames(element).detect(
          function(v) { return drop.accept.include(v) } ) )) &&
      Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
    if(drop.hoverclass)
      Element.removeClassName(drop.element, drop.hoverclass);
    this.last_active = null;
  },

  activate: function(drop) {
    if(drop.hoverclass)
      Element.addClassName(drop.element, drop.hoverclass);
    this.last_active = drop;
  },

  show: function(point, element) {
    if(!this.drops.length) return;
    var drop, affected = [];

    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });

    if(affected.length>0)
      drop = Droppables.findDeepestChild(affected);

    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
    if (drop) {
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

      if (drop != this.last_active) Droppables.activate(drop);
    }
  },

  fire: function(event, element) {
    if(!this.last_active) return;
    Position.prepare();

    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
      if (this.last_active.onDrop) {
        this.last_active.onDrop(element, this.last_active.element, event);
        return true;
      }
  },

  reset: function() {
    if(this.last_active)
      this.deactivate(this.last_active);
  }
};

var Draggables = {
  drags: [],
  observers: [],

  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove);
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },

  unregister: function(draggable) {
    this.drags = this.drags.reject(function(d) { return d==draggable });
    if(this.drags.length == 0) {
      Event.stopObserving(document, "mouseup", this.eventMouseUp);
      Event.stopObserving(document, "mousemove", this.eventMouseMove);
      Event.stopObserving(document, "keypress", this.eventKeypress);
    }
  },

  activate: function(draggable) {
    if(draggable.options.delay) {
      this._timeout = setTimeout(function() {
        Draggables._timeout = null;
        window.focus();
        Draggables.activeDraggable = draggable;
      }.bind(this), draggable.options.delay);
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeDraggable = draggable;
    }
  },

  deactivate: function() {
    this.activeDraggable = null;
  },

  updateDrag: function(event) {
    if(!this.activeDraggable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    // Mozilla-based browsers fire successive mousemove events with
    // the same coordinates, prevent needless redrawing (moz bug?)
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;

    this.activeDraggable.updateDrag(event, pointer);
  },

  endDrag: function(event) {
    if(this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
  },

  keyPress: function(event) {
    if(this.activeDraggable)
      this.activeDraggable.keyPress(event);
  },

  addObserver: function(observer) {
    this.observers.push(observer);
    this._cacheObserverCallbacks();
  },

  removeObserver: function(element) {  // element instead of observer fixes mem leaks
    this.observers = this.observers.reject( function(o) { return o.element==element });
    this._cacheObserverCallbacks();
  },

  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag', 'onAfterDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, draggable, event);
      });
    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },

  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onDrag','onAfterDrag'].each( function(eventName) {
      Draggables[eventName+'Count'] = Draggables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
};

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

var Draggable = Class.create({
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){
            Draggable._dragging[element] = false
          }
        });
      },
      zindex: 1000,
      revert: false,
      quiet: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };

    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
      Object.extend(defaults, {
        starteffect: function(element) {
          element._opacity = Element.getOpacity(element);
          Draggable._dragging[element] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
        }
      });

    var options = Object.extend(defaults, arguments[1] || { });

    this.element = $(element);

    if(options.handle && Object.isString(options.handle))
      this.handle = this.element.down('.'+options.handle, 0);

    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;

    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

    Element.makePositioned(this.element); // fix IE

    this.options  = options;
    this.dragging = false;

    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);

    Draggables.register(this);
  },

  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Draggables.unregister(this);
  },

  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this.element,'left') || '0'),
      parseInt(Element.getStyle(this.element,'top') || '0')]);
  },

  initDrag: function(event) {
    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;

      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      var pos     = this.element.cumulativeOffset();
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });

      Draggables.activate(this);
      Event.stop(event);
    }
  },

  startDrag: function(event) {
    this.dragging = true;
    if(!this.delta)
      this.delta = this.currentDelta();

    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }

    if(this.options.ghosting) {
      this._clone = this.element.cloneNode(true);
      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
      if (!this._originallyAbsolute)
        Position.absolutize(this.element);
      this.element.parentNode.insertBefore(this._clone, this.element);
    }

    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }

    Draggables.notify('onStart', this, event);

    if(this.options.starteffect) this.options.starteffect(this.element);
  },

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);

    if(!this.options.quiet){
      Position.prepare();
      Droppables.show(pointer, this.element);
    }

    Draggables.notify('onDrag', this, event);

    this.draw(pointer);
    if(this.options.change) this.options.change(this);

    if(this.options.scroll) {
      this.stopScrolling();

      var p;
      if (this.options.scroll == window) {
        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
      } else {
        p = Position.page(this.options.scroll);
        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
        p[1] += this.options.scroll.scrollTop + Position.deltaY;
        p.push(p[0]+this.options.scroll.offsetWidth);
        p.push(p[1]+this.options.scroll.offsetHeight);
      }
      var speed = [0,0];
      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
      this.startScrolling(speed);
    }

    // fix AppleWebKit rendering
    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
    Draggables.notify('onAfterDrag', this, event);
    Event.stop(event);
  },

  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }

    if(this.options.ghosting) {
      if (!this._originallyAbsolute)
        Position.relativize(this.element);
      delete this._originallyAbsolute;
      Element.remove(this._clone);
      this._clone = null;
    }

    var dropped = false;
    if(success) {
      dropped = Droppables.fire(event, this.element);
      if (!dropped) dropped = false;
    }
    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && Object.isFunction(revert)) revert = revert(this.element);

    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (dropped == 0 || revert != 'failure')
        this.options.reverteffect(this.element,
          d[1]-this.delta[1], d[0]-this.delta[0]);
    } else {
      this.delta = d;
    }

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect)
      this.options.endeffect(this.element);

    Draggables.deactivate(this);
    Droppables.reset();
  },

  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },

  endDrag: function(event) {
    if(!this.dragging) return;
    this.stopScrolling();
    this.finishDrag(event, true);
    Event.stop(event);
  },

  draw: function(point) {
    var pos = this.element.cumulativeOffset();
    if(this.options.ghosting) {
      var r   = Position.realOffset(this.element);
      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
    }

    var d = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];

    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
    }

    var p = [0,1].map(function(i){
      return (point[i]-pos[i]-this.offset[i])
    }.bind(this));

    if(this.options.snap) {
      if(Object.isFunction(this.options.snap)) {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(Object.isArray(this.options.snap)) {
        p = p.map( function(v, i) {
          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
      } else {
        p = p.map( function(v) {
          return (v/this.options.snap).round()*this.options.snap }.bind(this));
      }
    }}

    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
      style.left = p[0] + "px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
      style.top  = p[1] + "px";

    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },

  stopScrolling: function() {
    if(this.scrollInterval) {
      clearInterval(this.scrollInterval);
      this.scrollInterval = null;
      Draggables._lastScrollPointer = null;
    }
  },

  startScrolling: function(speed) {
    if(!(speed[0] || speed[1])) return;
    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
    this.lastScrolled = new Date();
    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
  },

  scroll: function() {
    var current = new Date();
    var delta = current - this.lastScrolled;
    this.lastScrolled = current;
    if(this.options.scroll == window) {
      with (this._getWindowScroll(this.options.scroll)) {
        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
          var d = delta / 1000;
          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
        }
      }
    } else {
      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
    }

    Position.prepare();
    Droppables.show(Draggables._lastPointer, this.element);
    Draggables.notify('onDrag', this);
    if (this._isScrollChild) {
      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
      if (Draggables._lastScrollPointer[0] < 0)
        Draggables._lastScrollPointer[0] = 0;
      if (Draggables._lastScrollPointer[1] < 0)
        Draggables._lastScrollPointer[1] = 0;
      this.draw(Draggables._lastScrollPointer);
    }

    if(this.options.change) this.options.change(this);
  },

  _getWindowScroll: function(w) {
    var T, L, W, H;
    with (w.document) {
      if (w.document.documentElement && documentElement.scrollTop) {
        T = documentElement.scrollTop;
        L = documentElement.scrollLeft;
      } else if (w.document.body) {
        T = body.scrollTop;
        L = body.scrollLeft;
      }
      if (w.innerWidth) {
        W = w.innerWidth;
        H = w.innerHeight;
      } else if (w.document.documentElement && documentElement.clientWidth) {
        W = documentElement.clientWidth;
        H = documentElement.clientHeight;
      } else {
        W = body.offsetWidth;
        H = body.offsetHeight;
      }
    }
    return { top: T, left: L, width: W, height: H };
  }
});

Draggable._dragging = { };

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

var SortableObserver = Class.create({
  initialize: function(element, observer) {
    this.element   = $(element);
    this.observer  = observer;
    this.lastValue = Sortable.serialize(this.element);
  },

  onStart: function() {
    this.lastValue = Sortable.serialize(this.element);
  },

  onEnd: function() {
    Sortable.unmark();
    if(this.lastValue != Sortable.serialize(this.element))
      this.observer(this.element)
  }
});

var Sortable = {
  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,

  sortables: { },

  _findRootElement: function(element) {
    while (element.tagName.toUpperCase() != "BODY") {
      if(element.id && Sortable.sortables[element.id]) return element;
      element = element.parentNode;
    }
  },

  options: function(element) {
    element = Sortable._findRootElement($(element));
    if(!element) return;
    return Sortable.sortables[element.id];
  },

  destroy: function(element){
    element = $(element);
    var s = Sortable.sortables[element.id];

    if(s) {
      Draggables.removeObserver(s.element);
      s.droppables.each(function(d){ Droppables.remove(d) });
      s.draggables.invoke('destroy');

      delete Sortable.sortables[s.element.id];
    }
  },

  create: function(element) {
    element = $(element);
    var options = Object.extend({
      element:     element,
      tag:         'li',       // assumes li children, override with tag: 'tagname'
      dropOnEmpty: false,
      tree:        false,
      treeTag:     'ul',
      overlap:     'vertical', // one of 'vertical', 'horizontal'
      constraint:  'vertical', // one of 'vertical', 'horizontal', false
      containment: element,    // also takes array of elements (or id's); or false
      handle:      false,      // or a CSS class
      only:        false,
      delay:       0,
      hoverclass:  null,
      ghosting:    false,
      quiet:       false,
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,

      // these take arrays of elements or ids and can be
      // used for better initialization performance
      elements:    false,
      handles:     false,

      onChange:    Prototype.emptyFunction,
      onUpdate:    Prototype.emptyFunction
    }, arguments[1] || { });

    // clear any old sortable with same element
    this.destroy(element);

    // build options for the draggables
    var options_for_draggable = {
      revert:      true,
      quiet:       options.quiet,
      scroll:      options.scroll,
      scrollSpeed: options.scrollSpeed,
      scrollSensitivity: options.scrollSensitivity,
      delay:       options.delay,
      ghosting:    options.ghosting,
      constraint:  options.constraint,
      handle:      options.handle };

    if(options.starteffect)
      options_for_draggable.starteffect = options.starteffect;

    if(options.reverteffect)
      options_for_draggable.reverteffect = options.reverteffect;
    else
      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
        element.style.top  = 0;
        element.style.left = 0;
      };

    if(options.endeffect)
      options_for_draggable.endeffect = options.endeffect;

    if(options.zindex)
      options_for_draggable.zindex = options.zindex;

    // build options for the droppables
    var options_for_droppable = {
      overlap:     options.overlap,
      containment: options.containment,
      tree:        options.tree,
      hoverclass:  options.hoverclass,
      onHover:     Sortable.onHover
    };

    var options_for_tree = {
      onHover:      Sortable.onEmptyHover,
      overlap:      options.overlap,
      containment:  options.containment,
      hoverclass:   options.hoverclass
    };

    // fix for gecko engine
    Element.cleanWhitespace(element);

    options.draggables = [];
    options.droppables = [];

    // drop on empty handling
    if(options.dropOnEmpty || options.tree) {
      Droppables.add(element, options_for_tree);
      options.droppables.push(element);
    }

    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
      var handle = options.handles ? $(options.handles[i]) :
        (options.handle ? $(e).select('.' + options.handle)[0] : e);
      options.draggables.push(
        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
      Droppables.add(e, options_for_droppable);
      if(options.tree) e.treeNode = element;
      options.droppables.push(e);
    });

    if(options.tree) {
      (Sortable.findTreeElements(element, options) || []).each( function(e) {
        Droppables.add(e, options_for_tree);
        e.treeNode = element;
        options.droppables.push(e);
      });
    }

    // keep reference
    this.sortables[element.identify()] = options;

    // for onupdate
    Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.tag);
  },

  findTreeElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
    if(Element.isParent(dropon, element)) return;

    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
      return;
    } else if(overlap>0.5) {
      Sortable.mark(dropon, 'before');
      if(dropon.previousSibling != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, dropon);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    } else {
      Sortable.mark(dropon, 'after');
      var nextElement = dropon.nextSibling || null;
      if(nextElement != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, nextElement);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    }
  },

  onEmptyHover: function(element, dropon, overlap) {
    var oldParentNode = element.parentNode;
    var droponOptions = Sortable.options(dropon);

    if(!Element.isParent(dropon, element)) {
      var index;

      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
      var child = null;

      if(children) {
        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);

        for (index = 0; index < children.length; index += 1) {
          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
            offset -= Element.offsetSize (children[index], droponOptions.overlap);
          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
            child = index + 1 < children.length ? children[index + 1] : null;
            break;
          } else {
            child = children[index];
            break;
          }
        }
      }

      dropon.insertBefore(element, child);

      Sortable.options(oldParentNode).onChange(element);
      droponOptions.onChange(element);
    }
  },

  unmark: function() {
    if(Sortable._marker) Sortable._marker.hide();
  },

  mark: function(dropon, position) {
    // mark on ghosting only
    var sortable = Sortable.options(dropon.parentNode);
    if(sortable && !sortable.ghosting) return;

    if(!Sortable._marker) {
      Sortable._marker =
        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
          hide().addClassName('dropmarker').setStyle({position:'absolute'});
      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
    }
    var offsets = dropon.cumulativeOffset();
    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});

    if(position=='after')
      if(sortable.overlap == 'horizontal')
        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
      else
        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});

    Sortable._marker.show();
  },

  _tree: function(element, options, parent) {
    var children = Sortable.findElements(element, options) || [];

    for (var i = 0; i < children.length; ++i) {
      var match = children[i].id.match(options.format);

      if (!match) continue;

      var child = {
        id: encodeURIComponent(match ? match[1] : null),
        element: element,
        parent: parent,
        children: [],
        position: parent.children.length,
        container: $(children[i]).down(options.treeTag)
      };

      /* Get the element containing the children and recurse over it */
      if (child.container)
        this._tree(child.container, options, child);

      parent.children.push (child);
    }

    return parent;
  },

  tree: function(element) {
    element = $(element);
    var sortableOptions = this.options(element);
    var options = Object.extend({
      tag: sortableOptions.tag,
      treeTag: sortableOptions.treeTag,
      only: sortableOptions.only,
      name: element.id,
      format: sortableOptions.format
    }, arguments[1] || { });

    var root = {
      id: null,
      parent: null,
      children: [],
      container: element,
      position: 0
    };

    return Sortable._tree(element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
    var index = '';
    do {
      if (node.id) index = '[' + node.position + ']' + index;
    } while ((node = node.parent) != null);
    return index;
  },

  sequence: function(element) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[1] || { });

    return $(this.findElements(element, options) || []).map( function(item) {
      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
    });
  },

  setSequence: function(element, new_sequence) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[2] || { });

    var nodeMap = { };
    this.findElements(element, options).each( function(n) {
        if (n.id.match(options.format))
            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
        n.parentNode.removeChild(n);
    });

    new_sequence.each(function(ident) {
      var n = nodeMap[ident];
      if (n) {
        n[1].appendChild(n[0]);
        delete nodeMap[ident];
      }
    });
  },

  serialize: function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments[1] || { });
    var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);

    if (options.tree) {
      return Sortable.tree(element, arguments[1]).children.map( function (item) {
        return [name + Sortable._constructIndex(item) + "[id]=" +
                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1]).map( function(item) {
        return name + "[]=" + encodeURIComponent(item);
      }).join('&');
    }
  }
};

// Returns true if child is contained within element
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;
  if (child.parentNode == element) return true;
  return Element.isParent(child.parentNode, element);
};

Element.findChildren = function(element, only, recursive, tagName) {
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
    if(e.tagName && e.tagName.toUpperCase()==tagName &&
      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
        elements.push(e);
    if(recursive) {
      var grandchildren = Element.findChildren(e, only, recursive, tagName);
      if(grandchildren) elements.push(grandchildren);
    }
  });

  return (elements.length>0 ? elements.flatten() : []);
};

Element.offsetSize = function (element, type) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};// script.aculo.us scriptaculous.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009

// Copyright (c) 2005-2009 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.3',
  require: function(libraryName) {
    try{
      // inserting via DOM fails in Safari 2.0, so brute force approach
      document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
    } catch(e) {
      // for xhtml+xml served content, fall back to DOM methods
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = libraryName;
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

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

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('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();/**
 * @category   Objects
 * @package    n/a
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

$a = function(url, callback){
    return new Ajax.Request(url, {
        method: 'get',
        onSuccess : callback 
    });
}

$ap = function(url, callback){
    var parameters = false;
    if (url.indexOf("?") != -1){
        parameters = url.split("?")[1];
        url = url.split("?")[0];
    }
    return new Ajax.Request(url, {
        method: 'post',
        parameters : parameters || '',
        onSuccess : callback 
    });
}

String.prototype.ucFirst = function () {
    return this.substr(0,1).toUpperCase() + this.substr(1,this.length);
};

String.prototype.trim = function(){
    return this.replace (/^\s+/, '').replace (/\s+$/, '');
}

Array.prototype.implode = function(delimiter){
    var value = "";
    this.each(function(e){
        value = value + e + delimiter;
    }.bind(this));
    return value.substring(0,value.length - 1);
}

//Falls wir im IE sind dann kennt er indexOF nicht, also nachbaun ;)
if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){ if(this[i]==obj) return i; }
        return -1;
    }
}


document.getScrollLeft = function(){
    var x = 0;
    if (self.pageXOffset){ 
        x = self.pageXOffset;
    }else if (document.documentElement && document.documentElement.scrollLeft){ 
        x = document.documentElement.scrollLeft; 
    }else if (document.body) { 
        x = document.body.scrollLeft;
    }

    return x;
}


function clone(e) {
  var newObj = (e instanceof Array) ? [] : {};
  for (i in e) {
    if (i == 'clone') continue;
    if (e[i] && typeof e[i] == "object") {
      newObj[i] = clone(e[i]);
    } else newObj[i] = e[i]
  } return newObj;
};


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":            
            mixed_value = mixed_value;
            val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
            break;
        case "array":
        case "object":            
            val = "a";
            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;
}

function extendClass(class1, class2){
    try{
        eval(class1 + ".prototype = new " + class2 + "();");
        eval(class1 + ".prototype.super = '" + class1 + "';");
        return true;
    }catch(e){
        return false;
    }
}

function disableSelection(target){
    if (typeof target.onselectstart!="undefined"){ //IE route
            target.onselectstart=function(){return false}
    }else if (typeof target.style.MozUserSelect!="undefined"){ //Firefox route
            target.style.MozUserSelect="none";
    }else{ //All other route (ie: Opera)
            target.onmousedown=function(){return false}
    }
    target.style.cursor = "default";
}


Object.extend(Event, {
  KEY_STRG : 17,
  KEY_SHIFT : 16 
});

Prototype.Browser.Konqueror = navigator.userAgent.indexOf('Konqueror/') > -1;
Prototype.Browser.IE8 = navigator.userAgent.indexOf('MSIE 8.') > -1;

Array.prototype.observe = function(event, callback){
    this.each(function(e){
        e.observe(event, callback);
    }.bind(this));
}

Array.prototype.draggable = function(options){
    this.each(function(e){
        new Draggable(e.id, options);
    }.bind(this));
}

Array.prototype.show = function(){
    this.each(function(e){
    	e.show();
    }.bind(this));
}

Array.prototype.hide = function(){
    this.each(function(e){
    	e.hide();
    }.bind(this));
}

Array.prototype.remove = function(){
    this.each(function(e){
    	e.remove();
    }.bind(this));
}


/**
 * 
 * 
 * @params string formular         Formular ID
 * @params array/string 'optional' Without given default values
 */
function form2object(formular){
    var without = arguments[1]
                  ? arguments[1] instanceof Array ? arguments[1] : [arguments[1]]
                  : [];

    var result = {};
    $(formular).select("input").each(function(input){
        if (input.name && without.indexOf($F(input)) == -1) {
            if (input.type == 'checkbox') {

                var value = input.checked == true ? 'on' : 'off' ;

                if (without.indexOf(value) == -1) {
                    result[input.name] = value;
                }

            } else {
                result[input.name] = input.value;
            }
        }
    });

    $(formular).select("select").each(function(input){
        if (input.name && without.indexOf($F(input)) == -1) {
            result[input.name] = $F(input);
        }
    });

    $(formular).select("textarea").each(function(textarea){
        if (textarea.name) {
            var resultvar = textarea.value.replace(/\n/g, "").replace(/\t/g, "");
            if (without.indexOf(resultvar) == -1) {
                result[textarea.name] = resultvar;
            }
        }
    });

    return result;
}

function object2table(table_json){

    var myTable = new Element("table");
    var myThead = new Element("thead");
    var myTBody = new Element("tbody");
    if (table_json.attributes.id) myTable.id = table_json.attributes.id;
    if (table_json.attributes.border) myTable.border = table_json.attributes.border;

    if (typeof table_json.attributes !== 'undefined') {
        if (table_json.attributes.id) myTable.id = table_json.attributes.id;
        if (table_json.attributes.border) myTable.border = table_json.attributes.border;
    }

    table_json.thead.each(function(tr){

		var container = new Element("tr");
        tr.childs.each(function(td){
            if (td.type == "td") var innerContainer = new Element("td").update(td.value && td.value.isJSON() ? td.value.evalJSON() : td.value ? td.value : '');
            if (td.type == "th") var innerContainer = new Element("th").update(td.value && td.value.isJSON() ? td.value.evalJSON() : td.value ? td.value : '');
            innerContainer.addClassName(td["class"]);
            container.appendChild(innerContainer);
        });
        myThead.appendChild(container);

	});

    table_json.tbody.each(function(tr){
        var container = new Element("tr");
        tr.childs.each(function(td){
            if (td.type == "td") var innerContainer = new Element("td").update(td.value && td.value.isJSON() ? td.value.evalJSON() : td.value ? td.value : '');
            if (td.type == "th") var innerContainer = new Element("th").update(td.value && td.value.isJSON() ? td.value.evalJSON() : td.value ? td.value : '');
			innerContainer.addClassName(td["class"]);
            container.appendChild(innerContainer);
        });
        myTBody.appendChild(container);
    });
    myTable.appendChild(myThead);
    myTable.appendChild(myTBody);
    return myTable;
}

function table2object(table){
    var result = {
	'attributes' : {},
        'thead' : [],
        'tbody' : []
    };

    if (table.id) result.attributes.id = table.id;
    if (table.border) result.attributes.border = table.border;

    table.select("thead tr").each(function(tr){
        var mytr = [];
        var found = false;
        tr.select("*").each(function(td){
			if ((td.nodeName.toLowerCase() == "td") || (td.nodeName.toLowerCase() == "th")){
				found = true;
				mytr.push({
					'value' : td.innerHTML.isJSON() ? td.innerHTML.toJSON() : td.innerHTML,
					'type' : td.nodeName.toLowerCase(),
					'class' : td.className
				});
			}
        });
        if (found){
            result.thead.push({
                'type' : 'tr',
                'childs' : mytr
            });
        }
    });



    table.select("tbody tr").each(function(tr){
        var mytr = [];
        var found = false;
        tr.select("*").each(function(td){
			if ((td.nodeName.toLowerCase() == "td") || (td.nodeName.toLowerCase() == "th")){
				found = true;
				mytr.push({
					'value' : td.innerHTML.isJSON() ? td.innerHTML.toJSON() : td.innerHTML,
					'type' : td.nodeName.toLowerCase(),
					'class' : td.className
				});
			}
        });

        if (found){
            result.tbody.push({
                'type' : 'tr',
                'childs' : mytr
            });
         }
    });

    return result;
}

function isStringAUrl(myString){
    var myString = myString.toLowerCase();
    if (myString.indexOf("http://") != -1){
        return true;
    }else if (myString.indexOf("www.") != -1){
        return true;
    }else {
        return false;
    }

}
/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();function utf8_decode ( str_data ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
    // +      input by: Aman Gupta
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Norman "zEh" Fuchs
    // +   bugfixed by: hitwork
    // +   bugfixed by: Onno Marsman
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // *     example 1: utf8_decode('Kevin van Zonneveld');
    // *     returns 1: 'Kevin van Zonneveld'

    var tmp_arr = [], i = 0, ac = 0, c1 = 0, c2 = 0, c3 = 0;
    
    str_data += '';
    
    while ( i < str_data.length ) {
        c1 = str_data.charCodeAt(i);
        if (c1 < 128) {
            tmp_arr[ac++] = String.fromCharCode(c1);
            i++;
        } else if ((c1 > 191) && (c1 < 224)) {
            c2 = str_data.charCodeAt(i+1);
            tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
            i += 2;
        } else {
            c2 = str_data.charCodeAt(i+1);
            c3 = str_data.charCodeAt(i+2);
            tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
        }
    }

    return tmp_arr.join('');
}function base64_decode (data) {
    // http://kevin.vanzonneveld.net
    // +   original by: Tyler Akins (http://rumkin.com)
    // +   improved by: Thunder.m
    // +      input by: Aman Gupta
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Onno Marsman
    // +   bugfixed by: Pellentesque Malesuada
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // -    depends on: utf8_decode
    // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
    // *     returns 1: 'Kevin van Zonneveld'

    // mozilla has this native
    // - but breaks in 2.0.0.12!
    //if (typeof this.window['btoa'] == 'function') {
    //    return btoa(data);
    //}

    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, dec = "", tmp_arr = [];

    if (!data) {
        return data;
    }

    data += '';

    do {  // unpack four hexets into three octets using index points in b64
        h1 = b64.indexOf(data.charAt(i++));
        h2 = b64.indexOf(data.charAt(i++));
        h3 = b64.indexOf(data.charAt(i++));
        h4 = b64.indexOf(data.charAt(i++));

        bits = h1<<18 | h2<<12 | h3<<6 | h4;

        o1 = bits>>16 & 0xff;
        o2 = bits>>8 & 0xff;
        o3 = bits & 0xff;

        if (h3 == 64) {
            tmp_arr[ac++] = String.fromCharCode(o1);
        } else if (h4 == 64) {
            tmp_arr[ac++] = String.fromCharCode(o1, o2);
        } else {
            tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
        }
    } while (i < data.length);

    dec = tmp_arr.join('');
    dec = this.utf8_decode(dec);

    return dec;
}/**
 * @category   Camao
 * @package    Camao_Exception
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */


var EXCEPTION_ERROR = 1;
var EXCEPTION_WARNING = 2;
var EXCEPTION_NOTIFICATION = 3;

function Camao_Exception() {
    var infos = { collection : [] };
//    this.__construct = function(param){ this.init(param); }

    /**
     * Initiate the Object
     *
     * @param  void
     * @return void
     
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){}
    }
*/
    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     
    this.getPossibleOptions = function(){
        return {
            'void' : '[void] this class doesnt need any options'
        };
    }
*/

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     
    this.validateOptions = function(){
        if (!infos.class){ 
            infos.class = "Anonymous";
        }
        return true;
    }
*/
    /**
     * Fire up a message
     *
     * @param  string type Defines the Error type -
                           EXCEPTION_ERROR, EXCEPTION_WARNING, EXCEPTION_NOTIFICATION
     * @return bool 
     */
    this.throwException = function(myType, myString){
        if (Prototype.Browser.Gecko){
            eval("infos.className = this.super");
            if (DEVELOPER){
                if (myType == EXCEPTION_ERROR){
                    console.error(infos.className + " => " + myString);
                }else if (myType == EXCEPTION_WARNING){
                    console.warn(infos.className + " => " + myString);
                }else if (myType == EXCEPTION_NOTIFICATION){
                    this.collectInformations(myString);
                }
            }else{
                //send mail ?
            }
        }else{
            if (DEVELOPER){
                alert(myString);
            }
        }
    }

    this.collectInformations = function(myString){
        infos.collection.push(myString);
        if (infos.timeout) window.clearTimeout(infos.timeout);
        infos.timeout = this.fireException.bind(this).delay(1);
    }
    this.fireException = function(){
        if(typeof console != "undefined"){
            console.groupCollapsed(infos.className + " Informations");
            infos.collection.each(function(e){
                console.info(e);
            });
            console.groupEnd();
            infos.collection = [];
        }
    }
}/**
 * @category   Camao
 * @package    Camao_Cache
 * @copyright  Copyright (c) 2010 CAMAO AG
 * @autor      Markus Zillig <markus.zillig@camao.de>
 */

function Camao_Ajax()
{
    /**
     * __construct()
     * 
     * @params
     * @return
     */
    this.__construct = function()
    {
        
    }

    /**
     * Call given object by function with supplied parameters
     * 
     * @params string response  Json response
     * @return void;
     */
    this.setResponse = function(response)
    {
        if (!response.isJSON()) {
            this.throwException(EXCEPTION_ERROR, 'response is not a json string');
            return;
        }

        // get response object
        var r = response.evalJSON();

        // check for response handler
        if (!Base.isset(r.object)) {
            this.throwException(EXCEPTION_ERROR, 'response object is not defined:'+r.object);
            return;
        }

        // check response handler function
        if (typeof Base.get(r.object)[r.func] != 'function') {
            this.throwException(EXCEPTION_ERROR, 'response function is not defined:'+r.func);
            return;
        }

        // call response handler function
        Base.get(r.object)[r.func](r.options);
        
    }
}

extendClass("Camao_Ajax", "Camao_Exception");/**
 * @category   Camao
 * @package    Camao_Autocompletion
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

/**
 * Formular target Constanten.
 *
 * @var constant
 */


function Camao_Autocompletion() {
    var infos = {};

    /* Default Functions */
    this.__construct = function(param){ if(param) this.init(param);};

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){

            infos.target.observe("keyup", this.keyUpEvent.bind(this));
            infos.target.observe("focus", this.focusEntryEvent.bind(this));
            infos.target.observe("blur", this.blurEntryEvent.bind(this));

        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    };

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'target' : '[Dom Element] of the Input field',
            'request_url' : '[STRING] the Post url',
            'request_param' : '[OBJECT] params for the post'
        };
    };

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        if (!infos.request_url){ this.throwException(EXCEPTION_ERROR, "options.request_url is undefined!"); status = false;}
        if (!infos.request_param) infos.request_param = {};
        return status;
    };


    this.keyUpEvent = function(){
        if (infos.timeout) window.clearTimeout(infos.timeout);
        infos.timeout = this.doAutocompletion.bind(this).delay(0.5);
    };

    this.doAutocompletion = function(){
        if (infos.hasFocus == false) return false;
        if (infos.target.value == "") return false;

        Object.extend(infos.request_param, { 'value' : infos.target.value });

        new Ajax.Request(infos.request_url, {
            method: 'post',
            parameters : infos.request_param,
            onSuccess: function(transport) { 
                this.onReciveEntries(transport);
            }.bind(this),
            onFailure: function(response) {
                if (response.status == 404){
                    this.throwException(EXCEPTION_ERROR, "Action doesnt Exist!");
                }else{
                    this.throwException(EXCEPTION_ERROR, "Ajax request failed!");
                }
            }.bind(this)
        });        
    };
    
    this.focusEntryEvent = function(){ infos.hasFocus = true; this.onFocusEntry(infos.target); };
    this.blurEntryEvent = function(){ infos.hasFocus = false; };
    //Callback Functions
    this.onReciveEntries = function(transport){
        this.throwException(EXCEPTION_NOTIFICATION, "You should overload this.onReciveEntries ;)!");
    };
    this.onFocusEntry = function(entry){};

}

extendClass("Camao_Autocompletion", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Cron
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_CRON_CANCEL_AFTER = 20;
function Camao_Cron(){
    var infos = { q : [], count : 0};

    /* Default Functions */
    this.__construct = function(param){
        this.init(param);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            this.checkNow();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }
    
    this.checkNow = function(){
        var response = false;
        if (infos.rule.onDifferents){
            response = this.checkOnDifferents();
        }else if(infos.rule.onElementExists){
            response = this.checkOnElementExists();
        }else if(infos.rule.onClassExists){
            response = this.checkOnClassExists();
        }else{
            response = true;
        }

        if (response == false){
            infos.count++;
            if (infos.count <= CAMAO_CRON_CANCEL_AFTER){
                this.checkNow.bind(this).delay(0.5);
            }else{
                this.throwException(EXCEPTION_ERROR, "stop interval! After " + CAMAO_CRON_CANCEL_AFTER + " tries!");
            }
        }
    }

    this.checkOnDifferents = function(){
        var diffarray = infos.rule.onDifferents;
        if (eval(diffarray[0]) != eval(diffarray[1])){
            infos.count = 0;
            infos.callback();
            return true;
        }else{
            return false;
        }
    }
    
    this.checkOnElementExists = function(){
        if ($(infos.target)){
            infos.count = 0;
            infos.callback();
            return true;
        }else{
            return false;
        }
    }

    this.checkOnClassExists = function(){
        if (typeof infos.rule.onClassExists == "function"){
            infos.count = 0;
            infos.callback();
            return true;
        }else{
            return false;
        }
    }
    
    
    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {};
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){
            this.throwException(EXCEPTION_ERROR, "options.target is undefined!");
            status = false;
        }

        if (!infos.rule){
            this.throwException(EXCEPTION_ERROR, "options.rule is undefined!");
            status = false;
        }

        if (!infos.callback){
            this.throwException(EXCEPTION_ERROR, "options.callback is undefined!");
            status = false;
        }
        return status;
    }

    /* Callback Functions */
}

extendClass("Camao_Cron", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Cookie
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_COOKIE_DEFAULT_EXPIRE = 60 * 60 * 24 * 30 * 12;

function Camao_Cookie(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(options){
        this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'expire' : '[Int] cookie expire time'
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.expire){
            infos.expire = CAMAO_COOKIE_DEFAULT_EXPIRE;
            this.throwException(EXCEPTION_NOTIFICATION, "options.expire is undefined, we use " + CAMAO_COOKIE_DEFAULT_EXPIRE);
        }

        return status;
    }

    
    this.setCookie = function(cname, cvalue){
        var exp = new Date();
        exp.setTime(exp.getTime() + infos.expire);
        document.cookie = cname + "=" + escape(cvalue) + "; path=/; expires=" + exp.toGMTString();
    }

    this.getCookie = function(name){
        var found = false;
        var cookies = document.cookie.split(";");
        cookies.each(function(cookie){
            var parts = cookie.split("=");
            if ( (parts[0].replace(/^\s+|\s+$/g, '') == name) && (parts[1].replace(/^\s+|\s+$/g, '')) ) found = parts[1];
        }.bind(this));
        return found;

    }

    this.deleteCookie = function(name){
        this.getCookie(name) ? this.setCookie(name, '') : this.throwException(EXCEPTION_NOTIFICATION, "Cant delete cookie (" + name + "), cookie not found!");
    }

    this.removeCookie = function(name){ this.deleteCookie(name); }
    this.clearCookie = function(){ document.cookie = ""; }

    /* Callback Functions */
}

extendClass("Camao_Cookie", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Content
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_Content(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(param){
    }

    /**
     * Initiate the Object
     * @return void
     */
    this.init = function(){}

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return void
     */
    this.getPossibleOptions = function(){
        return {};
    }

    this.doing = function(options){
        if (this.validateOptions(options)){
            if (options.doing.toLowerCase() == "insert"){
                options.target.insert(options.content);
            }else if (options.doing.toLowerCase() == "update"){
                options.target.update(options.content);
            }
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }

    }

    this.isElementInInvisibleContainer = function(element){
        if (typeof element == 'object') {
            var returnval = false;
            var start = element;
            while (start.tagName.toLowerCase() != "body"){
                start = $(start).up();
                if (!start.visible()) returnval = true;
            }
            return returnval;
        }
    }

    this.isElementInContainer = function(element, container){
        if (typeof element == 'object') {
            var returnval = false;
            var start = element;
            if(start.tagName.toLowerCase() == "html") return false;
            while (start.tagName.toLowerCase() != "body"){
                if (start == container) returnval = true;
                start = $(start).up();
            }
            return returnval;
        }
    }

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(options){
        var status = true;
        if (!options.doing){ this.throwException(EXCEPTION_ERROR, "options.do is undefined!"); status = false;}
        if (!options.content){ this.throwException(EXCEPTION_ERROR, "options.content is undefined!"); status = false;}
        if (!options.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}

        return status;
    }


    
    /* Callback Functions */

}

extendClass("Camao_Content", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Dropdown
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */
var CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABEL_CLASSNAME = "dropDownLabel";
var CAMAO_DROPDOWN_DEFAULT_TEMPLATE_OPENED_CLASSNAME = "dropDownOpen";
var CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABELTEXT_CLASSNAME = "labelText";
var CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LIST_CLASSNAME = "list";
var CAMAO_DROPDOWN_DEFAULT_TEMPLATE_ENTRY_CLASSNAME = "entry";
var CAMAO_DROPDOWN_LIST_CLOSE_CHAR = Event.KEY_ESC;

function Camao_Dropdown(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(param){
        if (typeof param != "undefined") this.init(param);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            this.createEvents();
            this.replaceDropdown();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ 
            this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); 
            status = false;
        }else{
            if (infos.target.nodeName.toLowerCase() != "select"){
                this.throwException(EXCEPTION_ERROR, "options.target is not a 'select'-Tag!"); 
                status = false;
            }
        }

        if (!infos.template) {
            this.throwException(EXCEPTION_NOTIFICATION, "options.template is undefined! We create a default template!"); 
            infos.template = this.createFallbackDropdown();
        }else{
            if (infos.template.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABEL_CLASSNAME).length != 1){
                this.throwException(EXCEPTION_ERROR, "We cant find the label element with the '" + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABEL_CLASSNAME + "' classname!"); 
                status = false;
            }else if (infos.template.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LIST_CLASSNAME).length != 1){
                this.throwException(EXCEPTION_ERROR, "We cant find the list element with the '" + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LIST_CLASSNAME + "' classname!"); 
                status = false;
            }else if (infos.template.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_ENTRY_CLASSNAME).length != 1){
                this.throwException(EXCEPTION_ERROR, "We cant find the entry element with the '" + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_ENTRY_CLASSNAME + "' classname!"); 
                status = false;
            }
        }
        return status;
    }

    this.createEvents = function(){
        infos.observe_document_click = function(e){
            var contentObj = Base.get("Camao_Content");
            if (!contentObj.isElementInContainer(e.target, infos.usedTemplate)){
                this.closeList();
            }
        }.bind(this);

        document.observe("click", infos.observe_document_click);

        infos.observe_document_keyup = function(e){
            if(e.keyCode == CAMAO_DROPDOWN_LIST_CLOSE_CHAR){
                this.closeList();
            }
        }.bind(this);

        document.observe("keyup", infos.observe_document_keyup);
    }

    this.destroy = function(){
        document.stopObserving("click", infos.observe_document_click);
        document.stopObserving("keyup", infos.observe_document_keyup);
        infos.target.show();
        infos.target.name = infos.target.name.replace("_CAMAO_DROPDOWN_TEMP","");
        infos.usedTemplate.remove();
    }

    this.getInformations = function(){
        var results = [];
        infos.target.select("option").each(function(option){
            results.push({
                'value' : option.value,
                'name' : option.innerHTML.trim(),
                'selected' : option.selected
            });
        });
        return results;
    }

    this.createFallbackDropdown = function(){
        var tempDiv = new Element("div").update(''+
            '<div class="dropdown" style="display:none">' + 
                '<div class="' + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABEL_CLASSNAME + '"></div>' + 
                '<div>' + 
                    '<ul class="' + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LIST_CLASSNAME + '">' + 
                    '<li class="' + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_ENTRY_CLASSNAME + '"></li>' + 
                    '</ul>' + 
                '</div>' + 
            '</div>'
        );
        tempDiv.hide();
        $$("body")[0].appendChild(tempDiv);
        return tempDiv.down(".dropdown");
    }

    this.createDropdown = function(){
        var foundSelectedEntry = false;

        infos.usedTemplate = infos.template.cloneNode(true);
        infos.listTemplate = infos.usedTemplate.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LIST_CLASSNAME)[0];
        infos.labelTemplate = infos.usedTemplate.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABEL_CLASSNAME)[0];
        infos.labelText = infos.usedTemplate.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_LABELTEXT_CLASSNAME)[0];

        infos.usedTemplate.appendChild(
            new Element("input", { 'type' : 'hidden', 'name' : infos.target.name })
        );

        infos.target.name += "_CAMAO_DROPDOWN_TEMP";
        infos.usedTemplate.id = "";

        var dropdownInfos = this.getInformations();
        var entryTemplate = infos.usedTemplate.select("." + CAMAO_DROPDOWN_DEFAULT_TEMPLATE_ENTRY_CLASSNAME)[0];

        infos.listTemplate.hide();

        dropdownInfos.each(function(option){
            var usedEntryTemplate = entryTemplate.cloneNode(true);

            if (option.value.toLowerCase().indexOf("http://") != -1){
                if (usedEntryTemplate.select("a").length == 0){
                    usedEntryTemplate.appendChild(new Element("a"));
                }
                usedEntryTemplate.select("a")[0].href = option.value;
                if (usedEntryTemplate.select("a span").length == 0){
                    usedEntryTemplate.select("a")[0].update(option.name);
                }else{
                    usedEntryTemplate.select("a span")[0].update(option.name);
                }
            }else{
                //usedEntryTemplate.update(option.name);
                if (usedEntryTemplate.select("span").length == 0){
                    usedEntryTemplate.update(option.name);
                }else{
                    usedEntryTemplate.select("span")[0].update(option.name);
                }
            }

            infos.listTemplate.appendChild(usedEntryTemplate);

            usedEntryTemplate.appendChild(
                new Element("div", { 'className' : 'valueHolder', 'style' : 'display:none;' } ).update(option.value)
            );

            if (option.selected){
                infos.labelText.update(option.name);
                infos.usedTemplate.select("input")[0].value = option.value;
                foundSelectedEntry = true;
            }
        });

        if(dropdownInfos.length>0)
            infos.listTemplate.lastChild.addClassName("last");

        
        if (foundSelectedEntry == false){
            infos.labelText.update(dropdownInfos[0].name);
            infos.usedTemplate.select("input")[0].value = dropdownInfos[0].value;
        }
        entryTemplate.hide();
    }

    this.createDropdownEvents = function(){
        infos.labelTemplate.observe("click", this.labelClickEvent.bind(this));
        infos.listTemplate.observe("click", this.listClickEvent.bind(this));
    }

    this.replaceDropdown = function(){
        this.createDropdown();
        this.createDropdownEvents();
        infos.usedTemplate.show();
        infos.target.hide();
        new Insertion.After(infos.target, infos.usedTemplate);
    }

    this.labelClickEvent = function(){
        this.onLabelClickEvent();
        infos.listTemplate.toggle();
        infos.labelTemplate.addClassName(CAMAO_DROPDOWN_DEFAULT_TEMPLATE_OPENED_CLASSNAME);
    }

    this.listClickEvent = function(e){
        this.onEntryClickEvent(e.target);
        //console.log(e.target);
        var usedElement = e.target;
        if(usedElement.nodeName.toLowerCase() != "a"){
            var foundHolder = false;
            while (foundHolder == false){
                if (usedElement.select(".valueHolder").length == 0){
                    usedElement = usedElement.up();
                    if (typeof usedElement == "undefined"){
                        break;
                    }
                }else{
                    foundHolder = true;
                }
            }
            if (foundHolder == false){
                this.throwException(EXCEPTION_ERROR, "We cant find the valueHolder element!"); 
                return false;
            }
            if (usedElement.down("a")){
                window.location.href = usedElement.down("a").href;
                return true;
            }
            var myvalue = usedElement.select(".valueHolder")[0].innerHTML.trim();
            infos.usedTemplate.select("input")[0].value = myvalue;
        }
        infos.labelText.update(e.target.innerHTML.trim());
        this.closeList();
    }

    this.closeList = function(){
        infos.listTemplate.hide();
        infos.labelTemplate.removeClassName(CAMAO_DROPDOWN_DEFAULT_TEMPLATE_OPENED_CLASSNAME);
    }
    
    /* Callback Functions */
    this.onLabelClickEvent = function(){}
    this.onEntryClickEvent = function(entry){}

}

extendClass("Camao_Dropdown", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Editable
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_EDITABLE_DEFAULT_END_EDIT_CHAR = Event.KEY_ESC;
var CAMAO_EDITABLE_DEFAULT_CREATE_NEW_LIST_ENTRY = Event.KEY_TAB;

function Camao_Editable(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(options){
        if (typeof options != "undefined") this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            this.setTargetToEdit();
            this.createEvents();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }

    this.createEvents = function(){
        infos.keyDownEvent = this.keyDownEvent.bind(this);
        document.observe("keydown", infos.keyDownEvent);
    }

    this.destroy = function(){
        document.stopObserving("keydown", infos.keyDownEvent);
        if (infos.mode == "edit"){
            this.setTargetToView();
        }
    }

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        return status;
    }

    this.setTargetToEdit = function(){
        infos.mode = "edit";
        if (infos.target.hasClassName("list")){
            this.setListFieldToEdit();
        }else if (infos.target.hasClassName("upload")){
        this.onUploadFieldToEditEvent(infos.target);
    }else{
            this.setTextFieldToEdit();
        }
    }

    this.setTargetToView = function(){
        infos.mode = "view";
        if ((infos.target.hasClassName("list")) && (infos.target.select("input").length > 0) ){
            infos.target.select("li").each(function(li){
                if (li.select("input")[0].value != ""){
                    li.update(li.select("input")[0].value);
                }else{
                    li.remove();
                }
            });
        }else if (infos.target.hasClassName("text")){
            if (infos.target.select("input").length > 0){
                infos.target.update(infos.target.select("input")[0].value);
            }
        }
        this.onTargetToViewEvent();
    }
  
    this.setTextFieldToEdit = function(){
        var myInput = new Element("input", { 'value' : infos.target.innerHTML } );
        infos.target.update(" ");
        infos.target.appendChild(myInput);
        myInput.focus();
    }



    this.setListFieldToEdit = function(){
        if (infos.target.down("li")){
            infos.target.select("li").each(function(li){
                var myInput = new Element("input", { 'value' : li.innerHTML } );
                li.update(" ");
                li.appendChild(myInput);
            });
        }else{
            var myUl = new Element("ul").update("<li><input value='" + infos.target.innerHTML + "' /></li>");
            infos.target.update(" ");
            infos.target.appendChild(myUl);
            myUl.select("input")[0].focus();
        }
    }

    this.createNewListEntry = function(){
        var myLi = new Element("li").update("<input value='' />");
        infos.target.select("ul")[0].appendChild(myLi);
        myLi.select("input")[0].focus();
    }

    this.keyDownEvent = function(e){
    
        switch(e.which){
            case CAMAO_EDITABLE_DEFAULT_END_EDIT_CHAR : 
            Event.stop(e);
            this.setTargetToView();
            break;
            case CAMAO_EDITABLE_DEFAULT_CREATE_NEW_LIST_ENTRY :
            var inContainer = Base.get("Camao_Content").isElementInContainer(e.target, infos.target);
            if (inContainer){
                if (infos.target.hasClassName("list")){
                if (infos.mode == "edit"){
                    this.createNewListEntry();
                    Event.stop(e);
                }
                }
            }
            break;
        }
    }
    
    /* Callback Functions */
    this.onTargetToViewEvent = function(){}
    this.onUploadFieldToEditEvent = function(node){
        this.throwException(EXCEPTION_ERROR, "You must overload this function to get Upload handling!!");
    }
}

extendClass("Camao_Editable", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Expandable
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_Expandable(){
    this.infos = {};
    var infos = {};

    /* Default Functions */
    this.__construct = function(options){
        if (options) this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }


    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        return status;
    }

    this.destroy = function(){
    }


    this.expand = function(){
        infos.target.up().insert(infos.target.cloneNode(true), { 'after' : infos.target});
    }


    /* Callback Functions */
}

extendClass("Camao_Expandable", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Formular
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

/**
 * Formular target Constanten.
 *
 * @var constant
 */


function Camao_Formular() {
    var infos = {
        'visit_fields' : new Array(),
        'preselect_values' : new Array(),
        'form_elements' :  new Array(),
        'validation_class' : Base.get("Camao_Validation").getValidClasses()
    };

    /* Default Functions */
    this.__construct = function(param){
        
        if (param) {
            Object.extend(infos, param);
            if (!infos.generateRules) this.init();
        }
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(){
        if (this.validateOptions()){
            this.collectInformations();
            this.createFakePasswordFields();
            this.createEvents();
            if (infos.autofocus){
                var firstElement = this.findFirstInputElementInCollectionByType("text");
                if (firstElement != false){
                    this.setFirstElementFocusDelay.delay(1.5,firstElement.element);
                }
            }

        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'target' : '[Dom Element] from the Formular',
            'do_validation' : '[BOOL] to de/activate the validation',
            'error_class' : '[String] will added to the element if validation failed',
            'preselect_fields' : '[BOOL] to de/activate the preselection',
            'use_ajax' : '[BOOL] to de/activate the ajax transporter',
            'custom_submit_button' : '[Dom Element] set a custom submit button',
            'autofocus' : '[BOOL] to de/activate the auto focus for the firs element',
            'crypt_password' : '[BOOL / String] crypt the password before submiting, valid options false, md5, sha1'
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}

        if (!infos.do_validation){ 
            infos.do_validation = false;
        }else if(infos.do_validation == true){
            if (!infos.error_class){ infos.error_class = "error";}
        if (!infos.validation_behavior) infos.validation_behavior = "onsubmit";
        }

        if (typeof infos.use_ajax == "undefined") infos.use_ajax = false;
        if (typeof infos.crypt_password == "undefined") infos.crypt_password = false;
        if (typeof infos.autofocus == "undefined"){
            infos.autofocus = true;
            this.throwException(EXCEPTION_NOTIFICATION, "options.autofocus is undefined, we use 'true'");
        }
        if (!infos.custom_submit_button) infos.custom_submit_button = false;
        return status;
    }

    this.createFakePasswordFields = function(){
        infos.target.select("input").each(function(original){
            if (original.type == "password"){
                original.hide();
                var fake = new Element("input", { 'name' : 'fake_' + original.name, 'value' : (infos.preselect_fields == true ? original.value : '') , 'type' : 'text' });
                original.insert({ before:fake});
                fake.observe("focus", function(original,fake){
                    original.show();
                    fake.hide();
                    original.focus();
                }.bind(this,original, fake))
        
                original.observe("blur", function(original,fake){
                    if (original.value == ""){
                        original.hide();
                        fake.show();
                    }
                }.bind(this,original, fake))
            }
        }.bind(this));
    }


    /**
     * Collect all Form fields
     *
     * @param  void

     * @return void
     */
    this.collectInformations = function(){
        infos.target.select("input").each(function(e){
            infos.form_elements.push({ 'element' : e, 'start_value' : $F(e)});
        }.bind(this));

        infos.target.select("select").each(function(e){
            infos.form_elements.push({ 'element' : e, 'start_value' : $F(e)});
        }.bind(this));

        infos.target.select("textarea").each(function(e){
            infos.form_elements.push({ 'element' : e, 'start_value' : $F(e)});
        }.bind(this));
    }

    this.findFirstInputElementInCollectionByType = function(type){
        var Camao_ContentObj = Base.get("Camao_Content");
        for (var i = 0; i < infos.form_elements.length; i++){
            var element = infos.form_elements[i]
            if (element.element.type == type){
                if (!Camao_ContentObj.isElementInInvisibleContainer(element.element))
                    return infos.form_elements[i];
            }
        }
        return false;
    }   

    this.findLastInputElementInCollection = function(){
        var Camao_ContentObj = Base.get("Camao_Content");
        for (var i = infos.form_elements.length - 1; i >= 0; i--){
            var element = infos.form_elements[i];
            if (!Camao_ContentObj.isElementInInvisibleContainer(element.element))
                return infos.form_elements[i];
        }
        return false;
    }   

    /**
     * Create the Form field events like onFocus and onBlur
     *
     * @param  void
     * @return void
     */
    this.createEvents = function(){
        if (infos.custom_submit_button){
        if ( (infos.custom_submit_button.href == window.location) || (infos.custom_submit_button.href == "") ){ 
        infos.custom_submit_button.href = "javascript:;";
        }

            infos.custom_submit_button.observe("click", this.linkSubmitFormular.bind(this));

            //Wir geben dem letzten input field ein event mit um mit enter das form abzusenden
            var lastElement = this.findLastInputElementInCollection();
            if (lastElement) lastElement.element.observe("keydown", this.lastInputFieldKeyDownEvent.bind(this,lastElement));
        }else{
        infos.target.onsubmit = function(){ return this.submitFormular(); }.bind(this);
        }
        infos.form_elements.each(function(elementObj){
            elementObj.element.observe("focus", this.elementGotFocus.bind(this, elementObj));
            elementObj.element.observe("blur", this.elementLeaveFocus.bind(this, elementObj));
        }.bind(this));
    }

    /**
     * Keydown event from the last input field in the form
     *
     * @param  event
     * @return void
     */
    this.lastInputFieldKeyDownEvent = function(lastElement, e){
        if(e.which == Event.KEY_RETURN) {
            this.elementLeaveFocus(lastElement);
            Event.stop(e);
            this.linkSubmitFormular();
        }
    }

    /**
     * Set a Element as (In)valid
     *
     * @param  void
     * @return void
     */
    this.setElementAsInvalide = function(element){  element.addClassName(infos.error_class); }
    this.setElementAsValide = function(element){ element.removeClassName(infos.error_class); }


    /**
     * Event : A form field got the Focus
     *
     * @param  domelement
     * @return void
     */
    this.elementGotFocus = function(elementObj){ 
        if (infos.visit_fields.indexOf(elementObj.element) == -1) infos.visit_fields.push(elementObj.element); 

        if (infos.preselect_fields){ 
            if ((elementObj.element.type != "button") && (elementObj.element.type != "submit")){
                if (elementObj.element.value == elementObj.start_value) elementObj.element.value = "";
            }
        }

    }

    /**
     * Event : A form field leave the Focus
     *
     * @param  void
     * @return void
     */
    this.elementLeaveFocus = function(elementObj){

        if (infos.preselect_fields){ 
            if (elementObj.element.value == ""){
                if (elementObj.element.type != "password"){
                    elementObj.element.value = elementObj.start_value;
                }
            }
        }
        if (infos.crypt_password){
            if (elementObj.element.type == "password"){
                elementObj.element.value = Base.get("Camao_Crypt").crypt(elementObj.element.value, infos.crypt_password);
            }
        }

        if (infos.do_validation && infos.validation_behavior == "direct"){
        this.onValidationStart();

        infos.validation_class.each(function(myclass){
        if (elementObj.element.className.indexOf(myclass) != -1){
            this.validateEntry(elementObj, myclass);
        }
        }.bind(this));
    }
    }

    /**
     * Submiting Formular via custom button
     *
     * @param  event
     * @return boolean
     */
    this.linkSubmitFormular = function(){
        this.onSubmitFormular();
        if (this.doOnSubmitFormular()){
            if (this.doSubmit()) {
                if (infos.use_ajax){
                    infos.target.request({
                        method: 'post',
                        onSuccess:  function(response) {
                            if(response.responseJSON){
                                this.onJSONResponse(response.responseJSON);
                            }else{
                                this.onTextResponse(response.responseText);
                            }
                        }.bind(this)
                    });
                    return false;
                }
                infos.target.submit();
                return true;

            } else {
                return false;
            }

        }else{
            return false;
        }
    }
    /**
     * Submiting Formular
     *
     * @param  event
     * @return boolean
     */
    this.submitFormular = function(){
        this.onSubmitFormular();
        if (this.doOnSubmitFormular()){
            if (this.doSubmit()) {
                if (infos.use_ajax){
                    infos.target.request({
                        method: 'post',
                        onSuccess:  function(response) {
                            if(response.responseJSON){
                                this.onJSONResponse(response.responseJSON);
                            }else{
                                this.onTextResponse(response.responseText);
                            }
                        }.bind(this)
                    });
                    //Wir muessen false returnen da sonst das form abgeschickt wird
                    return false;
                }
                return true;

            } else {
                return false;
            }

        }else{
            return false;
        }
    }

    this.doOnSubmitFormular = function(){
        var status = true;

        if (infos.do_validation){// && infos.validation_behavior == "onsubmit"){
        if (arguments[0] !== true) this.onValidationStart();
            infos.form_elements.each(function(elementObj){
                infos.validation_class.each(function(myclass){
                    if (elementObj.element.className.indexOf(myclass) != -1){
                        var retstatus = this.validateEntry(elementObj, myclass);
                        if (retstatus == false) status = false;
                    }
                }.bind(this));
            }.bind(this));
        }
        return status;
    }

    this.validateEntry = function(elementObj, myclass){
        var status = true;
        var doValidation = true;
        validation_options = {};

        // check if value is the preselected value and element is required with preselect enabled then return as invalid value
        if ((infos.preselect_fields == true                   &&
            elementObj.element.className.indexOf("_*") != -1  && 
            elementObj.start_value == $F(elementObj.element))
            &&
            (elementObj.element.type == 'text'                ||
             elementObj.element.type == 'password'            ||
             elementObj.element.type == 'textarea')
            ){
                this.onValidationError(elementObj.element, myclass);
                status = false;

        } else {
            if (elementObj.element.className.indexOf("(") != -1){
                var param_part1 = elementObj.element.className.split("(");
                var param_part2 = param_part1[1].split(")");
                var param = param_part2[0];

                if (param.indexOf("-") != -1){
                    var param_options_part = param.split("-");
                    if (param_options_part.length == 2){
                        //We have a min and max range a) a int range or b) a string length
                        Object.extend(validation_options, {
                        "type" : "range",
                        "min" : param_options_part[0],
                        "max" : param_options_part[1]
                        });
                        
                    }else{
                        this.throwException(EXCEPTION_ERROR, "cant interpret the condition -> " + param);
                    }

                }else if (param.indexOf(">") != -1){
                    var param_options_part = param.split(">");
                    var param_value = param_options_part[1];
                    if (!isNaN(param_value)) {
                        Object.extend(validation_options, {
                        "type" : "greater",
                        "value" : param_value
                        });
                    }else{
                        this.throwException(EXCEPTION_ERROR, "cant interpret the condition -> " + param);
                    }

                }else if (param.indexOf("<") != -1){
                    var param_options_part = param.split("<");
                    var param_value = param_options_part[1];
                    if (!isNaN(param_value)) {
                        Object.extend(validation_options, {
                        "type" : "lower",
                        "value" : param_value
                        });
                    }else{
                        this.throwException(EXCEPTION_ERROR, "cant interpret the condition -> " + param);
                    }

                }else{
                    this.throwException(EXCEPTION_ERROR, "cant interpret the condition -> " + param);
                }
            }

            var used_value = $F(elementObj.element);

            if (elementObj.element.type == "checkbox") doValidation = false;

            if ((myclass == "numbers") && (used_value != "")){
                //we make it to a int!
                used_value = parseInt(used_value);
            }

            if ( (doValidation == true) && (used_value.trim() != "") && (myclass != "_*") ){
                var validation_status = Base.get("Camao_Validation").validate(used_value, myclass, validation_options);
                if (validation_status != true){
                    this.onValidationError(elementObj.element, myclass, validation_status);
                    status = false;
                }else{
                    this.onValidationSuccess(elementObj.element, myclass);
                }

            }else{
                if (elementObj.element.className.indexOf("_*") != -1){
                    if (elementObj.element.type == "checkbox"){
                        status = elementObj.element.checked;
                        if (status == true){
                            this.onValidationSuccess(elementObj.element, myclass);

                        }else{
                            this.onValidationError(elementObj.element, myclass, CAMAO_VALIDATION_ERROR_NOT_CHECKED);
                        }

                    }else if (elementObj.element.type == "radio"){
                        var name = elementObj.element.getAttribute('name');
                        var allowed    = true;
                        var useAllowed = false;
                        var status     = false;

                        elementObj.element.up().select('input[type="radio"][name="'+name+'"]').each(function(element){
                            if (element.checked == true) {
                                status = true;

                                if (element.className.indexOf("_allowed") == -1){
                                    allowed = false;
                                }
                            }

                            if (element.className.indexOf("_allowed") != -1){
                                useAllowed = true;
                            }
                        });

                        if (useAllowed == true && allowed == false){
                            status = false;
                        }

                        if (status == false) {
                            this.onValidationError(elementObj.element, myclass);

                        } else {
                            this.onValidationSuccess(elementObj.element, myclass);
                        }

                    } else {
                        if ($F(elementObj.element).trim() == ""){
                            this.onValidationError(elementObj.element, myclass);
                            status = false;
                        }
                    }
                }
            }
        }

        return status;
    }

    /**
     * Insert values to form by input name
     * 
     * @params array values
     * @return void;
     */
    this.insertValues = function(values)
    {
        var element;
        for (var index in values) {
            element = $$("input[name='"+index+"']");
            if (typeof element[0] != 'undefined') {
               element[0].setValue(values[index]);
            }
        }

        // validate on generateRules
        infos.validate_startup = true;
    }

    /**
     * Append rules on formular input fields
     */
    this.generateRules = function(options)
    {
        var element;
        for (var index in options) {
            element = $$("input[name='"+index+"']");
            if (typeof element[0] != 'undefined') {
               element[0].addClassName(options[index]);
            }
        }

        // init formular
        this.init();

        // validation if error happened in backend
        if (infos.validate_startup) this.doOnSubmitFormular(true);
        infos.validate_startup = false;
    }

    /**
     * Append checksum to form
     * 
     * @params string checksum  md5
     * @return void;
     */
    this.appendChecksum = function(checksum)
    {
        var input = new Element('input', {'type':'hidden','value':checksum,'name':'Camao_Formular' });

        infos.target.insert(input);
    }

    /**
     * Set the focus to the element
     *
     * @param  event
     * @return boolean
     */
    this.setFirstElementFocusDelay = function(element){ element.focus(); }

    /**
     * 
     */
    this.doSubmit = function(){
        return true;
    }

    this.onSubmitFormular = function(){}
    this.onJSONResponse = function(responseJSON){ }
    this.onTextResponse = function(responseText){ eval(responseText); }

    this.onValidationStart = function(){}
    this.onValidationError = function(element, myclass, validation_status){ element.addClassName(infos.error_class); }
    this.onValidationSuccess = function(element, myclass){ element.removeClassName(infos.error_class); }
}

extendClass("Camao_Formular", "Camao_Exception");
/**
 * @category   Camao_Form
 * @package    Csrf
 * @autor      Markus Zillig <markus.zillig@camao.de>
 */

function Camao_Form_Csrf()
{
    /**
     * var object
     */
    var options = { };

    /**
     * var boolean;
     */
    this._autoDetectHidden = false;

    /**
     * var string;
     */
    this._ajaxurl = false;

    /**
     * __construct()
     * 
     * Setup Csrf Javascript Object
     * 
     * @params object config
     * @return void;
     */
    this.__construct = function(config)
    {
        // setup ajax url
        if (config.ajaxUrl) {
            this._ajaxUrl = config.ajaxUrl;

            // setup auto detection if set
            if (true == config.autoDetect) {
                // setup auto detection hidden forms
                if (true == config.autoDetectHidden) {
                    this._autoDetectHidden = config.autoDetectHidden;
                }

                // start detecting
                this.detectForms();
            }
        }
    }

    /**
     * Detect Forms in HTML code and register them with tokens
     * 
     * @return boolean true if forms found;
     */
    this.detectForms = function()
    {
        var forms = false == this._autoDetectHidden ? $('form') : $('form:not(:hidden)') ;

        if (!forms) return false;

        $.ajax({
            url         : this._ajaxUrl,
            processData : forms.toJSON()
        });

        return true;
    }

    /**
     *
     * @params object options
     * @params string options.target  Form we want to append token
     */
    this.add = function(options)
    {
        // check for valid form ID
        var form;

        if (form = $(options.target)) {
            // setup name
            var input1 = new Element('input', {'type':'hidden','value':options.value,'name':options.name+'[hash]'});
            var input2 = new Element('input', {'type':'hidden','value':options.target,'name':options.name+'[id]'});

            // append to form (check for fieldset)
            if (form.select('fieldset')[0]) {
                return [
                    form.select('fieldset')[0].appendChild(input1),
                    form.select('fieldset')[0].appendChild(input2)
                ];
            }

            return [
                form.appendChild(input1),
                form.appendChild(input2)
            ];
        }
    }
}/**
 * @category   Camao
 * @package    Camao_MouseEvent
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_MouseEvent(){
    var infos = { 'x' : 0, 'y' : 0 };

    /* Default Functions */
    this.__construct = function(){
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            document.observe("mousemove", this.mouseMoveEvent.bind(this));
            
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
       return {
            'x' : '[x] x'
       };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        return status;
    }

    this.mouseMoveEvent = function(e){
        this.onMouseMoveEvent(e);
        infos.event = e;
        infos.y = Event.pointerY(e);
        infos.x = Event.pointerX(e);
    }

    this.getEvent = function(){ return infos.event; }
    this.getPosition = function(){ return {'x' : infos.x, 'y' : infos.y}; }

    
    /* Callback Functions */
    this.onMouseMoveEvent = function(e){}
}

extendClass("Camao_MouseEvent", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Navigation
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_NAVIGATION_SELECTION_CLASS = "selected";
var CAMAO_NAVIGATION_DEFAULT_BEHAVIOR = "list";
function Camao_Navigation(){
    this.infos = {};
    var infos = { 
        'observer' : {}, 
        'childs' : [], 
        KEY_SHIFT : false, 
        first_KEY_SHIFT : true,
        KEY_STRG : false
    };

    /* Default Functions */
    this.__construct = function(options){
        if (options) this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            //The Public version of infos needs some data
            this.infos.target = infos.target;
            //The Focus manager handle the activation of this obj
            Base.get("Camao_Navigation_Focus").addNavigationObj(this);
            this.createEvents();

            //Search all Childs and set first Element at active
            if (infos.behavior == "table"){
                infos.childs = infos.target.select("tbody " + infos.child_tag_name);
                infos.active = infos.childs[0].select("td")[0];
            }else{
                infos.childs = infos.target.select(infos.child_tag_name);
                infos.active = infos.childs[0];
            }
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    this.reinit = function(){
    var options = (typeof arguments[0] != "undefined") ? arguments[0] : {};
    if (options.target){ 
        infos.target = options.target;
        this.infos.target = options.target;
    }
    if (infos.behavior == "table"){
        infos.childs = infos.target.select("tbody " + infos.child_tag_name);
    }else{
        infos.childs = infos.target.select(infos.child_tag_name);
    }
    if (options.target){
        infos.active = infos.childs[0];
        infos.active = infos.childs[0].select("td")[0];

        infos.target.observe("click", infos.observer.target_click);
        infos.target.observe("dblclick", infos.observer.target_dblclick);

    }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'target' : '[Dom Element] Contains the content to navigate',
            'child_tag_name' : '[String] define the child name, default is autodetect',
            'classname' : '[String] define the class name for the selected element',
            'wrap' : '[BOOL] activate / deactivate the wrap selection',
            'behavior' : '[String] behavior for the object, possible : list, autocompletion',
            'multiselection' : '[BOOL] activate / deactivate multiselection'
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        if (!infos.classname){
            infos.classname = CAMAO_NAVIGATION_SELECTION_CLASS;
            this.throwException(EXCEPTION_NOTIFICATION, "options.classname is undefined, we use '" + infos.classname + "'");
        }

        if (typeof infos.header_selection_allowed == "undefined"){
            infos.header_selection_allowed = false;
            this.throwException(EXCEPTION_NOTIFICATION, "options.header_selection_allowed is undefined, we use 'false'");
        }


        if (typeof infos.wrap == "undefined"){
            infos.wrap = true;
            this.throwException(EXCEPTION_NOTIFICATION, "options.wrap is undefined, we use 'true'");
        }

        if (typeof infos.navigation_active == "undefined"){
            infos.navigation_active = true;
            this.throwException(EXCEPTION_NOTIFICATION, "options.navigation_active is undefined, we use 'true'");
        }

        if (typeof infos.multiselection == "undefined"){
            infos.multiselection = false;
            this.throwException(EXCEPTION_NOTIFICATION, "options.multiselection is undefined, we use 'false'");
        }else{
            disableSelection(infos.target);
            this.throwException(EXCEPTION_NOTIFICATION, "We disable the selection for this element to fix some browser selection bugs...");
        }

        if (!infos.behavior){
            if (infos.target.tagName.toLowerCase() == "table"){
                infos.behavior = "table";
            }else{
                infos.behavior = CAMAO_NAVIGATION_DEFAULT_BEHAVIOR;
            }
            this.throwException(EXCEPTION_NOTIFICATION, "options.behavior is undefined, we use '" + infos.behavior + "'");
        }

        if (!infos.child_tag_name){
            if (infos.behavior == "table"){
                infos.child_tag_name = "tr";
            }else{
                var firstElement = infos.target.down();
                infos.child_tag_name = firstElement.tagName.toLowerCase();
            }
            if (infos.child_tag_name){
                this.throwException(EXCEPTION_NOTIFICATION, "options.child_tag_name is undefined, we use '" + infos.child_tag_name + "'");
            }else{
                this.throwException(EXCEPTION_ERROR, "try to locate options.child_tag_name failed, no childs found for options.target!'");
                status = false;
            }
        }

        return status;
    }

    this.destroy = function(){
        document.stopObserving("keydown", infos.observer.keydown);
        document.stopObserving("keydown", infos.observer.keyup);
        infos.target.stopObserving("click", infos.observer.target_click);
        Base.get("Camao_Navigation_Focus").removeNavigationObj(this);
    }


    this.createEvents = function(){
        infos.observer.keydown = this.documentKeyDownEvent.bind(this);
        document.observe("keydown", infos.observer.keydown);

        infos.observer.keyup = this.documentKeyUpEvent.bind(this);
        document.observe("keyup", infos.observer.keyup);

        infos.observer.target_click = this.targetClickEvent.bind(this);
        infos.target.observe("click", infos.observer.target_click);

        infos.observer.target_dblclick = function(){
        this.entryDblClickEvent(infos.active);
    }.bind(this);
        infos.target.observe("dblclick", infos.observer.target_dblclick);
    }

    this.documentKeyDownEvent = function(event){
        if (infos.navigation_active){
            //We use multiselection, letzt handler there keys (shift, strg)
            if (infos.multiselection) this.handleHoldedKey(event, "pressed");

            if (event.keyCode == Event.KEY_UP){
                this.selectPreviousEntry();
                Event.stop(event);
            }else if (event.keyCode == Event.KEY_DOWN){
                this.selectNextEntry();
                Event.stop(event);
            }else if (event.keyCode == Event.KEY_RETURN){
                this.entryEnterClickEvent(infos.active);
            }

            if (infos.behavior == "table"){
                if (event.keyCode == Event.KEY_RIGHT){
                    this.selectRightEntry();
                }else if (event.keyCode == Event.KEY_LEFT){
                    this.selectLeftEntry();
                }
            }else if (infos.behavior == "list"){
                if (event.keyCode == Event.KEY_RIGHT){
                    this.selectNextEntry();
                }else if (event.keyCode == Event.KEY_LEFT){
                    this.selectPreviousEntry();
                }
            }else if (infos.behavior == "level"){
                if (event.keyCode == Event.KEY_RIGHT){
                    this.selectNextChildEntry();
                }else if (event.keyCode == Event.KEY_LEFT){
                    this.selectParentEntry();
                }
            }
        }
    }

    this.documentKeyUpEvent = function(event){
        if (infos.multiselection){
            this.handleHoldedKey(event, "release");
        }
    }

    this.handleHoldedKey = function(event, status){
        if (event.keyCode == Event.KEY_SHIFT){
            if (status == "pressed"){
                infos.KEY_SHIFT = true;
            }else{
                infos.KEY_SHIFT = false;
                infos.first_KEY_SHIFT = true;
            }
        }else if (event.keyCode == Event.KEY_STRG){
            if (status == "pressed"){
                infos.KEY_STRG = true;
            }else{
                infos.KEY_STRG = false;
            }
        }
    }

    this.targetClickEvent = function(event){
        if (infos.navigation_active){
            var target = event.target;
            var childName = infos.child_tag_name;

            if (infos.behavior == "table") childName = "td";
            if (target.nodeName.toLowerCase() != childName){ 
        if (childName == "td" && infos.header_selection_allowed){
            if (target.nodeName.toLowerCase() != "th"){ 
            target = target.up(childName);
            }
        }else{
            target = target.up(childName);
        }
        }

            if (
        (target && target.nodeName.toLowerCase() == childName)
            ||
        (target && infos.header_selection_allowed && childName == "td" && target.nodeName.toLowerCase() == "th")
           )
        {
                //We select different entries
                if (infos.multiselection && infos.KEY_STRG == true) {
                    this.selectEntry(target,false);
                //We select a range of entries
                }else if (infos.multiselection && infos.KEY_SHIFT == true) {
                    this.selectRange(target);
                //We select a singe entry
                }else{
                    this.selectEntry(target,true);
                }

                if (infos.behavior == "autocompletion"){
                    this.entryClickEvent(target);
                }
            }
        }
    }

    this.activateNavigation = function(){ infos.navigation_active = true; }
    this.deActivateNavigation = function(){ infos.navigation_active = false; }

    /* Navigation funktions */

    this.selectParentEntry = function(){
        if (infos.active && infos.active.up(infos.child_tag_name)){
            if (infos.active.up(infos.child_tag_name)){
                infos.active = infos.active.up(infos.child_tag_name);
                this.selectEntry(infos.active,true);
            }
        }
    }

    this.selectNextChildEntry = function(){
        if (infos.active && infos.active.down(infos.child_tag_name)){
            infos.active = infos.active.down(infos.child_tag_name);
            if (infos.active) this.selectEntry(infos.active,true);
        }
    }

    this.selectRange = function(target){
        if (infos.first_KEY_SHIFT){
            this.resetSelection();
            infos.first_KEY_SHIFT = false;
        }
        var startEntry = infos.active;
        var endEntry = target;
        if (infos.behavior == "table"){
            //maybe we need to switch the start with the end

            //We are on the same TR
            if (startEntry.up() == endEntry.up()){
                var startEntry_childs = startEntry.up().select("td");
                if (startEntry_childs.indexOf(startEntry) > startEntry_childs.indexOf(endEntry)){
                    var tmp = startEntry;
                    startEntry = endEntry;
                    endEntry = tmp;
                }
            //We are on different TRs
            }else{
                if (infos.childs.indexOf(startEntry.up()) > infos.childs.indexOf(endEntry.up())){
                    var tmp = startEntry;
                    startEntry = endEntry;
                    endEntry = tmp;
                }
            }

            var startTrFound = false;
            var startTrs = [];
            infos.childs.each(function(tr){
                if (tr == startEntry.up()) startTrFound = true;
                if (startTrFound) startTrs.push(tr);
                if (tr == endEntry.up()) startTrFound = false;
            }.bind(this));

            var foundStart = false;
            startTrs.each(function(tr){
                var childs = tr.select("td");

                childs.each(function(e){
                    if (e == startEntry) foundStart = true; 
                    if (foundStart){
                        this.selectEntry(e,false);
                        if (e == endEntry) foundStart = false;
                    }
                }.bind(this));

            }.bind(this));

        }else{
            var childs = startEntry.up().select(infos.child_tag_name);

            //maybe we need to switch the start with the end
            if (childs.indexOf(startEntry) > childs.indexOf(endEntry)){
                var tmp = startEntry;
                startEntry = endEntry;
                endEntry = tmp;
            }

            var startfound = false;
            infos.childs.each(function(e){
                if (e == startEntry) startfound = true; 
                if (startfound){
                    this.selectEntry(e,false);
                    if (e == endEntry) startfound = false;
                }
            }.bind(this));
        }

        infos.active = target;
    }
    
    this.selectRightEntry = function(){
    var nextElement = infos.active.next();
    if (infos.behavior = "table") var nextElement = infos.active.next("td");
        if (nextElement){
            if (infos.multiselection && infos.KEY_SHIFT){
                this.selectRange(infos.active.next());
            }else{
                this.selectEntry(infos.active.next(), true);
            }
        }else{
            if (infos.wrap) this.selectEntry(infos.active.up().select("td")[0],true);
        }

    }

    this.selectLeftEntry = function(){
    var previousElement = infos.active.previous();
    if (infos.behavior = "table") var previousElement = infos.active.previous("td");
        if (previousElement){
            if (infos.multiselection && infos.KEY_SHIFT){
                this.selectRange(infos.active.previous());
            }else{
                this.selectEntry(infos.active.previous(), true);
            }

        }else{
            if (infos.wrap) this.selectEntry(infos.active.up().select("td").last(), true);
        }

    }

    this.selectNextEntry = function(){
        if (infos.behavior == "table"){
            var tr = infos.active.up();
            if (tr.next()){
                tr = tr.next();
                var td = tr.select("td")[infos.active.up().select("td").indexOf(infos.active)];
                if (infos.multiselection && infos.KEY_SHIFT){
                    this.selectRange(td);
                }else{
                    this.selectEntry(td, true);
                }
            }else{
                if (infos.wrap){
                    var first_tr = infos.target.select("tbody tr")[0];
                    var td = first_tr.select("td")[infos.active.up().select("td").indexOf(infos.active)];
                    if (infos.multiselection && infos.KEY_SHIFT){
                        this.selectEntry(td,false);
                    }else{
                        this.selectEntry(td,true);
                    }
                }
            }
        }else{
            if (infos.active.next()){
                if (infos.multiselection && infos.KEY_SHIFT){
                    this.selectRange(infos.active.next());
                }else{
                    this.selectEntry(infos.active.next(), true);
                }
            }else{
                if (infos.wrap){
                    if (infos.multiselection && infos.KEY_SHIFT){
                        this.selectEntry(infos.active.up().down(),false);
                    }else{
                        this.selectEntry(infos.active.up().down(),true);
                    }
                }
            }
        }
    }

    this.selectPreviousEntry = function(){
        if (infos.behavior == "table"){
            var tr = infos.active.up();
            if (tr.previous()){
                tr = tr.previous();
                var td = tr.select("td")[infos.active.up().select("td").indexOf(infos.active)];
                if (infos.multiselection && infos.KEY_SHIFT){
                    this.selectRange(td);
                }else{
                    this.selectEntry(td, true);
                }
            }else{
                if (infos.wrap){
                    var last_tr = infos.target.select("tbody tr")[infos.target.select("tbody tr").length - 1];
                    var td = last_tr.select("td")[infos.active.up().select("td").indexOf(infos.active)];
                    this.selectEntry(td,true);
                }
            }
        }else{
            if (infos.active.previous()){
                if (infos.multiselection && infos.KEY_SHIFT){
                    this.selectRange(infos.active.previous());
                }else{
                    this.selectEntry(infos.active.previous(), true);
                }
            }else{

                if (infos.wrap){
                    if (infos.multiselection && infos.KEY_SHIFT){
                        this.selectEntry(infos.childs[infos.childs.length - 1],false);
                    }else{
                        this.selectEntry(infos.childs[infos.childs.length - 1],true);
                    }
                }
            }
        }
    }

    this.selectEntry = function(node, resetSelection){
        this.onSelectEntryEvent(node);
        if (resetSelection == true){
            this.resetSelection();
            infos.active = node;
            infos.active.addClassName(infos.classname);
        }else{
            infos.active = node;
            if (infos.multiselection && infos.KEY_STRG){
                if (infos.active.hasClassName(infos.classname)){
                    infos.active.removeClassName(infos.classname);
                }else{
                    infos.active.addClassName(infos.classname);
                }
            }else{
                infos.active.addClassName(infos.classname);
            }
        }
    }

    this.resetSelection = function(){
        infos.target.select("." + infos.classname).each(function(e){
            e.removeClassName(infos.classname);
        }.bind(this));
    }

    this.entryClickEvent = function(node){
    this.onEntryClickEvent(node);
    }

    this.entryDblClickEvent = function(node){
        if (infos.multiselection){
            this.onEntryDblClickEvent(infos.target.select("." + infos.classname));
        }else{
            this.onEntryDblClickEvent(node);
        }
    }

    this.entryEnterClickEvent = function(node){
        if (infos.multiselection){
            this.onEntryEnterClickEvent(infos.target.select("." + infos.classname));
        }else{
            this.onEntryEnterClickEvent(node);
        }
    }

    /* Callback Functions */
    this.onEntryClickEvent = function(node){ 
        this.throwException(EXCEPTION_NOTIFICATION, "you should overload this.onEntryClickEvent function to handle the click / enter event!");
    }

    this.onEntryDblClickEvent = function(node){ 
        this.throwException(EXCEPTION_NOTIFICATION, "you should overload this.onEntryDblClickEvent function to handle the click / enter event!");
    }

    this.onEntryEnterClickEvent = function(node){ 
        this.throwException(EXCEPTION_NOTIFICATION, "you should overload this.onEntryEnterClickEvent function to handle the click / enter event!");
    }

    this.onSelectEntryEvent = function(node){}
}

extendClass("Camao_Navigation", "Camao_Exception");
/**
 * @category   Camao / Navigation
 * @package    Camao_Navigation_Focus
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_Navigation_Focus(){
    var infos = { Camao_NavigationObjs : [] };

    /* Default Functions */
    this.__construct = function(){ }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){ }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){ return { }; }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){ return true; }


    this.addNavigationObj = function(Camao_NavigationObj){
        infos.Camao_NavigationObjs.push(Camao_NavigationObj);
        this.createEvents(Camao_NavigationObj);
        if (infos.autoInitTimeout) window.clearTimeout(infos.autoInitTimeout);
        infos.autoInitTimeout = this.autoInit.bind(this).delay(0.1);
    }

    this.removeNavigationObj = function(Camao_NavigationObj){
        infos.Camao_NavigationObjs[infos.Camao_NavigationObjs.indexOf(Camao_NavigationObj)] = "";
        infos.Camao_NavigationObjs = infos.Camao_NavigationObjs.without("");
    }

    this.autoInit = function(){
        this.deactivateAllNavigations();
        infos.Camao_NavigationObjs[0].activateNavigation();
    }

    this.createEvents = function(Camao_NavigationObj){
        Camao_NavigationObj.infos.target.observe("click", this.navigationGotFocus.bind(this, Camao_NavigationObj));
    }

    this.navigationGotFocus = function(Camao_NavigationObj){
        this.deactivateAllNavigations();
        Camao_NavigationObj.activateNavigation();
    }

    this.deactivateAllNavigations = function(){
        infos.Camao_NavigationObjs.each(function(Camao_NavigationObj){
            Camao_NavigationObj.deActivateNavigation();
        }.bind(this));
    }
    
    /* Callback Functions */
}

extendClass("Camao_Navigation_Focus", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Position
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_Position(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(){
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){

        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    this.setElementToScreenMiddle = function(element){
        Base.get("Camao_Cron",true,{
            'target' : $(element),
            'rule' : { 'onDifferents' : ['$(infos.target).getDimensions().height', 0]},
            'callback' : this.setElementToScreenMiddleDelay.bind(this,element)
        });
    }

    this.setElementToScreenMiddleDelay = function(element){
        var dimensions = $(element).getDimensions();

        var viewport = document.viewport;
        if ( (dimensions.height == 0) && (dimensions.height == 0) ){
            this.throwException(EXCEPTION_ERROR, "Element cant set to middle! No dimensions found (widthXheight), inspect the target !");
        }else{
            var middleTop = (viewport.getHeight() / 2) - (dimensions.height / 2) + document.viewport.getScrollOffsets()[1];
            var middleLeft = (viewport.getWidth() / 2) - (dimensions.width / 2) + document.getScrollLeft();

            //Wir verschieben das element niemals ausserhalb des sichtbaren bereichs

            if (document.viewport.getHeight() < dimensions.height){
                middleTop = middleTop + (document.viewport.getScrollOffsets()[1] - middleTop);
            }

            if (middleTop < 0) middleTop = 0;

//console.log(middleTop);
            $(element).setStyle({
                'top' : middleTop + 'px',
                'left' : middleLeft + 'px'
            });
        }
        this.onElementToScreenMiddle();
    }

    this.setElementToMousePosition = function(element){
        var position = Base.get("Camao_MouseEvent").getPosition();

        element.setStyle({
            'top' : position.y + 10 + 'px',
            'left' : position.x + 10 + 'px'
        });
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'x' : '[x] x'
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        return status;
    }


    
    /* Callback Functions */
    this.onElementToScreenMiddle = function(){ }

}

extendClass("Camao_Position", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_ScrollableElement
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

/*
    <div>
        <div class="previous">Back</div>
        <div>
            <div class="image">
                <img src="" />
            </div>
            <div class="image">
                <img src="" />
            </div>
            <div class="image">
                <img src="" />
            </div>
            <div class="image">
                <img src="" />
            </div>
        </div>
        <div class="next">forward</div>
    </div>
*/

var CAMAO_SCROLLABLEELEMENT_DEFAULT_CLICK_BEHAVIOUR = "smooth";
var CAMAO_SCROLLABLEELEMENT_DEFAULT_END_BEHAVIOUR = "stop";

function Camao_ScrollableElement(){
    var infos = {
        'observe' : {},
    'dimensions' : {}
    };

    /* Default Functions */
    this.__construct = function(options){
        this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            this.createEvents();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ 
            this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); 
            status = false;
        }else{
            if (infos.target.select(".previous").length == 0){
                this.throwException(EXCEPTION_ERROR, "we cant find the 'previous'-Button class inside options.target!"); 
                status = false;
            }
            if (infos.target.select(".next").length == 0){
                this.throwException(EXCEPTION_ERROR, "we cant find the 'next'-Button class inside options.target!"); 
                status = false;
            }
            if (infos.target.select(".image").length == 0){
                this.throwException(EXCEPTION_ERROR, "we cant find a image with 'image'-Class inside options.target!"); 
                status = false;
            }
        }

        if (typeof infos.clickBehaviour == "undefined"){
            infos.clickBehaviour = CAMAO_SCROLLABLEELEMENT_DEFAULT_CLICK_BEHAVIOUR;
            this.throwException(EXCEPTION_NOTIFICATION, "options.clickBehaviour is undefined! We use '" + CAMAO_SCROLLABLEELEMENT_DEFAULT_CLICK_BEHAVIOUR + "' "); 
        }

        if (typeof infos.endBehaviour == "undefined"){
            infos.endBehaviour = CAMAO_SCROLLABLEELEMENT_DEFAULT_END_BEHAVIOUR;
            this.throwException(EXCEPTION_NOTIFICATION, "options.endBehaviour is undefined! We use '" + CAMAO_SCROLLABLEELEMENT_DEFAULT_END_BEHAVIOUR + "' "); 
        }

        infos.scrollableContainer = infos.target.select(".image")[0].up();
        infos.visibleContainer = infos.scrollableContainer.up();
        infos.scrollableContainer.setStyle({
            'position' : 'relative'
        });
    
        infos.dimensions.scrollableContainer = infos.scrollableContainer.getDimensions();
        infos.dimensions.visibleContainer = infos.visibleContainer.getDimensions();
    
        infos.dimensions.realScrollableContainer = 0;
        infos.target.select(".image").each(function(image){
            infos.dimensions.realScrollableContainer += image.getDimensions().width;
        });

        return status;
    }

    this.createEvents = function(){
        var onEvent = "click";

        if (infos.clickBehaviour == "smooth") onEvent = "mousedown";
        
        infos.observe.gotoPrevious = this.moveEvent.bind(this, "left");
        infos.observe.gotoNext = this.moveEvent.bind(this, "right");

        infos.target.select(".previous")[0].observe(onEvent, infos.observe.gotoPrevious);
        infos.target.select(".next")[0].observe(onEvent, infos.observe.gotoNext);

        if (infos.clickBehaviour == "smooth"){
            infos.observe.keyup = this.keyupEvent.bind(this);
            infos.target.select(".next")[0].observe("mouseup", infos.observe.keyup);
            infos.target.select(".previous")[0].observe("mouseup", infos.observe.keyup);
        }

    }
    
    this.keyupEvent = function(){
        infos.moveStatus = "inactive";
    }

    this.moveEvent = function(direction){
        this.onMoveEvent(direction);
        if (infos.clickBehaviour == "smooth"){
            infos.moveStatus = "active";
            this.moveContainer(direction);
        }else if (infos.clickBehaviour == "jump"){
            this.moveContainer(direction);
        }
    }

    this.moveSmooth = function(direction){
        if ( (infos.clickBehaviour == "smooth") && (infos.moveStatus == "active") ){
            if (direction == "right"){
                infos.scrollableContainer.setStyle({
                    'left' : (parseInt(infos.scrollableContainer.getStyle("left")) + 5) + "px"
                });
            }else{
                infos.scrollableContainer.setStyle({
                    'left' : (parseInt(infos.scrollableContainer.getStyle("left")) - 5) + "px"
                });
            }
            var posCalc = parseInt(infos.scrollableContainer.getStyle("left")) - infos.dimensions.visibleContainer.width;

            var tmpImages = infos.target.select(".image");

            //Wir sind am ende angekommen
            if (posCalc == parseInt("-" + infos.dimensions.realScrollableContainer)){

                tmpImages[0].up().appendChild(tmpImages[0]);

                //Wir sind am anfang
            }else if(infos.scrollableContainer.getStyle("left") == "0px"){

            }

            this.moveSmooth.bind(this).delay(0.06, direction);
        }
    }

    this.moveContainer = function(direction){
        if ( (infos.clickBehaviour == "smooth") && (infos.moveStatus == "active") ){
            if (direction == "right"){
                var newLeft = (parseInt(infos.scrollableContainer.getStyle("left")) + 10) + "px"; 
            }else{
                var newLeft = (parseInt(infos.scrollableContainer.getStyle("left")) - 10) + "px"; 
            }

            if (this.isPositionInRange(newLeft) == true){
                infos.scrollableContainer.setStyle({
                    'left' : newLeft
                });
                this.moveContainer.bind(this).delay(0.06, direction);
            }
       }else if(infos.clickBehaviour == "jump"){

       }
    }  

    this.isPositionInRange = function(newLeft){
        var posCalc = parseInt(newLeft) - infos.dimensions.visibleContainer.width;
    
        var setNewPosition = true;
        //Wir sind am ende angekommen
        if (posCalc == parseInt("-" + infos.dimensions.realScrollableContainer)){
            if (infos.endBehaviour == "stop") setNewPosition = false;
        //Wir sind am anfang
        }else if(newLeft == "0px"){
            if (infos.endBehaviour == "stop") setNewPosition = false;
        }
        return setNewPosition;
    }

    this.destroy = function(){
        var onEvent = "click";

        if (infos.behaviour == "smooth") onEvent = "keydown";

        infos.target.select(".previous")[0].observe(onEvent, infos.observe.gotoPrevious);
        infos.target.select(".next")[0].observe(onEvent, infos.observe.gotoNext);

        if (infos.behaviour == "smooth"){
            infos.target.select(".next")[0].stopObserve("keyup", infos.observe.keyup);
            infos.target.select(".previous")[0].stopObserve("keyup", infos.observe.keyup);
        }
    }

    /* Callback Functions */
    this.onMoveEvent = function(direction){}
}

extendClass("Camao_ScrollableElement", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Lightbox
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_LIGHTBOX_DEFAULT_CLOSE_BUTTON_CLASS = "camao_lightbox_close";
var CAMAO_LIGHTBOX_DEFAULT_CLOSE_CHAR = Event.KEY_ESC;
var CAMAO_LIGHTBOX_DEFAULT_SHAPE_BACKGROUND = "#ccc";
var CAMAO_LIGHTBOX_DEFAULT_OPACITY = 0.5;

var CAMAO_LIGHTBOX_USE_SHAPE = true;
var CAMAO_LIGHTBOX_ELEMENT_ID = "lightbox";
var CAMAO_LIGHTBOX_SHAPE_ID = "shape";
var CAMAO_LIGHTBOX_POSITION = "middle";

function Camao_Lightbox(){
    var infos = { 'observe' : {}};

    /* Default Functions */
    this.__construct = function(param){
        if (param) this.init(param);
    }

    /**
     * Initiate the Object
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            this.createElements();
            this.setContent();
            this.createDoucmentEvents();
            this.resizeEvent();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return void
     */
    this.getPossibleOptions = function(){
        return {
            'use_shape' : '[Bool] de/activate the shape, default is true',
            'lightbox_id' : '[String] lightbox id, default is ' + CAMAO_LIGHTBOX_ELEMENT_ID,
            'shape_id' : '[String] shape id, default is ' + CAMAO_LIGHTBOX_SHAPE_ID,
            'position' : '[String] lightbox position, default is ' + CAMAO_LIGHTBOX_POSITION + ', possible [middle. mouse]',
            'content' : '[Dyn] accept HTML code, Request URI or DOM element',
            'params' : '[Dyn] params for the request'
        };
    }

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (typeof infos.use_shape == "undefined"){ 
            infos.use_shape = CAMAO_LIGHTBOX_USE_SHAPE
            this.throwException(EXCEPTION_NOTIFICATION, "options.use_shape is undefined, we use '" + CAMAO_LIGHTBOX_USE_SHAPE + "'");
        }

        if (!infos.lightbox_id){ 
            infos.lightbox_id = CAMAO_LIGHTBOX_ELEMENT_ID
            this.throwException(EXCEPTION_NOTIFICATION, "options.lighbox_id is undefined, we use '" + CAMAO_LIGHTBOX_ELEMENT_ID + "'");
        }

        if ($(infos.lighbox_id)){
            this.throwException(EXCEPTION_ERROR, "options.lighbox_id '" + infos.lighbox_id + "' already in use!");
            status = false;
        }

        if (infos.use_shape){
            if ($(infos.shape_id)){
                this.throwException(EXCEPTION_ERROR, "options.shape_id '" + infos.lighbox_id + "' already in use!");
                status = false;
            }

            if (!infos.shape_id){ 
                infos.shape_id = CAMAO_LIGHTBOX_SHAPE_ID
                this.throwException(EXCEPTION_NOTIFICATION, "options.shape_id is undefined, we use '" + CAMAO_LIGHTBOX_SHAPE_ID + "'");
            }
        }

        if (!infos.position){ 
            infos.position = CAMAO_LIGHTBOX_POSITION
            this.throwException(EXCEPTION_NOTIFICATION, "options.position is undefined, we use '" + CAMAO_LIGHTBOX_POSITION + "'");
        }

        if (typeof infos.opacity == "undefined"){
            infos.opacity = CAMAO_LIGHTBOX_DEFAULT_OPACITY
            this.throwException(EXCEPTION_NOTIFICATION, "options.opacity is undefined, we use '" + CAMAO_LIGHTBOX_DEFAULT_OPACITY + "'");
        }
        
        if (!infos.content){
            this.throwException(EXCEPTION_ERROR, "options.content is undefined!");
            status = false;
        }else{
            if (typeof infos.content == "string"){
                if ((infos.content.indexOf("<") != -1) && (infos.content.indexOf(">") != -1)){
                    infos.content_type = "html";
                    this.throwException(EXCEPTION_NOTIFICATION, "options.content is identify as HTML Content");
                }else if (infos.content.indexOf("/") != -1) {
                    infos.content_type = "url";
                    this.throwException(EXCEPTION_NOTIFICATION, "options.content is identify as Request URL");
                }else{
                    infos.content_type = "html";
                    this.throwException(EXCEPTION_NOTIFICATION, "options.content is identify as Request html");
                }
            }else if (typeof infos.content == "object"){
                infos.content_type = "dom";
                this.throwException(EXCEPTION_NOTIFICATION, "options.content is identify as Dom Element");
            }else{
                this.throwException(EXCEPTION_ERROR, "cant identify options.content!");
                status = false;
            }
        }

        return status;
    }


    this.createElements = function(){
        var body = $$("body")[0];
        infos.element_lightbox = new Element("div", { 'id' : infos.lightbox_id }).setStyle({
            'position' : 'absolute',
            'zIndex' : 1
        });
        infos.element_lightbox.hide();
        body.appendChild(infos.element_lightbox);


        if (infos.use_shape){

            var usedHeight = typeof document.height == "undefined" ? document.viewport.getHeight() : document.height;
            if (usedHeight == 1){
                usedHeight = document.viewport.getHeight()
            }

            infos.element_shape = new Element("div", { 'id' : infos.shape_id }).setStyle({
                'width' : document.viewport.getWidth() + 'px',
                'height' : usedHeight  + 'px',
                'top' : '0px',
                'left' : '0px',
                'position' : 'absolute'
            }).setOpacity(infos.opacity);

            if (infos.element_shape.getStyle("backgroundColor") == "transparent"){
                this.throwException(EXCEPTION_NOTIFICATION, "the shape is transparent! we use " + CAMAO_LIGHTBOX_DEFAULT_SHAPE_BACKGROUND + " as background color!");
                infos.element_shape.setStyle({ 'background' : CAMAO_LIGHTBOX_DEFAULT_SHAPE_BACKGROUND});
            }
            body.appendChild(infos.element_shape);
        }
    }

    this.createDoucmentEvents = function(){
        //PROTOTYPEJS BUG!
        //Es ist spaeter nicht mehr moeglich den observer zu entfernen
        //daher speichern wir den 'bind' in einer variable zwichen.
        infos.observe.keydown = this.keyDownEvent.bind(this);
        document.observe("keydown", infos.observe.keydown);

        infos.observe.resize = this.resizeEvent.bind(this);
        Event.observe(document.onresize ? document : window, "resize", infos.observe.resize);

        infos.observe.scroll = this.scrollEvent.bind(this);
        document.observe("scroll", infos.observe.scroll);

        if (!infos.use_shape){
            infos.observe.mousedown = this.mouseDownEvent.bind(this);
            document.observe("mousedown", infos.observe.mousedown);
        }else{
            infos.element_shape.observe("click", this.destroy.bind(this));
        }
    }

    this.setPosition = function(){

        var position = infos.position;
        if ( (position != "middle") && (position != "mouse") ){
            this.throwException(EXCEPTION_NOTIFICATION, "unknown position, use fallback to " + CAMAO_LIGHTBOX_POSITION);
            position = CAMAO_LIGHTBOX_POSITION;
        }

        if (position == "middle"){
            Base.get("Camao_Position").onElementToScreenMiddle = function(){
                infos.element_lightbox.show();
            }.bind(this);
            Base.get("Camao_Position").setElementToScreenMiddle(infos.element_lightbox);


        }else if (position == "mouse"){
            Base.get("Camao_Position").setElementToMousePosition(infos.element_lightbox);
        }

    }



    this.setContent = function(){
        if (infos.content_type == "html"){
            infos.element_lightbox.update(infos.content);
            this.createEvents();
        }else if (infos.content_type == "dom"){
            var newElement = infos.content; //.cloneNode(true);
          //  if (newElement.id) newElement.id = "";
            if (!newElement.visible()) newElement.show();
            infos.element_lightbox.insert(newElement);
            this.createEvents();

        }else if (infos.content_type == "url"){
            new Ajax.Request(infos.content, {
                'parameters': infos.params || {},
                'method': 'post',
                'evalJS': true,
                'onSuccess': function(r) {
                    if (r.responseJSON){
                        var json = r.responseJSON;
                        if (!json.content){
                            this.throwException(EXCEPTION_NOTIFICATION, "cant find content part in JSON response!");
                        }else{
                            infos.element_lightbox.update(json.content);
                        }
                    }else{
                        infos.element_lightbox.update(r.responseText);
                    }
                    this.createEvents();
                }.bind(this)
            });
        }
    }

    this.createEvents = function(){
        infos.element_lightbox.select("." + CAMAO_LIGHTBOX_DEFAULT_CLOSE_BUTTON_CLASS).each(function(e){
            e.observe("click", this.destroy.bind(this));
        }.bind(this));
        this.onLightboxLoaded();
    }

    this.keyDownEvent = function(e) {
        if(e.which == CAMAO_LIGHTBOX_DEFAULT_CLOSE_CHAR) {
            Event.stop(e);
            this.destroy();
        }
    }

    this.resizeEvent = function(){
        this.setPosition();
        if (infos.use_shape){
            var usedHeight = typeof document.height == "undefined" ? document.viewport.getHeight() : document.height;
            if (usedHeight == 1){
                usedHeight = document.viewport.getHeight()
            }

            infos.element_shape.setStyle({
                'width' : document.viewport.getWidth() + document.getScrollLeft() + 'px',
                'height' : usedHeight + document.viewport.getScrollOffsets()[1] + 'px'
            });
        }
    }

    this.scrollEvent = function(){
        if (infos.resizedelay) window.clearTimeout(infos.resizedelay);
        infos.resizedelay = this.resizeEvent.bind(this).delay(0.2);
    }

    this.mouseDownEvent = function(e){
        if (!$(e.target).up("#" + infos.lightbox_id)) this.destroy();
    }

    this.destroy = function(){
        if (infos.element_lightbox) infos.element_lightbox.hide();
        if (infos.element_shape) infos.element_shape.remove();
        document.stopObserving("keydown", infos.observe.keydown);
        document.stopObserving("scroll", infos.observe.scroll);
        Event.stopObserving(document.onresize ? document : window, "resize", infos.observe.resize);
        if (infos.observ_mousedown) document.stopObserving("mousedown", infos.observe.mousedown);
        this.onLightboxDestroy();
    }



    /* Callback Functions */
    this.onLightboxLoaded = function(){}
    this.onLightboxDestroy = function(){}
}

extendClass("Camao_Lightbox", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Upload
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

/**
 * Formular target Constanten.
 *
 * @var constant
 */

var FLASH_UPLOAD_URL = "/upload";
var FLASH_UPLOAD_SIZE_LIMIT = "5 MB";

function Camao_Upload(){
    var infos = {
       'entries' : new Array()
    };

    this.infos = infos;

    /* Default Functions */
    this.__construct = function(){
    };

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            infos.dummy = new Element("img");
            infos.dummy.onerror = function(){
                this.throwException(EXCEPTION_ERROR, "options.upload_infos.button_image_url doesnt exists!");
            }.bind(this);
            if (typeof infos.upload_infos.button_width == "undefined"){
    
                infos.dummy.onload = function(){
                    if ((infos.dummy.width == 0) || (infos.dummy.height == 0)){
                        this.throwException(EXCEPTION_ERROR, "cant get upload button image size, bug?!");
                    }else{
                        infos.upload_infos.button_width = infos.dummy.width;
                        infos.upload_infos.button_height = infos.dummy.height;
                        this.loadFlashUploader();
                    }
                }.bind(this);
            }else{
                this.loadFlashUploader();
            }
            infos.dummy.src = infos.upload_infos.button_image_url;

        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    };

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'use_queue' : '[BOOL] de/activate the queue',
            'target' : '[Dom Element] from the queue',
            'template' : '[Dom Element] from the queue template',
            'session_id' : '[INT] PHP SessionID to handel the Upload correct',
            'button_placeholder_id' : '[Dom Element] the upload button placeholder',
            'post_params' : '[Object] define some post valiablen',
            'upload_infos' : {
                'upload_url' : '[STRING] Upload URL',
                'size_limit' : '[INT] Maximum upload size / per file',
                'allowed_file_types' : '[STRING] allowed filetypes , sample *.jpg;*.png',
                'button_image_url' : '[STRING] url to the upload button'
            }
        };
    };


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (typeof infos.use_queue == "boolean" && infos.use_queue == true){
            if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
            if (!infos.template){ this.throwException(EXCEPTION_ERROR, "options.template is undefined!"); status = false;}
        }else{
            infos.use_queue = false;
        }
        if (!infos.session_id){ this.throwException(EXCEPTION_ERROR, "options.session_id is undefined!"); status = false;}
        if (!infos.post_params){ infos.post_params = {}; }

        if (!infos.upload_infos){ 
            this.throwException(EXCEPTION_ERROR, "options.upload_infos is undefined!"); status = false;
        }else{
            if (!infos.upload_infos.upload_url){
                infos.upload_infos.upload_url = FLASH_UPLOAD_URL;
                this.throwException(EXCEPTION_NOTIFICATION, "options.upload_infos.upload_url is undefined, we use " + FLASH_UPLOAD_URL);
            }
            if (!infos.upload_infos.size_limit){
                infos.upload_infos.size_limit = FLASH_UPLOAD_SIZE_LIMIT;
                this.throwException(EXCEPTION_NOTIFICATION, "options.upload_infos.size_limit is undefined, we use " + FLASH_UPLOAD_SIZE_LIMIT);
            }
    
            if (!infos.upload_infos.allowed_file_types){
                this.throwException(EXCEPTION_ERROR, "options.upload_infos.allowed_file_types is undefined!");
                status = false;
            }
            if (!infos.upload_infos.button_image_url){
                this.throwException(EXCEPTION_ERROR, "options.upload_infos.button_image_url is undefined!");
                status = false;
            }
            if (!infos.button_placeholder_id){
                this.throwException(EXCEPTION_ERROR, "options.button_placeholder_id is undefined!");
                status = false;
            }
    
        }
        return status;
    };

    
    this.loadFlashUploader = function(){
        Object.extend(infos.post_params, { "PHPSESSID" : infos.session_id });

        //Wir initialisieren unser Entry Template
        infos.uploader = new SWFUpload({ 
            file_post_name : "flash_upload",
            post_params : infos.post_params,
    
            upload_url : infos.upload_infos.upload_url, 
            flash_url : ROOT_JS + "/Camao/Ressources/Upload/swfupload.swf", 
    
            file_size_limit : infos.upload_infos.size_limit, 
            file_types : infos.upload_infos.allowed_file_types,
            file_types_description : "Upload",
            file_upload_limit : infos.upload_infos.file_upload_limit ? infos.upload_infos.file_upload_limit : "100",
            file_queue_limit : infos.upload_infos.file_queue_limit ? infos.upload_infos.file_queue_limit : "50",

            button_image_url : infos.upload_infos.button_image_url,
            button_placeholder_id : infos.button_placeholder_id,
            button_width: infos.upload_infos.button_width,
            button_height: infos.upload_infos.button_height,
    
            file_queued_handler : this.newFileQueued.bind(this),
            upload_start_handler : this.uploadStartEventHandler.bind(this), 
            upload_success_handler : this.uploadSuccessEventHandler.bind(this),
            upload_progress_handler : this.uploadProgress.bind(this),
            upload_error_handler : this.uploadError.bind(this)
        });
    };

    this.cleanup = function(){ infos.target.update(" "); };
    this.startUploadNow = function(){ infos.uploader.startUpload(); };

    /* Flash Functions */

    this.newFileQueued = function(fileObj){
        if (infos.use_queue){
           var cloned = infos.template.cloneNode(true);
            cloned.id = "upload_" + fileObj.index;
            cloned.select(".filename")[0].update(fileObj.name);
            cloned.select(".filesize")[0].update(fileObj.size);
            infos.target.appendChild(cloned);
        }
        
        infos.entries.push(fileObj.index);
        this.onNewFileQueued(fileObj);
        
        //this.newFileQueuedEvent(fileObj, cloned);
    };

    this.getPostParam = function(k)
    {
        return infos.post_params[k];
    };

    this.addPostParam = function(k, v)
    {
        infos.uploader.addPostParam(k, v);
    };

    this.removePostParam = function(k)
    {
        infos.uploader.removePostParam(k);
    };


    this.uploadProgress = function(fileObj, bytesLoaded, bytesTotal){
        var percent = (100 * bytesLoaded) / bytesTotal;
        percent = (percent >= 100) ? 100 : percent;
        this.onUploadProgess(fileObj, percent);
    };

    this.uploadSuccessEventHandler = function(fileObj, response){
        this.onUploadSuccess(fileObj,response);
        
        //Wir entfernen den eintrag aus dem array
        infos.entries[infos.entries.indexOf(fileObj.index)] = " ";
        infos.entries = infos.entries.without(" ");
        
        //Existiert noch ein eintrag ? dann laden wir ihn direkt hoch
        if (infos.entries[0]) this.startUploadNow();
    };

    this.uploadStartEventHandler = function(fileObj){ this.onUploadStartEventHandler(); };

    this.uploadError = function(file, errorCode, message){
        this.onUploadError(file, errorCode, message);
        
        switch (errorCode) {
            case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
                this.throwException(EXCEPTION_ERROR, "HTTP Error, maybe you have forgott to set the sessionID!");
                break;
            case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
                this.throwException(EXCEPTION_ERROR, "Wrong Upload Url defined or wrong answer.");
                break;
            case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
                this.throwException(EXCEPTION_ERROR, "Upload Failed.");
                break;
            case SWFUpload.UPLOAD_ERROR.IO_ERROR:
                this.throwException(EXCEPTION_ERROR, "I/O Error, source broken?!");
                break;
            case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
                this.throwException(EXCEPTION_ERROR, "Security Error");
                break;
            case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
                this.throwException(EXCEPTION_ERROR, "Upload limit exceeded");
                break;
            case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:
                this.throwException(EXCEPTION_ERROR, "The file was not found");
                break;
            case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
                this.throwException(EXCEPTION_ERROR, "File Validation Failed");
                break;
            case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
            case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
                this.throwException(EXCEPTION_WARNING, "File upload stopped!");
                break;
            default:
                this.throwException(EXCEPTION_WARNING, "Unhandled Error: " + error_code);
                break;
        }

    };

    this.setFileUploadLimit = function(val)
    {
        infos.uploader.setFileUploadLimit(val);
    };

    this.setFileQueueLimit = function(val)
    {
        infos.uploader.setFileQueueLimit(val);
    };

    /* Callback Functions */

    this.onUploadSuccess = function(fileObj,response){ };
    this.onNewFileQueued = function(fileObj){
        if (!infos.use_queue){ this.startUploadNow(); }
    };

    this.onUploadProgess = function(fileObj, percentDone) { };
    this.onUploadError = function(file, errorCode, message){ };
    this.onUploadStartEventHandler = function(){ };
}

extendClass("Camao_Upload", "Camao_Exception");
/**
 * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
 *
 * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
 *
 * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */


/* ******************* */
/* Constructor & Init  */
/* ******************* */
var SWFUpload;

if (SWFUpload == undefined) {
	SWFUpload = function (settings) {
		this.initSWFUpload(settings);
	};
}

SWFUpload.prototype.initSWFUpload = function (settings) {
	try {
		this.customSettings = {};	// A container where developers can place their own settings associated with this instance.
		this.settings = settings;
		this.eventQueue = [];
		this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
		this.movieElement = null;


		// Setup global control tracking
		SWFUpload.instances[this.movieName] = this;

		// Load the settings.  Load the Flash movie.
		this.initSettings();
		this.loadFlash();
		this.displayDebugInfo();
	} catch (ex) {
		delete SWFUpload.instances[this.movieName];
		throw ex;
	}
};

/* *************** */
/* Static Members  */
/* *************** */
SWFUpload.instances = {};
SWFUpload.movieCount = 0;
SWFUpload.version = "2.2.0 2009-03-25";
SWFUpload.QUEUE_ERROR = {
	QUEUE_LIMIT_EXCEEDED	  		: -100,
	FILE_EXCEEDS_SIZE_LIMIT  		: -110,
	ZERO_BYTE_FILE			  		: -120,
	INVALID_FILETYPE		  		: -130
};
SWFUpload.UPLOAD_ERROR = {
	HTTP_ERROR				  		: -200,
	MISSING_UPLOAD_URL	      		: -210,
	IO_ERROR				  		: -220,
	SECURITY_ERROR			  		: -230,
	UPLOAD_LIMIT_EXCEEDED	  		: -240,
	UPLOAD_FAILED			  		: -250,
	SPECIFIED_FILE_ID_NOT_FOUND		: -260,
	FILE_VALIDATION_FAILED	  		: -270,
	FILE_CANCELLED			  		: -280,
	UPLOAD_STOPPED					: -290
};
SWFUpload.FILE_STATUS = {
	QUEUED		 : -1,
	IN_PROGRESS	 : -2,
	ERROR		 : -3,
	COMPLETE	 : -4,
	CANCELLED	 : -5
};
SWFUpload.BUTTON_ACTION = {
	SELECT_FILE  : -100,
	SELECT_FILES : -110,
	START_UPLOAD : -120
};
SWFUpload.CURSOR = {
	ARROW : -1,
	HAND : -2
};
SWFUpload.WINDOW_MODE = {
	WINDOW : "window",
	TRANSPARENT : "transparent",
	OPAQUE : "opaque"
};

// Private: takes a URL, determines if it is relative and converts to an absolute URL
// using the current site. Only processes the URL if it can, otherwise returns the URL untouched
SWFUpload.completeURL = function(url) {
	if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
		return url;
	}
	
	var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
	
	var indexSlash = window.location.pathname.lastIndexOf("/");
	if (indexSlash <= 0) {
		path = "/";
	} else {
		path = window.location.pathname.substr(0, indexSlash) + "/";
	}
	
	return /*currentURL +*/ path + url;
	
};


/* ******************** */
/* Instance Members  */
/* ******************** */

// Private: initSettings ensures that all the
// settings are set, getting a default value if one was not assigned.
SWFUpload.prototype.initSettings = function () {
	this.ensureDefault = function (settingName, defaultValue) {
		this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
	};
	
	// Upload backend settings
	this.ensureDefault("upload_url", "");
	this.ensureDefault("preserve_relative_urls", false);
	this.ensureDefault("file_post_name", "Filedata");
	this.ensureDefault("post_params", {});
	this.ensureDefault("use_query_string", false);
	this.ensureDefault("requeue_on_error", false);
	this.ensureDefault("http_success", []);
	this.ensureDefault("assume_success_timeout", 0);
	
	// File Settings
	this.ensureDefault("file_types", "*.*");
	this.ensureDefault("file_types_description", "All Files");
	this.ensureDefault("file_size_limit", 0);	// Default zero means "unlimited"
	this.ensureDefault("file_upload_limit", 0);
	this.ensureDefault("file_queue_limit", 0);

	// Flash Settings
	this.ensureDefault("flash_url", "swfupload.swf");
	this.ensureDefault("prevent_swf_caching", true);
	
	// Button Settings
	this.ensureDefault("button_image_url", "");
	this.ensureDefault("button_width", 1);
	this.ensureDefault("button_height", 1);
	this.ensureDefault("button_text", "");
	this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
	this.ensureDefault("button_text_top_padding", 0);
	this.ensureDefault("button_text_left_padding", 0);
	this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
	this.ensureDefault("button_disabled", false);
	this.ensureDefault("button_placeholder_id", "");
	this.ensureDefault("button_placeholder", null);
	this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
	this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
	
	// Debug Settings
	this.ensureDefault("debug", false);
	this.settings.debug_enabled = this.settings.debug;	// Here to maintain v2 API
	
	// Event Handlers
	this.settings.return_upload_start_handler = this.returnUploadStart;
	this.ensureDefault("swfupload_loaded_handler", null);
	this.ensureDefault("file_dialog_start_handler", null);
	this.ensureDefault("file_queued_handler", null);
	this.ensureDefault("file_queue_error_handler", null);
	this.ensureDefault("file_dialog_complete_handler", null);
	
	this.ensureDefault("upload_start_handler", null);
	this.ensureDefault("upload_progress_handler", null);
	this.ensureDefault("upload_error_handler", null);
	this.ensureDefault("upload_success_handler", null);
	this.ensureDefault("upload_complete_handler", null);
	
	this.ensureDefault("debug_handler", this.debugMessage);

	this.ensureDefault("custom_settings", {});

	// Other settings
	this.customSettings = this.settings.custom_settings;
	
	// Update the flash url if needed
	if (!!this.settings.prevent_swf_caching) {
		this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
	}
	
	if (!this.settings.preserve_relative_urls) {
		//this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);	// Don't need to do this one since flash doesn't look at it
		this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
		this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
	}
	
	delete this.ensureDefault;
};

// Private: loadFlash replaces the button_placeholder element with the flash movie.
SWFUpload.prototype.loadFlash = function () {
	var targetElement, tempParent;

	// Make sure an element with the ID we are going to use doesn't already exist
	if (document.getElementById(this.movieName) !== null) {
		throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
	}

	// Get the element where we will be placing the flash movie
	targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;

	if (targetElement == undefined) {
		throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
	}

	// Append the container and load the flash
	tempParent = document.createElement("div");
	tempParent.innerHTML = this.getFlashHTML();	// Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
	targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);

	// Fix IE Flash/Form bug
	if (window[this.movieName] == undefined) {
		window[this.movieName] = this.getMovieElement();
	}
	
};

// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
SWFUpload.prototype.getFlashHTML = function () {
	// Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
	return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
				'<param name="wmode" value="', this.settings.button_window_mode, '" />',
				'<param name="movie" value="', this.settings.flash_url, '" />',
				'<param name="quality" value="high" />',
				'<param name="menu" value="false" />',
				'<param name="allowScriptAccess" value="always" />',
				'<param name="flashvars" value="' + this.getFlashVars() + '" />',
				'</object>'].join("");
};

// Private: getFlashVars builds the parameter string that will be passed
// to flash in the flashvars param.
SWFUpload.prototype.getFlashVars = function () {
	// Build a string from the post param object
	var paramString = this.buildParamString();
	var httpSuccessString = this.settings.http_success.join(",");
	
	// Build the parameter string
	return ["movieName=", encodeURIComponent(this.movieName),
			"&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
			"&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
			"&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
			"&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
			"&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
			"&amp;params=", encodeURIComponent(paramString),
			"&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
			"&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
			"&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
			"&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
			"&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
			"&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
			"&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
			"&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
			"&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
			"&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
			"&amp;buttonText=", encodeURIComponent(this.settings.button_text),
			"&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
			"&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
			"&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
			"&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
			"&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
			"&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
		].join("");
};

// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
// The element is cached after the first lookup
SWFUpload.prototype.getMovieElement = function () {
	if (this.movieElement == undefined) {
		this.movieElement = document.getElementById(this.movieName);
	}

	if (this.movieElement === null) {
		throw "Could not find Flash element";
	}
	
	return this.movieElement;
};

// Private: buildParamString takes the name/value pairs in the post_params setting object
// and joins them up in to a string formatted "name=value&amp;name=value"
SWFUpload.prototype.buildParamString = function () {
	var postParams = this.settings.post_params; 
	var paramStringPairs = [];

	if (typeof(postParams) === "object") {
		for (var name in postParams) {
			if (postParams.hasOwnProperty(name)) {
				paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
			}
		}
	}

	return paramStringPairs.join("&amp;");
};

// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
// all references to the SWF, and other objects so memory is properly freed.
// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
// Credits: Major improvements provided by steffen
SWFUpload.prototype.destroy = function () {
	try {
		// Make sure Flash is done before we try to remove it
		this.cancelUpload(null, false);
		

		// Remove the SWFUpload DOM nodes
		var movieElement = null;
		movieElement = this.getMovieElement();
		
		if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
			// Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
			for (var i in movieElement) {
				try {
					if (typeof(movieElement[i]) === "function") {
						movieElement[i] = null;
					}
				} catch (ex1) {}
			}

			// Remove the Movie Element from the page
			try {
				movieElement.parentNode.removeChild(movieElement);
			} catch (ex) {}
		}
		
		// Remove IE form fix reference
		window[this.movieName] = null;

		// Destroy other references
		SWFUpload.instances[this.movieName] = null;
		delete SWFUpload.instances[this.movieName];

		this.movieElement = null;
		this.settings = null;
		this.customSettings = null;
		this.eventQueue = null;
		this.movieName = null;
		
		
		return true;
	} catch (ex2) {
		return false;
	}
};


// Public: displayDebugInfo prints out settings and configuration
// information about this SWFUpload instance.
// This function (and any references to it) can be deleted when placing
// SWFUpload in production.
SWFUpload.prototype.displayDebugInfo = function () {
	this.debug(
		[
			"---SWFUpload Instance Info---\n",
			"Version: ", SWFUpload.version, "\n",
			"Movie Name: ", this.movieName, "\n",
			"Settings:\n",
			"\t", "upload_url:               ", this.settings.upload_url, "\n",
			"\t", "flash_url:                ", this.settings.flash_url, "\n",
			"\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
			"\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
			"\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
			"\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
			"\t", "file_post_name:           ", this.settings.file_post_name, "\n",
			"\t", "post_params:              ", this.settings.post_params.toString(), "\n",
			"\t", "file_types:               ", this.settings.file_types, "\n",
			"\t", "file_types_description:   ", this.settings.file_types_description, "\n",
			"\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
			"\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
			"\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
			"\t", "debug:                    ", this.settings.debug.toString(), "\n",

			"\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",

			"\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
			"\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
			"\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
			"\t", "button_width:             ", this.settings.button_width.toString(), "\n",
			"\t", "button_height:            ", this.settings.button_height.toString(), "\n",
			"\t", "button_text:              ", this.settings.button_text.toString(), "\n",
			"\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
			"\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
			"\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
			"\t", "button_action:            ", this.settings.button_action.toString(), "\n",
			"\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",

			"\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
			"Event Handlers:\n",
			"\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
			"\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
			"\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
			"\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
			"\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
			"\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
			"\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
			"\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
			"\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
			"\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
		].join("")
	);
};

/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
	the maintain v2 API compatibility
*/
// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
SWFUpload.prototype.addSetting = function (name, value, default_value) {
    if (value == undefined) {
        return (this.settings[name] = default_value);
    } else {
        return (this.settings[name] = value);
	}
};

// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
SWFUpload.prototype.getSetting = function (name) {
    if (this.settings[name] != undefined) {
        return this.settings[name];
	}

    return "";
};



// Private: callFlash handles function calls made to the Flash element.
// Calls are made with a setTimeout for some functions to work around
// bugs in the ExternalInterface library.
SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
	argumentArray = argumentArray || [];
	
	var movieElement = this.getMovieElement();
	var returnValue, returnString;

	// Flash's method if calling ExternalInterface methods (code adapted from MooTools).
	try {
		returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
		returnValue = eval(returnString);
	} catch (ex) {
		throw "Call to " + functionName + " failed";
	}
	
	// Unescape file post param values
	if (returnValue != undefined && typeof returnValue.post === "object") {
		returnValue = this.unescapeFilePostParams(returnValue);
	}

	return returnValue;
};

/* *****************************
	-- Flash control methods --
	Your UI should use these
	to operate SWFUpload
   ***************************** */

// WARNING: this function does not work in Flash Player 10
// Public: selectFile causes a File Selection Dialog window to appear.  This
// dialog only allows 1 file to be selected.
SWFUpload.prototype.selectFile = function () {
	this.callFlash("SelectFile");
};

// WARNING: this function does not work in Flash Player 10
// Public: selectFiles causes a File Selection Dialog window to appear/ This
// dialog allows the user to select any number of files
// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
// for this bug.
SWFUpload.prototype.selectFiles = function () {
	this.callFlash("SelectFiles");
};


// Public: startUpload starts uploading the first file in the queue unless
// the optional parameter 'fileID' specifies the ID 
SWFUpload.prototype.startUpload = function (fileID) {
	this.callFlash("StartUpload", [fileID]);
};

// Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
// If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
	if (triggerErrorEvent !== false) {
		triggerErrorEvent = true;
	}
	this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
};

// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
// If nothing is currently uploading then nothing happens.
SWFUpload.prototype.stopUpload = function () {
	this.callFlash("StopUpload");
};

/* ************************
 * Settings methods
 *   These methods change the SWFUpload settings.
 *   SWFUpload settings should not be changed directly on the settings object
 *   since many of the settings need to be passed to Flash in order to take
 *   effect.
 * *********************** */

// Public: getStats gets the file statistics object.
SWFUpload.prototype.getStats = function () {
	return this.callFlash("GetStats");
};

// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
// change the statistics but you can.  Changing the statistics does not
// affect SWFUpload accept for the successful_uploads count which is used
// by the upload_limit setting to determine how many files the user may upload.
SWFUpload.prototype.setStats = function (statsObject) {
	this.callFlash("SetStats", [statsObject]);
};

// Public: getFile retrieves a File object by ID or Index.  If the file is
// not found then 'null' is returned.
SWFUpload.prototype.getFile = function (fileID) {
	if (typeof(fileID) === "number") {
		return this.callFlash("GetFileByIndex", [fileID]);
	} else {
		return this.callFlash("GetFile", [fileID]);
	}
};

// Public: addFileParam sets a name/value pair that will be posted with the
// file specified by the Files ID.  If the name already exists then the
// exiting value will be overwritten.
SWFUpload.prototype.addFileParam = function (fileID, name, value) {
	return this.callFlash("AddFileParam", [fileID, name, value]);
};

// Public: removeFileParam removes a previously set (by addFileParam) name/value
// pair from the specified file.
SWFUpload.prototype.removeFileParam = function (fileID, name) {
	this.callFlash("RemoveFileParam", [fileID, name]);
};

// Public: setUploadUrl changes the upload_url setting.
SWFUpload.prototype.setUploadURL = function (url) {
	this.settings.upload_url = url.toString();
	this.callFlash("SetUploadURL", [url]);
};

// Public: setPostParams changes the post_params setting
SWFUpload.prototype.setPostParams = function (paramsObject) {
	this.settings.post_params = paramsObject;
	this.callFlash("SetPostParams", [paramsObject]);
};

// Public: addPostParam adds post name/value pair.  Each name can have only one value.
SWFUpload.prototype.addPostParam = function (name, value) {
	this.settings.post_params[name] = value;
	this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: removePostParam deletes post name/value pair.
SWFUpload.prototype.removePostParam = function (name) {
	delete this.settings.post_params[name];
	this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: setFileTypes changes the file_types setting and the file_types_description setting
SWFUpload.prototype.setFileTypes = function (types, description) {
	this.settings.file_types = types;
	this.settings.file_types_description = description;
	this.callFlash("SetFileTypes", [types, description]);
};

// Public: setFileSizeLimit changes the file_size_limit setting
SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
	this.settings.file_size_limit = fileSizeLimit;
	this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
};

// Public: setFileUploadLimit changes the file_upload_limit setting
SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
	this.settings.file_upload_limit = fileUploadLimit;
	this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
};

// Public: setFileQueueLimit changes the file_queue_limit setting
SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
	this.settings.file_queue_limit = fileQueueLimit;
	this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
};

// Public: setFilePostName changes the file_post_name setting
SWFUpload.prototype.setFilePostName = function (filePostName) {
	this.settings.file_post_name = filePostName;
	this.callFlash("SetFilePostName", [filePostName]);
};

// Public: setUseQueryString changes the use_query_string setting
SWFUpload.prototype.setUseQueryString = function (useQueryString) {
	this.settings.use_query_string = useQueryString;
	this.callFlash("SetUseQueryString", [useQueryString]);
};

// Public: setRequeueOnError changes the requeue_on_error setting
SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
	this.settings.requeue_on_error = requeueOnError;
	this.callFlash("SetRequeueOnError", [requeueOnError]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
	if (typeof http_status_codes === "string") {
		http_status_codes = http_status_codes.replace(" ", "").split(",");
	}
	
	this.settings.http_success = http_status_codes;
	this.callFlash("SetHTTPSuccess", [http_status_codes]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
	this.settings.assume_success_timeout = timeout_seconds;
	this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
};

// Public: setDebugEnabled changes the debug_enabled setting
SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
	this.settings.debug_enabled = debugEnabled;
	this.callFlash("SetDebugEnabled", [debugEnabled]);
};

// Public: setButtonImageURL loads a button image sprite
SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
	if (buttonImageURL == undefined) {
		buttonImageURL = "";
	}
	
	this.settings.button_image_url = buttonImageURL;
	this.callFlash("SetButtonImageURL", [buttonImageURL]);
};

// Public: setButtonDimensions resizes the Flash Movie and button
SWFUpload.prototype.setButtonDimensions = function (width, height) {
	this.settings.button_width = width;
	this.settings.button_height = height;
	
	var movie = this.getMovieElement();
	if (movie != undefined) {
		movie.style.width = width + "px";
		movie.style.height = height + "px";
	}
	
	this.callFlash("SetButtonDimensions", [width, height]);
};
// Public: setButtonText Changes the text overlaid on the button
SWFUpload.prototype.setButtonText = function (html) {
	this.settings.button_text = html;
	this.callFlash("SetButtonText", [html]);
};
// Public: setButtonTextPadding changes the top and left padding of the text overlay
SWFUpload.prototype.setButtonTextPadding = function (left, top) {
	this.settings.button_text_top_padding = top;
	this.settings.button_text_left_padding = left;
	this.callFlash("SetButtonTextPadding", [left, top]);
};

// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
SWFUpload.prototype.setButtonTextStyle = function (css) {
	this.settings.button_text_style = css;
	this.callFlash("SetButtonTextStyle", [css]);
};
// Public: setButtonDisabled disables/enables the button
SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
	this.settings.button_disabled = isDisabled;
	this.callFlash("SetButtonDisabled", [isDisabled]);
};
// Public: setButtonAction sets the action that occurs when the button is clicked
SWFUpload.prototype.setButtonAction = function (buttonAction) {
	this.settings.button_action = buttonAction;
	this.callFlash("SetButtonAction", [buttonAction]);
};

// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
SWFUpload.prototype.setButtonCursor = function (cursor) {
	this.settings.button_cursor = cursor;
	this.callFlash("SetButtonCursor", [cursor]);
};

/* *******************************
	Flash Event Interfaces
	These functions are used by Flash to trigger the various
	events.
	
	All these functions a Private.
	
	Because the ExternalInterface library is buggy the event calls
	are added to a queue and the queue then executed by a setTimeout.
	This ensures that events are executed in a determinate order and that
	the ExternalInterface bugs are avoided.
******************************* */

SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
	// Warning: Don't call this.debug inside here or you'll create an infinite loop
	
	if (argumentArray == undefined) {
		argumentArray = [];
	} else if (!(argumentArray instanceof Array)) {
		argumentArray = [argumentArray];
	}
	
	var self = this;
	if (typeof this.settings[handlerName] === "function") {
		// Queue the event
		this.eventQueue.push(function () {
			this.settings[handlerName].apply(this, argumentArray);
		});
		
		// Execute the next queued event
		setTimeout(function () {
			self.executeNextEvent();
		}, 0);
		
	} else if (this.settings[handlerName] !== null) {
		throw "Event handler " + handlerName + " is unknown or is not a function";
	}
};

// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
// we must queue them in order to garentee that they are executed in order.
SWFUpload.prototype.executeNextEvent = function () {
	// Warning: Don't call this.debug inside here or you'll create an infinite loop

	var  f = this.eventQueue ? this.eventQueue.shift() : null;
	if (typeof(f) === "function") {
		f.apply(this);
	}
};

// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
// properties that contain characters that are not valid for JavaScript identifiers. To work around this
// the Flash Component escapes the parameter names and we must unescape again before passing them along.
SWFUpload.prototype.unescapeFilePostParams = function (file) {
	var reg = /[$]([0-9a-f]{4})/i;
	var unescapedPost = {};
	var uk;

	if (file != undefined) {
		for (var k in file.post) {
			if (file.post.hasOwnProperty(k)) {
				uk = k;
				var match;
				while ((match = reg.exec(uk)) !== null) {
					uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
				}
				unescapedPost[uk] = file.post[k];
			}
		}

		file.post = unescapedPost;
	}

	return file;
};

// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
SWFUpload.prototype.testExternalInterface = function () {
	try {
		return this.callFlash("TestExternalInterface");
	} catch (ex) {
		return false;
	}
};

// Private: This event is called by Flash when it has finished loading. Don't modify this.
// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
SWFUpload.prototype.flashReady = function () {
	// Check that the movie element is loaded correctly with its ExternalInterface methods defined
	var movieElement = this.getMovieElement();

	if (!movieElement) {
		this.debug("Flash called back ready but the flash movie can't be found.");
		return;
	}

	this.cleanUp(movieElement);
	
	this.queueEvent("swfupload_loaded_handler");
};

// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
// This function is called by Flash each time the ExternalInterface functions are created.
SWFUpload.prototype.cleanUp = function (movieElement) {
	// Pro-actively unhook all the Flash functions
	try {
		if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
			this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
			for (var key in movieElement) {
				try {
					if (typeof(movieElement[key]) === "function") {
						movieElement[key] = null;
					}
				} catch (ex) {
				}
			}
		}
	} catch (ex1) {
	
	}

	// Fix Flashes own cleanup code so if the SWFMovie was removed from the page
	// it doesn't display errors.
	window["__flash__removeCallback"] = function (instance, name) {
		try {
			if (instance) {
				instance[name] = null;
			}
		} catch (flashEx) {
		
		}
	};

};


/* This is a chance to do something before the browse window opens */
SWFUpload.prototype.fileDialogStart = function () {
	this.queueEvent("file_dialog_start_handler");
};


/* Called when a file is successfully added to the queue. */
SWFUpload.prototype.fileQueued = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("file_queued_handler", file);
};


/* Handle errors that occur when an attempt to queue a file fails. */
SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
};

/* Called after the file dialog has closed and the selected files have been queued.
	You could call startUpload here if you want the queued files to begin uploading immediately. */
SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
	this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
};

SWFUpload.prototype.uploadStart = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("return_upload_start_handler", file);
};

SWFUpload.prototype.returnUploadStart = function (file) {
	var returnValue;
	if (typeof this.settings.upload_start_handler === "function") {
		file = this.unescapeFilePostParams(file);
		returnValue = this.settings.upload_start_handler.call(this, file);
	} else if (this.settings.upload_start_handler != undefined) {
		throw "upload_start_handler must be a function";
	}

	// Convert undefined to true so if nothing is returned from the upload_start_handler it is
	// interpretted as 'true'.
	if (returnValue === undefined) {
		returnValue = true;
	}
	
	returnValue = !!returnValue;
	
	this.callFlash("ReturnUploadStart", [returnValue]);
};



SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
};

SWFUpload.prototype.uploadError = function (file, errorCode, message) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_error_handler", [file, errorCode, message]);
};

SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
};

SWFUpload.prototype.uploadComplete = function (file) {
	file = this.unescapeFilePostParams(file);
	this.queueEvent("upload_complete_handler", file);
};

/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
   internal debug console.  You can override this event and have messages written where you want. */
SWFUpload.prototype.debug = function (message) {
	this.queueEvent("debug_handler", message);
};


/* **********************************
	Debug Console
	The debug console is a self contained, in page location
	for debug message to be sent.  The Debug Console adds
	itself to the body if necessary.

	The console is automatically scrolled as messages appear.
	
	If you are using your own debug handler or when you deploy to production and
	have debug disabled you can remove these functions to reduce the file size
	and complexity.
********************************** */
   
// Private: debugMessage is the default debug_handler.  If you want to print debug messages
// call the debug() function.  When overriding the function your own function should
// check to see if the debug setting is true before outputting debug information.
SWFUpload.prototype.debugMessage = function (message) {
	if (this.settings.debug) {
		var exceptionMessage, exceptionValues = [];

		// Check for an exception object and print it nicely
		if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
			for (var key in message) {
				if (message.hasOwnProperty(key)) {
					exceptionValues.push(key + ": " + message[key]);
				}
			}
			exceptionMessage = exceptionValues.join("\n") || "";
			exceptionValues = exceptionMessage.split("\n");
			exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
			SWFUpload.Console.writeLine(exceptionMessage);
		} else {
			SWFUpload.Console.writeLine(message);
		}
	}
};

SWFUpload.Console = {};
SWFUpload.Console.writeLine = function (message) {
	var console, documentForm;

	try {
		console = document.getElementById("SWFUpload_Console");

		if (!console) {
			documentForm = document.createElement("form");
			document.getElementsByTagName("body")[0].appendChild(documentForm);

			console = document.createElement("textarea");
			console.id = "SWFUpload_Console";
			console.style.fontFamily = "monospace";
			console.setAttribute("wrap", "off");
			console.wrap = "off";
			console.style.overflow = "auto";
			console.style.width = "700px";
			console.style.height = "350px";
			console.style.margin = "5px";
			documentForm.appendChild(console);
		}

		console.value += message + "\n";

		console.scrollTop = console.scrollHeight - console.clientHeight;
	} catch (ex) {
		alert("Exception: " + ex.name + " Message: " + ex.message);
	}
};
/**
 * @category   Camao
 * @package    Camao_Table
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

function Camao_Table(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(options){
        this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
            if (infos.use_navigation){
                infos.Camao_NavigationObj = Base.get("Camao_Navigation",true,{
                    'target' : infos.target,
                    'header_selection_allowed' : true
                });
            }
            this.createEvents();
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    this.reinit = function(){
        if (infos.use_navigation){
            infos.Camao_NavigationObj.reinit({ 'target' : infos.target});
        }
    }

    this.createEvents = function(){
        if (infos.use_navigation){
            infos.Camao_NavigationObj.onSelectEntryEvent = function(node){ 
                this.onSelectEntryEvent(node);
            }.bind(this);

            infos.Camao_NavigationObj.onEntryDblClickEvent = function(node){ 
                this.onEntryDblClickEvent(node);
            }.bind(this);

            infos.Camao_NavigationObj.onEntryEnterClickEvent = function(node){ 
                this.onEntryEnterClickEvent(node);
            }.bind(this);

        }
    }

    this.destroy = function(){
        infos.Camao_NavigationObj.destroy();
    }

    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }


    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        if (typeof infos.use_navigation == "undefined"){ 
            this.throwException(EXCEPTION_ERROR, "options.use_navigation is undefined, we set it to false!"); 
            infos.use_navigation = false;
        }

        if (typeof infos.alternate == "undefined"){ 
            this.throwException(EXCEPTION_NOTIFICATION, "options.alternate is undefined, we set it to false!"); 
        }
        if (typeof infos.mark_first_row == "undefined"){ 
            this.throwException(EXCEPTION_NOTIFICATION, "options.mark_first_row is undefined, we set it to false!"); 
        }
        if (typeof infos.mark_last_row == "undefined"){ 
            this.throwException(EXCEPTION_NOTIFICATION, "options.mark_last_row is undefined, we set it to false!"); 
        }

        return status;
    }
    this.addRow = function(){
        var options = (typeof arguments[0] != "undefined") ? arguments[0] : {};
        var tr = new Element("tr");
        var row_count = infos.target.select("tr")[0].select("td,th").length;
        for (var i = 0; i < row_count; i++){
            var td = new Element(options.head ? "th" : "td");
            if ( (typeof options.content != "undefined") && (typeof options.content[i] != "undefined") ){
                td.update(options.content[i]);
            }else{
                td.update("&nbsp;");
            }
            td.addClassName(options.type || "text");
            tr.appendChild(td);
        }

        if (typeof options.position != "undefined"){
            infos.target.select("tbody tr")[options.position].insert({ before : tr});
        }else{
            infos.target.select("tbody")[0].appendChild(tr);
        }
        infos.Camao_NavigationObj.reinit();
        this.onAddRow(tr);
    }

    this.removeRow = function(tr){
        this.onRemoveRow(tr);
        tr.remove();
    }

    this.addColumn = function(){
        var options = (typeof arguments[0] != "undefined") ? arguments[0] : {};
        var i = 0;
        infos.target.select("tr").each(function(tr){
            var td = new Element(options.head ? "th" : "td");
            if ( (typeof options.content != "undefined") && (typeof options.content[i] != "undefined") ){
                td.update(options.content[i]);
            }else{
                td.update("&nbsp;");
            }

            td.addClassName(options.type || "text");

            if (typeof options.position != "undefined"){
                tr.select("td,th")[options.position].insert({ before : td});
            }else{
                tr.appendChild(td);
            }
            i++;
        });
        infos.Camao_NavigationObj.reinit();
        this.onAddColumn();
    }

    this.removeColumn = function(number){
        this.onRemoveColumn(number);
        infos.target.select("tr").each(function(tr){
            tr.select("td,th")[number].remove();
        });
    }

    this.setRowAsHeader = function(options){
        if (typeof options.position == "undefined"){
            this.throwException(EXCEPTION_ERROR, "options.position is undefined, we cant set the header!"); 
        }else{
            var tr = infos.target.select("tr")[options.position];
            var values = [];
            tr.select("td").each(function(td){
                values.push({
                    'html' : td.innerHTML
                }).addClassName(td.className);
                td.remove();
            }.bind(this));
            values.each(function(html){
                tr.appendChild(new Element("th",{ 'class' : eval("html.class") }).update(html.html));
            }.bind(this));
        }
    }

    this.setColumnAsHeader = function(options){
        if (typeof options.position == "undefined"){
            this.throwException(EXCEPTION_ERROR, "options.position is undefined, we cant set the header!"); 
        }else{
            infos.target.select("tr").each(function(tr){
                var td = tr.select("th,td")[options.position];
                td.up().replaceChild(new Element("th").addClassName(td.className).update(td.innerHTML),td);
            });
        }
    }

    this.toJSON = function(){
        return Object.toJSON(table2object(infos.target));
    }

    this.setJSON = function(json){
        //var temp_id = infos.target.id;
        var mytable = object2table(json.evalJSON());

        if (infos.alternate){
            var trs = mytable.select("tr");
            var setOdd = false;
            for (var i = 0; i < trs.length; i++){
                if (setOdd == false){
                    trs[i].addClassName("even");
                    setOdd = true;
                }else{
                    trs[i].addClassName("odd");
                    setOdd = false;
                }
            }
        }
        if (infos.mark_first_row){
            var trs = mytable.select("tr");
            if(trs.length>0)
                trs[0].addClassName("first");
        }
        if (infos.mark_last_row){
            var trs = mytable.select("tr");
            if(trs.length>0)
                trs[trs.length-1].addClassName("last");
        }

        mytable.border = 0;
        var a =$(infos.target).up();
        infos.target.up().replaceChild(mytable, infos.target);

        infos.target = mytable;
        this.reinit();
    }

    /* Callback Functions */
    this.onAddRow = function(tr){}
    this.onAddColumn = function(){}
    this.onRemoveRow = function(tr){}
    this.onRemoveColumn = function(number){}
    this.onEditEvent = function(node){}
    this.onEntryDblClickEvent = function(node){}
    this.onEntryEnterClickEvent = function(node){}
    this.onSelectEntryEvent = function(node){}


}

extendClass("Camao_Table", "Camao_Exception");
/**
 * @category   Camao
 * @package    Camao_Validation
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_VALIDATION_ERROR_TO_SHORT = 0;
var CAMAO_VALIDATION_ERROR_TO_LONG = 1;
var CAMAO_VALIDATION_ERROR_TO_LOWER = 2;
var CAMAO_VALIDATION_ERROR_TO_GREATER = 3;
var CAMAO_VALIDATION_ERROR_INVALID_CHARS = 4;
var CAMAO_VALIDATION_ERROR_NOT_CHECKED = 5;


function Camao_Validation() {
    var infos = {
        valid : {
            '_url'  : /^(([\w]+:)?\/\/)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(\/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?$/,
            '_date' : /^(\d{2}).*(\d{2}).*(\d{4})$/,
            '_text' : /^[a-zA-ZÃ„Ã–ÃœÃ¤Ã¶Ã¼ÃŸ.,\-:/ \n_]+$/,
            '_numbers' : /^[0-9.,]+$/,
            '_time' : /^[0-9:]+$/,
            '_print' : /^[a-zA-Z0-9Ã„Ã–ÃœÃ¤Ã¶Ã¼ÃŸ@%.,()\-:/ \n_]+$/,
            '_email' : /^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\.([a-zA-Z])+([a-zA-Z])+/,
            '_phone' : /^[0-9\\ /+-]+$/
        }
    };

    /* Default Functions */
    this.__construct = function(param){ }

    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }

    this.getValidClasses = function(){
        var result = [];
        for(var i in infos.valid){
            result.push(i);
        }
    result.push("_*");
        return result;
    }
    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
            'void' : '[void] this class doesnt need any options'
        };
    }

    this.validateOptions = function(){
        var status = true;
        return status;
    }

    this.validate = function(string, type, validation_options){
    if (validation_options.type){
        if (validation_options.type == "range"){
        if(typeof string == "string"){
            if (validation_options.min && string.length < parseInt(validation_options.min)) return CAMAO_VALIDATION_ERROR_TO_SHORT;
            if (validation_options.max && string.length > parseInt(validation_options.max)) return CAMAO_VALIDATION_ERROR_TO_LONG;
        }else{
            if (validation_options.min && parseInt(string) < parseInt(validation_options.min)) return CAMAO_VALIDATION_ERROR_TO_LOWER;
            if (validation_options.max && parseInt(string) > parseInt(validation_options.max)) return CAMAO_VALIDATION_ERROR_TO_GREATER;
        }
        }else if (validation_options.type == "greater"){
        if(typeof string == "string"){
            if (validation_options.value && string.length < parseInt(validation_options.value)) return CAMAO_VALIDATION_ERROR_TO_SHORT;
        }else{
            if (validation_options.value && parseInt(string) < parseInt(validation_options.value)) return CAMAO_VALIDATION_ERROR_TO_LOWER;
        }
        }else if (validation_options.type == "lower"){
        if(typeof string == "string"){
            if (validation_options.value && string.length > parseInt(validation_options.value)) return CAMAO_VALIDATION_ERROR_TO_LONG;
        }else{
            if (validation_options.value && parseInt(string) > parseInt(validation_options.value)) return CAMAO_VALIDATION_ERROR_TO_GREATER;
        }
        }
    }

        if (eval("typeof infos.valid." + type) == "undefined"){
            this.throwException(EXCEPTION_ERROR, "Invalid type requested ! " + type);
            return false;
        }else{
        if (eval("infos.valid." + type + ".test(string)")){
        return true;
        }else{
        return CAMAO_VALIDATION_ERROR_INVALID_CHARS;
        }
        }
    }
}

extendClass("Camao_Validation", "Camao_Exception");var ACTIVE_WYSIWYG = [];

var CAMAO_API_REQUEST_URL = "/sitepannel/handler";
var CAMAO_API_DEFAULT_LANGUAGE = "de_DE";
var VIEW_MODE = "edit";


function Camao_Api(){
    var infos = {};

    this.__construct = function(param){
        infos.structure = {
            messages : {},
            options : {},
            data : {},
            inline : {
                elements : [],
                containers: []
            },
            sitepannels : []
        }

        infos.structure.options.document_id = param.document_id || 0;
        infos.structure.options.language = param.language || CAMAO_API_DEFAULT_LANGUAGE;

        infos.response = clone(infos.structure);
        this.addSitepannelRequest("root", { 
            'options' : { 'action' : 'load', 'id' : 'sitepannel' }
        });

        infos.way_to_tree = param.way_to_tree;
        
        this.sendRequest();

        Base.get("Camao_Elements");
        Base.get("Camao_Containers");
    }
    
    this.getDocumentId = function(){
        return infos.response.options.document_id;
    }

    this.getLanguage = function(){
        return infos.response.options.language;
    }


    this.wayToTree = function(){
        return infos.way_to_tree;
    }

    
    this.handleResponse = function(response){
        var json = response.responseText.evalJSON();
        try{
            if (json["sitepannels"]) Base.get("Camao_Sitepannel").handleResponse(json["sitepannels"]);

            // messages
            if (json["messages"]){
                for (var index in json["messages"]) {
                    // errors
                    if (json["messages"][index]['type'] == 'error') {
                        Base.get('Camao_Modul_Warnings').createMessage(json["messages"][index]);
                    }
                }
            }

            if (json["options"]){
                if (json["options"].action == "reload"){
                    this.doReload(json["options"].url);
                }
            }
        }catch(e){
            console.log(e);
        }
    }
    
    this.doReload = function(url){

        var existsUnsavedData = Base.get("Camao_Elements").showElementSaveDialog(function(json){
          
            $$(".element .edit-mode").each(function(e){
                if (e.visible()){
                    Base.get("Camao_Elements").menuLayerClickHandler({ 'target' : e.down(".save") });
                }
            });

            window.setTimeout(function(url){
                window.location.href = (url) ? (url) : window.location.href.replace("#","");
            }.bind(this, json), 1000);
            
        }.bind(this,url), function(){
            window.location.href = (url) ? (url) : window.location.href.replace("#","");
        }.bind(this, url));

        if (existsUnsavedData === false){
            window.location.href = (url) ? (url) : window.location.href.replace("#","");
        }
      
    }
    
    this.startRequestTimeout = function(){
        if (infos.requestTimeout) window.clearTimeout(infos.requestTimeout);
        infos.requestTimeout = this.sendRequest.delay(1);
    }

    this.addContainerRequest = function(options){ 
        infos.response.inline.containers.push(options); 
        this.sendRequest();
    }
    
    this.addElementRequest = function(options){ 
        infos.response.inline.elements.push(options); 
        this.sendRequest();
    }

    this.addModulRequest = function(sitepannelId, options){
        var sitepannel = this.getSitepannelByIdOrCreateIt(sitepannelId);
        sitepannel.content.push(options);
        this.sendRequest();
    }

    this.addSitepannelRequest = function(target, data){
        if (target == "root"){
            infos.response.sitepannels.push(data);
        }else{
            var sitepannel = this.getSitepannelByIdOrCreateIt(target);
            sitepannel.content.push(data);
        }
        this.sendRequest();
    }

    //Liefert eine Referenz zum gesuchten Sitepannel, falls nicht vorhanden erstellt er das sitepannel
    this.getSitepannelByIdOrCreateIt = function(sitepannelId){
        for (var i = 0; i < infos.response.sitepannels.length; i++){
            var sitepannel = infos.response.sitepannels[i];
            if ( (sitepannel.options) && (sitepannel.options.id) ){
                if (sitepannel.options.id == sitepannelId) return sitepannel;
            }
        }

        this.addSitepannelRequest("root", { 
            'options' : { 'id' : sitepannelId },
            'content' : []
        });
        return this.getSitepannelByIdOrCreateIt(sitepannelId);
    }

    this.sendRequest = function(){
        if (infos.sendTimeout) window.clearTimeout(infos.sendTimeout);
        infos.sendTimeout = this.sendRequestDelay.bind(this).delay(0.1);
    }

    this.sendRequestDelay = function(){
        new Ajax.Request(CAMAO_API_REQUEST_URL, {
            'parameters': { 'data' : Object.toJSON(infos.response) },
            'method': 'post',
            'evalJS': true,
            'onCreate' : function(){
                this.showShape();
            }.bind(this),
            'onSuccess': function(transport){
                this.hideShape();
                this.handleResponse(transport);
            }.bind(this)
        });
        infos.response = clone(infos.structure);
    }
    
    this.showShape = function() {
        if (typeof infos.element_shape == "undefined"){
                infos.element_shape = new Element("div");
            infos.element_shape.addClassName("waitSymbol")
                $$("body")[0].appendChild(infos.element_shape);
                var Camao_MouseEventObj = Base.get("Camao_MouseEvent");
                Camao_MouseEventObj.init();
                Camao_MouseEventObj.onMouseMoveEvent = function(e){
                        if (infos.element_shape.visible()){
                                infos.element_shape.setStyle({
                            'top' : Base.get("Camao_MouseEvent").getPosition().y - 15  + "px",
                            'left' : Base.get("Camao_MouseEvent").getPosition().x - 15 + "px"                                   
                                });
                        }
                }.bind(this)

        }
        infos.element_shape.show();
        }
    
    this.hideShape = function(){
        infos.element_shape.hide();     
    }
}/**
 * @category   Camao
 * @package    Camao_Tree
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

var CAMAO_TREE_SELECTED_NODE_CLASS = "selected";

function Camao_Tree(){
    var infos = {};

    /* Default Functions */
    this.__construct = function(options){
    	this.init(options);
    }

    /**
     * Initiate the Object
     *
     * @param  object
     *         domelement $target defined the form target
     * @return void
     */
    this.init = function(options){
        Object.extend(infos, options);
        if (this.validateOptions()){
        	this.getTree(0);
        }else{
            this.throwException(EXCEPTION_ERROR, "Function stops here. The options are not valid");
        }
    }
    /**
     * Shows up the possible options for this class
     *
     * @param  void
     * @return bool 
     */
    this.getPossibleOptions = function(){
        return {
        };
    }

    /**
     * Validate the Init params
     *
     * @param  void
     * @return bool 
     */
    this.validateOptions = function(){
        var status = true;
        if (!infos.target){ this.throwException(EXCEPTION_ERROR, "options.target is undefined!"); status = false;}
        if (!infos.sitepannelId){ this.throwException(EXCEPTION_ERROR, "options.sitepannelId is undefined!"); status = false;}
        if (!infos.modulId){ this.throwException(EXCEPTION_ERROR, "options.modulId is undefined!"); status = false;}
        return status;
    }

    this.getTree = function(rootId){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
    		'options' : { 'action' : 'get_tree', 'tree_root_id' : rootId, 'id' : infos.modulId  }
    	});
    }
    
	
	this.createNode = function(parent_node_id, after_node_id){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
    		'options' : { 
    			'action' : 'create_node', 
				'id' : infos.modulId,
	            'parent_node_id' : parent_node_id,
	            'after_node_id' : after_node_id
	        }
    	});
	} 

	this.duplicateNode = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
    		'options' : { 
    			'action' : 'duplicate_node', 
				'node_id' : infos.active_node.node_id, 
				'id' : infos.modulId
			}
    	});
	} 

	this.deleteNode = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
    		'options' : { 
    			'action' : 'delete_node', 
				'node_id' : infos.active_node.node_id, 
				'id' : infos.modulId
			}
    	});
	} 
	
	
	this.moveNode = function(parent_node_id, after_node_id){
		Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
			'options' : {
				'action' : 'move_node',
				'id' : "Sitemap",
				'node_id' : infos.active_node.node_id, 
				'parent_node_id' : parent_node_id,
				'after_node_id' : after_node_id
			}
		});     
	
	}
	
    
	this.createTree = function(sitepannelId, modul){
		var way = Base.get("Camao_Api").wayToTree();
		//infos.active_node = false;
		var ul = new Element("ul").addClassName("documentTree");
		modul.data.content.each(function(node){
		
			var li = new Element("li", { 
				'id' : 'node_' + node.node_id }).addClassName((node.childCount > 0) ? 'button folder' :  'button page');

			var nodeName = new Element("span").addClassName("nodeLabel").update(node.node_name || "Unknown");

			if (way.indexOf(node.node_id) != -1){
				infos.active_node = node;
				if (node.childCount > 0) this.getTree(node.node_id);
			}
			
			if (node.childCount > 0){
				var handler = new Element("span").addClassName("icon_hasChild");//.update("+");
				li.appendChild(handler);
				handler.observe("click", this.handleClickEvents.bind(this, node.node_id));
                handler.setStyle({
                    'cursor': 'pointer'
                });
			}

			li.appendChild(nodeName);

			nodeName.observe("click", this.openDocumentEvent.bind(this, node.node_url));
            nodeName.setStyle({
                'cursor': 'pointer'
            });
			ul.appendChild(li);
		}.bind(this));

		//Wenn unsere Sitemap im verschieben modus ist, mÃ¼ssen wir die neuen targets erstellen
		if (MODUL_SITEMAP_MODE == "move"){
			Base.get("Camao_Modul_Sitemap").setUlAsTargetForMove(ul, true);
		}else if (MODUL_SITEMAP_MODE == "new"){
			Base.get("Camao_Modul_Sitemap").setUlAsTargetForNew(ul, true);
		}
		if ($('node_' + modul.data.options.parent_node_id)){
			$('node_' + modul.data.options.parent_node_id).appendChild(ul);
			$('node_' + modul.data.options.parent_node_id).select(".icon_hasChild").first().addClassName("opened");
		}else if (modul.data.options.parent_node_id == 0){
			infos.target.appendChild(ul);
		}
	}
	
	this.selectActiveNode = function(){
		var way = Base.get("Camao_Api").wayToTree();
		var last = way.last();
		if ($("node_" + last)) $("node_" + last).addClassName(CAMAO_TREE_SELECTED_NODE_CLASS);
	}

	
	this.handleClickEvents = function(node_id){
		var node = $("node_" + node_id);
		
		if (node.select("li").length > 0){
			node.select("ul")[0].toggle();
			if(node.select("ul")[0].visible())
			    node.select(".icon_hasChild").first().addClassName("opened");
			else
			    node.select(".icon_hasChild").first().removeClassName("opened");
		}else{
			this.getTree(node_id);
		}
	}
	

	
	this.openDocumentEvent = function(node_url){
		window.location = node_url;
	}


          
    /* Callback Functions */

}

extendClass("Camao_Hover", "Camao_Exception");
var CAMAO_SITEPANNEL_DEFAULT_WIDTH = 300;
function Camao_Sitepannel() {
    var infos = { 'move' : false };

    this.__contruct = function(){
    }

    this.handleResponse = function(response){
        for (var i in response){
            var sitepannel = response[i];
            if(typeof sitepannel == "object"){
	            //Sitepannel load response
	            if (sitepannel.options && sitepannel.options.action == "load"){
	                //we got a template
	                if(sitepannel.template){
	                    var sitepannelContainer = this.createSitepannel(sitepannel);
                        this.createSitepannelEvents(sitepannelContainer);
	                }

	            }

                if ( (sitepannel.content) && (sitepannel.content.length > 0) ){
                    Base.get("Camao_Modul").handleResponse(sitepannel.options.id, sitepannel.content);
                }
            }
        }
    }

    this.createSitepannel = function(sitepannel){
        var sitepannelContainer = new Element("div");
        sitepannelContainer.id = sitepannel.options.id;
        sitepannelContainer.update(sitepannel.template);

        $$("body")[0].appendChild(sitepannelContainer);

        sitepannelContainer.setStyle({ 'width' : (sitepannel.options.width || CAMAO_SITEPANNEL_DEFAULT_WIDTH) + "px" });
        $(document.body).style.paddingLeft = sitepannelContainer.select(".sitepanel")[0].getDimensions().width+"px";
        return sitepannelContainer;
    }

    this.createSitepannelEvents = function(sitepannelContainer){
        Event.observe(window,"resize", this.onResize.bind(this, sitepannelContainer));

        $("moveSitepanel").observe("mousedown", this.startSitepannelMove.bind(this));
    }

    this.startSitepannelMove = function(){
      infos.move = true;
      infos.moveSitepannelBind = this.moveSitepannel.bind(this);
      infos.stopSitepannelMoveBind = this.stopSitepannelMove.bind(this);

      document.observe("mousemove", infos.moveSitepannelBind);
      document.observe("mouseup", infos.stopSitepannelMoveBind);

    }

    this.stopSitepannelMove = function(){
      infos.move = false;
      document.stopObserving("mousemove", infos.moveSitepannelBind);
      document.stopObserving("mouseup", infos.stopSitepannelMoveBind);
    }

    this.moveSitepannel = function(e){
      if (infos.move){
        $$(".sitepanel")[0].style.left = e.clientX + "px";
        console.log(e);
      }
    }

    this.onResize = function(sitepannelContainer) {
        $(sitepannelContainer).select(".module")[0].setStyle({ height: (document.viewport.getDimensions().height-$(sitepannelContainer).select(".module")[0].cumulativeOffset()[1])+"px" });
        if ($(sitepannelContainer).select(".module")[0].scrollHeight == document.viewport.getDimensions().height-$(sitepannelContainer).select(".module")[0].cumulativeOffset()[1]){
            if(!Prototype.Browser.WebKit)
                $(sitepannelContainer).select(".module")[0].addClassName("moduleNoScroll");
            /*
            $(sitepannelContainer).select(".module")[0].setStyle({
              "overflowY" : "hidden"
            });
            */
        }else{
            if(!Prototype.Browser.WebKit)
                $(sitepannelContainer).select(".module")[0].removeClassName("moduleNoScroll");
            /*
            $(sitepannelContainer).select(".module")[0].setStyle({
              "overflowY" : "scroll"
            });
            */
        }
    }

}

extendClass("Camao_Sitepannel", "Camao_Exception");function Camao_Elements() {
    var infos = {};

    this.__construct = function(){
        this.createEvents();
    }


    this.createEvents = function(){
        $$(".element-menu-layer").observe("click", this.menuLayerClickHandler.bind(this));
        $$(".element").draggable({
                ghosting: true,
                revert: true,
                handle : 'drag',
                onStart : function(){
                if (VIEW_MODE == "live") Base.get("Camao_Modul_Mode").setViewAsEdit();
                        $$("div.dropzone_element").show();
                },
                onEnd : function(){
                        $$("div.dropzone_element").hide();
                }
        });
    }

    this.edit = function(modul){
        var node = $("element_" + modul.options.node_id);
        node.down(".content").update(modul.data.template);
        node.down(".normal-mode").hide();
        node.down(".edit-mode").show();
        for (var i in modul.data.content){
            var foundNodes = node.select('[name="' + i + '"]');
            if (foundNodes.length > 0){
                if (foundNodes[0].type == "checkbox"){
                    if (modul.data.content[i] == "on"){
                        foundNodes[0].checked = true;
                    }else{
                        foundNodes[0].checked = false;
                    }
                }else{
                    foundNodes[0].value = modul.data.content[i];
                }
            }
        }

        node.select(".camaoUploadField").each(function(e){
            var Camao_UploadObj = Base.get("Camao_Upload", true);


            var options = {
                'container' : node,
                'session_id' : Base.get("Camao_Cookie").getCookie("mSID"),
                'button_placeholder_id' : e.id,
                'post_params' : {
                    'attribute_name' : e.up().className,
                    'node_id' : modul.options.node_id
                },
                'upload_infos' : {
                    'button_width' : 40,
                    'button_height' : 20,
                    'allowed_file_types' : "*",
                    'button_image_url' : '/media/images/control/upload.jpg',
                    'upload_url' : "/site/upload"
                }
            };
            Camao_UploadObj.init(options);


            Camao_UploadObj.onUploadSuccess = function(fileObj,response){
                response = response.evalJSON();
                if (this.infos.button_placeholder_id == "camaoUploadField_element_head_headline"){
                    $(this.infos.container).select(".camaoUploadField_element_headline_image")[0].src = "/thumb_BinaryStorage_" + response[0] + "_not_100x.jpg";
                    $(this.infos.container).select('input[name="asset_id_headline"]')[0].value = response[0];

                }else if (this.infos.button_placeholder_id == "camaoUploadField_element_head_keyvisual"){
                    $(this.infos.container).select(".camaoUploadField_element_keyvisual_image")[0].src = "/thumb_BinaryStorage_" + response[0] + "_not_100.jpg";
                    $(this.infos.container).select('input[name="asset_id_keyvisual"]')[0].value = response[0];
                }else if (this.infos.button_placeholder_id == "camaoUploadField_element_image_slider"){
                    window.newImageUploaded(response[0]);
                }else if (this.infos.button_placeholder_id == "camaoUploadField_element_custom_width_image"){
                    $(this.infos.container).select(".camaoUploadField_element_image")[0].src = "/thumb_BinaryStorage_" + response[0] + "_fcp_200x.jpg";
                    $(this.infos.container).select('input[name="asset_id"]')[0].value = response[0];
                }else{
                    var mywidth = $(this.infos.container).up(".layoutColumnInner").getDimensions().width;
                    $(this.infos.container).select(".camaoUploadField_element_image")[0].src = "/thumb_BinaryStorage_" + response[0] + "_fcp_" + mywidth + "x.jpg";
                    $(this.infos.container).select('input[name="asset_id"]')[0].value = response[0];
                }
            }

        });
    }

    this.rerender = function(modul){
        var temp_node = new Element("div").update(modul.data.content);
        $$("body")[0].appendChild(temp_node);
        var node = $("element_" + modul.options.node_id);

        node.up().up().replaceChild(temp_node.down(), node.up());
        node = $("element_" + modul.options.node_id);

        node.select(".element-menu-layer").observe("click", this.menuLayerClickHandler.bind(this));
        
        [node].draggable({
            ghosting: true,
            revert: true,
            handle : 'drag',
            onStart : function(){
                if (VIEW_MODE == "live") Base.get("Camao_Modul_Mode").setViewAsEdit();
                $$("div.dropzone_element").show();
            },
            onEnd : function(){
                $$("div.dropzone_element").hide();
            }
        });
       
        if (VIEW_MODE != "live"){
            node.select(".element-menu-layer").show();
            node.down(".normal-mode").show();
            node.down(".edit-mode").hide();
        }
    }

    this.menuLayerClickHandler = function(myEvent){
        var target = $(myEvent.target);
        var node_id = (typeof target.up != "undefined") ? target.up(".element").id.replace("element_", "") : false;

        switch(target.className) {
            case 'edit':
                Base.get("Camao_Api").addModulRequest("sitepannel_1", {
                    'options' : {
                        'action' : 'edit',
                        'id' : "Element",
                        'node_id' : node_id
                    }
                });

                break;
            case 'delete':

                $("popup-warning").select(".content")[0].update(dic_sitepanel_modul_element_delete_confirm);
                $("popup-warning").select(".button-ok")[0].stopObserving("click");
                $("popup-warning").select(".button-ok")[0].observe("click", function(node_id){
                    Base.get("Camao_Api").addModulRequest("sitepannel_1", {
                        'options' : {
                            'action' : 'delete',
                            'id' : "Element",
                            'node_id' : node_id
                        }
                    });

                    $("element_" + node_id).up().remove();
                    Base.get("Camao_Document").createDropzones();

                    if (Prototype.Browser.IE){
                            window.setTimeout("window.location.href = window.location.href",100);
                    }
                }.bind(this,node_id));

                Base.get("Camao_Lightbox", true, {
                    "content" : $("popup-warning"),
                    "use_shape" : true,
                    "lightbox_id" : "control_lightbox",
                    "shape_id" : "control_shape",
                    "opacity" : .3
                });

                break;
            case 'save':
                if (ACTIVE_WYSIWYG.length > 0){
                    ACTIVE_WYSIWYG.each(function(e){
                        if ($("element_" + node_id).select('textarea[name="node_content"]')[0] == e.content){
                            e.content.value = e.editor.getData();
                            e.editor.destroy();
                        }
                    });
                }
                
                if (typeof save_table != "undefined" && save_table == true){
                    var table_json = Camao_TableObj.toJSON();
                    $("element_" + node_id).select('input[name="table_json"]')[0].value = table_json;
                    Camao_TableObj.destroy();
                }

                if(target.up(".element").select("form")[0]) {
                    var formdata = form2object(target.up(".element").select("form")[0]);
                    Base.get("Camao_Api").addModulRequest("sitepannel_1", {
                        'options' : {
                            'action' : 'save',
                            'id' : "Element",
                            'node_id' : node_id
                        },
                        'content' : formdata
                    });
                }

                if (typeof save_table != "undefined" && save_table == true){
                    window.setTimeout("window.location.href = window.location.href", 1000);
                }
                
                break;
            case 'cancel':
                Base.get("Camao_Api").doReload(window.location);
                break;
        }
    }
    
    
    this.showElementSaveDialog = function(callbackYes, callbackNo){
        var saveButtons = [];

        $$(".element .edit-mode").each(function(e){
            if (e.visible()){
                saveButtons.push(e.down(".save"));
            }
        });

        if (saveButtons.length > 0){
            $("popup-warning").select(".content")[0].update(dic_sitepanel_global_modul_edit_mode_warning);
            $("popup-warning").select(".button-ok")[0].stopObserving("click");
            $("popup-warning").select(".camao_lightbox_close")[1].stopObserving("click");

            $("popup-warning").select(".button-ok")[0].observe("click", callbackYes);
            $("popup-warning").select(".camao_lightbox_close")[1].observe("click", callbackNo);



            Base.get("Camao_Lightbox", true, {
                "content" : $("popup-warning"),
                "use_shape" : true,
                "lightbox_id" : "control_lightbox",
                "shape_id" : "control_shape",
                "opacity" : .3
            });
            return true;
        }else{
            return false;
        }
      
    }

}

extendClass("Camao_Elements", "Camao_Exception");function Camao_Containers() {
    var infos = {};

    this.__construct = function(){
    	this.createEvents();
    }

    
    this.createEvents = function(){
        $$(".container-menu-layer").observe("click", this.menuLayerClickHandler.bind(this));
        $$(".container").draggable({ 
            revert: true,
            handle : 'drag',
            onStart : function(event){
                if (VIEW_MODE == "live") Base.get("Camao_Modul_Mode").setViewAsEdit();
                                   
                $$("div.dropzone_container").show();
			},
			onEnd : function(){
    			$$("div.dropzone_container").hide();
			}
		});
    	
    	
    }
    
    this.menuLayerClickHandler = function(myEvent){ 
    	var target = $(myEvent.target);
    	var node_id = target.up(".container").id.replace("container_", "");

    	switch(target.className) {
	    	case 'delete':

	    		$("popup-warning").select(".content")[0].update(dic_sitepanel_modul_container_delete_confirm);
	    		$("popup-warning").select(".button-ok")[0].stopObserving("click");
	    		$("popup-warning").select(".button-ok")[0].observe("click", function(node_id){
		        	Base.get("Camao_Api").addModulRequest("sitepannel_1", {
		                'options' : {
		    	            'action' : 'delete',
		    	            'id' : "Container",
		    	            'node_id' : node_id
		        		}
		            });
		        	$("container_" + node_id).previous(".dropzone").remove();
	        		$("container_" + node_id).remove();
	        		
	        		if (Prototype.Browser.IE){
	        			window.setTimeout("window.location.href = window.location.href",100);
	        		}
	    		}.bind(this,node_id));
	    		
	            Base.get("Camao_Lightbox", true, {
	                "content" : $("popup-warning"),
	                "use_shape" : true,
	                "lightbox_id" : "control_lightbox",
	                "shape_id" : "control_shape",
	                "opacity" : .3
	            });	    		
	        	break;
    	}
    }

}

extendClass("Camao_Elements", "Camao_Exception");var CAMAO_MODUL_TOGGLE_HANDLER = "toggle";
var CAMAO_MODUL_TOGGLE_CLOSED_CLASS = "closed";


function Camao_Modul(){
    var infos = {};

    this.__contruct = function(){}

    this.handleResponse = function(sitepannelId, response){
        response.each(function(modul){
            Base.get("Camao_Modul_" + modul.options.id).handleResponse(sitepannelId, modul);
        }.bind(this));
    
    }

    this.createModul = function(sitepannelID, modul){
        //Wir erstellen das Template
        var sitepannelModul = new Element("div", { 'id' : "Modul_" + modul.options.id } ).update(modul.data.template);
        sitepannelModul.addClassName("modul");

		//IE6 problem, er feuert hiereine unbekannte fehlermeldung,aber es geht...
		$(sitepannelID).select(".module")[0].appendChild(sitepannelModul);
        //Wir fuellen das Template mit werten
        if (modul.data.content){
            for (var i in modul.data.content){
                var element = sitepannelModul.select("." + i);
                if (element.length == 1){
                	var target = element[0];
                	if (target.tagName.toLowerCase() == "input"){
                		target.value = modul.data.content[i];
                	}else if (target.tagName.toLowerCase() == "select"){
                		if ((typeof modul.data.content[i] != "undefined") && (target.select('option[value="' + modul.data.content[i] + '"]')[0]) ){
                			target.select('option[value="' + modul.data.content[i] + '"]')[0].selected = true;
                		}
                	}else{
                		target.update(modul.data.content[i]);
                	}
                }else{
                	if (typeof modul.data.content[i] != "object"){
                		this.throwException(EXCEPTION_ERROR, "Unique element with class '" + i + "' cant be found!");
                	}
                }
            }
        }

        if (modul.options.open == false){
        	var content = sitepannelModul.select(".content")[0];
        	var handler = sitepannelModul.select("." + CAMAO_MODUL_TOGGLE_HANDLER)[0];
        	if (content){
        		content.hide();
        		handler.addClassName(CAMAO_MODUL_TOGGLE_CLOSED_CLASS);
        	}else{
                this.throwException(EXCEPTION_ERROR, "Cant find content part for modul " + modul.options.id + "!");
        	}
        }

        this.createDefaultEvents(sitepannelID, sitepannelModul);
    }

    //Events for all Module
    this.createDefaultEvents = function(sitepannelId, sitepannelModul){
        //Create the toggle Event
    	var handler = sitepannelModul.select("." + CAMAO_MODUL_TOGGLE_HANDLER)[0];
    	if (handler){
    		handler.observe("click", this.toggleModulEvent.bind(this, sitepannelId,  sitepannelModul));
    	}else{
            this.throwException(EXCEPTION_ERROR, "Toggle handler cant be found for modul !" + sitepannelModul.id);
    	}
    }


    this.toggleModulEvent = function(sitepannelId, sitepannelModul){
        var content = sitepannelModul.select(".content")[0];
        content.toggle();
        var handler = sitepannelModul.select("." + CAMAO_MODUL_TOGGLE_HANDLER)[0];
        if(content.visible())
            handler.removeClassName(CAMAO_MODUL_TOGGLE_CLOSED_CLASS);
        else
            handler.addClassName(CAMAO_MODUL_TOGGLE_CLOSED_CLASS);
        
        Base.get("Camao_Api").addModulRequest(sitepannelId, {
            'options' : {
                'action' : 'update',
                'open' : content.visible(),
                'id' : sitepannelModul.id.replace("Modul_","")
            }
        });

        Base.get("Camao_Api").sendRequest();
        Base.get("Camao_Sitepannel").onResize($(sitepannelId));


    }

}

extendClass("Camao_Modul", "Camao_Exception");var DRAG_STATUS = false;
var globalDragScrollTopStart = 0;
var globalModuleContainerOverflow = false;

function getScrollTop() {
  var ScrollTop = document.body.scrollTop;
  if (ScrollTop == 0) {
    if (window.pageYOffset)
      ScrollTop = window.pageYOffset;
    else
      ScrollTop = (document.body.parentElement) ? document.body.parentElement.scrollTop : 0;
  }
  return ScrollTop;
}

function Camao_Modul_Container() {
  var infos = {};

  this.__construct = function(){
    Base.get("Camao_Cron",true,{
      'target' : "Modul_Container",
      'rule' : { 'onElementExists' : true },
      'callback' : this.createEvents.bind(this)
    });
  }

  this.handleResponse = function(sitepannelId, modul){
    infos.sitepannelId = sitepannelId;
    
    if (modul.data.options.action == "render"){
      this.createModul(sitepannelId, modul);
    }
    
    if (modul.data.options.action == "dropzones"){
      if (modul.data.content == "all"){
        if (DRAG_STATUS == true){
          $$("div.dropzone_container").show();
        }
      }
    }
    
    if (modul.data.content && modul.data.content.containers){
      var target = $("Modul_Container").select(".ModulContainer_containers")[0];
      modul.data.content.containers.each(function(container){
        var listitem = new Element("li");
        var element = new Element("img");
        element.src = "/media/images/control/icon/container/container_" + container + ".png";
        element.id = "drag_" + container;
        element.addClassName("drag_container");
        listitem.appendChild(element);
        
        try {
          var containerName = eval("dic_sitepanel_modul_container_" + container);
        }catch(e){
          var containerName = container.gsub('_', '/');
        }

        var text = new Element("span");
        text.update(containerName);
        listitem.appendChild(text);
        target.appendChild(listitem);

        new Draggable(element.id, {
          ghosting: true,
          revert : true,
          onStart : function(myElement){
            if (VIEW_MODE == "live") Base.get("Camao_Modul_Mode").setViewAsEdit();
            try { myElement.element.addClassName("dragging") } catch(err){};
            DRAG_STATUS = true;
            Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
              'options' : {
                'action' : 'get_dropzone',
                'id' : "Container"
              }
            });

            globalDragScrollTopStart = getScrollTop();
            globalModuleContainerOverflow = $(myElement.element).up(".module").hasClassName("moduleNoScroll");
            $(myElement.element).up(".module").addClassName("moduleNoScroll");
            if(Prototype.Browser.WebKit)
            $(myElement.element).up(".module").addClassName("moduleNoScrollWebKit");
            
            $$(".element-menu-layer").hide();
            $$(".container-menu-layer").hide();
          },
          onAfterDrag : function(element,e) {
            if(globalDragScrollTopStart==getScrollTop()) $(element.element).style.top=(e.clientY-Math.round($(element.element).getHeight()/2))+"px";
                      
            if(globalDragScrollTopStart!=getScrollTop()) {
              $(element.element).style.top = (parseInt($(element.element).getStyle('top'))+(globalDragScrollTopStart-getScrollTop()))+'px';
            }
          },
          
          onEnd : function(myElement){
            DRAG_STATUS = false;
            //if (Prototype.Browser.IE){
            $$("div.dropzone_container").hide();
            try { myElement.element.removeClassName("dragging") } catch(err){};
            if(!globalModuleContainerOverflow) {
              $(myElement.element).up(".module").removeClassName("moduleNoScroll");
              if(Prototype.Browser.WebKit) $(myElement.element).up(".module").removeClassName("moduleNoScrollWebKit");
            }
            
            $$(".element-menu-layer").show();
            $$(".container-menu-layer").show();

          }
        });
      });
    }
  }

  this.createEvents = function(){
    Base.get("Camao_Document").createDropzones();
  }
}

extendClass("Camao_Modul_Container", "Camao_Modul");var globalDragScrollTopStart = 0;
var globalModuleContainerOverflow = false;
var globalIsDragActive = false;
function getScrollTop() {
    var ScrollTop = document.body.scrollTop;
    if (ScrollTop == 0) {
        if (window.pageYOffset)
            ScrollTop = window.pageYOffset;
        else
            ScrollTop = (document.body.parentElement) ? document.body.parentElement.scrollTop : 0;
    }
    return ScrollTop;
}
function Camao_Modul_Element() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Element",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
    }

    this.handleResponse = function(sitepannelId, modul){
        infos.sitepannelId = sitepannelId;
        if (modul.data.options.action == "render"){
            this.createModul(sitepannelId, modul);
        }

        if (modul.data.options.action == "dropzones"){
            if (modul.data.content == "all"){
                $$("div.dropzone_element").show();
            }
        }

        if (modul.data.options.action == "edit"){
            Base.get("Camao_Elements").edit(modul);
        }

        if (modul.data.options.action == "rerender"){
            Base.get("Camao_Elements").rerender(modul);
        }

        if (modul.data.content && modul.data.content.elements){
            var target = $("Modul_Element").select(".ModulElement_elements")[0];
            var counter = 1;
            for (var i in modul.data.content.elements){
                var container = modul.data.content.elements[i];
                var containerName = i;

                var newContainerLi = new Element("li");
                newContainerLi.update(containerName);
                newContainerLi.addClassName("headlineContainer");
                newContainerLi.addClassName("toggle");
                newContainerLi.observe("click", this.toggleElements.bind(this));
                target.appendChild(newContainerLi);


                for (var x in container){
                    var element = x;

                    var newLi = new Element("li");
                    var icon = new Element("span").addClassName('icon');
                    icon.setStyle({backgroundImage:'url(/media/images/control/icon/elemente/element_' + element + '.png)'})
                    newLi.appendChild(icon);

                    icon.observe("mouseover", this.showElementPreview.bind(this, element));
                    icon.observe("mouseout", this.hideElementPreview.bind(this, element));

                    try {
                      var elementName = eval("dic_sitepanel_modul_element_" + element);
                    }catch(e){
                      var elementName = "dic_sitepanel_modul_element_" + element;
                    }

                    var text = new Element("span").update('<span class="counterElements">'+counter+".</span> " + elementName);
                    newLi.appendChild(text);

                    newLi.id = "drag_" + element;
                    newLi.addClassName("drag_element");

                    target.appendChild(newLi);

                    new Draggable(newLi.id, {
                        ghosting: true,
                        revert: true,
                        onStart : function(element){
                            globalIsDragActive = true;
                            if (VIEW_MODE == "live") Base.get("Camao_Modul_Mode").setViewAsEdit();
                            $$(".element-menu-layer").hide();
                            $$(".container-menu-layer").hide();
                            
                            $("element-preview").hide();
                            try { element.element.addClassName("dragging") } catch(err){};
                            Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
                                'options' : {
                                    'action' : 'get_dropzone',
                                    'id' : "Element"
                                }
                            });
                            globalDragScrollTopStart = getScrollTop();
                            globalModuleContainerOverflow = $(element.element).up(".module").hasClassName("moduleNoScroll");
                            $(element.element).up(".module").addClassName("moduleNoScroll");
                            if(Prototype.Browser.WebKit)
                                $(element.element).up(".module").addClassName("moduleNoScrollWebKit");
                        },
                        onAfterDrag : function(element,e) {
                            if(globalDragScrollTopStart==getScrollTop())
                                $(element.element).style.top=(e.clientY-Math.round($(element.element).getHeight()/2))+"px";
                            if(globalDragScrollTopStart!=getScrollTop()) {
                                $(element.element).style.top = (parseInt($(element.element).getStyle('top'))+(globalDragScrollTopStart-getScrollTop()))+'px';
                            }
                        },
                        onEnd : function(element){
                            globalIsDragActive = false;
                            globalIsAutoscrollTimerActive = false;
                            $$("div.dropzone_element").hide();
                            $$(".element-menu-layer").show();
                            $$(".container-menu-layer").show();
                            try { element.element.removeClassName("dragging") } catch(err){};
                            if(!globalModuleContainerOverflow) {
                                $(element.element).up(".module").removeClassName("moduleNoScroll");
                                if(Prototype.Browser.WebKit)
                                    $(element.element).up(".module").removeClassName("moduleNoScrollWebKit");
                            }
                            //hack to kill the ghost
                            //element.element.setAttribute("style", "");
                            //$("temp_styler").update("");
                        }
                    });
                    counter++;
                }

                var groupStatus = modul.options.group[containerName.toLowerCase().replace(/ /g, "_")];
                if (groupStatus == false){
                  this.toggleElements({ 'target' : newContainerLi, 'save' : false });
                }

           }
        }
    }

    this.showElementPreview = function(element){
        if (!$("element-preview").visible() && globalIsDragActive == false){
            var pos = $("drag_" + element).cumulativeOffset();
            var scrollPos = $("sitepannel_1").down(".module").scrollTop;
            var sitepannelSize = $("sitepannel_1").down(".sitepanel").getDimensions();
            try {
                var elementDescription = eval("dic_sitepanel_modul_element_" + element + "_preview_description");
            }catch(e){
                var elementDescription = "sitepanel_modul_element_" + element + "_preview_description";
            }
            if(elementDescription=="sitepanel_modul_element_" + element + "_preview_description")
                elementDescription="";
            try {
                var elementRecommendation = eval("dic_sitepanel_modul_element_" + element + "_preview_recommendation");
            }catch(e){
                var elementRecommendation = "sitepanel_modul_element_" + element + "_preview_recommendation";
            }
            if(elementRecommendation=="sitepanel_modul_element_" + element + "_preview_recommendation")
                elementRecommendation="";

            if(elementDescription!="") {
                $("element-preview").down(".element-preview-label-description").update(elementDescription);
                $("element-preview").down(".element-preview-label-description").show();
            } else {
                $("element-preview").down(".element-preview-label-description").hide();
            }
            if(elementRecommendation!="") {
                $("element-preview").down(".element-preview-label-recommendation").update(elementRecommendation);
                $("element-preview").down(".element-preview-label-recommendation").show();
            } else {
                $("element-preview").down(".element-preview-label-recommendation").hide();
            }

            $("element-preview").down(".preview").setStyle({
                "backgroundImage" : "url(/media/images/control/preview/elemente/element_" + element + ".png)"
            });
            $("element-preview").down(".arrowTop").hide();
            $("element-preview").down(".arrowBottom").hide();

            if (sitepannelSize.height - (pos[1] - scrollPos + 161) > 80){
                $("element-preview").down(".preview").setStyle({
                  'top' : pos[1] - scrollPos + 30 + "px",
                  'left' : pos[0] + "px"

                });

                $("element-preview").down(".arrowTop").setStyle({
                  'top' : pos[1] - scrollPos + 20 + "px",
                  'left' : pos[0] + 10 + "px"

                });


                $("element-preview").down(".arrowTop").show();
            }else{

                $("element-preview").down(".preview").setStyle({
                  'top' : pos[1] - scrollPos - 166 + "px",
                  'left' : pos[0] + "px"
                });

                $("element-preview").down(".arrowBottom").setStyle({
                  'top' : pos[1] - scrollPos - 6 + "px",
                  'left' : pos[0] + 10 + "px"

                });

                $("element-preview").down(".arrowBottom").show();
            }

            $("element-preview").show();
        }
    }

    this.hideElementPreview = function(element){
        $("element-preview").hide();
    }


    this.toggleElements = function(e){
        e.target.toggleClassName("closed","");
        var target = e.target.next("li");
        while(target.hasClassName("drag_element")){
          target.toggle();
          target = target.next("li");
          if (typeof target == "undefined") break;
        }

        var open = true;
        if (e.target.hasClassName("closed")){
          open = false;
        }

        if (typeof e.save == "undefined"){
          Base.get("Camao_Api").addModulRequest(1, {
              'options' : {
                  'action' : 'update',
                  'group' : e.target.innerHTML.toLowerCase().replace(/ /g, "_"),
                  'open' : open,
                  'id' : "Element"
              }
          });
          Base.get("Camao_Api").sendRequest();
        }

    }


    this.createEvents = function(){
    }
}

extendClass("Camao_Modul_Element", "Camao_Modul");function Camao_Modul_Language() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Language",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
    	
    }

    this.handleResponse = function(sitepannelId, modul){
    	infos.sitepannelId = sitepannelId;
    	
    	if (modul.data.options.action == "render"){
    		this.createModul(sitepannelId, modul);
    	}

    	if (modul.data.content && modul.data.content.languages){
        	var target = $("Modul_Language").select(".ModulLanguages_languages")[0];
        	var template = $("Modul_Language").select(".ModulLanguages_templates .ModulLanguages_template_language")[0];
        	
        	languages = modul.data.content.languages;
        	languages.each(function(e){
        		var language = $(template.cloneNode(true));
        		language.removeClassName("ModulLanguages_template_language");
        		
        		language.select(".language_key")[0].update(e.key);
        		language.select(".language_name")[0].update(e.name);
        		
        		language.select("a")[0].href = e.url;
        		
        		
        		if (e.key == Base.get("Camao_Api").getLanguage()){
        			language.addClassName("active");
        			$("Modul_Language").select(".headline")[0].update($("Modul_Language").select(".headline")[0].innerHTML + "<span class=\"subHeader currentLanguage\"> / " + e.key + "</span>");
        		}

        		target.appendChild(language);
        	}.bind(this));
            
            //if we have only 1 language, hide the box
            if (languages.length == 1){
                $("Modul_Language").hide();
            }
        }
    }

    this.createEvents = function(){
    	$("Modul_Language").select(".ModulLanguages_empty_language").observe("click", this.doLanguageEmptyEvent.bind(this))
    	$("Modul_Language").select(".ModulLanguages_copy_language_to_all").observe("click", this.doCopyLanguageToAllEvent.bind(this))
		$("Modul_Language").select(".ModulLanguages_empty_content").observe("click", this.doEmptContentEvent.bind(this))
		$("Modul_Language").select(".ModulLanguages_go_live").observe("click", this.doGoLiveEvent.bind(this))
		$("Modul_Language").select(".ModulLanguages_reset").observe("click", this.doResetEvent.bind(this))

		$("Modul_Language").select(".ModulLanguages_set_default").observe("click", this.doSetStatus.bind(this, "default"));
		$("Modul_Language").select(".ModulLanguages_set_ok").observe("click", this.doSetStatus.bind(this, "ok"));
		
    }
    
    this.doLanguageEmptyEvent = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'clear_language',
	            'id' : "Language"
    		}
        });     	
    }

    this.doCopyLanguageToAllEvent = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'copy_language_to_all',
	            'id' : "Language"
    		}
        });     	
    }

    this.doEmptContentEvent = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'empty_content',
	            'id' : "Language"
    		}
        });     	
    }    

    this.doGoLiveEvent = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'go_live',
	            'id' : "Language"
    		}
        });     	
    }    

    this.doResetEvent = function(){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'reset',
	            'id' : "Language"
    		}
        });     	
    }    
    
    this.doSetStatus = function(status){
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'change_status',
	            'id' : "Language",
	            'status' : status
    		}
        });     	
    }    
}

extendClass("Camao_Modul_Language", "Camao_Modul");function Camao_Modul_Metadaten() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Metadaten",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
    	
    }

    this.handleResponse = function(sitepannelId, modul){
        this.createModul(sitepannelId, modul);
        infos.sitepannelId = sitepannelId;

    	if (modul.data.options.action == "exist_url"){
    		$("popup-warning").select(".content")[0].update(dic_sitepanel_global_modul_metadaten_url_warning);
    		$("popup-warning").select(".button-ok")[0].stopObserving("click");
    		
            Base.get("Camao_Lightbox", true, {
                "content" : $("popup-warning"),
                "use_shape" : true,
                "lightbox_id" : "control_lightbox",
                "shape_id" : "control_shape",
                "opacity" : .3
            });	  
    	}else if (modul.data.options.action == "url_corrupt"){
    		$("popup-warning").select(".content")[0].update(dic_sitepanel_global_modul_metadaten_url_corrupt);
    		$("popup-warning").select(".button-ok")[0].stopObserving("click");
    		
            Base.get("Camao_Lightbox", true, {
                "content" : $("popup-warning"),
                "use_shape" : true,
                "lightbox_id" : "control_lightbox",
                "shape_id" : "control_shape",
                "opacity" : .3
            });	  
    	
    	}
    	        
        
    }


    this.createEvents = function(){
    	$("Modul_Metadaten").select(".ModulMetadaten_save")[0].observe("click", this.saveChanges.bind(this));
    }
    
    this.saveChanges = function(){
    	var formdata = form2object($("Modul_Metadaten").select("form")[0]);
    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
	            'action' : 'update',
	            'id' : "Metadaten",
	            'document_id' : Base.get("Camao_Api").getDocumentId()
    		},
    		'data' : {
    			'content' : formdata
    		}
        });
        
    }
}


extendClass("Camao_Modul_Metadaten", "Camao_Modul");function Camao_Modul_Mode() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Mode",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
    }

    this.handleResponse = function(sitepannelId, modul){
/*    	if (modul.data.options.action == "render"){
    		this.createModul(sitepannelId, modul);
    	}
*/
    }


    this.createEvents = function(){
        $("Modul_Mode").select(".cfswitchTrack").observe("click", this.modeChangeEvent.bind(this, "toggle"));
        $("Modul_Mode").select(".cfswitchTextEdit").observe("click", this.modeChangeEvent.bind(this, "edit"));
        $("Modul_Mode").select(".cfswitchTextLive").observe("click", this.modeChangeEvent.bind(this, "live"));

    	this.setViewAsEdit();
    }

    this.modeChangeEvent = function(newView, event){
        if (newView == "toggle"){
            if (VIEW_MODE == "edit"){
                this.setViewAsLive();
            }else if (VIEW_MODE == "live"){
                this.setViewAsEdit();
            }
        }else if (VIEW_MODE == "edit"){
            if (newView == "live") this.setViewAsLive();
        }else if (VIEW_MODE == "live"){
            if (newView == "edit") this.setViewAsEdit();
        }
    }


    this.setViewAsLive = function(){
        var cancelSwitch = false;
        

        
        var existsUnsavedData = Base.get("Camao_Elements").showElementSaveDialog(function(){
          
            $$(".element .edit-mode").each(function(e){
                if (e.visible()){
                    Base.get("Camao_Elements").menuLayerClickHandler({ 'target' : e.down(".save") });
                }
            });

            VIEW_MODE = "live";
            $$(".element-menu-layer").hide();
            $$(".container-menu-layer").hide();
            $$("body")[0].removeClassName('edit');
        }, function(){});
    
        if (existsUnsavedData === false){
            VIEW_MODE = "live";
            $$(".element-menu-layer").hide();
            $$(".container-menu-layer").hide();
            $$("body")[0].removeClassName('edit');

        }
    }

	this.setViewAsEdit = function(){
		VIEW_MODE = "edit";
		$$(".element-menu-layer").show();
        $$(".container-menu-layer").show();
        $$(".element-menu-layer").each(function(element){
            try {
                var pNoteType = $(element).select(".nodetype")[0];
                var pMenu = $(element).select("ul")[0];
                // update nodetype-Text (replace text in brackets)
                //pNoteType.update(pNoteType.innerHTML.replace(/\(.*\)/,''));
                if(element.getWidth()-pNoteType.getWidth()-pMenu.getWidth()<0) {
                    element.addClassName("element-menu-layer-compact");
                }
            } catch(e) {
            }
        });
        $$(".container-menu-layer").each(function(element){
            try {
                var pNoteType = $(element).select(".nodetype")[0];
                var pMenu = $(element).select("ul")[0];
                // update nodetype-Text (replace text in brackets)
                //pNoteType.update(pNoteType.innerHTML.replace(/\(.*\)/,''));
                if(element.getWidth()-pNoteType.getWidth()-pMenu.getWidth()<0) {
                    element.addClassName("element-menu-layer-compact");
                }
            } catch(e) {
            }
        });
        $$("body")[0].addClassName('edit');
	}

}

extendClass("Camao_Modul_Mode", "Camao_Modul");function Camao_Modul_Settings() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Settings",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
        
    }

    this.handleResponse = function(sitepannelId, modul){
        this.createModul(sitepannelId, modul);
        infos.sitepannelId = sitepannelId;
        
    }


    this.createEvents = function(){
        $("Modul_Settings").select(".ModulSettings_save")[0].observe("click", this.saveChanges.bind(this));
    }
    
    this.saveChanges = function(){
        var formdata = form2object($("Modul_Settings").select("form")[0]);
        Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
            'options' : {
                'action' : 'update',
                'id' : "Settings",
                'document_id' : Base.get("Camao_Api").getDocumentId()
            },
            'data' : {
                'content' : formdata
            }
        });
        
    }
}


extendClass("Camao_Modul_Settings", "Camao_Modul");var MODUL_SITEMAP_MODE = "view";
function Camao_Modul_Sitemap() {
    var infos = {};

    this.__construct = function(){
        Base.get("Camao_Cron",true,{
            'target' : "Modul_Sitemap",
            'rule' : { 'onElementExists' : true },
            'callback' : this.createEvents.bind(this)
        });
    	
    }

    this.handleResponse = function(sitepannelId, modul){
    	infos.sitepannelId = sitepannelId;

    	if (modul.data.options.action == "render"){
    		this.createModul(sitepannelId, modul);
            this.loadTree(sitepannelId);
            
            
    	}else if (modul.data.options.action == "build"){
    		var Camao_TreeObj = Base.get("Camao_Tree");


    		Camao_TreeObj.createTree(sitepannelId, modul);
    		Camao_TreeObj.selectActiveNode();
            
            Base.get("Camao_Sitepannel").onResize($(sitepannelId));
            
    	}else if (modul.data.options.action == "insert_places"){
    		if (modul.data.content == "all"){
    			if (MODUL_SITEMAP_MODE == "move"){
    				this.createTargetsForMove();
    			}else if (MODUL_SITEMAP_MODE == "new"){
    				this.createTargetsForNew();
    			}
    		}
    	}
    }


    this.loadTree = function(sitepannelId){
    	Base.get("Camao_Tree", false, {
    		'target' : $("Modul_Sitemap").select(".ModulSitemap_sitemap")[0],
    		'sitepannelId' : sitepannelId,
    		'modulId' : 'sitemap'
    	});
    }

    
    this.createEvents = function(){
    	$("Modul_Sitemap").select(".ModulSitemap_new_page")[0].observe("click", this.createNewNode.bind(this));
    	$("Modul_Sitemap").select(".ModulSitemap_cancel_new_page")[0].observe("click", this.cancelCreateNewNode.bind(this));
    	
    	$("Modul_Sitemap").select(".ModulSitemap_move_page")[0].observe("click", this.moveNode.bind(this));
    	$("Modul_Sitemap").select(".ModulSitemap_cancel_move_page")[0].observe("click", this.cancelMoveNode.bind(this));
    	
    	
    	$("Modul_Sitemap").select(".ModulSitemap_duplicate_page")[0].observe("click", this.dublicateNode.bind(this));
    	$("Modul_Sitemap").select(".ModulSitemap_delete_page")[0].observe("click", this.deleteNode.bind(this));
    	$("Modul_Sitemap").select(".ModulSitemap_expand_tree")[0].observe("click", this.expandTree.bind(this));
    	

    	    	
    }	   
    
    this.createNewNode = function(){
    	if (MODUL_SITEMAP_MODE != "new"){
    		$("Modul_Sitemap").select(".ModulSitemap_new_page")[0].hide();
    		$("Modul_Sitemap").select(".ModulSitemap_cancel_new_page")[0].show();
    		
	    	MODUL_SITEMAP_MODE = "new";

	    	Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
	            'options' : {
		            'action' : 'get_insert_places',
		            'id' : "Sitemap"
	    		}
	        });  
    	}
    }
    
    this.dublicateNode = function(){
    	Base.get("Camao_Tree").duplicateNode();
    }
    
    this.deleteNode = function(){
    	if (!confirm("sure")) return false;
    	Base.get("Camao_Tree").deleteNode();
    }
    
    this.expandTree = function(){
    	//TODO
    }
    
    this.cancelCreateNewNode = function(){
    	MODUL_SITEMAP_MODE = "view";

    	var masterUl = $("Modul_Sitemap").select(".ModulSitemap_sitemap ul")[0];
    	masterUl.stopObserving("click", infos.observeNewClickEvent);

    	
    	
    	$("Modul_Sitemap").select(".ModulSitemap_sitemap li.insertHere").remove();
		$("Modul_Sitemap").select(".ModulSitemap_new_page")[0].show();
		$("Modul_Sitemap").select(".ModulSitemap_cancel_new_page")[0].hide();
    	
    }
    
    this.cancelMoveNode = function(){
    	MODUL_SITEMAP_MODE = "view";
    	var masterUl = $("Modul_Sitemap").select(".ModulSitemap_sitemap ul")[0];
    	masterUl.stopObserving("click", infos.observeMoveClickEvent);
    	
    	$("Modul_Sitemap").select(".ModulSitemap_sitemap li.insertHere").remove();
		$("Modul_Sitemap").select(".ModulSitemap_move_page")[0].show();
		$("Modul_Sitemap").select(".ModulSitemap_cancel_move_page")[0].hide();
    }
    
    this.isRootNode = function(){
        // TODO: better root node check!
        return ($$(".ModulSitemap_sitemap ul.documentTree").first().select("li").first().hasClassName("selected"));
    }
    
    this.moveNode = function(){
        if (MODUL_SITEMAP_MODE != "move"){
            $("Modul_Sitemap").select(".ModulSitemap_move_page")[0].hide();
            $("Modul_Sitemap").select(".ModulSitemap_cancel_move_page")[0].show();
            MODUL_SITEMAP_MODE = "move";

            Base.get("Camao_Api").addModulRequest(infos.sitepannelId, {
                'options' : {
                    'action' : 'get_insert_places',
                    'id' : "Sitemap"
                }
            });  
        }
    }

    
    this.createTargetsForMove = function(){
    	var masterUl = $("Modul_Sitemap").select(".ModulSitemap_sitemap ul")[0];
    	this.setUlAsTargetForMove(masterUl);
    	infos.observeMoveClickEvent = this.moveTargetClickEvent.bind(this);
    	masterUl.observe("click", infos.observeMoveClickEvent);
    	
    	$("Modul_Sitemap").select(".ModulSitemap_sitemap ul").each(function(myul){
	    	var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
	    	myul.appendChild(insertHere);
    	});
    }
    
    this.moveTargetClickEvent = function(e){
		if (MODUL_SITEMAP_MODE == "move"){
    		if ($(e.target).hasClassName("insertHere")){
    			var parent_node_id = $(e.target).up("li") ? $(e.target).up("li").id.replace("node_","") : false;
    			var after_node_id = $(e.target).previous("li") ? $(e.target).previous("li").id.replace("node_","") : false;

                

                
                $("popup-warning").select(".content")[0].update(dic_sitepanel_global_move_node_confirm);
                $("popup-warning").select(".button-ok")[0].stopObserving("click");
                $("popup-warning").select(".button-ok")[0].observe("click", function(parent_node_id, after_node_id){
                    Base.get("Camao_Tree").moveNode(parent_node_id, after_node_id);
                }.bind(this,parent_node_id, after_node_id));
                
                Base.get("Camao_Lightbox", true, {
                    "content" : $("popup-warning"),
                    "use_shape" : true,
                    "lightbox_id" : "control_lightbox",
                    "shape_id" : "control_shape",
                    "opacity" : .3
                });          
                
    			
    		}
		}	
    }
    
    this.setUlAsTargetForMove = function(ul){
		if (typeof arguments[1] != "undefined"){
	    	var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
	    	ul.appendChild(insertHere);
		}

        var i = 0;
        ul.select("li").each(function(myLi){
            if (i > 0){
                var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
                myLi.insert({ before : insertHere} );
            }
            if (myLi.select(".icon_hasChild").length == 0){
                var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
                myLi.appendChild(insertHere);
            }
            i++;
            
        });

    }
    
    
    
    
    this.createTargetsForNew = function(){
    	var masterUl = $("Modul_Sitemap").select(".ModulSitemap_sitemap ul")[0];
    	this.setUlAsTargetForNew(masterUl);

    	infos.observeNewClickEvent = this.createTargetClickEvent.bind(this);
    	masterUl.observe("click", infos.observeNewClickEvent);

    	$("Modul_Sitemap").select(".ModulSitemap_sitemap ul").each(function(myul){
            var insertHere = new Element("li").addClassName("insertHere").update("insert here");
            myul.appendChild(insertHere);
    	});
    }
    
    this.createTargetClickEvent = function(e){
		if (MODUL_SITEMAP_MODE == "new"){
    		if ($(e.target).hasClassName("insertHere")){
    			var parent_node_id = $(e.target).up("li") ? $(e.target).up("li").id.replace("node_","") : false;
    			var after_node_id = $(e.target).previous("li") ? $(e.target).previous("li").id.replace("node_","") : false;


                
                
                $("popup-warning").select(".content")[0].update(dic_sitepanel_modul_sitemap_new_confirm);
                $("popup-warning").select(".button-ok")[0].stopObserving("click");
                $("popup-warning").select(".button-ok")[0].observe("click", function(parent_node_id, after_node_id){
                  Base.get("Camao_Tree").createNode(parent_node_id, after_node_id);
                }.bind(this,parent_node_id, after_node_id));
                
                Base.get("Camao_Lightbox", true, {
                    "content" : $("popup-warning"),
                    "use_shape" : true,
                    "lightbox_id" : "control_lightbox",
                    "shape_id" : "control_shape",
                    "opacity" : .3
                });                     
                
    		}
		}
    }
    
    
    this.setUlAsTargetForNew = function(ul){
		if (typeof arguments[1] != "undefined"){
	    	var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
	    	ul.appendChild(insertHere);
		}

		var i = 0;
		ul.select("li").each(function(myLi){
            if (i > 0){
                var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
                myLi.insert({ before : insertHere} );
            }
            if (myLi.select(".icon_hasChild").length == 0){
                var insertHere = new Element("li").addClassName("insertHere").addClassName("newSubNode").update("insert here");
                myLi.appendChild(insertHere);
            }
            i++;
			
		});

    }
        
    
}

extendClass("Camao_Modul_Sitemap", "Camao_Modul");
function Camao_Modul_Warnings() {
    var infos = { messageCount : 0};

    this.__contruct = function(){}

    this.handleResponse = function(sitepannelId, modul){
        this.createModul(sitepannelId, modul);
    }
    
    this.createMessage = function(message){
        infos.messageCount++;

        if ($("Modul_Warnings")) {
            var container = $("Modul_Warnings").select(".ModulWarnings_output_" + message.type)[0];
            if (container){
                var li = new Element("li").update(message.content);
                li.observe("click", this.removeMessage.bind(this, li));
                container.appendChild(li);
                $("Modul_Warnings").select(".count")[0].update(infos.messageCount);
            }else{
                this.throwException(EXCEPTION_ERROR, "Unknown message type!! (" + message.type + ")");
            }

        } else {
            alert(message.type.ucFirst() + ": " + message.content.ucFirst());
        }
    }
    
    this.removeMessage = function(element){
        element.remove();
        infos.messageCount--;
        if (infos.messageCount == 0){
            $("Modul_Warnings").select(".count")[0].update("");
        }else{
            $("Modul_Warnings").select(".count")[0].update(infos.messageCount);
        }
    }


    this.createEvents = function(){

    }

}


extendClass("Camao_Modul_Warnings", "Camao_Modul");var globalIsAutoscrollTimerTopActive = false;
var globalIsAutoscrollTimerBottomActive = false;

function Camao_Document() {
    var infos = {
    };

    this.__construct = function(){
    	this.createEvents();
    }



    this.createEvents = function(){

            
      document.observe("mousemove", function(e){
        if (globalIsDragActive){
          if ($(document).height - e.clientY < 100){
              if (globalIsAutoscrollTimerBottomActive == false){
                  globalIsAutoscrollTimerBottomActive = true;
                  globalIsAutoscrollTimerTopActive = false;
                  this.startAutoscrollTimerBottom();
              }
              
          }else if (e.clientY < 100){
              if (globalIsAutoscrollTimerTopActive == false){
                  globalIsAutoscrollTimerTopActive = true;
                  globalIsAutoscrollTimerBottomActive = false;
                  this.startAutoscrollTimerTop();
              }
          }else{
            globalIsAutoscrollTimerTopActive = false;
            globalIsAutoscrollTimerBottomActive = false;
          }
        }
      }.bind(this));      
      
    }
    
    this.startAutoscrollTimerBottom = function(){
       if (globalIsAutoscrollTimerBottomActive){
          window.scrollTo(0,getScrollTop() + 10);
          if (globalIsDragActive){
            
              //warum auch immer, prototype delay klappt nicht
              window.setTimeout("Base.get('Camao_Document').startAutoscrollTimerBottom()", 10);
          }

       }
    }

    this.startAutoscrollTimerTop = function(){
       if (globalIsAutoscrollTimerTopActive){
          window.scrollTo(0,getScrollTop() - 10);
          if (globalIsDragActive){
            
              //warum auch immer, prototype delay klappt nicht
              window.setTimeout("Base.get('Camao_Document').startAutoscrollTimerTop()", 10);
          }
       }
    }

    this.newDropzone = function(name){
        var dropzone = new Element("div", { 'className' : 'dropzone ' + name });
        dropzone.hide();
        return dropzone;
    }


    this.createDropzones = function(){
      $$(".dropzone").remove();
      this.createContainerDropzones();
      this.createElementDropzones();
      this.createDropzoneEvents();

      this.cleanDuplicatedContainerDropzones();
      this.cleanDuplicatedContainerDropzones();

      this.cleanDuplicatedElementDropzones();
      this.cleanDuplicatedElementDropzones();

    }

    this.createContainerDropzones = function(){
        if ($$(".container").length == 0){
          $$(".document")[0].insert(this.newDropzone("dropzone_container"));
        }else{
          $$(".container").each(function(container){
            container.insert({ before : this.newDropzone("dropzone_container")});
            container.insert({ after : this.newDropzone("dropzone_container")});

            container.select(".column").each(function(column){
                column.insert({ bottom : this.newDropzone("dropzone_container")});
            }.bind(this));

          }.bind(this));
        }

        $$(".elementContainer").each(function(element){
          element.insert({ before : this.newDropzone("dropzone_container")});

        }.bind(this));

    }

    this.createElementDropzones = function(){
        $$(".elementContainer").each(function(element){
          element.insert({ before : this.newDropzone("dropzone_element")});

        }.bind(this));

        $$(".container").each(function(container){
          container.select(".column").each(function(column){
              column.insert({ top : this.newDropzone("dropzone_element")});
              column.insert({ bottom : this.newDropzone("dropzone_element")});
          }.bind(this));
        }.bind(this));
    }


    this.cleanDuplicatedContainerDropzones = function(){
      $$(".dropzone_container").each(function(container){
          if (container.next() && container.next().hasClassName("dropzone_container")) container.next().remove();

      }.bind(this));

    }

    this.cleanDuplicatedElementDropzones = function(){
      $$(".dropzone_element").each(function(container){
          if (container.next() && container.next().hasClassName("dropzone_element")) container.next().remove();

          if (container.next() && !container.next().visible() && container.next().next() && container.next().next().hasClassName("dropzone_element")) container.next().next().remove();

      }.bind(this));

    }



    this.createDropzoneEvents = function(){
        $$(".dropzone_container").each(function(dropzone){
            Droppables.add(dropzone, {
                accept : "drag_container",
                onDrop : this.handleDropEventContainer.bind(this),
                hoverclass : "hover"
            });

            Droppables.add(dropzone, {
                accept : "container",
                onDrop : this.handleDropEventContainer.bind(this),
                hoverclass : "hover"
            });

        }.bind(this));

        $$(".dropzone_element").each(function(dropzone){
            Droppables.add(dropzone, {
                accept : "drag_element",
                onDrop : this.handleDropEventElement.bind(this),
                hoverclass : "hover"
            });

            Droppables.add(dropzone, {
                accept : "element",
                onDrop : this.handleDropEventElement.bind(this),
                hoverclass : "hover"
            });
        }.bind(this));

    }

    this.handleDropEventContainer = function(dragElement, dropElement){
        var parent_node_id = false;
        var parent_node = dropElement.up();

        while(!parent_node.id) parent_node = parent_node.up();

        parent_node_id = parent_node.id.replace("column_", "");
        parent_node_id = parent_node_id.replace("document_", "");

        //if (dropElement.up(".layoutColumn")) parent_node_id = dropElement.up(".layoutColumn").id.replace("column_", "");

        var drag_node_id = false;
        var drag_element_id = false;

        // create event
        if (dragElement.id.indexOf("drag_") != -1){
            drag_element_id = dragElement.id.replace("drag_", "");

        // move event
        }else if (dragElement.id.indexOf("container_") != -1){
            drag_node_id   = dragElement.id.replace("container_", "").trim();
        }



        var previousElement = dropElement.previous(".nestedNode");
        if (typeof previousElement == "undefined"){
            inser_after_node_id = false;
        }else{
            if (previousElement.hasClassName("elementContainer")){
              inser_after_node_id = previousElement.down().id.split("_");
            }else{
              inser_after_node_id = previousElement.id.split("_");
            }
            inser_after_node_id = inser_after_node_id[1];
        }

        Base.get("Camao_Api").addModulRequest(1, {
            'options' : {
                'action' : 'insert',
                'id' : "Container",
                'parent_node_id' : parent_node_id,
                'inser_after_node_id' : inser_after_node_id,
                'element_name' : drag_element_id,
                'drag_node_id' : drag_node_id
            }
        });

    }

    this.handleDropEventElement = function(dragElement, dropElement){
        var parent_node = dropElement.up();
        while(!parent_node.id) parent_node = parent_node.up();

        if (typeof parent_node == "undefined"){
            parent_node_id = false;
        }else{
            parent_node_id = parent_node.id.replace("column_","");
        }

        var before_node_id = false;

        var afterElement = dropElement.previous(".nestedNode");

        if (afterElement) {
            if (afterElement.hasClassName("elementContainer")){
              before_node_id = afterElement.down().id.split("_");
            }else{
              before_node_id = afterElement.id.split("_");
            }
            before_node_id = before_node_id[1];
        }

        drag_element_id = false;
        drag_node_id = false;
        if (dragElement.id.indexOf("drag_") != -1){
            drag_element_id = dragElement.id.replace("drag_", "element_");
        }else if (dragElement.id.indexOf("element_") != -1){
            drag_node_id = dragElement.id.replace("element_", "").trim();
        }
        

        var doWork = true;
        if (typeof before_node_id == "string" && typeof drag_node_id == "string"){
            if (before_node_id == drag_node_id) doWork = false;
          
        }

        if (doWork){
          Base.get("Camao_Api").addModulRequest(1, {
              'options' : {
                  'action' : 'insert',
                  'id' : "Element",
                  'parent_node_id' : parent_node_id,
                  'before_node_id' : before_node_id,
                  'element_name' : drag_element_id,
                  'drag_node_id' : drag_node_id
              }
          });
        }
    }


}

extendClass("Camao_Document", "Camao_Exception");/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();/*****************************************************************************
scalable Inman Flash Replacement (sIFR) version 3, revision 436.

Copyright 2006 â€“ 2008 Mark Wubben, <http://novemberborn.net/>

Older versions:
* IFR by Shaun Inman
* sIFR 1.0 by Mike Davidson, Shaun Inman and Tomas Jogin
* sIFR 2.0 by Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben

See also <http://novemberborn.net/sifr3> and <http://wiki.novemberborn.net/sifr3>.

This software is licensed and provided under the CC-GNU LGPL.
See <http://creativecommons.org/licenses/LGPL/2.1/>
*****************************************************************************/

var sIFR=new function(){var O=this;var E={ACTIVE:"sIFR-active",REPLACED:"sIFR-replaced",IGNORE:"sIFR-ignore",ALTERNATE:"sIFR-alternate",CLASS:"sIFR-class",LAYOUT:"sIFR-layout",FLASH:"sIFR-flash",FIX_FOCUS:"sIFR-fixfocus",DUMMY:"sIFR-dummy"};E.IGNORE_CLASSES=[E.REPLACED,E.IGNORE,E.ALTERNATE];this.MIN_FONT_SIZE=6;this.MAX_FONT_SIZE=126;this.FLASH_PADDING_BOTTOM=5;this.VERSION="436";this.isActive=false;this.isEnabled=true;this.fixHover=true;this.autoInitialize=true;this.setPrefetchCookie=true;this.cookiePath="/";this.domains=[];this.forceWidth=true;this.fitExactly=false;this.forceTextTransform=true;this.useDomLoaded=true;this.useStyleCheck=false;this.hasFlashClassSet=false;this.repaintOnResize=true;this.replacements=[];var L=0;var R=false;function Y(){}function D(c){function d(e){return e.toLocaleUpperCase()}this.normalize=function(e){return e.replace(/\n|\r|\xA0/g,D.SINGLE_WHITESPACE).replace(/\s+/g,D.SINGLE_WHITESPACE)};this.textTransform=function(e,f){switch(e){case"uppercase":return f.toLocaleUpperCase();case"lowercase":return f.toLocaleLowerCase();case"capitalize":return f.replace(/^\w|\s\w/g,d)}return f};this.toHexString=function(e){if(e.charAt(0)!="#"||e.length!=4&&e.length!=7){return e}e=e.substring(1);return"0x"+(e.length==3?e.replace(/(.)(.)(.)/,"$1$1$2$2$3$3"):e)};this.toJson=function(g,f){var e="";switch(typeof(g)){case"string":e='"'+f(g)+'"';break;case"number":case"boolean":e=g.toString();break;case"object":e=[];for(var h in g){if(g[h]==Object.prototype[h]){continue}e.push('"'+h+'":'+this.toJson(g[h]))}e="{"+e.join(",")+"}";break}return e};this.convertCssArg=function(e){if(!e){return{}}if(typeof(e)=="object"){if(e.constructor==Array){e=e.join("")}else{return e}}var l={};var m=e.split("}");for(var h=0;h<m.length;h++){var k=m[h].match(/([^\s{]+)\s*\{(.+)\s*;?\s*/);if(!k||k.length!=3){continue}if(!l[k[1]]){l[k[1]]={}}var g=k[2].split(";");for(var f=0;f<g.length;f++){var n=g[f].match(/\s*([^:\s]+)\s*\:\s*([^;]+)/);if(!n||n.length!=3){continue}l[k[1]][n[1]]=n[2].replace(/\s+$/,"")}}return l};this.extractFromCss=function(g,f,i,e){var h=null;if(g&&g[f]&&g[f][i]){h=g[f][i];if(e){delete g[f][i]}}return h};this.cssToString=function(f){var g=[];for(var e in f){var j=f[e];if(j==Object.prototype[e]){continue}g.push(e,"{");for(var i in j){if(j[i]==Object.prototype[i]){continue}var h=j[i];if(D.UNIT_REMOVAL_PROPERTIES[i]){h=parseInt(h,10)}g.push(i,":",h,";")}g.push("}")}return g.join("")};this.escape=function(e){return escape(e).replace(/\+/g,"%2B")};this.encodeVars=function(e){return e.join("&").replace(/%/g,"%25")};this.copyProperties=function(g,f){for(var e in g){if(f[e]===undefined){f[e]=g[e]}}return f};this.domain=function(){var f="";try{f=document.domain}catch(g){}return f};this.domainMatches=function(h,g){if(g=="*"||g==h){return true}var f=g.lastIndexOf("*");if(f>-1){g=g.substr(f+1);var e=h.lastIndexOf(g);if(e>-1&&(e+g.length)==h.length){return true}}return false};this.uriEncode=function(e){return encodeURI(decodeURIComponent(e))};this.delay=function(f,h,g){var e=Array.prototype.slice.call(arguments,3);setTimeout(function(){h.apply(g,e)},f)}}D.UNIT_REMOVAL_PROPERTIES={leading:true,"margin-left":true,"margin-right":true,"text-indent":true};D.SINGLE_WHITESPACE=" ";function U(e){var d=this;function c(g,j,h){var k=d.getStyleAsInt(g,j,e.ua.ie);if(k==0){k=g[h];for(var f=3;f<arguments.length;f++){k-=d.getStyleAsInt(g,arguments[f],true)}}return k}this.getBody=function(){return document.getElementsByTagName("body")[0]||null};this.querySelectorAll=function(f){return window.parseSelector(f)};this.addClass=function(f,g){if(g){g.className=((g.className||"")==""?"":g.className+" ")+f}};this.removeClass=function(f,g){if(g){g.className=g.className.replace(new RegExp("(^|\\s)"+f+"(\\s|$)"),"").replace(/^\s+|(\s)\s+/g,"$1")}};this.hasClass=function(f,g){return new RegExp("(^|\\s)"+f+"(\\s|$)").test(g.className)};this.hasOneOfClassses=function(h,g){for(var f=0;f<h.length;f++){if(this.hasClass(h[f],g)){return true}}return false};this.ancestorHasClass=function(g,f){g=g.parentNode;while(g&&g.nodeType==1){if(this.hasClass(f,g)){return true}g=g.parentNode}return false};this.create=function(f,g){var h=document.createElementNS?document.createElementNS(U.XHTML_NS,f):document.createElement(f);if(g){h.className=g}return h};this.getComputedStyle=function(h,i){var f;if(document.defaultView&&document.defaultView.getComputedStyle){var g=document.defaultView.getComputedStyle(h,null);f=g?g[i]:null}else{if(h.currentStyle){f=h.currentStyle[i]}}return f||""};this.getStyleAsInt=function(g,i,f){var h=this.getComputedStyle(g,i);if(f&&!/px$/.test(h)){return 0}return parseInt(h)||0};this.getWidthFromStyle=function(f){return c(f,"width","offsetWidth","paddingRight","paddingLeft","borderRightWidth","borderLeftWidth")};this.getHeightFromStyle=function(f){return c(f,"height","offsetHeight","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth")};this.getDimensions=function(j){var h=j.offsetWidth;var f=j.offsetHeight;if(h==0||f==0){for(var g=0;g<j.childNodes.length;g++){var k=j.childNodes[g];if(k.nodeType!=1){continue}h=Math.max(h,k.offsetWidth);f=Math.max(f,k.offsetHeight)}}return{width:h,height:f}};this.getViewport=function(){return{width:window.innerWidth||document.documentElement.clientWidth||this.getBody().clientWidth,height:window.innerHeight||document.documentElement.clientHeight||this.getBody().clientHeight}};this.blurElement=function(g){try{g.blur();return}catch(h){}var f=this.create("input");f.style.width="0px";f.style.height="0px";g.parentNode.appendChild(f);f.focus();f.blur();f.parentNode.removeChild(f)}}U.XHTML_NS="http://www.w3.org/1999/xhtml";function H(r){var g=navigator.userAgent.toLowerCase();var q=(navigator.product||"").toLowerCase();var h=navigator.platform.toLowerCase();this.parseVersion=H.parseVersion;this.macintosh=/^mac/.test(h);this.windows=/^win/.test(h);this.linux=/^linux/.test(h);this.quicktime=false;this.opera=/opera/.test(g);this.konqueror=/konqueror/.test(g);this.ie=false/*@cc_on||true@*/;this.ieSupported=this.ie&&!/ppc|smartphone|iemobile|msie\s5\.5/.test(g)/*@cc_on&&@_jscript_version>=5.5@*/;this.ieWin=this.ie&&this.windows/*@cc_on&&@_jscript_version>=5.1@*/;this.windows=this.windows&&(!this.ie||this.ieWin);this.ieMac=this.ie&&this.macintosh/*@cc_on&&@_jscript_version<5.1@*/;this.macintosh=this.macintosh&&(!this.ie||this.ieMac);this.safari=/safari/.test(g);this.webkit=!this.konqueror&&/applewebkit/.test(g);this.khtml=this.webkit||this.konqueror;this.gecko=!this.khtml&&q=="gecko";this.ieVersion=this.ie&&/.*msie\s(\d\.\d)/.exec(g)?this.parseVersion(RegExp.$1):"0";this.operaVersion=this.opera&&/.*opera(\s|\/)(\d+\.\d+)/.exec(g)?this.parseVersion(RegExp.$2):"0";this.webkitVersion=this.webkit&&/.*applewebkit\/(\d+).*/.exec(g)?this.parseVersion(RegExp.$1):"0";this.geckoVersion=this.gecko&&/.*rv:\s*([^\)]+)\)\s+gecko/.exec(g)?this.parseVersion(RegExp.$1):"0";this.konquerorVersion=this.konqueror&&/.*konqueror\/([\d\.]+).*/.exec(g)?this.parseVersion(RegExp.$1):"0";this.flashVersion=0;if(this.ieWin){var l;var o=false;try{l=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")}catch(m){try{l=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");this.flashVersion=this.parseVersion("6");l.AllowScriptAccess="always"}catch(m){o=this.flashVersion==this.parseVersion("6")}if(!o){try{l=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(m){}}}if(!o&&l){this.flashVersion=this.parseVersion((l.GetVariable("$version")||"").replace(/^\D+(\d+)\D+(\d+)\D+(\d+).*/g,"$1.$2.$3"))}}else{if(navigator.plugins&&navigator.plugins["Shockwave Flash"]){var n=navigator.plugins["Shockwave Flash"].description.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var p=n.replace(/^\D*(\d+\.\d+).*$/,"$1");if(/r/.test(n)){p+=n.replace(/^.*r(\d*).*$/,".$1")}else{if(/d/.test(n)){p+=".0"}}this.flashVersion=this.parseVersion(p);var j=false;for(var k=0,c=this.flashVersion>=H.MIN_FLASH_VERSION;c&&k<navigator.mimeTypes.length;k++){var f=navigator.mimeTypes[k];if(f.type!="application/x-shockwave-flash"){continue}if(f.enabledPlugin){j=true;if(f.enabledPlugin.description.toLowerCase().indexOf("quicktime")>-1){c=false;this.quicktime=true}}}if(this.quicktime||!j){this.flashVersion=this.parseVersion("0")}}}this.flash=this.flashVersion>=H.MIN_FLASH_VERSION;this.transparencySupport=this.macintosh||this.windows||this.linux&&(this.flashVersion>=this.parseVersion("10")&&(this.gecko&&this.geckoVersion>=this.parseVersion("1.9")||this.opera));this.computedStyleSupport=this.ie||!!document.defaultView.getComputedStyle;this.fixFocus=this.gecko&&this.windows;this.nativeDomLoaded=this.gecko||this.webkit&&this.webkitVersion>=this.parseVersion("525")||this.konqueror&&this.konquerorMajor>this.parseVersion("03")||this.opera;this.mustCheckStyle=this.khtml||this.opera;this.forcePageLoad=this.webkit&&this.webkitVersion<this.parseVersion("523");this.properDocument=typeof(document.location)=="object";this.supported=this.flash&&this.properDocument&&(!this.ie||this.ieSupported)&&this.computedStyleSupport&&(!this.opera||this.operaVersion>=this.parseVersion("9.61"))&&(!this.webkit||this.webkitVersion>=this.parseVersion("412"))&&(!this.gecko||this.geckoVersion>=this.parseVersion("1.8.0.12"))&&(!this.konqueror)}H.parseVersion=function(c){return c.replace(/(^|\D)(\d+)(?=\D|$)/g,function(f,e,g){f=e;for(var d=4-g.length;d>=0;d--){f+="0"}return f+g})};H.MIN_FLASH_VERSION=H.parseVersion("8");function F(c){this.fix=c.ua.ieWin&&window.location.hash!="";var d;this.cache=function(){d=document.title};function e(){document.title=d}this.restore=function(){if(this.fix){setTimeout(e,0)}}}function S(l){var e=null;function c(){try{if(l.ua.ie||document.readyState!="loaded"&&document.readyState!="complete"){document.documentElement.doScroll("left")}}catch(n){return setTimeout(c,10)}i()}function i(){if(l.useStyleCheck){h()}else{if(!l.ua.mustCheckStyle){d(null,true)}}}function h(){e=l.dom.create("div",E.DUMMY);l.dom.getBody().appendChild(e);m()}function m(){if(l.dom.getComputedStyle(e,"marginLeft")=="42px"){g()}else{setTimeout(m,10)}}function g(){if(e&&e.parentNode){e.parentNode.removeChild(e)}e=null;d(null,true)}function d(n,o){l.initialize(o);if(n&&n.type=="load"){if(document.removeEventListener){document.removeEventListener("DOMContentLoaded",d,false)}if(window.removeEventListener){window.removeEventListener("load",d,false)}}}function j(){l.prepareClearReferences();if(document.readyState=="interactive"){document.attachEvent("onstop",f);setTimeout(function(){document.detachEvent("onstop",f)},0)}}function f(){document.detachEvent("onstop",f);k()}function k(){l.clearReferences()}this.attach=function(){if(window.addEventListener){window.addEventListener("load",d,false)}else{window.attachEvent("onload",d)}if(!l.useDomLoaded||l.ua.forcePageLoad||l.ua.ie&&window.top!=window){return}if(l.ua.nativeDomLoaded){document.addEventListener("DOMContentLoaded",i,false)}else{if(l.ua.ie||l.ua.khtml){c()}}};this.attachUnload=function(){if(!l.ua.ie){return}window.attachEvent("onbeforeunload",j);window.attachEvent("onunload",k)}}var Q="sifrFetch";function N(c){var e=false;this.fetchMovies=function(f){if(c.setPrefetchCookie&&new RegExp(";?"+Q+"=true;?").test(document.cookie)){return}try{e=true;d(f)}catch(g){}if(c.setPrefetchCookie){document.cookie=Q+"=true;path="+c.cookiePath}};this.clear=function(){if(!e){return}try{var f=document.getElementsByTagName("script");for(var g=f.length-1;g>=0;g--){var h=f[g];if(h.type=="sifr/prefetch"){h.parentNode.removeChild(h)}}}catch(j){}};function d(f){for(var g=0;g<f.length;g++){document.write('<script defer type="sifr/prefetch" src="'+f[g].src+'"><\/script>')}}}function b(e){var g=e.ua.ie;var f=g&&e.ua.flashVersion<e.ua.parseVersion("9.0.115");var d={};var c={};this.fixFlash=f;this.register=function(h){if(!g){return}var i=h.getAttribute("id");this.cleanup(i,false);c[i]=h;delete d[i];if(f){window[i]=h}};this.reset=function(){if(!g){return false}for(var j=0;j<e.replacements.length;j++){var h=e.replacements[j];var k=c[h.id];if(!d[h.id]&&(!k.parentNode||k.parentNode.nodeType==11)){h.resetMovie();d[h.id]=true}}return true};this.cleanup=function(l,h){var i=c[l];if(!i){return}for(var k in i){if(typeof(i[k])=="function"){i[k]=null}}c[l]=null;if(f){window[l]=null}if(i.parentNode){if(h&&i.parentNode.nodeType==1){var j=document.createElement("div");j.style.width=i.offsetWidth+"px";j.style.height=i.offsetHeight+"px";i.parentNode.replaceChild(j,i)}else{i.parentNode.removeChild(i)}}};this.prepareClearReferences=function(){if(!f){return}__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}};this.clearReferences=function(){if(f){var j=document.getElementsByTagName("object");for(var h=j.length-1;h>=0;h--){c[j[h].getAttribute("id")]=j[h]}}for(var k in c){if(Object.prototype[k]!=c[k]){this.cleanup(k,true)}}}}function K(d,g,f,c,e){this.sIFR=d;this.id=g;this.vars=f;this.movie=null;this.__forceWidth=c;this.__events=e;this.__resizing=0}K.prototype={getFlashElement:function(){return document.getElementById(this.id)},getAlternate:function(){return document.getElementById(this.id+"_alternate")},getAncestor:function(){var c=this.getFlashElement().parentNode;return !this.sIFR.dom.hasClass(E.FIX_FOCUS,c)?c:c.parentNode},available:function(){var c=this.getFlashElement();return c&&c.parentNode},call:function(c){var d=this.getFlashElement();if(!d[c]){return false}return Function.prototype.apply.call(d[c],d,Array.prototype.slice.call(arguments,1))},attempt:function(){if(!this.available()){return false}try{this.call.apply(this,arguments)}catch(c){if(this.sIFR.debug){throw c}return false}return true},updateVars:function(c,e){for(var d=0;d<this.vars.length;d++){if(this.vars[d].split("=")[0]==c){this.vars[d]=c+"="+e;break}}var f=this.sIFR.util.encodeVars(this.vars);this.movie.injectVars(this.getFlashElement(),f);this.movie.injectVars(this.movie.html,f)},storeSize:function(c,d){this.movie.setSize(c,d);this.updateVars(c,d)},fireEvent:function(c){if(this.available()&&this.__events[c]){this.sIFR.util.delay(0,this.__events[c],this,this)}},resizeFlashElement:function(c,d,e){if(!this.available()){return}this.__resizing++;var f=this.getFlashElement();f.setAttribute("height",c);this.getAncestor().style.minHeight="";this.updateVars("renderheight",c);this.storeSize("height",c);if(d!==null){f.setAttribute("width",d);this.movie.setSize("width",d)}if(this.__events.onReplacement){this.sIFR.util.delay(0,this.__events.onReplacement,this,this);delete this.__events.onReplacement}if(e){this.sIFR.util.delay(0,function(){this.attempt("scaleMovie");this.__resizing--},this)}else{this.__resizing--}},blurFlashElement:function(){if(this.available()){this.sIFR.dom.blurElement(this.getFlashElement())}},resetMovie:function(){this.sIFR.util.delay(0,this.movie.reset,this.movie,this.getFlashElement(),this.getAlternate())},resizeAfterScale:function(){if(this.available()&&this.__resizing==0){this.sIFR.util.delay(0,this.resize,this)}},resize:function(){if(!this.available()){return}this.__resizing++;var g=this.getFlashElement();var f=g.offsetWidth;if(f==0){return}var e=g.getAttribute("width");var l=g.getAttribute("height");var m=this.getAncestor();var o=this.sIFR.dom.getHeightFromStyle(m);g.style.width="1px";g.style.height="1px";m.style.minHeight=o+"px";var c=this.getAlternate().childNodes;var n=[];for(var k=0;k<c.length;k++){var h=c[k].cloneNode(true);n.push(h);m.appendChild(h)}var d=this.sIFR.dom.getWidthFromStyle(m);for(var k=0;k<n.length;k++){m.removeChild(n[k])}g.style.width=g.style.height=m.style.minHeight="";g.setAttribute("width",this.__forceWidth?d:e);g.setAttribute("height",l);if(sIFR.ua.ie){g.style.display="none";var j=g.offsetHeight;g.style.display=""}if(d!=f){if(this.__forceWidth){this.storeSize("width",d)}this.attempt("resize",d)}this.__resizing--},replaceText:function(g,j){var d=this.sIFR.util.escape(g);if(!this.attempt("replaceText",d)){return false}this.updateVars("content",d);var f=this.getAlternate();if(j){while(f.firstChild){f.removeChild(f.firstChild)}for(var c=0;c<j.length;c++){f.appendChild(j[c])}}else{try{f.innerHTML=g}catch(h){}}return true},changeCSS:function(c){c=this.sIFR.util.escape(this.sIFR.util.cssToString(this.sIFR.util.convertCssArg(c)));this.updateVars("css",c);return this.attempt("changeCSS",c)},remove:function(){if(this.movie&&this.available()){this.movie.remove(this.getFlashElement(),this.id)}}};var X=new function(){this.create=function(p,n,j,i,f,e,g,o,l,h,m){var k=p.ua.ie?d:c;return new k(p,n,j,i,f,e,g,o,["flashvars",l,"wmode",h,"bgcolor",m,"allowScriptAccess","always","quality","best"])};function c(s,q,l,h,f,e,g,r,n){var m=s.dom.create("object",E.FLASH);var p=["type","application/x-shockwave-flash","id",f,"name",f,"data",e,"width",g,"height",r];for(var o=0;o<p.length;o+=2){m.setAttribute(p[o],p[o+1])}var j=m;if(h){j=W.create("div",E.FIX_FOCUS);j.appendChild(m)}for(var o=0;o<n.length;o+=2){if(n[o]=="name"){continue}var k=W.create("param");k.setAttribute("name",n[o]);k.setAttribute("value",n[o+1]);m.appendChild(k)}l.style.minHeight=r+"px";while(l.firstChild){l.removeChild(l.firstChild)}l.appendChild(j);this.html=j.cloneNode(true)}c.prototype={reset:function(e,f){e.parentNode.replaceChild(this.html.cloneNode(true),e)},remove:function(e,f){e.parentNode.removeChild(e)},setSize:function(e,f){this.html.setAttribute(e,f)},injectVars:function(e,g){var h=e.getElementsByTagName("param");for(var f=0;f<h.length;f++){if(h[f].getAttribute("name")=="flashvars"){h[f].setAttribute("value",g);break}}}};function d(p,n,j,h,f,e,g,o,k){this.dom=p.dom;this.broken=n;this.html='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="'+f+'" width="'+g+'" height="'+o+'" class="'+E.FLASH+'"><param name="movie" value="'+e+'"></param></object>';var m="";for(var l=0;l<k.length;l+=2){m+='<param name="'+k[l]+'" value="'+k[l+1]+'"></param>'}this.html=this.html.replace(/(<\/object>)/,m+"$1");j.style.minHeight=o+"px";j.innerHTML=this.html;this.broken.register(j.firstChild)}d.prototype={reset:function(f,g){g=g.cloneNode(true);var e=f.parentNode;e.innerHTML=this.html;this.broken.register(e.firstChild);e.appendChild(g)},remove:function(e,f){this.broken.cleanup(f)},setSize:function(e,f){this.html=this.html.replace(e=="height"?/(height)="\d+"/:/(width)="\d+"/,'$1="'+f+'"')},injectVars:function(e,f){if(e!=this.html){return}this.html=this.html.replace(/(flashvars(=|\"\svalue=)\")[^\"]+/,"$1"+f)}}};this.errors=new Y(O);var A=this.util=new D(O);var W=this.dom=new U(O);var T=this.ua=new H(O);var G={fragmentIdentifier:new F(O),pageLoad:new S(O),prefetch:new N(O),brokenFlashIE:new b(O)};this.__resetBrokenMovies=G.brokenFlashIE.reset;var J={kwargs:[],replaceAll:function(d){for(var c=0;c<this.kwargs.length;c++){O.replace(this.kwargs[c])}if(!d){this.kwargs=[]}}};this.activate=function(){if(!T.supported||!this.isEnabled||this.isActive||!C()||a()){return}G.prefetch.fetchMovies(arguments);this.isActive=true;this.setFlashClass();G.fragmentIdentifier.cache();G.pageLoad.attachUnload();if(!this.autoInitialize){return}G.pageLoad.attach()};this.setFlashClass=function(){if(this.hasFlashClassSet){return}W.addClass(E.ACTIVE,W.getBody()||document.documentElement);this.hasFlashClassSet=true};this.removeFlashClass=function(){if(!this.hasFlashClassSet){return}W.removeClass(E.ACTIVE,W.getBody());W.removeClass(E.ACTIVE,document.documentElement);this.hasFlashClassSet=false};this.initialize=function(c){if(!this.isActive||!this.isEnabled){return}if(R){if(!c){J.replaceAll(false)}return}R=true;J.replaceAll(c);if(O.repaintOnResize){if(window.addEventListener){window.addEventListener("resize",Z,false)}else{window.attachEvent("onresize",Z)}}G.prefetch.clear()};this.replace=function(x,u){if(!T.supported){return}if(u){x=A.copyProperties(x,u)}if(!R){return J.kwargs.push(x)}if(this.onReplacementStart){this.onReplacementStart(x)}var AM=x.elements||W.querySelectorAll(x.selector);if(AM.length==0){return}var w=M(x.src);var AR=A.convertCssArg(x.css);var v=B(x.filters);var AN=x.forceSingleLine===true;var AS=x.preventWrap===true&&!AN;var q=AN||(x.fitExactly==null?this.fitExactly:x.fitExactly)===true;var AD=q||(x.forceWidth==null?this.forceWidth:x.forceWidth)===true;var s=x.ratios||[];var AE=x.pixelFont===true;var r=parseInt(x.tuneHeight)||0;var z=!!x.onRelease||!!x.onRollOver||!!x.onRollOut;if(q){A.extractFromCss(AR,".sIFR-root","text-align",true)}var t=A.extractFromCss(AR,".sIFR-root","font-size",true)||"0";var e=A.extractFromCss(AR,".sIFR-root","background-color",true)||"#FFFFFF";var o=A.extractFromCss(AR,".sIFR-root","kerning",true)||"";var AW=A.extractFromCss(AR,".sIFR-root","opacity",true)||"100";var k=A.extractFromCss(AR,".sIFR-root","cursor",true)||"default";var AP=parseInt(A.extractFromCss(AR,".sIFR-root","leading"))||0;var AJ=x.gridFitType||(A.extractFromCss(AR,".sIFR-root","text-align")=="right")?"subpixel":"pixel";var h=this.forceTextTransform===false?"none":A.extractFromCss(AR,".sIFR-root","text-transform",true)||"none";t=/^\d+(px)?$/.test(t)?parseInt(t):0;AW=parseFloat(AW)<1?100*parseFloat(AW):AW;var AC=x.modifyCss?"":A.cssToString(AR);var AG=x.wmode||"";if(!AG){if(x.transparent){AG="transparent"}else{if(x.opaque){AG="opaque"}}}if(AG=="transparent"){if(!T.transparencySupport){AG="opaque"}else{e="transparent"}}else{if(e=="transparent"){e="#FFFFFF"}}for(var AV=0;AV<AM.length;AV++){var AF=AM[AV];if(W.hasOneOfClassses(E.IGNORE_CLASSES,AF)||W.ancestorHasClass(AF,E.ALTERNATE)){continue}var AO=W.getDimensions(AF);var f=AO.height;var c=AO.width;var AA=W.getComputedStyle(AF,"display");if(!f||!c||!AA||AA=="none"){continue}c=W.getWidthFromStyle(AF);var n,AH;if(!t){var AL=I(AF);n=Math.min(this.MAX_FONT_SIZE,Math.max(this.MIN_FONT_SIZE,AL.fontSize));if(AE){n=Math.max(8,8*Math.round(n/8))}AH=AL.lines}else{n=t;AH=1}var d=W.create("span",E.ALTERNATE);var AX=AF.cloneNode(true);AF.parentNode.appendChild(AX);for(var AU=0,AT=AX.childNodes.length;AU<AT;AU++){var m=AX.childNodes[AU];if(!/^(style|script)$/i.test(m.nodeName)){d.appendChild(m.cloneNode(true))}}if(x.modifyContent){x.modifyContent(AX,x.selector)}if(x.modifyCss){AC=x.modifyCss(AR,AX,x.selector)}var p=P(AX,h,x.uriEncode);AX.parentNode.removeChild(AX);if(x.modifyContentString){p.text=x.modifyContentString(p.text,x.selector)}if(p.text==""){continue}var AK=Math.round(AH*V(n,s)*n)+this.FLASH_PADDING_BOTTOM+r;if(AH>1&&AP){AK+=Math.round((AH-1)*AP)}var AB=AD?c:"100%";var AI="sIFR_replacement_"+L++;var AQ=["id="+AI,"content="+A.escape(p.text),"width="+c,"renderheight="+AK,"link="+A.escape(p.primaryLink.href||""),"target="+A.escape(p.primaryLink.target||""),"size="+n,"css="+A.escape(AC),"cursor="+k,"tunewidth="+(x.tuneWidth||0),"tuneheight="+r,"offsetleft="+(x.offsetLeft||""),"offsettop="+(x.offsetTop||""),"fitexactly="+q,"preventwrap="+AS,"forcesingleline="+AN,"antialiastype="+(x.antiAliasType||""),"thickness="+(x.thickness||""),"sharpness="+(x.sharpness||""),"kerning="+o,"gridfittype="+AJ,"flashfilters="+v,"opacity="+AW,"blendmode="+(x.blendMode||""),"selectable="+(x.selectable==null||AG!=""&&!sIFR.ua.macintosh&&sIFR.ua.gecko&&sIFR.ua.geckoVersion>=sIFR.ua.parseVersion("1.9")?"true":x.selectable===true),"fixhover="+(this.fixHover===true),"events="+z,"delayrun="+G.brokenFlashIE.fixFlash,"version="+this.VERSION];var y=A.encodeVars(AQ);var g=new K(O,AI,AQ,AD,{onReplacement:x.onReplacement,onRollOver:x.onRollOver,onRollOut:x.onRollOut,onRelease:x.onRelease});g.movie=X.create(sIFR,G.brokenFlashIE,AF,T.fixFocus&&x.fixFocus,AI,w,AB,AK,y,AG,e);this.replacements.push(g);this.replacements[AI]=g;if(x.selector){if(!this.replacements[x.selector]){this.replacements[x.selector]=[g]}else{this.replacements[x.selector].push(g)}}d.setAttribute("id",AI+"_alternate");AF.appendChild(d);W.addClass(E.REPLACED,AF)}G.fragmentIdentifier.restore()};this.getReplacementByFlashElement=function(d){for(var c=0;c<O.replacements.length;c++){if(O.replacements[c].id==d.getAttribute("id")){return O.replacements[c]}}};this.redraw=function(){for(var c=0;c<O.replacements.length;c++){O.replacements[c].resetMovie()}};this.prepareClearReferences=function(){G.brokenFlashIE.prepareClearReferences()};this.clearReferences=function(){G.brokenFlashIE.clearReferences();G=null;J=null;delete O.replacements};function C(){if(O.domains.length==0){return true}var d=A.domain();for(var c=0;c<O.domains.length;c++){if(A.domainMatches(d,O.domains[c])){return true}}return false}function a(){if(document.location.protocol=="file:"){if(O.debug){O.errors.fire("isFile")}return true}return false}function M(c){if(T.ie&&c.charAt(0)=="/"){c=window.location.toString().replace(/([^:]+)(:\/?\/?)([^\/]+).*/,"$1$2$3")+c}return c}function V(d,e){for(var c=0;c<e.length;c+=2){if(d<=e[c]){return e[c+1]}}return e[e.length-1]||1}function B(g){var e=[];for(var d in g){if(g[d]==Object.prototype[d]){continue}var c=g[d];d=[d.replace(/filter/i,"")+"Filter"];for(var f in c){if(c[f]==Object.prototype[f]){continue}d.push(f+":"+A.escape(A.toJson(c[f],A.toHexString)))}e.push(d.join(","))}return A.escape(e.join(";"))}function Z(d){var e=Z.viewport;var c=W.getViewport();if(e&&c.width==e.width&&c.height==e.height){return}Z.viewport=c;if(O.replacements.length==0){return}if(Z.timer){clearTimeout(Z.timer)}Z.timer=setTimeout(function(){delete Z.timer;for(var f=0;f<O.replacements.length;f++){O.replacements[f].resize()}},200)}function I(f){var g=W.getComputedStyle(f,"fontSize");var d=g.indexOf("px")==-1;var e=f.innerHTML;if(d){f.innerHTML="X"}f.style.paddingTop=f.style.paddingBottom=f.style.borderTopWidth=f.style.borderBottomWidth="0px";f.style.lineHeight="2em";f.style.display="block";g=d?f.offsetHeight/2:parseInt(g,10);if(d){f.innerHTML=e}var c=Math.round(f.offsetHeight/(2*g));f.style.paddingTop=f.style.paddingBottom=f.style.borderTopWidth=f.style.borderBottomWidth=f.style.lineHeight=f.style.display="";if(isNaN(c)||!isFinite(c)||c==0){c=1}return{fontSize:g,lines:c}}function P(c,g,s){s=s||A.uriEncode;var q=[],m=[];var k=null;var e=c.childNodes;var o=false,p=false;var j=0;while(j<e.length){var f=e[j];if(f.nodeType==3){var t=A.textTransform(g,A.normalize(f.nodeValue)).replace(/</g,"&lt;");if(o&&p){t=t.replace(/^\s+/,"")}m.push(t);o=/\s$/.test(t);p=false}if(f.nodeType==1&&!/^(style|script)$/i.test(f.nodeName)){var h=[];var r=f.nodeName.toLowerCase();var n=f.className||"";if(/\s+/.test(n)){if(n.indexOf(E.CLASS)>-1){n=n.match("(\\s|^)"+E.CLASS+"-([^\\s$]*)(\\s|$)")[2]}else{n=n.match(/^([^\s]+)/)[1]}}if(n!=""){h.push('class="'+n+'"')}if(r=="a"){var d=s(f.getAttribute("href")||"");var l=f.getAttribute("target")||"";h.push('href="'+d+'"','target="'+l+'"');if(!k){k={href:d,target:l}}}m.push("<"+r+(h.length>0?" ":"")+h.join(" ")+">");p=true;if(f.hasChildNodes()){q.push(j);j=0;e=f.childNodes;continue}else{if(!/^(br|img)$/i.test(f.nodeName)){m.push("</",f.nodeName.toLowerCase(),">")}}}if(q.length>0&&!f.nextSibling){do{j=q.pop();e=f.parentNode.parentNode.childNodes;f=e[j];if(f){m.push("</",f.nodeName.toLowerCase(),">")}}while(j==e.length-1&&q.length>0)}j++}return{text:m.join("").replace(/^\s+|\s+$|\s*(<br>)\s*/g,"$1"),primaryLink:k||{}}}};
var parseSelector=(function(){var B=/\s*,\s*/;var A=/\s*([\s>+~(),]|^|$)\s*/g;var L=/([\s>+~,]|[^(]\+|^)([#.:@])/g;var F=/(^|\))[^\s>+~]/g;var M=/(\)|^)/;var K=/[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;function H(R,P){P=P||document.documentElement;var S=R.split(B),X=[];for(var U=0;U<S.length;U++){var N=[P],W=G(S[U]);for(var T=0;T<W.length;){var Q=W[T++],O=W[T++],V="";if(W[T]=="("){while(W[T++]!=")"&&T<W.length){V+=W[T]}V=V.slice(0,-1)}N=I(N,Q,O,V)}X=X.concat(N)}return X}function G(N){var O=N.replace(A,"$1").replace(L,"$1*$2").replace(F,D);return O.match(K)||[]}function D(N){return N.replace(M,"$1 ")}function I(N,P,Q,O){return(H.selectors[P])?H.selectors[P](N,Q,O):[]}var E={toArray:function(O){var N=[];for(var P=0;P<O.length;P++){N.push(O[P])}return N}};var C={isTag:function(O,N){return(N=="*")||(N.toLowerCase()==O.nodeName.toLowerCase())},previousSiblingElement:function(N){do{N=N.previousSibling}while(N&&N.nodeType!=1);return N},nextSiblingElement:function(N){do{N=N.nextSibling}while(N&&N.nodeType!=1);return N},hasClass:function(N,O){return(O.className||"").match("(^|\\s)"+N+"(\\s|$)")},getByTag:function(N,O){return O.getElementsByTagName(N)}};var J={"#":function(N,P){for(var O=0;O<N.length;O++){if(N[O].getAttribute("id")==P){return[N[O]]}}return[]}," ":function(O,Q){var N=[];for(var P=0;P<O.length;P++){N=N.concat(E.toArray(C.getByTag(Q,O[P])))}return N},">":function(O,R){var N=[];for(var Q=0,S;Q<O.length;Q++){S=O[Q];for(var P=0,T;P<S.childNodes.length;P++){T=S.childNodes[P];if(T.nodeType==1&&C.isTag(T,R)){N.push(T)}}}return N},".":function(O,Q){var N=[];for(var P=0,R;P<O.length;P++){R=O[P];if(C.hasClass([Q],R)){N.push(R)}}return N},":":function(N,P,O){return(H.pseudoClasses[P])?H.pseudoClasses[P](N,O):[]}};H.selectors=J;H.pseudoClasses={};H.util=E;H.dom=C;return H})();/*****************************************************************************
It is adviced to place the sIFR JavaScript calls in this file, keeping it
separate from the `sifr.js` file. That way, you can easily swap the `sifr.js`
file for a new version, while keeping the configuration.

You must load this file *after* loading `sifr.js`.

That said, you're of course free to merge the JavaScript files. Just make sure
the copyright statement in `sifr.js` is kept intact.
*****************************************************************************/

// Make an object pointing to the location of the Flash movie on your web server.
// Try using the font name as the variable name, makes it easy to remember which
// object you're using. As an example in this file, we'll use Futura.
var helveticaNeueLt = { src: '/media/fonts/helvetica-neue-lt.swf' };

// Now you can set some configuration settings.
// See also <http://wiki.novemberborn.net/sifr3/JavaScript+Configuration>.
// One setting you probably want to use is `sIFR.useStyleCheck`. Before you do that,
// read <http://wiki.novemberborn.net/sifr3/DetectingCSSLoad>.

// sIFR.useStyleCheck = true;

sIFR.fitExactly = true;

// Next, activate sIFR:
sIFR.activate(helveticaNeueLt);

// If you want, you can use multiple movies, like so:
//
//    var futura = { src: '/path/to/futura.swf' };
//    var garamond = { src '/path/to/garamond.swf' };
//    var rockwell = { src: '/path/to/rockwell.swf' };
//    
//    sIFR.activate(futura, garamond, rockwell);
//
// Remember, there must be *only one* `sIFR.activate()`!

// Now we can do the replacements. You can do as many as you like, but just
// as an example, we'll replace all `<h1>` elements with the Futura movie.
// 
// The first argument to `sIFR.replace` is the `futura` object we created earlier.
// The second argument is another object, on which you can specify a number of
// parameters or "keyword arguemnts". For the full list, see "Keyword arguments"
// under `replace(kwargs, mergeKwargs)` at 
// <http://wiki.novemberborn.net/sifr3/JavaScript+Methods>.
// 
// The first argument you see here is `selector`, which is a normal CSS selector.
// That means you can also do things like '#content h1' or 'h1.title'.
//
// The second argument determines what the Flash text looks like. The main text
// is styled via the `.sIFR-root` class. Here we've specified `background-color`
// of the entire Flash movie to be a light grey, and the `color` of the text to
// be red. Read more about styling at <http://wiki.novemberborn.net/sifr3/Styling>.
sIFR.replace(helveticaNeueLt, {
  selector: 'h2.pageTitle, .sitemap h2',
  css: '.sIFR-root { background-color: transparent; color: #e7ac00; text-transform: lowercase; padding: 0;}',
  transparent: true,
  fitExactly: true,
  tuneHeight: -10,
  offsetTop: -5
 ,onReplacement: function(item){
    if(Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) >= 7) {
        document.getElementById(item.id).parentNode.style.zoom=1;
        item.getAlternate().style.display='none';
    }
}
});
/**
 * @category   Camao
 * @package    Camao_Index
 * @copyright  Copyright (c) 2010 CAMAO AG (http://www.camao.de)
 * @autor      Matthias Friedrich <matthias.friedrich@camao.de>
 */

try{

    if (typeof(Prototype) == "undefined"){
        alert("You need to Include Prototype !");
    }else if(parseInt(Prototype.Version.replace(/\./g,"")) < 161){
        alert("Your Prototype version is to old!");
    }else{
        var DEVELOPER = false;
        var ROOT_JS = typeof _ROOT_JS != "undefined" ? _ROOT_JS : "/media/js";

        /********************
         * Important Includes
         *******************/
        //include("Camao_ExtendObject");
        //include("Camao_Exception");

        /********************
         * Custom Includes
         *******************/
        //include("Camao_AdjustableImage");
        //include("Camao_Ajax");
        //include("Camao_Autocompletion");
        //include("Camao_Browser");
        //include("Camao_Browser_Function");
        //include("Camao_Browser_Position");
        //include("Camao_Cache");
        //include("Camao_Ckedit");
        //include("Camao_Console");
        //include("Camao_Content");
        //include("Camao_Cookie");
        //include("Camao_Cron");
        //include("Camao_Crypt");
        //include("Camao_Crypt_MD5");
        //include("Camao_Crypt_SHA1");
        //include("Camao_Csv");
        //include("Camao_DragAndDrop");
        //include("Camao_Dropdown");
        //include("Camao_Editable");
        //include("Camao_EventHandler");
        //include("Camao_Expendable");
        //include("Camao_Formular");
        //include("Camao_Google_Adsense");
        //include("Camao_Hover");
        //include("Camao_Lightbox");
        //include("Camao_Lightbox_Automatic");
        //include("Camao_Menu");
        //include("Camao_MouseEvent");
        //include("Camao_Navigation");
        //include("Camao_Navigation_Focus");
        //include("Camao_Parse");
        //include("Camao_Position");
        //include("Camao_Rating");
        //include("Camao_Ressources_Upload_swfupload");
        //include("Camao_Request_Filter");
        //include("Camao_ScrollableElement");
        //include("Camao_Table");
        //include("Camao_Tooltip");
        //include("Camao_Upload");
        //include("Camao_Validation");

        //CMS INCLUDES

        //include("Control_Api");
        //include("Control_Tree");
        //include("Control_Sitepannel");
        //include("Control_Elements");
        //include("Control_Containers");
        //include("Control_Modul_Abstract_Modul");
        //include("Control_Modul_ModulContainer");
        //include("Control_Modul_ModulElement");
        //include("Control_Modul_ModulLanguage");
        //include("Control_Modul_ModulMetadaten");
        //include("Control_Modul_ModulMode");
        //include("Control_Modul_ModulSettings");
        //include("Control_Modul_ModulSitemap");
        //include("Control_Modul_ModulWarnings");
        
        //we wait for the browser, if browser is idle, call the startup
        if(navigator.userAgent.indexOf('Konqueror/') != -1){
            Event.observe(window, "load", function(){ __boot.delay(1); });
        }else{
            document.observe("dom:loaded", function(){
                if (Prototype.Browser.WebKit){
                    include("Camao_Base").delay(1);
                }else{
                    include("Camao_Base");
                }
            });
        }
    }
}catch(e){
    alert(e);
}

function __boot(){
    Base = new Camao_Base();
    Base.__construct();

    if (typeof Camao_Google_AdsenseObj != "undefined") Camao_Google_AdsenseObj.domReadyEvent();

    if (typeof startUp == "function") startUp();

    document.fire("cms:loaded");
}

function include(filename) {
    var include_file = "/" + filename.replace(/_/g, "/");

    var script = new Element('script', { 'src' : ROOT_JS + include_file + ".js", 'type' : 'text/javascript' });
    script.observe("error", function(){  /*if (DEVELOPER) console.log(filename + " cant find file!");*/}.bind(this));
    $$("head")[0].appendChild(script);
}



//Stuff der vll irgendwann mal doch gebraucht wirdl

/*
    const GLOBAL_ALPHABET = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    const GLOBAL_NUMBERS = ["0","1","2","3","4","5","6","7","8","9"];
    const GLOBAL_OTHER_LETTERS = [" ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/",":",";","<","=",">","?","@","[","\\","]","^","_","`","{","|","}","~"];

    //Hidden Ajax Communication
    var iframe = new Element("iframe", { 'src' : '/robots.txt'});
    iframe.observe("load", function(){
        console.log(this.contentDocument.activeElement.childNodes[0].innerHTML);
    });
    $$("head")[0].appendChild(iframe);

*/


