// 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();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;
}/*****************************************************************************
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 self = this;

  var ClassNames  = {
    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'
  };
  
  ClassNames.IGNORE_CLASSES = [ClassNames.REPLACED, ClassNames.IGNORE, ClassNames.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 elementCount          = 0; // The number of replaced elements.
  var isInitialized         = false;

  function Errors() {
    this.fire = function(id) {
      if(this[id + 'Alert']) alert(this[id + 'Alert']);
      throw new Error(this[id]);
    };
  
    this.isFile      = 'sIFR: Did not activate because the page is being loaded from the filesystem.';
    this.isFileAlert = 'Hi!\n\nThanks for using sIFR on your page. Unfortunately sIFR couldn\'t activate, because it was loaded '
                        + 'directly from your computer.\nDue to Flash security restrictions, you need to load sIFR through a web'
                        + ' server.\n\nWe apologize for the inconvenience.';
  };
  
  function Util(sIFR) {
    function capitalize($) {
      return $.toLocaleUpperCase();
    }
    
    this.normalize = function(str) {
      // Replace linebreaks and &nbsp; by whitespace, then normalize.
      // Flash doesn't support no-breaking space characters, hence they're replaced by a normal space.
      return str.replace(/\n|\r|\xA0/g, Util.SINGLE_WHITESPACE).replace(/\s+/g, Util.SINGLE_WHITESPACE);
    };
    
    this.textTransform = function(type, str) {
      switch(type) {
        case 'uppercase':
          return str.toLocaleUpperCase();
        case 'lowercase':
          return str.toLocaleLowerCase();
        case 'capitalize':
          return str.replace(/^\w|\s\w/g, capitalize);
      }
      return str;
    };
    
    this.toHexString = function(str) {
      if(str.charAt(0) != '#' || str.length != 4 && str.length != 7) return str;
      
      str = str.substring(1);
      return '0x' + (str.length == 3 ? str.replace(/(.)(.)(.)/, '$1$1$2$2$3$3') : str);
    };
    
    this.toJson = function(obj, strFunc) {
      var json = '';
  
      switch(typeof(obj)) {
        case 'string':
          json = '"' + strFunc(obj) + '"';
          break;
        case 'number':
        case 'boolean':
          json = obj.toString();
          break;
        case 'object':
          json = [];
          for(var prop in obj) {
            if(obj[prop] == Object.prototype[prop]) continue;
            json.push('"' + prop + '":' + this.toJson(obj[prop]));
          }
          json = '{' + json.join(',') + '}';
          break;
      }
  
      return json;
    };
  
    this.convertCssArg = function(arg) {
      if(!arg) return {};
      if(typeof(arg) == 'object') {
        if(arg.constructor == Array) arg = arg.join('');
        else return arg;
      }
  
      var obj = {};
      var rules = arg.split('}');
  
      for(var i = 0; i < rules.length; i++) {
        var $ = rules[i].match(/([^\s{]+)\s*\{(.+)\s*;?\s*/);
        if(!$ || $.length != 3) continue;
  
        if(!obj[$[1]]) obj[$[1]] = {};
  
        var properties = $[2].split(';');
        for(var j = 0; j < properties.length; j++) {
          var $2 = properties[j].match(/\s*([^:\s]+)\s*\:\s*([^;]+)/);
          if(!$2 || $2.length != 3) continue;
          obj[$[1]][$2[1]] = $2[2].replace(/\s+$/, '');
        }
      }
  
      return obj;
    };
  
    this.extractFromCss = function(css, selector, property, remove) {
      var value = null;
  
      if(css && css[selector] && css[selector][property]) {
        value = css[selector][property];
        if(remove) delete css[selector][property];
      }
  
      return value;
    };
    
    this.cssToString = function(arg) {
      var css = [];
      for(var selector in arg) {
        var rule = arg[selector];
        if(rule == Object.prototype[selector]) continue;
  
        css.push(selector, '{');
        for(var property in rule) {
          if(rule[property] == Object.prototype[property]) continue;
          var value = rule[property];
          if(Util.UNIT_REMOVAL_PROPERTIES[property]) value = parseInt(value, 10);
          css.push(property, ':', value, ';');
        }
        css.push('}');
      }
  
      return css.join('');
    };
  
    this.escape = function(str) {
      return escape(str).replace(/\+/g, '%2B');
    };
    
    this.encodeVars = function(vars) {
      return vars.join('&').replace(/%/g, '%25');
    };
    
    this.copyProperties = function(from, to) {
      for(var property in from) {
        if(to[property] === undefined) to[property] = from[property];
      }
      return to;
    };
    
    this.domain = function() {
      var domain = '';
      // When trying to access document.domain on a Google-translated page with Firebug, I got an exception. 
      // Try/catch to be safe.
      try { domain = document.domain } catch(e) {};
      return domain;
    };
    
    this.domainMatches = function(domain, match) {
      if(match == '*' || match == domain) return true;
  
      var wildcard = match.lastIndexOf('*');
      if(wildcard > -1) {
        match = match.substr(wildcard + 1);
        var matchPosition = domain.lastIndexOf(match);
        if(matchPosition > -1 && (matchPosition + match.length) == domain.length) return true;
      }
      
      return false;
    };
    
    this.uriEncode = function(s) {
      return encodeURI(decodeURIComponent(s));  // Decode first, in case the URI was already encoded.
    };
    
    this.delay = function(ms, func, scope) {
      var args = Array.prototype.slice.call(arguments, 3);
      setTimeout(function() { func.apply(scope, args) }, ms);
    };
  };
  
  Util.UNIT_REMOVAL_PROPERTIES = {leading: true, 'margin-left': true, 'margin-right': true, 'text-indent': true};
  Util.SINGLE_WHITESPACE       = ' ';
  
  
  function DomUtil(sIFR) {
    var self = this;
    
    function getDimensionFromStyle(node, property, offsetProperty)
    {
      var dimension = self.getStyleAsInt(node, property, sIFR.ua.ie);
      if(dimension == 0) {
        dimension = node[offsetProperty];
        for(var i = 3; i < arguments.length; i++) dimension -= self.getStyleAsInt(node, arguments[i], true);
      }
      return dimension;
    }
    
    this.getBody = function() {
      return document.getElementsByTagName('body')[0] || null;
    };
    
    this.querySelectorAll = function(selector) {
      return window.parseSelector(selector);
    };
  
    this.addClass = function(name, node) {
      if(node) node.className = ((node.className || '') == '' ? '' : node.className + ' ') + name;
    };
    
    this.removeClass = function(name, node) {
      if(node) node.className = node.className.replace(new RegExp('(^|\\s)' + name + '(\\s|$)'), '').replace(/^\s+|(\s)\s+/g, '$1');
    };
  
    this.hasClass = function(name, node) {
      return new RegExp('(^|\\s)' + name + '(\\s|$)').test(node.className);
    };
    
    this.hasOneOfClassses = function(names, node) {
      for(var i = 0; i < names.length; i++) {
        if(this.hasClass(names[i], node)) return true;
      }
      return false;
    };
    
    this.ancestorHasClass = function(node, name) {
      node = node.parentNode;
      while(node && node.nodeType == 1) {
        if(this.hasClass(name, node)) return true;
        node = node.parentNode;
      }
      return false;
    };
  
    this.create = function(name, className) {
      var node = document.createElementNS ? document.createElementNS(DomUtil.XHTML_NS, name) : document.createElement(name);
      if(className) node.className = className;
      return node;
    };
    
    this.getComputedStyle = function(node, property) {
      var result;
      if(document.defaultView && document.defaultView.getComputedStyle) {
        var style = document.defaultView.getComputedStyle(node, null);
        result = style ? style[property] : null;
      } else {
        if(node.currentStyle) result = node.currentStyle[property];
      }
      return result || ''; // Ensuring a string.
    };
  
    this.getStyleAsInt = function(node, property, requirePx) {
      var value = this.getComputedStyle(node, property);
      if(requirePx && !/px$/.test(value)) return 0;
      return parseInt(value) || 0;
    };
    
    this.getWidthFromStyle = function(node) {
      return getDimensionFromStyle(node, 'width', 'offsetWidth', 'paddingRight', 'paddingLeft', 'borderRightWidth', 'borderLeftWidth');
    };
  
    this.getHeightFromStyle = function(node) {
      return getDimensionFromStyle(node, 'height', 'offsetHeight', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth');
    };
  
    this.getDimensions = function(node) {
      var width  = node.offsetWidth;
      var height = node.offsetHeight;
      
      if(width == 0 || height == 0) {
        for(var i = 0; i < node.childNodes.length; i++) {
          var child = node.childNodes[i];
          if(child.nodeType != 1) continue;
          width  = Math.max(width, child.offsetWidth);
          height = Math.max(height, child.offsetHeight);
        }
      }
      
      return {width: width, height: height};
    };
    
    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(element) {
      try {
        element.blur();
        return;
      } catch(e) {};
      
      // Move the focus to an input element, and then destroy it.
      var input = this.create('input');
      input.style.width  = '0px';
      input.style.height = '0px';
      element.parentNode.appendChild(input);
      input.focus();
      input.blur();
      input.parentNode.removeChild(input);
    };
  };
  
  DomUtil.XHTML_NS = 'http://www.w3.org/1999/xhtml';
  
  function UserAgentDetection(sIFR) {
    var ua              = navigator.userAgent.toLowerCase();
    var product         = (navigator.product || '').toLowerCase();
    var platform        = navigator.platform.toLowerCase();
  
    this.parseVersion = UserAgentDetection.parseVersion;
  
    this.macintosh        = /^mac/.test(platform);
    this.windows          = /^win/.test(platform);
    this.linux            = /^linux/.test(platform);
    this.quicktime        = false;
  
    this.opera            = /opera/.test(ua);
    this.konqueror        = /konqueror/.test(ua);
    this.ie               = false/*@cc_on || true @*/;
    this.ieSupported      = this.ie         && !/ppc|smartphone|iemobile|msie\s5\.5/.test(ua)/*@cc_on && @_jscript_version >= 5.5 @*/
    this.ieWin            = this.windows    && this.ie/*@cc_on && @_jscript_version >= 5.1 @*/;
    this.windows          = this.windows    && (!this.ie || this.ieWin);
    this.ieMac            = this.macintosh  && this.ie/*@cc_on && @_jscript_version < 5.1 @*/;
    this.macintosh        = this.macintosh  && (!this.ie || this.ieMac);
    this.safari           = /safari/.test(ua);
    this.webkit           = !this.konqueror && /applewebkit/.test(ua);
    this.khtml            = this.webkit     || this.konqueror;
    this.gecko            = !this.khtml     && product == 'gecko';
                          
    this.ieVersion        = this.ie         && /.*msie\s(\d\.\d)/.exec(ua)           ? this.parseVersion(RegExp.$1) : '0';
    this.operaVersion     = this.opera      && /.*opera(\s|\/)(\d+\.\d+)/.exec(ua)   ? this.parseVersion(RegExp.$2) : '0';
    this.webkitVersion    = this.webkit     && /.*applewebkit\/(\d+).*/.exec(ua)     ? this.parseVersion(RegExp.$1) : '0';
    this.geckoVersion     = this.gecko      && /.*rv:\s*([^\)]+)\)\s+gecko/.exec(ua) ? this.parseVersion(RegExp.$1) : '0';
    this.konquerorVersion = this.konqueror  && /.*konqueror\/([\d\.]+).*/.exec(ua)   ? this.parseVersion(RegExp.$1) : '0';
  
    this.flashVersion   = 0;
  
    if(this.ieWin) {
      var axo;
      var stop = false;
      try {
        axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7');
      } catch(e) {
        // In case the Flash 7 registry key does not exist, we need to test for specific 
        // Flash 6 installs before we can use the general key. 
        // See also <http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/>.
        // Many thanks to Geoff Stearns and Bobby van der Sluis for clarifying the problem and providing
        // examples of non-crashing code.
        try {
          axo                   = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
          this.flashVersion     = this.parseVersion('6');
          axo.AllowScriptAccess = 'always';
        } catch(e) { stop = this.flashVersion == this.parseVersion('6'); }
        
        if(!stop) try { axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); } catch(e) {}
      }
      
      if(!stop && axo) {
        this.flashVersion = this.parseVersion((axo.GetVariable('$version') || '').replace(/^\D+(\d+)\D+(\d+)\D+(\d+).*/g, '$1.$2.$3'));
      }
    } else if(navigator.plugins && navigator.plugins['Shockwave Flash']) {
      var d = navigator.plugins['Shockwave Flash'].description.replace(/^.*\s+(\S+\s+\S+$)/, '$1');
      var v = d.replace(/^\D*(\d+\.\d+).*$/, '$1');
      if(/r/.test(d)) v += d.replace(/^.*r(\d*).*$/, '.$1');
      else if(/d/.test(d)) v += '.0';
      this.flashVersion = this.parseVersion(v);
      
      // Watch out for QuickTime, which could be stealing the Flash handling! Also check to make sure the plugin for the Flash
      // MIMEType is enabled.
      var foundEnabled = false;
      for(var i = 0, valid = this.flashVersion >= UserAgentDetection.MIN_FLASH_VERSION; valid && i < navigator.mimeTypes.length; i++) {
        var mime = navigator.mimeTypes[i];
        if(mime.type != 'application/x-shockwave-flash') continue;
        if(mime.enabledPlugin) {
          foundEnabled = true;
          if(mime.enabledPlugin.description.toLowerCase().indexOf('quicktime') > -1) {
            valid = false;
            this.quicktime = true;
          }
        }
      }
      
      if(this.quicktime || !foundEnabled) this.flashVersion = this.parseVersion('0');
    }
    this.flash = this.flashVersion >= UserAgentDetection.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/* || this.konquerorVersion >= this.parseVersion('4.1')*/);
  };
  
  UserAgentDetection.parseVersion = function(s) {
    return s.replace(
      /(^|\D)(\d+)(?=\D|$)/g,
      function(s, nonDigit, digits) {
        s = nonDigit;
        for(var i = 4 - digits.length; i >= 0; i--) s += '0';
        return s + digits;
      }
    );
  };
  UserAgentDetection.MIN_FLASH_VERSION = UserAgentDetection.parseVersion('8');
  
  function FragmentIdentifier(sIFR) {
    this.fix = sIFR.ua.ieWin && window.location.hash != '';
  
    var cachedTitle;
    this.cache = function() {
      cachedTitle = document.title;
    };
  
    function doFix() {
      document.title = cachedTitle;
    }
  
    this.restore = function() {
      if(this.fix) setTimeout(doFix, 0);
    };
  };
  function PageLoad(sIFR) {
    var dummy = null;
    
    function pollLoad() {
      try {
        // IE hack courtesy of Diego Perini â€“ <http://javascript.nwbox.com/IEContentLoaded/>.
        // Merged polling taken from jQuery â€“ <http://dev.jquery.com/browser/trunk/jquery/src/event.js>
        if(sIFR.ua.ie || document.readyState != 'loaded' && document.readyState != 'complete') {
          document.documentElement.doScroll('left');
        }
      } catch(e) {
        return setTimeout(pollLoad, 10);
      }
      afterDomLoad();
    };
    
    function afterDomLoad() {
      if(sIFR.useStyleCheck) checkStyle();
      else if(!sIFR.ua.mustCheckStyle) fire(null, true);
    };
    
    function checkStyle() {
      dummy = sIFR.dom.create("div", ClassNames.DUMMY);
      sIFR.dom.getBody().appendChild(dummy);
      pollStyle();
    };
    
    function pollStyle() {
      if(sIFR.dom.getComputedStyle(dummy, 'marginLeft') == '42px') afterStyle();
      else setTimeout(pollStyle, 10);
    };
    
    function afterStyle() {
      if(dummy && dummy.parentNode) dummy.parentNode.removeChild(dummy);
      dummy = null;
      fire(null, true);
    };
    
    function fire(evt, preserveReplacements) {
      sIFR.initialize(preserveReplacements);
      
      // Remove handlers to prevent memory leak in Firefox 1.5, but only after onload.
      if(evt && evt.type == 'load') {
        if(document.removeEventListener) document.removeEventListener('DOMContentLoaded', fire, false);
        if(window.removeEventListener) window.removeEventListener('load', fire, false);
      }
    };
    
    // Unload detection based on the research from Moxiecode. <http://blog.moxiecode.com/2008/04/08/unload-event-never-fires-in-ie/>
    function verifyUnload() {
      sIFR.prepareClearReferences();
      if(document.readyState == 'interactive') {
        document.attachEvent('onstop', unloadByStop);
        setTimeout(function() { document.detachEvent('onstop', unloadByStop) }, 0);
      }
    };
    
    function unloadByStop() {
      document.detachEvent('onstop', unloadByStop);
      fireUnload();
    };
    
    function fireUnload() {
      sIFR.clearReferences();
    };
    
    this.attach = function() {
      if(window.addEventListener) window.addEventListener('load', fire, false);
      else window.attachEvent('onload', fire);
      
      if(!sIFR.useDomLoaded || sIFR.ua.forcePageLoad || sIFR.ua.ie && window.top != window) return;
      
      if(sIFR.ua.nativeDomLoaded) {
        document.addEventListener('DOMContentLoaded', afterDomLoad, false);
      } else if(sIFR.ua.ie || sIFR.ua.khtml) {
        pollLoad();
      } 
    };
    
    this.attachUnload = function() {
      if(!sIFR.ua.ie) return;
      window.attachEvent('onbeforeunload', verifyUnload);
      window.attachEvent('onunload', fireUnload);
    }
  };
  
  var PREFETCH_COOKIE = 'sifrFetch';
  
  function Prefetch(sIFR) {
    var hasPrefetched = false;
    
    this.fetchMovies = function(movies) {
      if(sIFR.setPrefetchCookie && new RegExp(';?' + PREFETCH_COOKIE + '=true;?').test(document.cookie)) return;
  
      try { // We don't know which DOM actions the user agent will allow
        hasPrefetched = true;
        prefetch(movies);
      } catch(e) {}
  
      if(sIFR.setPrefetchCookie) document.cookie = PREFETCH_COOKIE + '=true;path=' + sIFR.cookiePath;
    };
  
    this.clear = function() {
      if(!hasPrefetched) return;
  
      try {
        var nodes = document.getElementsByTagName('script');
        for(var i = nodes.length - 1; i >= 0; i--) {
          var node = nodes[i];
          if(node.type == 'sifr/prefetch') node.parentNode.removeChild(node);
        }
      } catch(e) {}
    };
    
    function prefetch(args) {
      for(var i = 0; i < args.length; i++) {
        document.write('<scr' + 'ipt defer type="sifr/prefetch" src="' + args[i].src + '"></' + 'script>');
      }
    }
  };
  
  function BrokenFlashIE(sIFR) {
    var active      = sIFR.ua.ie;
    var fixFlash    = active && sIFR.ua.flashVersion < sIFR.ua.parseVersion('9.0.115');
    var resetMovies = {};
    var registry    = {};
    
    this.fixFlash = fixFlash;
    
    this.register = function(flashNode) {
      if(!active) return;
      
      var id = flashNode.getAttribute('id');
      // Try cleaning up previous Flash <object>
      this.cleanup(id, false);
      
      registry[id] = flashNode;
      delete resetMovies[id];
      
      if(fixFlash) window[id] = flashNode;
    };
    
    this.reset = function() {
      if(!active) return false;
      
      for(var i = 0; i < sIFR.replacements.length; i++) {
        var flash = sIFR.replacements[i];
        var flashNode = registry[flash.id];
        if(!resetMovies[flash.id] && (!flashNode.parentNode || flashNode.parentNode.nodeType == 11)) {
          flash.resetMovie();
          resetMovies[flash.id] = true;
        }
      }
      
      return true;
    };
    
    this.cleanup = function(id, usePlaceholder) {
      var node = registry[id];
      if(!node) return;
      
      for(var expando in node) {
        if(typeof(node[expando]) == 'function') node[expando] = null;
      }
      registry[id] = null;
      if(fixFlash) window[id] = null;
      
      if(node.parentNode) {
        if(usePlaceholder && node.parentNode.nodeType == 1) {
          // Replace the Flash node by a placeholde element with the same dimensions. This stops the page from collapsing
          // when the Flash movies are removed.
          var placeholder          = document.createElement('div');
          placeholder.style.width  = node.offsetWidth  + 'px';
          placeholder.style.height = node.offsetHeight + 'px';
          node.parentNode.replaceChild(placeholder, node);
        } else {
          node.parentNode.removeChild(node);
        }
      }
    };
    
    this.prepareClearReferences = function() {
      if(!fixFlash) return;
      
      // Disable Flash cleanup, see <http://blog.deconcept.com/2006/05/18/flash-player-bug-streaming-content-innerhtml-ie/>
      // for more info.
      __flash_unloadHandler      = function(){};
      __flash_savedUnloadHandler = function(){};
    };
    
    this.clearReferences = function() {
      // Since we've disabled Flash' own cleanup, add all objects on the page to our registry so they can be cleaned up.
      if(fixFlash) {
        var objects = document.getElementsByTagName('object');
        for(var i = objects.length - 1; i >= 0; i--) registry[objects[i].getAttribute('id')] = objects[i];
      }
      
      for(var id in registry) {
        if(Object.prototype[id] != registry[id]) this.cleanup(id, true);
      }
    };
  }
  
  function FlashInteractor(sIFR, id, vars, forceWidth, events) {
    this.sIFR         = sIFR;
    this.id           = id;
    this.vars         = vars;
    // Type of value depends on SWF builder. This could use some improvement!
    this.movie        = null;
    
    this.__forceWidth = forceWidth;
    this.__events     = events;
    this.__resizing   = 0;
  }
  
  FlashInteractor.prototype = {
    getFlashElement: function() {
      return document.getElementById(this.id);
    },
    
    getAlternate: function() {
      return document.getElementById(this.id + '_alternate');
    },
    
    getAncestor: function() {
      var ancestor = this.getFlashElement().parentNode;
      return !this.sIFR.dom.hasClass(ClassNames.FIX_FOCUS, ancestor) ? ancestor : ancestor.parentNode;
    },
    
    available: function() {
      var flashNode = this.getFlashElement();
      return flashNode && flashNode.parentNode;
    },
    
    call: function(type) {
      var flashNode = this.getFlashElement();
      
      if (!flashNode[type]) {
        return false;
      }
      // In Firefox 2, exposed Flash methods aren't proper functions, there's no `apply()` method! This workaround
      // does work, though.
      return Function.prototype.apply.call(flashNode[type], flashNode, Array.prototype.slice.call(arguments, 1));
    },
    
    attempt: function() {
      if(!this.available()) return false;
      
      try {
        this.call.apply(this, arguments);
      } catch(e) {
        if(this.sIFR.debug) throw e;
        return false;
      }
      
      return true;
    },
    
    updateVars: function(name, value) {
      for(var i = 0; i < this.vars.length; i++) {
        if (this.vars[i].split('=')[0] == name) {
          this.vars[i] = name + '=' + value;
          break;
        }
      }
      
      var vars = this.sIFR.util.encodeVars(this.vars);
      this.movie.injectVars(this.getFlashElement(), vars);
      this.movie.injectVars(this.movie.html, vars);
    },
    
    storeSize: function(type, value) {
      this.movie.setSize(type, value);
      this.updateVars(type, value);
    },
    
    fireEvent: function(name) {
      if(this.available() && this.__events[name]) this.sIFR.util.delay(0, this.__events[name], this, this);
    },
    
    resizeFlashElement: function(height, width, firstResize) {
      if(!this.available()) return;
      
      this.__resizing++;
      
      var flashNode = this.getFlashElement();
      flashNode.setAttribute('height', height);
      
      // Reset element height as declared by `MovieCreator`
      this.getAncestor().style.minHeight = '';
      
      this.updateVars('renderheight', height);
      this.storeSize('height', height);
      if(width !== null) {
        flashNode.setAttribute('width', width);
        // Don't store the size, it may cause Flash to wrap the text when the movie is reset.
        this.movie.setSize('width', width);
      }
      if(this.__events.onReplacement) {
        this.sIFR.util.delay(0, this.__events.onReplacement, this, this);
        delete this.__events.onReplacement;
      }
      
      if(firstResize) {
        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 flashNode      = this.getFlashElement();
      var currentWidth   = flashNode.offsetWidth;
      
      // The Flash movie has no dimensions, which means it's not visible anyway. No need to recalculate.
      if(currentWidth == 0) return;
      
      var originalWidth  = flashNode.getAttribute('width');
      var originalHeight = flashNode.getAttribute('height');
      
      var ancestor       = this.getAncestor();
      var minHeight      = this.sIFR.dom.getHeightFromStyle(ancestor);
      
      // Remove Flash movie from flow
      flashNode.style.width  = '1px';
      flashNode.style.height = '1px';
      
      // Set a minimal height on the flashNode's parent, to stop a reflow
      ancestor.style.minHeight = minHeight + 'px';
      
      // Restore original content
      var nodes = this.getAlternate().childNodes;
      var clones = [];
      for(var i = 0; i < nodes.length; i++) {
        var node = nodes[i].cloneNode(true);
        clones.push(node);
        ancestor.appendChild(node);
      }
      
      // Calculate width
      var width = this.sIFR.dom.getWidthFromStyle(ancestor);
      
      // Remove original content again
      for(var i = 0; i < clones.length; i++) ancestor.removeChild(clones[i]);
      
      // Reset Flash movie flow
      flashNode.style.width = flashNode.style.height = ancestor.style.minHeight = '';
      flashNode.setAttribute('width', this.__forceWidth ? width : originalWidth);
      flashNode.setAttribute('height', originalHeight);
      
      // IE can get mightily confused about where to draw the Flash <object>. This is a workaround to force IE to repaint
      // the <object>.
      if(sIFR.ua.ie) {
        flashNode.style.display = 'none';
        var repaint = flashNode.offsetHeight;
        flashNode.style.display = '';
      }
      
      // Resize!
      if(width != currentWidth) {
        if(this.__forceWidth) this.storeSize('width', width);
        this.attempt('resize', width);
      }
      
      this.__resizing--;
    },
    
    // `content` must not be util.escaped when passed in.
    // alternate may be an array of nodes to be appended to the alternate content, use this
    // in XHTML documents.
    replaceText: function(content, alternate) {
      var escapedContent = this.sIFR.util.escape(content);
      if(!this.attempt('replaceText', escapedContent)) return false;
      
      this.updateVars('content', escapedContent);
      var node = this.getAlternate();
      if(alternate) {
        while(node.firstChild) node.removeChild(node.firstChild);
        for(var i = 0; i < alternate.length; i++) node.appendChild(alternate[i]);
      } else {
        try { node.innerHTML = content; } catch(e) {};
      }
      
      return true;
    },
    
    changeCSS: function(css) {
      css = this.sIFR.util.escape(this.sIFR.util.cssToString(this.sIFR.util.convertCssArg(css)));
      this.updateVars('css', css);
      return this.attempt('changeCSS', css);
    },
    
    remove: function() {
      if(this.movie && this.available()) this.movie.remove(this.getFlashElement(), this.id);
    }
  };
  
  var MovieCreator = new function() {
    this.create = function(sIFR, brokenFlash, node, fixFocus, id, src, width, height, vars, wmode, backgroundColor) {
      var klass = sIFR.ua.ie ? IEFlashMovie : FlashMovie;
      return new klass(
        sIFR, brokenFlash, node, fixFocus, 
        id, src, width, height, 
        ['flashvars', vars, 'wmode', wmode, 'bgcolor', backgroundColor, 'allowScriptAccess', 'always', 'quality', 'best']
      );
    }
    
    function FlashMovie(sIFR, brokenFlash, node, fixFocus, id, src, width, height, params) {
      var object = sIFR.dom.create('object', ClassNames.FLASH);
      var attrs  = ['type', 'application/x-shockwave-flash', 'id', id, 'name', id, 'data', src, 'width', width, 'height', height];
      for(var i = 0; i < attrs.length; i += 2) object.setAttribute(attrs[i], attrs[i + 1]);
      
      var insertion = object;
      if(fixFocus) {
        insertion = dom.create("div", ClassNames.FIX_FOCUS);
        insertion.appendChild(object);
      }
      
      for(var i = 0; i < params.length; i+=2) {
        if(params[i] == 'name') continue;
        
        var param = dom.create('param');
        param.setAttribute('name', params[i]);
        param.setAttribute('value', params[i + 1]);
        object.appendChild(param);
      }
      
      // Before removing the existing content, set its height such that the element
      // does not collapse. Height is restored in `FlashInteractor#resizeFlashElement`.
      node.style.minHeight = height + 'px';
      
      while(node.firstChild) node.removeChild(node.firstChild);
      node.appendChild(insertion);
      
      this.html = insertion.cloneNode(true);
    }
    
    FlashMovie.prototype = {
      reset: function(flashNode, alternate) {
        flashNode.parentNode.replaceChild(this.html.cloneNode(true), flashNode);
      },
      
      remove: function(flashNode, id) {
        flashNode.parentNode.removeChild(flashNode);
      },
      
      setSize: function(type, value) {
        this.html.setAttribute(type, value);
      },
      
      injectVars: function(flash, encodedVars) {
        var params = flash.getElementsByTagName('param');
        for(var i = 0; i < params.length; i++) {
          if(params[i].getAttribute('name') == 'flashvars') {
            params[i].setAttribute('value', encodedVars);
            break;
          }
        }
      }
    };
    
    function IEFlashMovie(sIFR, brokenFlash, node, fixFocus, id, src, width, height, params) {
      this.dom    = sIFR.dom;
      this.broken = brokenFlash;
      
      this.html = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="' + id +
        '" width="' + width + '" height="' + height + '" class="' + ClassNames.FLASH + '">' +
        '<param name="movie" value="' + src + '"></param></object>'
        ;
      var paramsHtml = '';
      for(var i = 0; i < params.length; i+=2) {
        paramsHtml += '<param name="' + params[i] + '" value="' + params[i + 1] + '"></param>';
      }
      this.html      = this.html.replace(/(<\/object>)/, paramsHtml + '$1');
      
      // Before removing the existing content, set its height such that the element
      // does not collapse. Height is restored in `FlashInteractor#resizeFlashElement`.
      node.style.minHeight = height + 'px';
      
      node.innerHTML = this.html;
      
      this.broken.register(node.firstChild);
    }
    
    IEFlashMovie.prototype = {
      reset: function(flashNode, alternate) {
        alternate            = alternate.cloneNode(true);
        var parent           = flashNode.parentNode;
        parent.innerHTML     = this.html;
        this.broken.register(parent.firstChild);
        parent.appendChild(alternate);
      },
      
      remove: function(flashNode, id) {
        this.broken.cleanup(id);
      },
      
      setSize: function(type, value) {
         this.html = this.html.replace(type == 'height' ? /(height)="\d+"/ : /(width)="\d+"/, '$1="' + value + '"');
      },
      
      injectVars: function(flash, encodedVars) {
        if(flash != this.html) return;
        this.html = this.html.replace(/(flashvars(=|\"\svalue=)\")[^\"]+/, '$1' + encodedVars);
      }
    };
  }
  
  this.errors =             new Errors(self);
  var util    = this.util = new Util(self);
  var dom     = this.dom  = new DomUtil(self);
  var ua      = this.ua   = new UserAgentDetection(self);
  var hacks   = {
    fragmentIdentifier:     new FragmentIdentifier(self),
    pageLoad:               new PageLoad(self),
    prefetch:               new Prefetch(self),
    brokenFlashIE:          new BrokenFlashIE(self)
  };
  this.__resetBrokenMovies = hacks.brokenFlashIE.reset;
  
  var replaceKwargsStore = {
    kwargs: [],
    replaceAll:  function(preserve) {
      for(var i = 0; i < this.kwargs.length; i++) self.replace(this.kwargs[i]);
      if(!preserve) this.kwargs = [];
    }
  };
  
  this.activate = function(/* â€¦ */) {
    if(!ua.supported || !this.isEnabled || this.isActive || !isValidDomain() || isFile()) return;
    hacks.prefetch.fetchMovies(arguments);
    
    this.isActive = true;
    this.setFlashClass();
    hacks.fragmentIdentifier.cache();
    hacks.pageLoad.attachUnload();
    
    if(!this.autoInitialize) return;
    
    hacks.pageLoad.attach();
  };
  
  this.setFlashClass = function() {
    if(this.hasFlashClassSet) return;
    
    dom.addClass(ClassNames.ACTIVE, dom.getBody() || document.documentElement);
    this.hasFlashClassSet = true;
  };
  
  this.removeFlashClass = function() {
    if(!this.hasFlashClassSet) return;
    
    dom.removeClass(ClassNames.ACTIVE, dom.getBody());
    dom.removeClass(ClassNames.ACTIVE, document.documentElement);
    this.hasFlashClassSet = false;
  };
  
  this.initialize = function(preserveReplacements) {
    if(!this.isActive || !this.isEnabled) return;
    if(isInitialized) {
      if(!preserveReplacements) replaceKwargsStore.replaceAll(false);
      return;
    }
    
    isInitialized = true;
    replaceKwargsStore.replaceAll(preserveReplacements);
    
    if(self.repaintOnResize) {
      if(window.addEventListener) window.addEventListener('resize', resize, false);
      else window.attachEvent('onresize', resize);
    }
    
    hacks.prefetch.clear();
  };
  
  this.replace = function(kwargs, mergeKwargs) {
    if(!ua.supported) return;
    
    // This lets you specify two kwarg objects so you don't have to repeat common settings.
    // The first object will be merged with the second, while properties in the second 
    // object have priority over those in the first. The first object is unmodified
    // for further use, the resulting second object will be used in the replacement.
    if(mergeKwargs) kwargs = util.copyProperties(kwargs, mergeKwargs);
    
    if(!isInitialized) return replaceKwargsStore.kwargs.push(kwargs);
    
    if(this.onReplacementStart) this.onReplacementStart(kwargs);
    
    var nodes = kwargs.elements || dom.querySelectorAll(kwargs.selector);
    if(nodes.length == 0) return;
    
    var src             = getSource(kwargs.src);
    var css             = util.convertCssArg(kwargs.css);
    var filters         = getFilters(kwargs.filters);
    
    var forceSingleLine = kwargs.forceSingleLine === true;
    var preventWrap     = kwargs.preventWrap === true && !forceSingleLine;
    var fitExactly      = forceSingleLine || (kwargs.fitExactly == null ? this.fitExactly : kwargs.fitExactly) === true;
    var forceWidth      = fitExactly || (kwargs.forceWidth == null ? this.forceWidth : kwargs.forceWidth) === true;
    var ratios          = kwargs.ratios || [];
    var pixelFont       = kwargs.pixelFont === true;
    var tuneHeight      = parseInt(kwargs.tuneHeight) || 0;
    var events          = !!kwargs.onRelease || !!kwargs.onRollOver || !!kwargs.onRollOut;
    
    // Alignment should be handled by the browser in this case.
    if(fitExactly) util.extractFromCss(css, '.sIFR-root', 'text-align', true);
    
    var fontSize        = util.extractFromCss(css, '.sIFR-root', 'font-size', true)        || '0';
    var backgroundColor = util.extractFromCss(css, '.sIFR-root', 'background-color', true) || '#FFFFFF';
    var kerning         = util.extractFromCss(css, '.sIFR-root', 'kerning', true)          || '';
    var opacity         = util.extractFromCss(css, '.sIFR-root', 'opacity', true)          || '100';
    var cursor          = util.extractFromCss(css, '.sIFR-root', 'cursor', true)           || 'default';
    var leading         = parseInt(util.extractFromCss(css, '.sIFR-root', 'leading'))      || 0;
    var gridFitType     = kwargs.gridFitType ||
                            (util.extractFromCss(css, '.sIFR-root', 'text-align') == 'right') ? 'subpixel' : 'pixel';
    var textTransform   = this.forceTextTransform === false 
                            ? 'none'
                            : util.extractFromCss(css, '.sIFR-root', 'text-transform', true) || 'none';
    // Only font sizes specified in pixels are supported.
    fontSize            = /^\d+(px)?$/.test(fontSize) ? parseInt(fontSize) : 0;
    // Make sure to support percentages and decimals
    opacity             = parseFloat(opacity) < 1 ? 100 * parseFloat(opacity) : opacity;
    
    var cssText = kwargs.modifyCss ? '' : util.cssToString(css);
    var wmode   = kwargs.wmode || '';
    if(!wmode) {
      if(kwargs.transparent) wmode = 'transparent';
      else if(kwargs.opaque) wmode = 'opaque';
    } 
    if(wmode == 'transparent') {
      if(!ua.transparencySupport) wmode = 'opaque';
      else backgroundColor = 'transparent';
    } else if(backgroundColor == 'transparent') {
      backgroundColor = '#FFFFFF';
    }
    
    for(var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      
      if(dom.hasOneOfClassses(ClassNames.IGNORE_CLASSES, node) || dom.ancestorHasClass(node, ClassNames.ALTERNATE)) continue;
      
      // Opera does not allow communication with hidden Flash movies. Visibility is tackled by sIFR itself, but
      // `display:none` isn't. Additionally, WebKit does not return computed style information for elements with
      // `display:none`. We'll prevent elements which have `display:none` or are contained in such an element from
      // being replaced. It's a bit hard to detect this, but we'll check for the dimensions of the element and its
      // `display` property.
      
      var dimensions = dom.getDimensions(node);
      var height     = dimensions.height;
      var width      = dimensions.width;
      var display    = dom.getComputedStyle(node, 'display');
      
      if(!height || !width || !display || display == 'none') continue;
      
      // Get the width (to approximate the final size).
      width = dom.getWidthFromStyle(node);
      
      var size, lines;
      if(!fontSize) {
        var calculation    = calculate(node);
        size               = Math.min(this.MAX_FONT_SIZE, Math.max(this.MIN_FONT_SIZE, calculation.fontSize));
        if(pixelFont) size = Math.max(8, 8 * Math.round(size / 8));
        
        lines = calculation.lines;
      } else {
        size  = fontSize;
        lines = 1;
      }
      
      var alternate = dom.create('span', ClassNames.ALTERNATE);
      // Clone the original content to the alternate element.
      var contentNode = node.cloneNode(true);
      // Temporarily append the contentNode to the document, to get around IE problems with resolved hrefs
      node.parentNode.appendChild(contentNode);
      for(var j = 0, l = contentNode.childNodes.length; j < l; j++) {
        var child = contentNode.childNodes[j];
        // Let's not keep <style> or <script> in the alternate content, since it may be
        // reintroduced to the DOM after resizing.
        if (!/^(style|script)$/i.test(child.nodeName)) {
          alternate.appendChild(child.cloneNode(true));
        }
      }
      
      // Allow the sIFR content to be modified
      if(kwargs.modifyContent) kwargs.modifyContent(contentNode, kwargs.selector);
      if(kwargs.modifyCss) cssText = kwargs.modifyCss(css, contentNode, kwargs.selector);
      
      var content = parseContent(contentNode, textTransform, kwargs.uriEncode);
      // Remove the contentNode again
      contentNode.parentNode.removeChild(contentNode);
      if(kwargs.modifyContentString) content.text = kwargs.modifyContentString(content.text, kwargs.selector);
      if(content.text == '') continue;
      
      // Approximate the final height to avoid annoying movements of the page
      var renderHeight = Math.round(lines * getRatio(size, ratios) * size) + this.FLASH_PADDING_BOTTOM + tuneHeight;
      if (lines > 1 && leading) {
        renderHeight += Math.round((lines - 1) * leading);
      }
      
      var forcedWidth = forceWidth ? width : '100%';
      
      var id   = 'sIFR_replacement_' + elementCount++;
      var vars = ['id='              + id,
                  'content='         + util.escape(content.text),
                  'width='           + width,
                  'renderheight='    + renderHeight, 
                  'link='            + util.escape(content.primaryLink.href   || ''),
                  'target='          + util.escape(content.primaryLink.target || ''),
                  'size='            + size, 
                  'css='             + util.escape(cssText),
                  'cursor='          + cursor,
                  'tunewidth='       + (kwargs.tuneWidth || 0),
                  'tuneheight='      + tuneHeight,
                  'offsetleft='      + (kwargs.offsetLeft || ''),
                  'offsettop='       + (kwargs.offsetTop  || ''),
                  'fitexactly='      + fitExactly,
                  'preventwrap='     + preventWrap,
                  'forcesingleline=' + forceSingleLine,
                  'antialiastype='   + (kwargs.antiAliasType || ''),
                  'thickness='       + (kwargs.thickness || ''),
                  'sharpness='       + (kwargs.sharpness || ''),
                  'kerning='         + kerning,
                  'gridfittype='     + gridFitType,
                  'flashfilters='    + filters,
                  'opacity='         + opacity,
                  'blendmode='       + (kwargs.blendMode || ''), 
                  'selectable='      + (kwargs.selectable == null || wmode != '' && !sIFR.ua.macintosh && sIFR.ua.gecko && sIFR.ua.geckoVersion >= sIFR.ua.parseVersion('1.9')
                                         ? 'true' 
                                         : kwargs.selectable === true
                                       ),
                  'fixhover='        + (this.fixHover === true),
                  'events='          + events,
                  'delayrun='        + hacks.brokenFlashIE.fixFlash,
                  'version='         + this.VERSION];
      
      var encodedVars = util.encodeVars(vars);
      var interactor  = new FlashInteractor(self, id, vars, forceWidth, {
        onReplacement: kwargs.onReplacement,
        onRollOver: kwargs.onRollOver,
        onRollOut: kwargs.onRollOut,
        onRelease: kwargs.onRelease
      });
      interactor.movie = MovieCreator.create(
        sIFR, hacks.brokenFlashIE, node, ua.fixFocus && kwargs.fixFocus, 
        id, src, forcedWidth, renderHeight, 
        encodedVars, wmode, backgroundColor
      );
      this.replacements.push(interactor);
      this.replacements[id] = interactor;
      if(kwargs.selector) {
        if(!this.replacements[kwargs.selector]) this.replacements[kwargs.selector] = [interactor];
        else this.replacements[kwargs.selector].push(interactor);
      }
      alternate.setAttribute('id', id + '_alternate');
      node.appendChild(alternate);
      dom.addClass(ClassNames.REPLACED, node);
    }
    
    hacks.fragmentIdentifier.restore();
  };
  
  this.getReplacementByFlashElement = function(node) {
    for(var i = 0; i < self.replacements.length; i++) {
      if(self.replacements[i].id == node.getAttribute('id')) return self.replacements[i];
    }
  };
  
  this.redraw = function() {
    for(var i = 0; i < self.replacements.length; i++) self.replacements[i].resetMovie();
  };
  
  this.prepareClearReferences = function() {
    hacks.brokenFlashIE.prepareClearReferences();
  };
  
  this.clearReferences = function() {
    hacks.brokenFlashIE.clearReferences();
    hacks = null;
    replaceKwargsStore = null;
    delete self.replacements;
  };
  
  // The goal here is not to prevent usage of the Flash movie, but running sIFR on possibly translated pages
  function isValidDomain() {
    if(self.domains.length == 0) return true;
    
    var domain = util.domain();
    for(var i = 0; i < self.domains.length; i++) {
      if(util.domainMatches(domain, self.domains[i])) {
        return true;
      }
    }
    
    return false;
  }
  
  function isFile() {
    if(document.location.protocol == 'file:') {
      if(self.debug) self.errors.fire('isFile');
      return true;
    }
    return false;
  }
  
  function getSource(src) {
    if(ua.ie && src.charAt(0) == '/') {
      src = window.location.toString().replace(/([^:]+)(:\/?\/?)([^\/]+).*/, '$1$2$3') + src;
    }
    
    return src;
  }
  
  // Gives a font-size to required vertical space ratio
  function getRatio(size, ratios) {
    for(var i = 0; i < ratios.length; i += 2) {
      if(size <= ratios[i]) return ratios[i + 1];
    }
    return ratios[ratios.length - 1] || 1;
  }
  
  function getFilters(obj) {
    var filters = [];
    for(var filter in obj) {
      if(obj[filter] == Object.prototype[filter]) continue;
      
      var properties = obj[filter];
      filter = [filter.replace(/filter/i, '') + 'Filter'];
      
      for(var property in properties) {
        if(properties[property] == Object.prototype[property]) continue;
        // Double-escaping (see end of function) makes it easier to parse the resulting string
        // in AS.
        filter.push(property + ':' + util.escape(util.toJson(properties[property], util.toHexString)));
      }
      
      filters.push(filter.join(','));
    }
    
    return util.escape(filters.join(';'));
  }
  
  function resize(evt) {
    var current  = resize.viewport;
    var viewport = dom.getViewport();
    
    if(current && viewport.width == current.width && viewport.height == current.height) return;
    resize.viewport = viewport;
    
    if(self.replacements.length == 0) return; // Nothing replaced yet, resize event is not important.
    
    if(resize.timer) clearTimeout(resize.timer);
    resize.timer = setTimeout(function() {
      delete resize.timer;
      for(var i = 0; i < self.replacements.length; i++) self.replacements[i].resize();
    }, 200);
  }
  
  function calculate(node) {
    var fontSize = dom.getComputedStyle(node, 'fontSize');
    var deduce = fontSize.indexOf('px') == -1;
    
    var html = node.innerHTML;
    if (deduce) {
      node.innerHTML = 'X';
    }
    
    // Reset padding and border, so offsetHeight works properly
    node.style.paddingTop = node.style.paddingBottom = node.style.borderTopWidth = node.style.borderBottomWidth = '0px';
    // 2em magically makes offsetHeight correct in IE
    node.style.lineHeight = '2em';
    // Provided display is block
    node.style.display = 'block';
    
    fontSize = deduce ? node.offsetHeight / 2 : parseInt(fontSize, 10);
    
    if (deduce) {
      node.innerHTML = html;
    }
    
    var lines = Math.round(node.offsetHeight / (2 * fontSize));
    
    node.style.paddingTop = node.style.paddingBottom = node.style.borderTopWidth = node.style.borderBottomWidth
                          = node.style.lineHeight = node.style.display = '';
    
    if (isNaN(lines) || !isFinite(lines) || lines == 0) {
      lines = 1;
    }
    
    return {fontSize: fontSize, lines: lines};
  }
  
  function parseContent(source, textTransform, uriEncode) {
    uriEncode = uriEncode || util.uriEncode;
    var stack = [], content = [];
    var primaryLink = null;
    var nodes = source.childNodes;
    var whiteSpaceEnd = false, firstText = false;
    
    var i = 0;
    while(i < nodes.length) {
      var node = nodes[i];
      
      if(node.nodeType == 3) {
        var text = util.textTransform(textTransform, util.normalize(node.nodeValue)).replace(/</g, '&lt;');
        if(whiteSpaceEnd && firstText) text = text.replace(/^\s+/, '');
        content.push(text);
        whiteSpaceEnd = /\s$/.test(text);
        firstText = false;
      }
      
      if(node.nodeType == 1 && !/^(style|script)$/i.test(node.nodeName)) {
        var attributes = [];
        var nodeName   = node.nodeName.toLowerCase();
        var className  = node.className || '';
        // If there are multiple classes, look for the specified sIFR class
        if(/\s+/.test(className)) {
          if(className.indexOf(ClassNames.CLASS) > -1) className = className.match('(\\s|^)' + ClassNames.CLASS + '-([^\\s$]*)(\\s|$)')[2];
          // or use the first class. This is because Flash does not support the use of multiple class names.
          // Flash doesn't support IDs either.
          else className = className.match(/^([^\s]+)/)[1];
        }
        if(className != '') attributes.push('class="' + className + '"');
        
        if(nodeName == 'a') {
          var href   = uriEncode(node.getAttribute('href') || '');
          var target = node.getAttribute('target') || '';
          attributes.push('href="' + href + '"', 'target="' + target + '"');
          
          if(!primaryLink) {
            primaryLink = {
              href: href,
              target: target
            };
          }
        }
        
        content.push('<' + nodeName + (attributes.length > 0 ? ' ' : '') + attributes.join(' ') + '>');
        firstText = true;
        
        if(node.hasChildNodes()) {
          // Push the current index to the stack and prepare to iterate
          // over the childNodes.
          stack.push(i);
          i = 0;
          nodes = node.childNodes;
          continue;
        } else if(!/^(br|img)$/i.test(node.nodeName)) content.push('</', node.nodeName.toLowerCase(), '>');
      }
      
      if(stack.length > 0 && !node.nextSibling) {
        // Iterating the childNodes has been completed. Go back to the position
        // before we started the iteration. If that position was the last child,
        // go back even further.
        do {
          i = stack.pop();
          nodes = node.parentNode.parentNode.childNodes;
          node = nodes[i];
          if(node) content.push('</', node.nodeName.toLowerCase(), '>');
        } while(i == nodes.length - 1 && stack.length > 0);
      }
      
      i++;
    }
    
    return {text: content.join('').replace(/^\s+|\s+$|\s*(<br>)\s*/g, '$1'), primaryLink: primaryLink || {}};
  }
};

/*=:project
    parseSelector 2.0.2
    
  =:description
    Provides an extensible way of parsing CSS selectors against a DOM in 
    JavaScript.

  =:file
    Copyright: 2006-2008 Mark Wubben.
    Author: Mark Wubben, <http://novemberborn.net/>
       
  =:license
    This software is licensed and provided under the CC-GNU LGPL. 
    See <http://creativecommons.org/licenses/LGPL/2.1/>
    
  =:support
    parseSelector supports the following user agents:
      * Internet Explorer 6 and above
      * Firefox 1.0 and above, and equivalent Gecko engine versions
      * Safari 2.0 and above
      * Opera 8.0 and above
      * Konqueror 3.5.5 and above
    It might work in other browsers and versions, but there are no guarantees. There is
    no verification made when parseSelector is run to ascertain the browser is supported.
  
  =:notes
    The parsing of CSS selectors as streams has been based on Dean Edwards
    excellent work with cssQuery. See <http://dean.edwards.name/my/cssQuery/>
    for more info.
*/

var parseSelector = (function() {
  var SEPERATOR       = /\s*,\s*/;
  var WHITESPACE      = /\s*([\s>+~(),]|^|$)\s*/g;
  var IMPLIED_ALL     = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
  var STANDARD_SELECT = /(^|\))[^\s>+~]/g;
  var INSERT_SPACE    = /(\)|^)/;
  var STREAM          = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
  
  function parseSelector(selector, node) {
    node = node || document.documentElement;
    var argSelectors = selector.split(SEPERATOR), result = [];
    
    for(var i = 0; i < argSelectors.length; i++) {
      var nodes = [node], stream = toStream(argSelectors[i]);
      for(var j = 0; j < stream.length;) {
        var token = stream[j++], filter = stream[j++], args = '';
        if(stream[j] == '(') {
          while(stream[j++] != ')' && j < stream.length) args += stream[j];
          args = args.slice(0, -1);
        }
        nodes = select(nodes, token, filter, args);
      }
      result = result.concat(nodes);
    }
    
    return result;
  }

  function toStream(selector) {
    var stream = selector.replace(WHITESPACE, '$1').replace(IMPLIED_ALL, '$1*$2').replace(STANDARD_SELECT, insertSpaces);
    return stream.match(STREAM) || [];
  }
  
  function insertSpaces(str) {
    return str.replace(INSERT_SPACE, '$1 ');
  }
  
  function select(nodes, token, filter, args) {
    return (parseSelector.selectors[token]) ? parseSelector.selectors[token](nodes, filter, args) : [];
  }
  
  var util = {
    toArray: function(enumerable) {
      var a = [];
      for(var i = 0; i < enumerable.length; i++) a.push(enumerable[i]);
      return a;
    }
  };
  
  var dom = {
    isTag: function(node, tag) {
      return (tag == '*') || (tag.toLowerCase() == node.nodeName.toLowerCase());
    },
  
    previousSiblingElement: function(node) {
      do node = node.previousSibling; while(node && node.nodeType != 1);
      return node;
    },
  
    nextSiblingElement: function(node) {
      do node = node.nextSibling; while(node && node.nodeType != 1);
      return node;
    },
  
    hasClass: function(name, node) {
      return (node.className || '').match('(^|\\s)'+name+'(\\s|$)');
    },
  
    getByTag: function(tag, node) {
      return node.getElementsByTagName(tag);
    }
  };

  var selectors = {
    '#': function(nodes, filter) {
      for(var i = 0; i < nodes.length; i++) {
        if(nodes[i].getAttribute('id') == filter) return [nodes[i]];
      }
      return [];
    },

    ' ': function(nodes, filter) {
      var result = [];
      for(var i = 0; i < nodes.length; i++) {
        result = result.concat(util.toArray(dom.getByTag(filter, nodes[i])));
      }
      return result;
    },
    
    '>': function(nodes, filter) {
      var result = [];
      for(var i = 0, node; i < nodes.length; i++) {
        node = nodes[i];
        for(var j = 0, child; j < node.childNodes.length; j++) {
          child = node.childNodes[j];
          if(child.nodeType == 1 && dom.isTag(child, filter)) result.push(child);
        }
      }
      return result;
    },

    '.': function(nodes, filter) {
      var result = [];
      for(var i = 0, node; i < nodes.length; i++) {
        node = nodes[i];
        if(dom.hasClass([filter], node)) result.push(node);
      }
      return result;
    }, 
        
    ':': function(nodes, filter, args) {
      return (parseSelector.pseudoClasses[filter]) ? parseSelector.pseudoClasses[filter](nodes, args) : [];
    }
    
  };

  parseSelector.selectors     = selectors;
  parseSelector.pseudoClasses = {};
  parseSelector.util          = util;
  parseSelector.dom           = dom;

  return parseSelector;
})();
var fontOfficinaSansStd = {
    src: '/media/swf/officinasansstd-book.swf'
};

// styling for pdf downloads
sIFR.replace(fontOfficinaSansStd, {
    selector: '.layoutColumns .elementUploadPdf h1'
    ,wmode: 'transparent'
    ,css: [
        '.sIFR-root { color: #8395A3; background: #DFE6EC; font-size: 17px; text-transform: uppercase; }'
      ]
   ,offsetTop: 4
   ,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;
        }
    }
});

// explicit styling for news items!
sIFR.replace(fontOfficinaSansStd, {
    selector: '.layoutColumns .uc'
    ,wmode: 'transparent'

    ,css: {
        '.sIFR-root': { 'color': '#383838', 'font-size': '36px', 'text-transform':'uppercase' },
        '.accentfix': { 'font-size' :'1px', 'display' : 'block', 'leading' : 5 }
      }
    ,modifyContentString: function(content) { 
        if(content.match(/[Ã„Ã–Ãœf]/)) // big letters fix -- 2DO: only when first line contains these letters
            return '<p class="accentfix"> </p>'+content;
        return content;
    }
    ,onReplacement: function(item){
        document.getElementById(item.id).parentNode.className="accentfix";
        if(Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 7) {
            document.getElementById(item.id).parentNode.style.zoom=1;
        }
    }
});
sIFR.replace(fontOfficinaSansStd, {
    selector: '.layoutColumns h1'
    ,css: {
        '.sIFR-root': { 'color': '#383838', 'font-size': '36px' },
        '.accentfix': { 'font-size' :'1px', 'display' : 'block', 'leading' : 5 }
      }
    ,modifyContentString: function(content) { 
        if(content.match(/[Ã„Ã–Ãœf]/)) // big letters fix -- 2DO: only when first line contains these letters
            return '<p class="accentfix"> </p>'+content;
        return content;
    }
    ,onReplacement: function(item){
        document.getElementById(item.id).parentNode.nextSibling.className="accentfix";
        if(Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 7) {
            document.getElementById(item.id).parentNode.style.zoom=1;
        }
    }
});

sIFR.replace(fontOfficinaSansStd, {
    selector: '.layoutColumns h2'
    ,wmode: 'transparent'
    ,css: [
        '.sIFR-root { color: #83929E; font-size: 18px; text-transform: uppercase; }'
      ]
   ,offsetTop: 4
   ,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;
        }
    }
});

sIFR.replace(fontOfficinaSansStd, {
    selector: '.layoutColumns h4'
    ,wmode: 'transparent'
    ,css: [
        '.sIFR-root { color: #83929E; font-size: 18px; text-transform: uppercase; }'
      ]
   ,offsetTop: 4
   ,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;
        }
    }
});


sIFR.replace(fontOfficinaSansStd, {
    selector: '.layouPageNavi .navigationMain ul.nl li'
    ,wmode: 'transparent'
    ,css: [
        '.sIFR-root { color: #383838; font-size: 18px; text-transform: uppercase; text-decoration: none; }',
        '.active { color: #0065B4; font-size: 18px; text-transform: uppercase;  text-decoration: none; }',
        '.inactive { color: #383838; font-size: 18px; text-transform: uppercase;  text-decoration: none; }',
        'a:hover { color: #0065B4; font-size: 18px; text-transform: uppercase;  text-decoration: none; }'
      ]
   ,offsetTop: 4
   ,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;
        }
    }
});

sIFR.replace(fontOfficinaSansStd, {
    selector: '.layouPageNavi .navigationSub ul.nl li'
    ,wmode: 'transparent'
    ,css: [
       '.sIFR-root { color: #a7adb3; font-size: 15px; text-transform: uppercase; text-decoration: none; }',
       '.active { color: #0065B4; font-size: 15px; text-transform: uppercase;  text-decoration: none; }',
       '.inactive { color: #a7adb3; font-size: 15px; text-transform: uppercase;  text-decoration: none; }',
       'a:hover { color: #0065B4; font-size: 15px; text-transform: uppercase;  text-decoration: none; }'
   ]
   ,offsetTop: 4
   ,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;
        }
    }
});

sIFR.activate(fontOfficinaSansStd);/**
 * @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 (!Base.get("Camao_Content").isElementInInvisibleContainer(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 (!Base.get("Camao_Content").isElementInInvisibleContainer(input)){
          if (input.name && without.indexOf($F(input)) == -1) {
              result[input.name] = $F(input);
          }
        }
    });

    $(formular).select("textarea").each(function(textarea){
        if (!Base.get("Camao_Content").isElementInInvisibleContainer(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;
    }

}


function htmlspecialchars (mString, quote_style, charset, double_encode) {
    if (mString == null) return "";
    var optTemp = 0,
        i = 0,
        noquotes = false;
    if (typeof quote_style === 'undefined' || quote_style === null) {
        quote_style = 2;
    }
//    string = string.toString();
    if (double_encode !== false) { // Put this first to avoid double-encoding
        mString = mString.replace(/&/g, '&amp;');
    }
    mString = mString.replace(/</g, '&lt;').replace(/>/g, '&gt;');

    var OPTS = {
        'ENT_NOQUOTES': 0,
        'ENT_HTML_QUOTE_SINGLE': 1,
        'ENT_HTML_QUOTE_DOUBLE': 2,
        'ENT_COMPAT': 2,
        'ENT_QUOTES': 3,
        'ENT_IGNORE': 4
    };
    if (quote_style === 0) {
        noquotes = true;
    }
    if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
        quote_style = [].concat(quote_style);
        for (i = 0; i < quote_style.length; i++) {
            // Resolve string input to bitwise e.g. 'ENT_IGNORE' becomes 4
            if (OPTS[quote_style[i]] === 0) {
                noquotes = true;
            }
            else if (OPTS[quote_style[i]]) {
                optTemp = optTemp | OPTS[quote_style[i]];
            }
        }
        quote_style = optTemp;
    }
    if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
        mString = mString.replace(/'/g, '&#039;');
    }
    if (!noquotes) {
        mString = mString.replace(/"/g, '&quot;');
    }

    return mString;
}

function htmlspecialchars_decode (string, quote_style) {

    var optTemp = 0,
        i = 0,
        noquotes = false;
    if (typeof quote_style === 'undefined') {
        quote_style = 2;
    }
    string = string.toString().replace(/&lt;/g, '<').replace(/&gt;/g, '>');
    var OPTS = {
        'ENT_NOQUOTES': 0,
        'ENT_HTML_QUOTE_SINGLE': 1,
        'ENT_HTML_QUOTE_DOUBLE': 2,
        'ENT_COMPAT': 2,
        'ENT_QUOTES': 3,
        'ENT_IGNORE': 4
    };
    if (quote_style === 0) {
        noquotes = true;
    }
    if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags
        quote_style = [].concat(quote_style);
        for (i = 0; i < quote_style.length; i++) {
            // Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
            if (OPTS[quote_style[i]] === 0) {
                noquotes = true;
            } else if (OPTS[quote_style[i]]) {
                optTemp = optTemp | OPTS[quote_style[i]];
            }
        }
        quote_style = optTemp;
    }
    if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
        string = string.replace(/&#0*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
        // string = string.replace(/&apos;|&#x0*27;/g, "'"); // This would also be useful here, but not a part of PHP
    }
    if (!noquotes) {
        string = string.replace(/&quot;/g, '"');
    }
    // Put this in last place to avoid escape being double-decoded
    string = string.replace(/&amp;/g, '&');

    return string;
}
/**
 * @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){
                console.log(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 (used_value === null) used_value = "";

            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 = $$("[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()
            }

            // FF Fix
            if(window.innerHeight && window.innerHeight>usedHeight)
                usedHeight=window.innerHeight;

            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" || infos.element_shape.getStyle("backgroundColor")==""){
                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);
        Event.observe(window, '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()
            }

            if(window.innerHeight && window.innerHeight>usedHeight)
                usedHeight=window.innerHeight;

            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(window, "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.throwException(EXCEPTION_NOTIFICATION, "Flash Upload initiated ");

    }

    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 if (this.infos.button_placeholder_id == "camaoUploadField_element_upload_pdf"){
                    $(this.infos.container).select('input[name="asset_id"]')[0].value = response[0];
                    $($(this.infos.container).select('.camaoUploadField_element_upload_pdf_delete')[0]).show();
                }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 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");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];

                    modul.data.content[i] = htmlspecialchars(modul.data.content[i], 'ENT_QUOTES');

                    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");/*	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}}}}();/* Pop-Up Calendar Built from Scratch by Marc Grabanski */
/* ported to prototype dependency and drastically reduced the number of observers */
/* current version unmaintained; porting by Jiju Thomas Mathew; http://www.saturn.in */

popUpCal = Class.create();

popUpCal.prototype = {
    selectedMonth: new Date().getMonth(), // 0-11
    selectedYear: new Date().getFullYear(), // 4-digit year
    selectedDay: new Date().getDate(),
    calendarId: 'calendarDiv',
    inputClass: 'datePick',
    minDate: null,
    maxDate: null,
    initialize: function () {
    },

	init: function () {
        var x = $$('.'+this.inputClass);
        this.minDate = new Date();
        this.maxDate = new Date(this.minDate.getTime() + (364 * 24 * 60 * 60 * 1000));
        if(calendarConfig !== undefined){
            if(calendarConfig.inputClass !== undefined)
                this.inputClass = calendarConfig.inputClass;
            if(calendarConfig.minDate !== undefined)
                this.minDate = calendarConfig.minDate;
            if(calendarConfig.maxDate !== undefined)
                this.maxDate = calendarConfig.maxDate;
        }
        this.inputObj = null;
        if(!$(this.calendarId)){
            var p = '';
            if(Prototype.Browser.IE){
               p = '<iframe id="' + this.calendarId + 'Frame" src="about:blank" frameborder="0" style="display:none"></iframe>';
            }
            p += '<div id="' + this.calendarId + '" class="isCalendarArea"></div>';
            new Insertion.Bottom($('datepickerHolder'), p);
        }
        
        // set the calendar position based on the input position
        for (var i=0; i<x.length; i++) {
           x[i].addClassName('isCalendarArea');
           Event.observe(x[i], 'focus', this.popupCalendar.bindAsEventListener(this));
        }
        Event.observe(document, 'click', this.calendarClose.bindAsEventListener(this));
        Event.observe(this.calendarId, 'click', this.calendarClicked.bindAsEventListener(this));
    },
    calendarClicked: function (evt){
        cElem = Event.element(evt);
        var evDone = false;
        if(cElem.hasClassName('monthNav')){
            if(cElem.id.toString() == 'prevMonth')
                this.eventPrevMonth();
            else
                this.eventNextMonth();
        }else if(cElem.hasClassName('sD')){
            this.selectedDay = cElem.innerHTML;
            this.inputObj.value = formatDate(this.selectedDay, this.selectedMonth, this.selectedYear);		
            this.closeCalendar();
        }
        Event.stop(evt);
    },
    calendarClose: function (evt){
        cElem = Event.element(evt);
        if(cElem.hasClassName('isCalendarArea') == false){
            this.closeCalendar();
        }else
            this.calendarClicked(evt);
    },
    closeCalendar: function(){
        $(this.calendarId).hide();
        if(Prototype.Browser.IE){
            $(this.calendarId + 'Frame').hide();
        }    
    },
    popupCalendar: function (evt) {
        this.inputObj = Event.element(evt);
        this.selectedMonth = new Date().getMonth();
        this.setPos(Event.element(evt)); 
        this.drawCalendar();
    },
    setPos: function(inputObj){
        $(this.calendarId).absolutize();
        var tPos = $(inputObj).cumulativeOffset();
        var tHeight = $(inputObj).getHeight();
        $(this.calendarId).setStyle({top: 'auto'});
        $(this.calendarId).show();
        if(Prototype.Browser.IE){
           $(this.calendarId).setStyle({zIndex: 2000});
           $(this.calendarId + 'Frame').absolutize();
           $(this.calendarId + 'Frame').setStyle({top: 'auto'});
           $(this.calendarId + 'Frame').show();
        }   
    },
    drawCalendar: function () {
		
		var html = '';
		html += '<table id="calendar" cellpadding="0" cellspacing="0"><tr>';
		html += '<th colspan="7" class="calendarHeader">'
		html += '<table cellpadding="0" cellspacing="0" width="100%"><tr><td><a id="prevMonth" class="monthNav">&laquo;</a></td>';
		html += '<td><a class="calTitle">' +getMonthName(this.selectedMonth)+' '+this.selectedYear+ '</a></td>';
		html += '<td><a id="nextMonth" class="monthNav">&raquo;</a></td></tr></table></th>';
		html += '</tr><tr class="weekDaysTitleRow">';
        var weekDays = new Array('S','M','T','W','T','F','S');
        for (var j=0; j<weekDays.length; j++) {
			html += '<td>'+weekDays[j]+'</td>';
        }
		
        var daysInMonth = getDaysInMonth(this.selectedYear, this.selectedMonth);
        var startDay = getFirstDayofMonth(this.selectedYear, this.selectedMonth);
        var numRows = 0;
        var printDate = 1;
        if (startDay != 7) {
            numRows = Math.ceil(((startDay+1)+(daysInMonth))/7); // calculate the number of rows to generate
        }
		
        // calculate number of days before calendar starts
        if (startDay != 7) {
            var noPrintDays = startDay + 1; 
        } else {
            var noPrintDays = 0; // if sunday print right away	
        }
		var today = new Date().getDate();
		var thisMonth = new Date().getMonth();
		var thisYear = new Date().getFullYear();
        // create calendar rows
        for (var e=0; e<numRows; e++) {
			html += '<tr class="weekDaysRow">';
            // create calendar days
            for (var f=0; f<7; f++) {
				if ( (printDate == today) 
					 && (this.selectedYear == thisYear) 
					 && (this.selectedMonth == thisMonth) 
					 && (noPrintDays == 0)) {
					html += '<td id="today" class="weekDaysCell">';
				} else {
                	html += '<td class="weekDaysCell">';
				}
                if (noPrintDays == 0) {
					if (printDate <= daysInMonth) {
						html += this.checkDateClickable(printDate);
					}
                    printDate++;
                }
                html += '</td>';
                if(noPrintDays > 0) noPrintDays--;
            }
            html += '</tr>';
        }
		html += '</table>';
        
        // add calendar to element to calendar Div
        $(this.calendarId).update(html);
        
        $(this.calendarId).setStyle({width: 'auto', height: 'auto', display: 'block'});
        var dimensions = $(this.calendarId).getDimensions();

        if(Prototype.Browser.IE){
            $(this.calendarId + 'Frame').setStyle({width: dimensions.width + 'px', height: dimensions.height + 'px', display: 'block'});
        }    
       
    }, 
    checkDateClickable: function(pDate){
       var rv = '<a class="sD">'+pDate+'</a>';
       if (((pDate < this.minDate.getDate()) 
            && (this.selectedMonth == this.minDate.getMonth())
            && (this.selectedYear == this.minDate.getFullYear())) ||
            ((pDate > this.maxDate.getDate()) 
            && (this.selectedMonth == this.maxDate.getMonth())
            && (this.selectedYear == this.maxDate.getFullYear()))) rv = pDate;
      return rv;
    },
    eventPrevMonth: function () {
        if(this.testRange('p')){
            this.selectedMonth--;
            if (this.selectedMonth < 0) {
                this.selectedMonth = 11;
                this.selectedYear--;
            }
            this.drawCalendar(); 
         }    
        },
    eventNextMonth: function () {
        if(this.testRange('n')){
            this.selectedMonth++;
            if (this.selectedMonth > 11) {
                this.selectedMonth = 0;
                this.selectedYear++;
            }
            this.drawCalendar(); 
         }   
        },
    testRange: function (dir){
     switch(dir){
        case 'p':
            tMonth = this.minDate.getMonth();
            tYear = this.minDate.getFullYear();
        break;
        case 'n':    
            tMonth = this.maxDate.getMonth();
            tYear = this.maxDate.getFullYear();
        break;       
     }   
      if(tMonth == this.selectedMonth && tYear == this.selectedYear) 
            return false;
      else
            return true;          
    }       
}

var calendarConfig = {
    inputClass: 'datePick',
    minDate: new Date()
}

calendarConfig.maxDate = new Date(calendarConfig.minDate.getTime() + (364 * 24 * 60 * 60 * 1000));


var myPopupHandler = {};
function myPageInit(){
   myPopupHandler = new popUpCal();
}

// Add calendar event that has wide browser support
//Event.observe(window, 'load', myPageInit);

/* Functions Dealing with Dates */

function formatDate(Day, Month, Year) {
    Month++; // adjust javascript month
    if (Month <10) Month = '0'+Month; // add a zero if less than 10
    if (Day < 10) Day = '0'+Day; // add a zero if less than 10
    var dateString = Day+'.'+Month+'.'+Year;
    return dateString;
}

function getMonthName(month) {
    var monthNames = new Array('January','February','March','April','May','June','July','August','September','October','November','December');
    return monthNames[month];
}

function getDayName(day) {
    var dayNames = new Array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday')
    return dayNames[day];
}

function getDaysInMonth(year, month) {
    return 32 - new Date(year, month, 32).getDate();
}

function getFirstDayofMonth(year, month) {
    var day;
    day = new Date(year, month, 0).getDay();
    return day;
}


    /* Accordeon */
    document.observe('cms:loaded', function(){
        // init accordion
        var startHash = document.location.hash.replace(/#/,'');
        var bHasStartItem = (startHash!="" && $(startHash));
        $$('div.elementAccordion').each(function(item,index){
            var bShow = (bHasStartItem && startHash==$(item).down("div.accordionHeader a").id) ||
                        (!bHasStartItem && index==0);
            if(!bShow) {
                $(item).down("div.accordionHeader a").addClassName('closed');
                $(item).down("div.accordionContent").hide();
            } else {
                $(item).down("div.accordionHeader a").addClassName('opend');
            }
        })
        
    });
    function infoboxclick(elem) {
        if ($(elem).hasClassName("closed")) {
            // clicked slide is closed, so open it and close all others currently open
            $$('div.elementAccordion div.accordionHeader a.opend').each(function(item) {
                new Effect.BlindUp(item.up("div.elementAccordion").down("div.accordionContent"));
                item.removeClassName('opend');
                item.addClassName('closed');
            });
            new Effect.BlindDown(elem.up("div.elementAccordion").down("div.accordionContent"));
            elem.removeClassName('closed');
            elem.addClassName('opend');
        } else {
            // clicked slide is open, so just close it
            new Effect.BlindUp(elem.up("div.elementAccordion").down("div.accordionContent"));
            elem.removeClassName('opend');
            elem.addClassName('closed');
        }
    }
/**
 * @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{
        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' : '/compressed' + ROOT_JS + include_file + ".js", 'type' : 'text/javascript' });
    $$("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);

*/


